Monday, April 18, 2016

Nordic nrf8001 library for ST STM32F4xx devices

Recently, I began experimenting with the Nordic nrf8001 BLE chip.  The device contains a BLE stack and manages device state through a command and control interface.  This interface uses native SPI for data transfer and two control signals, a request (“RQN”) sent from the application MCU to the nrf8001, and a ready (“RDY”) signal sent from nrf8001 to MCU, both of which are used to indicate if the respective device is able to communicate.

I was interested in using the nrf8001 as a BLE relay with a low power ST STM32L1xx MCU.  I’m still experimenting with that chip but first I ported it to the more ubiquitous ST STM32F407 discovery kit as a first step.  The library, examples, and makefile for building with GNU gcc arm and flashing with ST link can be found here.

Nordic Semi distributes an Arduino/AVR-based library on this GitHub repository.  The authors also include some general porting instructions in the documentation/libraries/BLE/nRF8001-Porting-ACI-Library.md.  This blog post explains the porting procedure to get the library running with example code on the ST STM32F4 discovery kit.


Picture 1: stm32f4 discovery kit, nrf8001 breakout, and logic analyzer


Picture 2: Screen capture of logic analyzer capture showing stm32f4 communicating with the nrf8001 chip.  You can download a sample capture from reset to idle here.

The process for porting can be summarized as follows:

1. Cloning ble-sdk-arduino repo
2. Setup a working ST STM32f4 template build, and flashing directory with USART and SPI support
3. Revise file extension from .cpp to .c
4. Make file modifications around directory structure
5. IO arduino wrapper library
6. Patch aci_setup function in src/BLE/ with a compile time check if it is STM32F4XX directive
7. SPI init routine and GPIO (REQN and RDYN)
8. Adjust structure field type widths (uint8_t -> uint16_t) to accommodate ST standard peripheral driver pin enumerations
9. Simple example and validate use on bench

These steps are described in more detail below:

1. Start by checking out the master branch of 'ble-sdk-arduino':

git clone https://github.com/NordicSemiconductor/ble-sdk-arduino.git

2. Check out the this template directory for STM32F4 development.  Check out the 'stm32f4_start_template' as a starting point, the 'master' branch has all the changes discussed implemented.  We are going to be building using the GCC ARM Embedded toolchain from Launchpad.  This template has SPI and USART support already added.

git clone https://github.com/shraken/nrf8001-stm32f4.git
cd nrf8001-stm32f4
git checkout -b stm32f4_start_template

3. Copy the source and header files from 'ble-sdk-arduino' to our stm32f4 template directory and rename all .cpp source files to .c file extension.

mkdir src/BLE
mkdir inc/BLE
cp ../ble-sdk-arduino/libraries/BLE/*.h inc/BLE
cp ../ble-sdk-arduino/libraries/BLE/*.cpp src/BLE
find ./BLE/src/ -depth -name "*.cpp" -exec sh -c 'mv "$1" "${1%.cpp}.c"' _ {} \;

4. Add the Nordic nRF8001 library source files to the 'SRC' variable in the Makefile.  Ensure that the source files are added as shown below.

SRC = ./src/main.c \
 ./src/millis.c \
 ./src/usart.c \
 ./src/spi.c \
 ./src/debug.c \
 ./src/stm32f4xx_it.c \
 ./src/system_stm32f4xx.c \
 ./src/BLE/acilib.c \
 ./src/BLE/aci_queue.c \
 ./src/BLE/aci_setup.c \
 ./src/BLE/hal_aci_tl.c \
 ./src/BLE/lib_aci.c \

You must also set the 'LIBPATH' variable to the path of the ST Standard Peripheral Driver library.  The README.md provides a GitHub repository link that you can clone for this library.

5. The 'ble-sdk-arduino' library makes uses of Arduino IO pin routines, namely (digitalRead, digitalWrite, and pinMode).  Dummy shell functions are defined for these routines in the file 'io_support.c' that call the ST ARM standard peripheral driver functions.

Be sure to add the 'io_support.c' file to the SRC variable previously described above in the Makefile.

6. Patch the 'aci_setup_fill' function so that the setup message generated from nRFgo studio and stored in 'services.h' is gets copied into a data buffer from code space.  The first part of 'aci_setup_fill' function should now resemble,

#if defined (__AVR__)
    //For Arduino copy the setup ACI message from Flash to RAM.
    memcpy_P(&msg_to_send, &(aci_stat->aci_setup_info.setup_msgs[*num_cmd_offset]), pgm_read_byte_near(&(aci_stat->aci_setup_info.setup_msgs[*num_cmd_offset].buffer[0]))+2); 
#elif defined(__PIC32MX__)
    //In ChipKit we store the setup messages in RAM
    //Add 2 bytes to the length byte for status byte, length for the total number of bytes
    memcpy(&msg_to_send, &(aci_stat->aci_setup_info.setup_msgs[*num_cmd_offset]), (aci_stat->aci_setup_info.setup_msgs[*num_cmd_offset].buffer[0]+2)); 
#elif defined(STM32F4XX)
    memcpy(&msg_to_send, &(aci_stat->aci_setup_info.setup_msgs[*num_cmd_offset]), (aci_stat->aci_setup_info.setup_msgs[*num_cmd_offset].buffer[0]+2)); 
#endif

The STM32F4XX directive is provided in the Makefile DEFS.

7. The SPI and GPIO configuration must be initialized.  Replace the following Arduino specific code block with a single call to 'init_spi1' function.

SPI.begin();
//Board dependent defines
#if defined (__AVR__)
    //For Arduino use the LSB first
    SPI.setBitOrder(LSBFIRST);
#elif defined(__PIC32MX__)
    //For ChipKit use MSBFIRST and REVERSE the bits on the SPI as LSBFIRST is not supported
  SPI.setBitOrder(MSBFIRST);
#endif
SPI.setClockDivider(a_pins->spi_clock_divider);
SPI.setDataMode(SPI_MODE0);

with the patch below, mode0/0 is assumed by 'init_spi1'.

// bring up the GPIO pins and SPI HW interface
if (init_spi1(NRF8001_SPI, SPI_BaudRatePrescaler_64) != E_SUCCESS) {
    log_err("GPIO and SPI HW bringup failed");
}

The pin assignment for REQN, RDYN, RESET, and SPI (MOSI, MISO, SCLK) are stored in the 'aci_pins_t' structure within the 'aci_state_t' global variable.  The values are stored as uint8_t so just the pin number is represented and the port must be addressed manually in the project.

The port and pins are defined in spi.h

#define NRF8001_SPI  SPI1

#define SPI_COMMON_PORT GPIOA
#define MOSI_PIN        GPIO_Pin_7
#define MISO_PIN        GPIO_Pin_6
#define SCLK_PIN        GPIO_Pin_5

#define RESET_GPIO_PORT GPIOA
#define RESET_PIN  GPIO_Pin_0

#define RDYN_GPIO_PORT  GPIOA
#define RDYN_PIN  GPIO_Pin_4

#define REQN_GPIO_PORT GPIOB
#define REQN_PIN   GPIO_Pin_0

and they are set in the application's setup function, for instance

aci_state.aci_pins.reqn_pin   = REQN_PIN;
aci_state.aci_pins.rdyn_pin   = RDYN_PIN;
aci_state.aci_pins.mosi_pin   = MOSI_PIN;
aci_state.aci_pins.miso_pin   = MISO_PIN;
aci_state.aci_pins.sck_pin    = SCLK_PIN;
aci_state.aci_pins.reset_pin  = RESET_PIN;

The pins are actually configured in the same 'hal_aci_tl_init' function where the 'init_spi1' function is called.

8. The uint8_t structure width we mentioned previously for pin assignments only supports 8 unique bitmasks.  The data width for each pin value is expanded to a uint16_t type to accommodate the 16 possible pins for each port.  In the file 'hal_aci_tl.h',

uint16_t reqn_pin;    //Required
uint16_t rdyn_pin;    //Required
uint16_t mosi_pin;    //Required
uint16_t miso_pin;    //Required
uint16_t sck_pin;    //Required

9. A simple example is included in the 'master' branch under examples/ which advertises and sends a notification every 1 second.  The notification packet payload contains a tick count of the systick timer.

Connect the STM32F4-discovery or STM32-nucleo-F401 to your computer using the onboard USB and ST-link debugger.  Follow the directions in the README.md for building and flashing.

After flashing, launch your favorite Bluetooth central explorer -- I'm using Nordic Master Control Panel.  If everything is connected correctly, you should see a device named 'Hello'.

Connect a serial FTDI adapter (3.3V) with TX/RX to PA2 (TX) and PA3 (RX), i'm using a sparkfun model in the picture above.  Reset the MCU and verify the debug messages are printed to the console.  The characters typed on the console are buffered and sent in a 20 byte notification packet to the central/host after 20 bytes have been entered.

Picture 3 Nordic Master Control Panel showing 'Hello' BLE device name example

Friday, October 23, 2015

Particle/Spark WiFi photon intro and first project

I recently picked up a Particle 'Photon' WiFi Device.  It's a pretty cool platform and idea.  I missed out on the Kickstarter but they are shipping the development kits and modules of the P0.

The P0 SOM is based around the BCM43362 WiFi chipset and uses a STM32F205 as the brain.  The STM32F205 firmware is open source and on github.

I set the device up through my iPhone.  The setup process with Tinker app was seamless in setup.  I ran into issue when I tried flashing through the build.particle.io with the LED blink example the device would go into an infinite flash cycle (flashing magenta LED).  I would then have to soft-reset the device to push it back into WiFi Host-mode so I could pair it again.

I also ran into some issues with my unit out-of-the-box.  Generally, the serial operation was flaky with frequent 'Serial Error' messages.

I ended up having to flash to the latest firmware (0.4.5) using dfu-util.  This resolved the issue with poor serial operation and inability to flash from the cloud-based IDE.

The code syntax interface is 'Wiring' similar syntax so easy to pickup if you've used the Arduino.  Now onto the fun stuff, the demo....

I threw together a project when I got back this evening.  My girlfriend has been leaving her hair straightener on in the mornings and will text me to check and turn it off before I go to work.  So, I thought I'd whip up an AC relay example to showcase how rapid and easy the development is.  The whole thing took me about 1 hour to put together.  I could probably condense this all into a single PCB and replace the mini-USB breakout used for power by an integrated AC-DC +5V module recommended on this electronics.stackexchange post.  A block diagram of the setup is shown below.  I used the Tinker iPhone app to control the device.  The D7 GPIO pin on the Photon is connected to the Powertail Switch 2 AC relay + input.


Required Parts:
1. Sparkfun mini USB breakout. - $2
2. Apple AC-to-DC 5V USB wall plug. - $10
3. Spark Photon Eval Board. - $20
4. Powerswitch Tail 2. - $26.00

Some pictures of the prototype:







Sunday, December 1, 2013

Real Time Clocks and Flash Memory

Hello again, this my second blog post.  My goal originally was to update weekly but with the recent holiday and other life events I've been forced to reduce the frequency of my updates.  I'm going to hold myself to the bi-weekly update though so all is well.

I wanted to give a short update about two pieces of code I worked on in the past week.  The background is that I'm working on a GPS Datalogging application that reports coordinates wirelessly to a server for logging where they can be viewed by at a later time.  The project is pretty far a long, I'll provide a full description at a later date.  My goal is to make the project an open hardware and open source platform so that hobbyists can build up their own board and modify the software as they see fit.  I intend to offer assembled boards and kits for a recovery fee + labor.  Below are some pictures of the board as I populate it.  The board is pretty small (2" x 2") and is meant to fit into a Hammond plastic enclosure.

Front-View

Back-View


Atmel AT45DB161D
So back to the technical details.  I'm using an Atmel AT45DB161D for data storage.  The AT45DB161D is a page-based 16 Megabit (2 Megabyte) flash memory storage that is controlled over an SPI interface.  I found some good AVR C code over at sparkfun that someone wrote for the ulogger application, documented here:
https://www.sparkfun.com/products/9228

The author bit banged the solution and I decided to port this over to the PSoC.  The interface is simple enough and is documented in the provided datasheet here:
https://www.sparkfun.com/datasheets/IC/AT45DB161D.pdf

I spent a couple hours trying to use the SPI component on the PSoC platform but gave up in the end.  The clock select line must be held low for the duration of the read-out but this conflicts with the Read function of the SPI block in PSoC.  I tried to manually control the Clock Select line but this caused additional timing and synchronization errors.  The bit bang solution is good enough for my needs.  I don't like tying down the main thread for bitbanging but none of this code blocks so it's of minimal concern.  I uploaded the solution on my website and it can be found here:
http://www.ece.ucdavis.edu/~shraken/files/code/psoc/AT45DB161D_example_PSOC.zip

Maxim DS1672
I'm using a Dallas Semi/Maxim DS1672 as a RTC.   This is an example that shows how to interface with the Maxim DS1672 Real Time Clock (RTC) component.  The DS1672 is a 32-bit RTC counter that has a backup power option.  The backup power can be supplied from a small coin cell battery or a super capacitor where the DS1672 will trickle charge.  The RTC component in PSoC does not include any such option and is therefore of limited use for when the PSoC is put into off mode.  The DS1672 is controlled over an I2C bus and the code is implemented in PSoC Creator for a PSoC3 test device.

The I2C code is clean and shows how the counter is initialized with a count of zero and starts counting.  There is a delay loop in the example file of 2.5 seconds to show that the counter is increment.  A 32-kHz watch crystal is placed on the breadboard a long with the compensation capacitors of magnitude 12.5 pF.  The SDA and SCL line requires two 10k pull up resistors as shown below.

I used the DS1672 in particuliar because it's powered by 3.3 Volts.  I also needed the backup battery option with the coin cell because the data logger will often times be off for long duration to conserve and minimize current consumption.

For those who are curious, the SOIC to DIP adapter is a SchmartBoard variety and can be purchased from here:
http://www.mouser.com/ProductDetail/SchmartBoard/204-0004-01/?qs=sGAEpiMZZMtgbBHFKsFQgu%2fEm7E3KH7v%2fkdDRzCx4mI%3d
DS1672 SOIC breadboard on a PSoC dev kit.

I upload the project to my website and it can be found here:
http://www.ece.ucdavis.edu/~shraken/files/code/psoc/DS1672_example_PSOC.zip

Monday, November 18, 2013

This is my first blog post for 'Unconventional Wisdom'.  I'm going to treat this blogger as a diary of sorts and something to express my ideas quickly down on paper.  The writing will feature updates on my electronic and software hobby projects.  I also intend to sketch out writings which will be published in a more formal manner at another time.  I'm not really sure why I chose to name the blog 'Unconventional Wisdom' ... other than the fact that I enjoy advice that goes against the grain and isn't steeped in lore.  I hate operating on anecdotal evidence, it's lazy, and I respect reason.

So what's up right now?

1. Graduate Thesis
Well, I'm finishing my graduate thesis work right now for my MSEE at University of California, Davis.  I'm in the process of characterizing the noise of my photodiode front-end.  My goal is to make a comparisson between a conventional transimpedance amplifier and integrator for making light measurements.  I've had good progress so far in characterizing thermal, shot, and amplifier noise of my OPA124 operational amplifier.  I've made measurements on a HP 3561 Dynamic Signal Analyzer.  The spectrum analyzer provides a magnitude frequency response between 0.000125 Hz and 100 kHz.  It's a great instrument, the datasheet can be found here:
http://www.accusrc.com/objects/catalog/product/extras/5350_3561a.pdf

I'm capturing the magnitude response over a USB-GPIB interface to my laptop.  An example of my shot noise measurements are shown below.  The photocurrent results in a shot noise current that increases the noise floor.  I measured a DC photocurrent of 71.3 nA, 729.7 nA, and 2.7 uA and compared theory to measured results.  The results matched up well and are as follows:



Shot Theory
 -109.4209

 -103.1324

  -97.9616

Shot Measured
 -109.5040

 -103.2240

  -98.2600

2. Personal Projects
I ordered some cool stuff from sparkfun this past week.  One of the items I played with is the Sparkfun LiPo Fuel Gauge.
https://www.sparkfun.com/products/10617

It's a module that has a Maxim MAX1704 chip for monitoring the charge of lithium polymer rechargeable batteries.

One of the Sparkfun engineers provided an Arduino sketch using I2C Wire to communicate with the chip and extract battery percentage and voltage.  I ported this code to the PSoC Creator environment on Sunday and tested using the CY8CKIT-001 with a PSoC3 module.  A picture of the setup with the output on a LCD screen is shown below:
I uploaded the example on my website and the file can be downloaded from this link.