Using the nRF51822’s Application Timer

Tags

, , , ,

There will be many times when I will want to fire off timer events.  I’m used to doing this at a level that is closer to “get back to me after one second five times.”  Where the one second and five times can easily be changed.  It’s so simple…I don’t need to understand anything about how the software gets this magic to happen.

I found getting a timer to fire off every second for five times required me to learn more about how the nRF51822’s clock + software drivers work.

The Goal

The goal of this post is to understand how to set up a timed event using the nRF51 DK and SDK.

Thanks To Those That Went Before

I like to take a moment to share my gratitude for the people who have provided me with the knowledge I needed to figure out what I wrote about in a post.

  • Chris Gammell – continues to be an excellent mentor.  I continue to take his Contextual Electronics course.  I am extremely grateful for the skills I have learned since starting his course in January of 2014.
  • The Nordic developers who tirelessly answer our questions on their DevZone.  It is VERY difficult to provide the level of respectful and useful support that comes from this team.  THANK YOU.

Open Source

  • The nRF51 Eclipse project (for Max OS X) for using the app_timer APIs is located at this GitHub repository.  Note: because it took me a bit of head knocking to get the Eclipse + nRF51 environment working on my Mac I wrote a post on how to get this environment up and running.

Choosing an API for Timed Events

Goldilocks had three bears.  The nRF51 appears to have three timer APIs that can be used to implement timed events.  Which approach should I take?

  • the APIs of app_timer.c – these are used in the BLE example apps.  After trying all methods, I ended up using the app_timer APIs.  The app_timer apis sit on top of the nRF51822’s RTC (Real Time Counter) APIs, providing a queue so that multiple timers can be set up on one of the RTC instances.  
  • the RTC APIs.  As noted in the nRF51 Series Reference ManualThe [Real Time Counter] is a 24 bit low-frequency clock with frequency prescaling and tick, compare, and overflow events.  There are two RTC instances available.
  • the hardware timer APIs.

The Hardware Timer

The timer example in the nRF51 SDK (timer_example) illustrates using a hardware timer to call an ISR every 1/2 a second.  This made me think the hardware timer was the way to go if I wanted a timer to fire off every second.  Using the hardware timer is simple and perhaps makes sense.  As noted to me by a Nordic developer:  If you are not concerned about power usage (maybe because you are experimenting or making a prototype) then you can just use TIMER1 and TIMER2 and do not get concerned about the HFCLK.

….BUT WAIT…

The HFCLK

The hardware timers use a high frequency clock source – either an external crystal or internal oscillator (see the Reference Manual for more detail).  While there are three timers – TIMER0, TIMER1, and TIMER2, buried within the reference manual for the BLE stack, TIMER0 is blocked for it’s use.

Power Hog

What’s more,  as noted in this Nordic DevZone postIf you use TIMER1 or TIMER2 then those will keep HFCLK enabled in sleep modes making your power go up…Any of the modules in the system that use HFCLK when started and not stopped before going to sleep will keep the HFCLK enabled. For example, if you enable and start Timer1 and then call sd_app_evt_wait(), then Timer1 will be on and it will keep the HFCLK on.

Given my newbie knowledge approach to these timers, there is a good chance I am missing other reasons why using the hardware timers is not the way to go for the scenario I seek.

The RTC

On to the Real Time Counter.  As noted in the nRF52 SDK documentation, “The RTC will run off the LFCLK.”  There are two RTC instances, RTC0 and RTC1.  The app_timer APIs use RTC1.  The BLE stack (called “the SoftDevice” – link to the S110 documentation) uses RTC0.  So if you plan to use the SoftDevice, the only available RTC instance is RTC1.  Or – said another way – if you use the Application Timer apis and the BLE SoftDevice stack there aren’t any RTC instances available.

The LFCLK

Similar to the HFCLK, the Low Frequency Clock source can be set to use either an external crystal or internal oscillator.  Even though I have been warned by Elicia in Making Embedded Systems : “Many small microcontrollers use an internal RC oscillator as their clock source. Although these make life easier for the hardware designer, their accuracy leaves a lot to be desired. Considerable drift can accumulate over time, and this can lead to errors in communication and some real-time applications.”  I decided I’d go with what is included until I can justify the addition of an external oscillator.  The Ladybug Blue timer scenarios use timed events to give spacing between ADC readings and not for maintaining a time or synchronization.

Power Saver

 this Nordic DevZone post notes: you can use RTC in system on low power mode. In this mode it’ll consume around 3uA = ION + IRTC + IX32k.

Another Nordic DevZone post notes a bit more detail: In system off mode, all clock sources and peripherals on the chip are turned off, and the only wakeup source is reset and pin change (for those that have this enabled)..Depending on whether you use a 32 kHz crystal or the internal RC oscillator, this gives a total consumption of I_on + I _RTC + I_X32k, typical 2.3 + 0.2 + 0.4 = 2.9 µA or i_on + I_RTC + I_RC32k = 2.3 + 0.2 + 0.8 = 3.3 µA

Repeating Timed Events

I was testing the  RTC nRF51 SDK example. While tick events fired regularly, I could not get the compare events to repeatedly fire.  While I do point out this is a “RTFC” where C = Code moment, I asked why this was happening on the Nordic DevZone.  Stefan kindly answered (and provided code): The internal event handler of the rtc driver, i.e. the nrf_drv_rtc_int_handler, handles the RTC0 peripheral interupt and then calls the registered event handler of the application. If you look into the nrf_drv_rtc_int_handler, you see that interrupts for any COMPARE[x] events are disabled, while TICK and OVERFLOW interrupts are not disabled. If you avoid disabling the COMPARE[x] interrupts, then the COMPARE[0] interrupt will be recurrent after you clear the RTC0 counter. (see the post for the code)

The app_timer (Application Timer) APIs

Given what I have learned, the app_timer APIs are most suitable to meet my needs.  Because:

  • they are an abstraction above the RTC APIs – the Ladybug Blue timer events would be shielded from the uses of the hardware and RTC timers.
  • they are easy to use.
  • many of the BLE examples use the app_timer APIs.  The use case of these examples are similar to the Ladybug Blue scenarios.
If you look at the main.c after downloading the app_timerTest.zip file and unzipping, notice how the main() routine:
  • Initializes the low frequency clock and the app_timer queue:

static void timers_init(void)

{

  //initialize the low frequency cloc

  uint32_t err_code = nrf_drv_clock_init(NULL);

  APP_ERROR_CHECK(err_code);

  nrf_drv_clock_lfclk_request();

  // Initialize timer module.

  APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);

 

  // Create timers.

  err_code = app_timer_create(&m_timer_id,

      APP_TIMER_MODE_REPEATED,

      timeout_handler);

  APP_ERROR_

CHECK(err_code);

}

  • start the timer(s):

static void timers_start(void)

{

  uint32_t err_code;

  // Start application timers.

  err_code = app_timer_start(m_timer_id, TIMER_TICKS, NULL);

  APP_ERROR_CHECK(err_code);

}

  • handle the callback:

static void timeout_handler(void * p_context)

{

  //  UNUSED_PARAMETER(p_context);

  SEGGER_RTT_WriteString (0, “–> in timeout handler\n”);

 Simple.  It took me awhile to get this working since I kept forgetting to initialize the low frequency clock source.

The PRESCALER VALUE

One variable that needs to be set is the value of the nRF51’s PRESCALER register.

From wikipediaThe prescaler takes the basic timer clock frequency (which may be the CPU clock frequency or may be some higher or lower frequency) and divides it by some value before feeding it to the timer.  

Another way to say this – the PRESCALER lumps the ticks into a grouping so that timed events can happen at larger time periods before an overflow occurs.

The  nRF52 SDK documentation has a nice visualization for when the PRESCALER is set to 1:

PRESCALER

Tick events happen every time the clock ticks. Counter events happen after (tick*(prescalar+1)) ticks.  We’re interested in counter events.

The  nRF52 SDK documentation has the following table:

PRESCALERtable

From this Nordic DevZone post

  • I noted at the beginning of this post the RTC (and hence app_timer which sits on top of RTC1) uses a 24 bit counter.  Thus the maximum value that can be held is 0xFFFFFF.
  • The clock source is the low frequency RC oscillator which runs at 32,768 HZ.
  • When the PRESCALER is 0, the maximum time before an overflow occurs is 0XFFFFFF/32768 seconds = 512 seconds.
  • The PRESCALER is a 12 bit register so the prescalar value can go from 0 to 212-1 = 4095.  The longest amount of time that can pass before the RTC overflows is then 512 s *(4095+1) = 582.542 hours.
  • When the PRESCALER is 0, the counter and ticks are the same, so the counter resolution = 1/32768 = 30.5µs.
  • When the PRESCALER is 4095 the counter resolution = 1/(32768/4095) = 125ms.  (i.e.: as the wikipedia article noted, take the clock frequency and divide it by the PRESCALER to get the counter frequency.  1/f = time resolution).

Overflow

For averaging ADC sampling (of ~100 at a frequency of 5HZ), overflow won’t be a problem and a PRESCALER value of 0 can be used.

For background pH and EC measurement timed events, there will be overflow since I plan to sample continually.  Luckily for me, the app_timer APIs handle overflow for me!

Time Drift

This Nordic DevZone post discusses the low frequency clock accuracy.  “PPM” is thrown around a bit, but I was only familiar with this term as it related to chemistry.  For example, the amount of nutrients I might give a plant is usually given in either Siemens or Parts Per Million (PPM).  This document from the digikey.com site says: Long term stability is usually expressed in parts per million or ppm. A typical specification of 10 ppm means that over a 1 ms interval the clock period can change by 10 ns: Dt=1ms*(10/1,000,000) =10 ns.

In the DevZone post, Ole and Stefan tell us: The…RC oscillator…has an accuracy of 250 ppm when calibrated.  In a 1 second interval, the clock can drift 1 second *(250/1,000,000) = 250µs.  Ole and Stefan go on to point out:  The only thing you can choose through this enum for the RC is the calibration interval. As given in the nRF51822 PS, the accuracy is specified when the temperature is relatively stable, and it is calibrated every 4 seconds, so this is the calibration interval that should be used for most (all?) applications.  When the RC is calibrated, the 16 MHz clock must run while calibration is ongoing, which causes an increase in the average current consumption of about 6-7 µA with a 4 s interval…

I might not understand this correctly…my interpretation:  When the PRESCALER is 0, the time elapsed before overflow is 512s.  Given the 250µs drift/s, the clock will drift 512*.00025 = .128s.  When the PRESCALER is 4095, the time elapsed before overflow is 2097152s, the clock drift will be 2097152s*.00025 ~= 524s or ~8.73 minutes.

Until proven otherwise, I do not see clock accuracy as being important for pH and EC measurements since it is more important to have a length of time between measurements that ensures stable readings. 

……….

Whew.  That’s it for now.  Ooh…YIPPEE!! I got boards back from OSHPark….a bit of soldering and hopefully I’ll be able to run these tests on the Ladybug Blue Alpha 1 board….

 

Automating BoM Creation

Tags

, , ,

Now that I have a better idea on the BoM creation workflow I use, I decided to automate the BoM creation process.

The (Google Script – which is Java Script) code is extremely crude.  I see it as a working prototype. While there is much (much) room for improvement, the current effort provides a framework to the direction I am heading.

Open Source

Here is an example Google Spreadsheet – BoM Parts Example.  The script I used is pasted near the bottom of this post.

An Ask

  • I don’t program in Java Script.  Also, I have no formal training or work experience in programming.  I’d appreciate any advice on script improvements.
  • I would be very grateful for a Google Script/Javascript snippet that given a part number within a Google Spreadsheet cell, can go to the distributor’s web page and get the 1, 100, 1000 piece price.  Right now I record the link to the distributor’s page, which is usually a page on Digikey.com.  I then record the 1, 100, 1000 piece price.  I would like to improve the BoM workflow process by getting the price information from the distributor (Digikey, Mouser, etc.) at the time of BoM worksheet creation.  Because I am not familiar with the code necessary to Get the price information given a part number from a given (or more generically – across) distributor, I have a learning curve that would absorb many hours.  I would then evolve the snippet to work within the workflow and publish on GitHub.  I would make sure to give you credit within any documentation as well as within the GitHub repository.

The BoM Workflow 

Here is an image of my BoM workflow:

BoMCreationWorkflow

I use Kicad to design and layout a PCB.  The EeSchema tool is used to create the schematic.  EeSchema includes a BoM generation utility that creates a CSV file.  I import the CSV file into a Google Spreadsheet.  The Google Spreadsheet includes a script that parses through the BoM CSV and match the rows within the BoM CSV with parts in the Parts Inventory Worksheet.  The script goes on to create the Completed BoM worksheet with rows that look like:

Type Name MFG Part Quantity 1 Piece Price 1 Piece SKU 100 Piece Price 100 Piece SKU 1000 Piece Price 1000 Piece SKU  
CONNECTOR LB_COIN_CELL Link Technologies BAT-HLD-001 1 0.28 0.28 0.233 0.233 0.22875 0.22875 http://www.digikey.com/product-detail/en/BAT-HLD-001/BAT-HLD-001-ND/1577235
CAPACITOR 1u Taiyo Yuden TMK212BJ105KG-T 3 0.16 0.48 0.0523 0.1569 0.03025 0.09075 http://www.digikey.com/product-detail/en/TMK212BJ105KG-T/587-1291-1-ND/931068
TEST POINT TESTPOINT N/A N/A 10 0 0 0 0 0 0  
CAPACITOR 47n Kemet C0805C473K5RACTU 1 0.1 0.1 0.0269 0.0269 0.0144 0.0144 http://www.digikey.com/product-detail/en/C0805C473K5RACTU/399-1166-1-ND/411441
CAPACITOR .1u Kemet C0805C104K5RACTU 12 0.1 1.2 0.02 0.24 0.012 0.144 http://www.digikey.com/product-detail/en/C0805C104K5RACTU/399-1170-1-ND/411445
LED LED Stanley Electric Co. PG1101W-TR 1 0.47 0.47 0.2169 0.2169 0.11832 0.11832 http://www.digikey.com/product-detail/en/PG1101W-TR/404-1046-1-ND/428848
DIODE DIODE Micro Commercial Co 1N4448W-TP 4 0.14 0.56 0.0816 0.3264 0.02719 0.10876 http://www.digikey.com/product-detail/en/1N4448W-TP/1N4448WTPMSCT-ND/789337
INDUCTOR 15nH Bourns Inc. CE201210-15NJ 2 0.1 0.2 0.056 0.112 0.0416 0.0832 http://www.digikey.com/product-detail/en/CE201210-15NJ/CE201210-15NJCT-ND/3741724
INDUCTOR 10uH Taiyo Yuden LBR2012T100K 2 0.1 0.2 0.0665 0.133 0.0494 0.0988 http://www.digikey.com/product-detail/en/LBR2012T100K/587-2045-1-ND/1788992
CONNECTOR LB_CONN_2 On Shore Technology OSTTE020104 1 0.38 0.38 0.2548 0.2548 0.182 0.182 http://www.digikey.com/product-search/en?x=19&y=18&lang=en&site=us&keywords=OSTTE020104
CONNECTOR LB_HEADER_4 N/A N/A 1 0.2 0.2 0.2 0.2 0.2 0.2  
CONNECTOR BNC 4UCON – 1000 minimum 6542 2 0.51 1.02 0.51 1.02 0.51 1.02 http://www.digikey.com/product-detail/en/RR1220P-102-D/RR12P1.0KDCT-ND/432830
RESISTOR 1K Yageo RC0805JR-071KL 7 0.1 0.7 0.0073 0.0511 0.00326 0.02282 http://www.digikey.com/product-detail/en/RC0805JR-071KL/311-1.0KARCT-ND/731165
RESISTOR 1K .5% Susumu RR1220P-102-D 1 0.11 0.11 0.0381 0.0381 0.01673 0.01673  
RESISTOR 10K Yageo C0805JR-0710KL 3 0.1 0.3 0.0073 0.0219 0.00326 0.00978 http://www.digikey.com/product-detail/en/RC0805JR-0710KL/311-10KARCT-ND/731188
RESISTOR 22K Stackpole Electronics Inc RMCF0805JT22K0 1 0.1 0.1 0.0088 0.0088 0.0032 0.0032 http://www.digikey.com/product-detail/en/RMCF0805JT22K0/RMCF0805JT22K0CT-ND/1942581
RESISTOR 3M Panasonic Electronic Components ERJ-6GEYJ305V 1 0.1 0.1 0.011 0.011 0.00496 0.00496 http://www.digikey.com/product-detail/en/ERJ-6GEYJ305V/P3.0MACT-ND/282477
RESISTOR 6M Yageo RC0805FR-075M9L 1 0.1 0.1 0.0099 0.0099 0.00443 0.00443 http://www.digikey.com/product-detail/en/RC0805FR-075M9L/311-5.90MCRCT-ND/730958
OP AMP MCP6242 Microchip Technology MCP6242-E/SN 1 0.43 0.43 0.27 0.27 0.27 0.27 http://www.digikey.com/product-detail/en/MCP6242-E%2FSN/MCP6242-E%2FSN-ND/716227
BLE Module MDBT40 Raytac MDBT40-256V3 1 5 5 4.15 4.15 3.55 3.55  
OP AMP MCP6244 Microchip Technology MCP6244-E/SL 2 0.68 1.36 0.43 0.86 0.43 0.86 http://www.digikey.com/product-search/en?keywords=mcp6244&stock=1
 Total           13.29   8.3407   7.0309  

A next step would be to integrate ordering parts with the distributor.  However, I do not do that in this prototype.

Perhaps this crude prototype is of use to you.  (if it is – please consider addressing an ask above).

The Script

The Parts BoM Example Spreadsheet is view only.  Here is the script:

function onOpen() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();

  var BoMmenuEntries = [ {name: “Create BoM”, functionName: “createBOM”}];

  ss.addMenu(“Create BoM”, BoMmenuEntries);

}

function createBOM() {

  //I set the name of the imported CSV file that was created within Kicad’s Eeschema tool, the name of the parts inventory sheet, and the name of the built BOM as script properties.

  //See the File/Project Properties UI…NOTE: It would be better if a UI came up and asked for these names.  I’m entering the names through properties just for the prototype.

  //BOMWS = Sheet in spreadsheet that is the built BoM

  //KICADWS = Sheet that was imported from CSV file generated from Kicad’s EeSchema tool

  //PARTSWS = Sheet containing the info on each part that is listed within the CSV file

  var scriptProperties = PropertiesService.getScriptProperties();

  var KICADWS = scriptProperties.getProperty(“KICADWS”);

  if (!KICADWS) {

    var ui = SpreadsheetApp.getUi();

    ui.alert(“Can’t Complete: Did not find a sheet with the right name for the imported Kicad CSV file.”);

    return;

    }

  var PARTWS = scriptProperties.getProperty(“PARTWS”);

  if (!PARTWS) {

    var ui = SpreadsheetApp.getUi();

    ui.alert(“Can’t Complete: Did not find a sheet with the right name for the Parts Inventory sheet.”);

    return;

  }  

  var BOMWS = scriptProperties.getProperty(“BOMWS”);

  if (!BOMWS) {

     var ui = SpreadsheetApp.getUi();

     ui.alert(“Can’t Complete: Need a name for the BoM sheet.”);

     return -1;

  }  

 

 ss = SpreadsheetApp.getActiveSpreadsheet();

  var cvPCBws = ss.getSheetByName(KICADWS);

  var partsws = ss.getSheetByName(PARTWS);

  var BOMws = ss.getSheetByName(BOMWS);

  //the sheet may not exist. In this case, create the sheet

  if (!BOMws) {

    BOMws = ss.insertSheet(BOMWS);

    }

  prepareBOMws(BOMws);

  //get the name/value and quantity rows of the parts in the cvPCBnew file

  //start at row = 1 and column = 1.  The range goes to row = # rows in cvPCBnew worksheet column = 2.

  var nPartsInDesign = cvPCBws.getLastRow();

  var values = cvPCBws.getSheetValues(1,1,nPartsInDesign,2); 

  //get the info on parts to use from the Parts Inventory worksheet

  //The Parts Inventory worksheet should have a row where the name is the same as the valueNameLookup.  I’ll loop through the rows in the worksheet and find it.

  //First, figure out how many rows are in the Parts Inventory worksheet.

  var nParts = partsws.getLastRow();

  //The PartsInventory starts with a header row.  So the actual number of parts is one less than the total number of rows in the worksheet

  var dataRangeOfNames = partsws.getRange(2,2,nParts -1);

  var partsNames = dataRangeOfNames.getValues();  

  //loop through each part in the cvPCBnew worksheet and create a row in the BoM worksheet

  //Using global variables to hold the total costs (see summing these up in the addRowToBOM function and then appending the row at the end of this function)

  onePieceSKUcost = 0;

  oneHundredPieceSKUcost = 0;

  oneThousandPieceSKUcost = 0;

  for (var i = 0;i<nPartsInDesign;i++){

    var valueNameLookup = values[i][0];

    var quantity = values [i][1];

    //locate the row in the parts Inventory worksheet that matches the value name from the worksheet created through cvPCBnew

    for (index in partsNames) {

      var currentPartName = partsNames[index].toString();

      if (currentPartName == valueNameLookup) {

        //the row was located, add it to the BoM worksheet

        //Note: the location of the row within the Parts Inventory worksheet adds 2, 1 for the Header, and another because the array starts at 0 but the worksheet rows start at 1.

        var rowInPartsInventory = +index + 2;

        //I use return to exit addRowToBOM.  If addRowToBOM fails, I return a -1 so the script will stop because of the top level return

        var err = addRowToBOM(rowInPartsInventory,quantity);

        if (err == -1) {

          return;

        }

        break;

      } 

    }

  }

  //Figure out the total cost for units of 1, 100, 1000 and add the info to a row of the BoM worksheet

  var totalRow = [‘TOTAL’,”,”,”,”,”,onePieceSKUcost,”,oneHundredPieceSKUcost,”,oneThousandPieceSKUcost];

   BOMws.appendRow(totalRow);

   var row = BOMws.getLastRow();

   var boldRange = BOMws.getRange(row,1,1,12);

   boldRange.setFontWeight(“bold”);

}

function addRowToBOM(rowInPartsInventory,quantity) {

    var scriptProperties = PropertiesService.getScriptProperties();

    var BOMWS = scriptProperties.getProperty(“BOMWS”);

    if (!BOMWS) {

      var ui = SpreadsheetApp.getUi();

      ui.alert(“Can’t Complete: Need a name for the BoM sheet.”);

      return -1;

    }  

    var PARTWS = scriptProperties.getProperty(“PARTWS”);

    var ss = SpreadsheetApp.getActiveSpreadsheet();

    var BOMws = ss.getSheetByName(BOMWS);

    var partsws = ss.getSheetByName(PARTWS);

    //create an array in which each element is one of the cells in the row of the spreadsheet.

    //*******>The parts inventory worksheet has 9 columns.  getSheetValues(row of interest, column of interest, number of rows, number of columns)

    var partsRow = partsws.getSheetValues(rowInPartsInventory,1,1,9);

    //fill in Category, Value, MFG, MFG_Part

    var BOMRow = new Array();

    for (var i=0;i<4;i++) {

      BOMRow[i] = partsRow[0][i].toString();

    }

    //add quantity

    BOMRow[4] = +quantity;

    //add 1 piece price

    BOMRow[5] = +partsRow[0][4];

    //add 1 piece SKU cost

    BOMRow[6] = +partsRow[0][4] * +quantity;

    onePieceSKUcost = onePieceSKUcost + BOMRow[6];

    //add 100 piece price

    BOMRow[7] = +partsRow[0][5];

    //add 100 piece SKU cost

    BOMRow[8] = +partsRow[0][5]* +quantity;

    oneHundredPieceSKUcost = oneHundredPieceSKUcost + BOMRow[8];

    //add 1000 piece price

    BOMRow[9] = +partsRow[0][6];

    //add 1000 piece SKU cost

    BOMRow[10] = +partsRow[0][6]* +quantity;

    oneThousandPieceSKUcost = oneThousandPieceSKUcost + BOMRow[10];

    //add BUY link (most likely link to Digikey page where part can be ordered)

    BOMRow[11] = partsRow[0][7];

    //add row to BoM worksheet

    BOMws.appendRow(BOMRow);

    return 0

}

function prepareBOMws(BOMws) {

    BOMws.clearContents();

    BOMws.clearFormats();

    //******> ..currently 12 columns are defined

    var headerRange = BOMws.getRange(1,1,1,12);

    headerRange.setFontWeight(“bold”);

    var headers = [“Type”,”Name”,”MFG”,”Part”,”Quantity”,”1 Piece Price”,”1 Piece SKU”,”100 Piece Price”,”100 Piece SKU”,”1,000 Piece Price”,”1,000 Piece SKU”,”Link”];

    BOMws.appendRow(headers);

    //turn off bold

    var range = BOMws.getRange(2,1,1,12);

    range.setFontWeight(“normal”);

 

}

 

 

 

 

 

Thanks for reading this far.  Please find many things to smile about.

Software Debugging the Ladybug Blue Lite

Tags

,

I’m loving the debugging capabilities when programming the nRF51822.  A great feature of the nRF51 DK is its support for programming and debugging of external nRF51822’s – like the one that will be on the Ladybug Blue Lite.  Definitely a YIPPEE moment!! No external SWD debugger is required.  If the nRF51 DK did not include a SEGGER J-Link OB debugger chip with debug out functionality, I would have to buy a debugging probe from Segger that cost between $60 to over $1000 (link to website).  The nRF51 DK is becoming quite a bargain!

The Goal

The goal of this post is to make sure the Ladybug Blue Lite has the correct circuitry to support programming and debugging through the nRF51 DK.  I will have more confidence in the part of the schematic that handles debugging traffic.

Thanks To Those That Went Before

Mahesh Venkitachalem wrote an extremely useful post on external programming of an nRF51822 chip using the nRF51 DK.  Thank you!

Adafruit provided a great prototype nRF51 Breakout Board (the Adafruit Bluefruit LE UART Friend) as well as exceptional tutorials on using the Bluefruit.

And a grateful thanks to Chris Gammell for his Contextual Electronics course as well as his mentorship on electronics – with an emphasis on schematic design, layout, as well as how different circuits work using tools like LTSpice.

The nRF51 support and development team at Nordic for providing a really great forum – the Nordic Developer Zone – where many of my questions were answered.  Their support has proven to be quite strong.  THANK YOU!

Debugging Pins on the nRF51 DK

The nRF51-DK User’s Guide (download link) points out the connectors P19 and P20 are used in support of external debugging.  The pin outs shown in the PCA10028 schematic and PCB document (download link) has the following diagrams for the two connectors:

 

pinsForJTAGDebuggingOnnRF51DK

P19 is the standard Cortex-M 10-pin debug connector.  I don’t have one of these cables so I will use the P20 connector.  As noted by Mahesh in his post, the P20 connector uses the 2.54mm pin spacing that makes it easy to hook up with breadboard wires I already have.  Looking at the layout (image from Mahesh’s post):

p20Andp19PinsonnRF51DK

Pin 8 is close to R10 and R9.  Pin 1 is closes to current measurement. 

External nRF51822

I’ll use Adafruit’s Bluefruit LE UART Friend for the external nRF51822 that I will program (or debug) to.  The back of the board expose pads for external SWD debugging:

There are four connections that must be made between an external nRF51822 and the nRF51 DK for SWD debugging:

  • Two connections are for SWD debugging traffic.  SWD debugging traffic goes over SWD (data i/o) and SWDCLK (clock).  
  • A 3V reference is required.  If the nRF51 DK detects the 3V power coming from the external nRF51822, it will target programming/debugging on the external nRF51822.
  • GND is required so that the chips share a common ground.

AdafruitBlueFruitBackSidePins

 

The Bluefruit exposes the SWD, SWC (clock), and 3V on the back.  The GND connection is exposed with the other available connections on the front.

BluefruitFront

The pin mappings from the P20 connector on the nRF51 DK and the Bluefruit are listed in the table below:

pins P20 Bluefruit
3V 3 3vo
SWD 4 SWD
SWC 5 SWC
GND 8 GND

Here is an image of the Bluefruit I used with wires I soldered on:

BluefruitWithWires

Here is an image of the Bluefruit connected up on a breadboard:

BluefruitConnectedOnBreadboard

An an image of the wires connected to the P20 on the nRF51 DK:

P20ConnectorWithWires

Software Test

Notice the lit LED in the image of the Bluefruit on a breadboard.  To test external programming/debugging, I wrote an app for the nRF51822 that turns on and off this light.  The Eclipse project – BlinkyBlueFruit – is located at this GitHub location.

Looking at the Bluefruit LE UART Friend schematic:

Pasted Image 7 31 15 4 32 PM

pins 18 and 19 of the nRF51822 on the Bluefruit have LEDs attached.  I’ll use pin 19.  Here are the lines in main.c:

int main(void)

{

  nrf_gpio_cfg_output(19);

 

    // Toggle LEDs.

    while (true)

    {

            nrf_gpio_pin_set(19);

            nrf_delay_ms(5000);

            nrf_gpio_pin_clear(19);

            nrf_delay_ms(1000);

    }

}

Memory Location

I’m thankful to Mahesh for pointing out checking the memory allocation for the nRF518122’s Flash and Ram in his blog post.  How much is allocated and it’s starting address are contained within the .ld file.  The .ld file comes along with each nRF51 SDK example and is part of the Make toolchain.  Where there is a Makefile there is a .ld file.  I discussed using nRF51 SDK projects in Eclipse in a previous post.

The chip that is being debugged needs to have the origin and length set correctly within the .ld file.  These values will vary based on the amount of Flash/Ram on the chip and whether a BLE software stack, like the S110 is used (link to S110 specification download).

Flash and Ram

Since I evolve my software from the SDK example, the first question is: Is there a difference in the amount of Flash and/or Ram on the nRF51822 chip used on the Bluefruit versus used on the nRF51 chip used on the nRF51 DK?

The Flash and Ram of the two chips:

  Flash Ram Source
nRF51 DK 256KB 32KB simple_adc_gcc_nrf51.ld
Bluefruit 256KB 32KB https://www.adafruit.com/product/2479

Both the chip on the nRF51 DK and the Bluefruit include 256KB of Flash and 32KB of Ram.

Memory Without the S110 Stack – nRF51 DK

Without the S110 stack, (usually :-) ) an .ld file from the nRF51 SDK sets the Flash and Ram memory to:

MEMORY

{

  FLASH (rx) : ORIGIN = 0x0, LENGTH = 0x40000

  RAM (rwx) :  ORIGIN = 0x20000000, LENGTH = 0x8000

}

I copied the above from the simple_adc_gcc_nrf51.ld file.  The amount of Flash = 0x40000 = 256KB.  The amount of Ram = 0x8000 = 32KB.

Memory With the S110 Stack – nRF51 DK

When BLE is added to the software, the values in the .ld file need to accommodate the Flash and Ram used by the BLE stack.  I use the S110 stack.  Here is a .ld file when the S110 stack is included:

MEMORY
{
FLASH (rx) : ORIGIN = 0x18000, LENGTH = 0x28000
RAM (rwx) : ORIGIN = 0x20002000, LENGTH = 0x6000

When the S110 stack is present, the Ram origin is set at 0x20002000, length = 24KB (i.e.: 0x6000 length).  Note:  Table 2 in the S110 Stack specification (link to download) states the S110 takes 8KB of Ram.  24KB + 8KB = 32KB.  That adds up nicely!

Based on what I read in Table 2, I thought the Flash origin would be 0x14000 (i.e.: 80KB for the S110 stack).  However, the example starts the Flash memory location for the app at 0x18000 – 96KB above 0x0.  Maybe this is so the S110 versions have room to grow?  Of course – I might be misinterpretting because I am not understanding something.

And…Blink!

I set up and used the same erase/flash make commands that I had used previously (see earlier discussion of setting up the Eclipse build environment).

It worked!  I have more confidence in what connections are needed on the Ladybug Blue Lite to support SWD debugging.  A definite YIPPEE!!! moment.

 

 

Thanks for reading this far.  Please find many things to smile about.

Using a Battery with the Ladybug Blue Lite

I decided on two Ladybug Blue models – one with pumps and one without.  The one without pump measures the pH and EC.  It doesn’t require a lot of current.  I’ve decided to make a PCB I’m calling the Ladybug Blue Lite that runs off of a battery.  This way, the pH and EC can be measured wherever there is a container of nutrient bath.

But which battery?

The Goal

The goal of this post is to pick a battery topology to power remote measuring of the pH and EC of a nutrient bath.  Results can be read on a smartphone.

Thanks to Those That Went Before

I am very grateful for all I have learned from Chris Gammell and Contextual Electronics.  His mentorship style has given me the confidence to keep at electronics and embedded systems programming even though I have no background in these fields.  I look back a year and get excited thinking about how much I have learned and been able to accomplish from designing a PCB to soldering the chips on the board to figuring out expectations on how the circuits should work.

The Battery

In Chris’s Contextual Electronics course, he mentored us through two topics:

  • battery topologies to drive sensors + microntrollers + 5V motors
  • buck, boost, and buck-boost converters
During one iteration of the power section of the Ladybug Blue Lite I designed for a LiPo battery (note: I found Adafruit’s description of Lithium ion polymer and Lithium ion batteries very helpful).  The Ladybug Blue Lite included the ability to recharge the battery through a USB micro port.

LDO and DC/DC Included

Then I took a closer look at the nRF51822’s data sheet (link to download the data sheet).  And lookey-lookey!
 
Pasted Image 7 29 15 1 48 PM
 
According to Table 20 in the data sheet (link to download the data sheet):
VDDValuesFornRF51822
A battery can connect directly to the nRF51822 then go through either an LDO or Buck-Boost converter provided by the nRF51822.
 As noted in the nRF51822 product specification (link to download the data sheet):
The nRF51 DC/DC buck converter transforms battery voltage to lower internal voltage with minimal powerloss. The converted voltage is then available for the linear regulator input. The DC/DC converter can be disabled when the supply voltage drops to the lower limit of the voltage range so the LDO can be used for low supply voltages. When enabled, the DC/DC converter operation is automatically suspended between radio events when only the low current regulator is needed internally.This feature is particularly useful for applications using battery technologies with nominal cell voltages higher than the minimum supply voltage with DC/DC enabled. The reduction in supply voltage level from a high voltage to a low voltage reduces the peak power drain from the battery. Used with a 3 V coin-cell battery, the peak current drawn from the battery is reduced by approximately 25%.
 
The reference manual goes on to state (link to download):

The DC/DC converter only reduces the power consumption used by the radio, it does not affect the powerused by the Flash, System, and Peripheral.

Enabling the DC/DC converter will not turn it on, but set it in a state where it automatically gets turned on when the radio is enabled and goes off again when the radio gets disabled. This is done to avoid wasting power running the DC/DC in between the radio events where current consumption is too low.

Good to know….Importantly – the heck with including these circuits within the power design of the Ladybug Blue Lite.  This will save additional BoM costs since one of the parts I was considering for the Buck-Boost converter is the TPS63030 which currently costs $2.50 on Digikey.com.  Not to mention the price of a LiPo battery and any connectors needed.

Not Designed for LiPo or Lithium Ion

The power supply must be between 1.8 – 3.6V (LDO) or 2.1 – 3.6V (DC/DC).  The LiPo battery I am testing with is rated at 3.7V.  After charging my DMM measures the voltage at 4.2V.  Even at 3.7V, plugging a LiPo battery directly into the chip will damage the chip.  If I were to continue down the path of a LiPo battery, I would explore using the advice given by Stefan in this Nordic Devzone post:
Since a lithium battery voltage is up to 4.2V but the supply voltage for the nRF51822 is 1.8V-3.6V, then I guess an efficient option is to have an external LDO drop the voltage down to 3.6V for the nRF51822, and have the DCDC enabled. Lithium batteries normally have voltage range of 3.0V-4.2V, so you should bypass your external LDO as your battery voltage drops below 3.6V.

Low End Voltage Requirements

That’s the high end of the power source.  What about the low end?  How much voltage is needed?  
    • VDD to the ICs:  There are only two chips to look at – the nRF51822 and the MCP6244 op amp.  The nRF51822’s minimum supply voltage is 1.8 (assuming LDO conversion).  The minimum supply voltage needed to power the MCP6244 is 1.4V.
    • there are three signals the rails of the op amp need to accommodate:
      • ph:  +/- 415 mV
      • Wien Bridge: ~ +/- 550mV
      • Gain Loop: the amplitude of the gain loop is controlled by pre-scaling the Wien Bridge wave form using a voltage divider.  Typically, the max gain for the scenarios the Ladybug was designed for (discussed in earlier posts) is 6.  If the Wien Bridge is shrunk to +/-150mV, the max amplitude of the gain loop is +/- 900mV.
    • using the on-board DC/DC (buck) converter requires a minimum of 2.1V. 
Thus the minimum voltage needed is ~ 2.1V
 
To be conservative, I will design for the absolute minimum voltage to be 2.4V.

Which Battery

I decided to go with what is used with the nRF51 DK – a CR2032 coin cell battery.

EnergizerCoinCellBattery

Looking at the data sheet for the CR2032 that came with the nRF51 DK (the Energizer CR2032), the voltage starts off at 3V:

ContinuousDischargeCharacteristicCR2032

The CR2032 provides more than enough current at 240mAh.  

Now I’ll update the design and also put together a circuit that checks the voltage level.  It certainly rings true to me that peeling an onion exposes a bag of more onions..

 

 

Thanks for reading this far.  I hope you find many things to smile about.

 

Switching from the ADS1015 to the nRF51822’s ADCs

Tags

, ,

The nRF51822 comes loaded with eight configurable ADC channels.  Why not take advantage of them?  I am evolving the design of the Ladybug Blue Lite to use the nRF51822’s ADCs instead of the ADS1015 + MUX.  In the previous plan, six op amps were used.  One for VGND, one for pH, and four for EC (one for Wien Bridge, one for the Gain loop, and two for rectification).  Then a MUX switched between VOUT and VIN signals.  Removing the MUX means two more op amps are needed to participate in rectification.  Now, instead of a MCP6242 and MCP6244, the Ladybug Blue Lite will use two MCP6244’s.  Another benefit is the reduction in complexity.  I see the complexity reduction to be highlighted most in the firmware.  Previous to this change, I used a timer to give about 10 seconds wait between measuring VIN and VOUT to allow the effect of switching signals to work it’s way out of the circuit.

The change to the BoM is:

Ladybug Blue Lite with ADS1015:

Total= $3.89
 
Ladybug Blue Lite without ADS1015:
  • no ADS1015
  • no MUX
  • exchange MCP6242 for MCP6244.  The MCP6244 costs $.68.  $.68 – $.43 = $.25
  • one additional MOSFET: $.17
Savings: $3.89 -$.25 – $.17 = $3.47 -> a significant savings!
 

The Goal

The goal of this post is to understand and evaluate the nRF51822’s ADC for pH and EC measurements.

Thanks to Those That Went Before

I am truly grateful to:

  • Chris Gammell – I continue to learn A LOT from his Contextual Electronics course.  Recently, the “Full Charge Ahead” section was a great introduction to designing and laying out LiPo batteries.  Chris is also an amazing mentor.
  • Ryan of Sparky’s Widgets has made it so much easier for us to sense pH and EC readings.  I started with Ryan’s schematics and design of the minipH and miniEC boards and have evolved my designs from there.  The work I am currently doing proudly stands on the shoulders of Ryan’s.  Thank you.
  • Adafruit for their attention to learning and support.  There is so much great stuff on their site!
  • OSH Park for their excellent PCB fabrication service and hiring such terrific folks that really care about the support.

What is Good Enough

I am not concerned with sampling rate.  But resolution is a factor.  The nRF51822’s maximum resolution is 10 bits.  The ADS1015 has a 12 bit resolution.  Is the loss in resolution going to negatively affect my goal of pH and nutrient adjustment?  It shouldn’t.  In an earlier post, I noted the minimum resolution for pH is 8 bit.  With EC, the VIN, VOUT, and VGND are all measured.  It should be fine for the resolution to be 10 bits.

Running a Test

To see if I can get expected results, my first test will measure VGND when the power source = 3.3V.  VGND measures 1.6V.  The test code I used (ADC_Simple) is available at this GitHub location.  It is zipped into an Eclipse project (Note: There are some hard coded paths that you would need to modify – mostly in the makefile).

ADC Configuration

The ADC HAL SDK documentation notes the following settings for the default configuration:

  • 10 bit resolution
  • 1/3 prescaling
  • internal 1200mV VREF
The 1/3 persecuting makes sense given VREF = 1200mV.  VOUT could be between 2.5-3V.  If it is at 3V, a pre-scaling of 1/3 allows the value to be correctly read (i.e.: 3000/3 -> 1000, which is below 1200mV).

VGND

VGND = 1.6V when measured with my DMM on my test setup.  Running the ADC_Simple (GitHub location) within Eclipse, the reading from the ADC with the default settings = 450.  Converting this to millivolts:
  • 10 bits = 1024 steps (0 to 1023)
  • reference = 1200mV
  • post-scale factor = 3
VGND mV = (1200mV/1023)*450*3 = 1584 mV, close enough to 1.6V.  The reading for VGND looks good enough.

VOUT

VOUT = 2.5V on my test setup.
ADC reading = 700 = 1200/1023*700*3 => VOUT =  2463 mV

VIN

VIN = 1.77 on the test setup.
ADC reading = 492 = (1200/1023)*492*3 => VOUT =  1731 mV
 
I note aall ADC reading are slightly scaled down.  This is to be expected as noted in this post.  Since all scale down, the results for the gain even out.  That and different circuits are measuring the voltage value so it is expected there will be variability.  I see this as reasonable variability.

Calculating the Resistance

While calculating the resistance isolates how accurate the circuit is, I’m curious to know how close the measurements came to the 200Ω resistor I am using in the test setup to represent an EC probe.
 
Resistance = 1K (i.e.: the feedback resistor I am using)/Gain-1
These readings are relative to VGND:
  • VOUT relative to VGND = 2463 – 1584 = 879mV
  • VIN relative to VGND = 1731 – 1584 = 147mV
Gain = VOUT/VIN = 879/147 = 5.98
Resistance = 1000/4.98 = 200.8Ω…pretty darn close!
 
 
 
So there we have it.  I’m going ahead and update the design to use the ADCs on the nRF51822.
 
 
 
That’s it for now. Thank you for reading this far.  Please find many things to smile about.
 

Measuring EC VIN and VOUT Using a Prototype Circuit and the nRF51822

Tags

,

Yesterday I spent around 3 hours putting together the pH and EC circuits on a breadboard.

pHAndECOnBreadBoard

The pieces of the prototype circuit consist of some sections of the circuit that I milled on the Othermill, a few components on SMT-> DIP boards, and Adafruit’s ADS1015 BoB.  All…gulp…wired together….if I don’t breathe…the circuit works.

I then set up a DMM, a scope and an I2C logic analyzer to test.

I hard code the EC probe’s resistance value by using a 200Ω resistor.  To be more exact, the resistor actually measures 198Ω.  This way I’ll know if my tests can get reasonably close to the resistor value.  Recall from previous EC tests that to calculate the EC:

  • measure VIN and VOUT in order to calculate the Gain from the EC probe’s resistance.  I.e.: Gain = 1+ VOUT/VIN
  • calculate the resistance and conductance read from the EC probe:  The layout specifies a 1K feedback resistor.  So R(measured) = 1K/Gain-1 and the EC value in Siemens is 1/R(measured)

to test, I put together an Eclipse project.  The project is located at this GitHub location (the ADS1015_Test.Zip).  I decided zipping up Eclipse project at different working states is an easy way to start testing since all the include files, code, makefile, debugging options… changes are there. 

 

Test 1: Measure VIN and VOUT using ADS1015_Test Project

Recall VIN is the DC peak value determined from the Wien Bridge Oscillator.  VOUT is the DC peak value determined from the gain loop in the EC circuit. The VOUT is amplified by how much the EC Probe (in this case the 200Ω resistor) amplifies the VIN signal.

For this test, I will “hard code” the MUX.  

MUXUsedinPrototype

Measuring VIN

When the MUX pin = GND, the signal to the ADS1015 is the VIN.  When the MUX pin = power source (in this case I am testing with a 3.3V power source), the signal sent to the ADS1015 is VIN.

I start with the MUX pin connected to GND.

MUXPinConnectedToGnd

 

I use the debugger and open an Jlinkrttclient session within terminal.  As shown above, the value I got from the ADS1015 for VIN = 2012.  I had set the ADS1015’s resolution (also called perhaps confusingly the gan) to GAIN_FOUR, which means each LSB of the ADC = .5 mV.  So VIN = 2012*.5 = 1006mV.  Hmmm…I was expecting a value of around 200mV.  Something is not right….

Measuring VOUT

Moving the MUX pin to VDD, I rerun the test program and get VOUT = 2028.

VOUTMeasurement

The value of 2028*.5 = 1.014V is pretty much what I was expecting for VOUT.  

 

Debugging VIN

I should be seeing VIN close to 200mV and VOUT close to 1000mV.  What’s going on with VIN?  The first debug test I’ll do is to look at the values going into the ADS1015 AIN1 pin.  This is the pin for VIN and VOUT after rectification (I discuss all this in detail in previous posts.  It takes me a bit longer than I want at this point to back link to these older posts…).  So the question I am answering is: What values does the ADS1015 receive as input prior to sending them over to the nRF51822 via I2C?

VIN

VIN

VOUT

VOUT

VGND

VGND

Scope measurements:

  • VGND = 1.8
  • VIN = 1.92 – 1.8 = .12V
  • VOUT = 2.8 – 1.8 = 1V
The scope values are closer to what I expected.  Before I unpack the I2C traffic, I’m going to check the code.  Perhaps the data type that I use within the ADS1015 library is not the right one to return ADC values.  These need to match.  First looking at Adafruit’s Arduino library for the differential, I note the function returns an int16_t:
 
int16_t Adafruit_ADS1015::readADC_Differential_0_1()
 
In my library, I’m returning an int16_t: 
int16_t readADC_Differential_VGND(nrf_drv_twi_t const * const  p_instance,int P)
 
In main.c, I set the variable to receive the reading to uint16_t:
 uint16_t rawADC = readADC_Differential_VGND(&twi,1);
 
Changing this to int_16_t, I get the following readings:
  • VIN = 318*.5 = 159mV
  • VOUT = 318*.5 = 159mV
Sigh…this makes me think there are cached buffers holding onto the value in between readings.  The first time I started with VOUT.  The second VIN.  Coincidence?
One more time… Here is a reading for VIN:
newreadingforVIN
 
Looking at the I2C traffic:
Packet ID Address Data Read/Write ACK/NAK
0 0x90 0x01 Write ACK
0 0x90 0xA7 Write ACK
0 0x90 0x83 Write ACK
1 0x90 0x00 Write ACK
  0x91 0x13 Read ACK
  0x91 0xC0 Read NAK

Looking at the ADS1015 data sheet (p 8 on the data sheet I’m looking at)

  • byte 1 to Address 0x90 -> ADS1015 address (see this link for how to figure out the ADS1015 address from 0x90).  Data = 0x01-> write to the config register
  • byte 2= 0xA7 = b1010 0111->MSB of what to write to the config register
  • byte 3 -> 0x83 = b1000 0011-> LSB of what to write to the config register

p. 15 of the data sheet has the map to what the config write bytes mean:

ConfigRegisterTable

Looking at the explanation of the MSB config bytes (p. 16)

MSB = b1010 0111

  15 14 13 12 11 10 9 8
BIT 1 0 1 0 0 1 1 1
  • Single shot reading (bit 15)
  • AINp = AIN1 and AINn = AIN3 (bits 14-12)
  • programmable gain = +/1 1.024V (GAIN_FOUR) (bits 11-9)
  • Power-down single-shot device operating mode (bit 8)

so far so good.  Now onto the LSB = b1000 0011

  7 6 5 4 3 2 1 0
BIT 1 0 0 0 0 0 1 1
  • 1600SPS datarate – the default. (bits 7-5)
  • Default comparator mode (bit 4)
  • Default comparator polarity (bit 3)
  • Default latching comparator (bit 2)
  • Disable comparator (bits 1-0) – which I assume means bits 4-2 are ignored.
So the config bytes seem to be set up correctly.  Back to the I2C traffic:
  • byte 4 -> write to the ADS1015, data = 0x00 which means write to the register that contains results (the “Conversion register”)
  • byte 5 -> 0x5F – the MSB byte of the ADC reading
  • byte 6 -> 0xE0 – the LSB byte 
So the ADC reading = 0x13C0.  This isn’t the value, because the data sheet notes the Conversion register’s first 4 bits are unused (see Table 8 on p. 15):
Table8ConversionRegister
Shifting over 4 bits,  0X13C0 -> 0x013C = 316…which is identical to what the API returned.  I decided to include the logic behind handling an ADC reading that is negative:
 
Values are stored in two’s complement.  This makes Adafruit’s code that reads the ADS1015 more sense:

// Read the conversion results

  uint16_t res = readRegister(m_i2cAddress, ADS1015_REG_POINTER_CONVERT) >> m_bitShift;

  if (m_bitShift == 0)

  {

    return (int16_t)res;

  }

  else

  {

    // Shift 12-bit results right 4 bits for the ADS1015,

    // making sure we keep the sign bit intact

    if (res > 0x07FF)

    {

      // negative number – extend the sign to 16th bit

      res |= 0xF000;

    }

  • use a uint16_t to store the value that comes back from the ADS1015
  • check if the 12th bit is a 1 (i.e.: 0x7FF = b0111 1111 1111…anything above 0x7FF – say 0x800 – has a 1 in the most significant bit, making is a negative value.  So extend negative throughout the 16 bits and return an int16_t

After working through a few more results, I’m thinking the challenge is actually the rectifying part of the circuit.  In the prototype I take a shortcut and make a rectifier with just an opamp, diode, and cap.  I wanted to try this over the more complicated FET solution I was using.  However, I’m thinking because the MUX causes such a fluctuation between signal switching, the FET solution is what I should use.  The results I am seeing is most likely an artifact of the rectifier since the signals are switching correctly.

Well – that was a good test session.  I got a much better feel for the meaning between the I2C packets.  I also validated the API is returning the same results as what I2C is returning and the configuration settings of the ADS1015 have been set correctly.

 

Now I’ll rebuild the rectifier circuit to go back to the one on the Ladybug Shield.

 

Thanks for reading this far.  Please find many things to smile about.                                

Learnings from Setting up nRF51822 Software Projects in Eclipse on OSX

Tags

,

 

I spent a couple more days getting my head around setting up nRF51822 projects in Eclipse.

The Goal

The goal of this post is to document “best practices” in setting up nRF51822 projects in Eclipse.  Through many (many) attempts, I was able to successfully build projects based on starting with an example project from the nRF51 SDK. 

Stuff to Know

It’s like “they” say – when you peel an onion you get a new bag of onions (although I am not sure who the “they” is).  In this case, the onion is the Eclipse environment.  In a previous post I mentioned the importance of the workspace and the project folder.  After more play, I have a better handle on the practical implications of these concepts.

Workspace

The workspace is a path within the file system that roots where the Projects where be located.  Eclipse allows us to change the workspace location which probably makes sense for larger projects, but for my current needs and abilities, changing the workspace location would cause confusion.

Project space

The key is to understand the purpose of the .project – Project Description File –  and .cproject files (link to Stackoverflow discussion on what these files are).  

Prior to realizing the .project and .cproject files would be in the folder that is tied to a Project in Eclipse, I was setting up the Project within a location within my file system outside of the workspace path.  Ultimately, I found this confusing so now all projects are contained within the folder that is tied to the workspace.

Bottom line: Once a project is initially set up, I check the folder in the filesystem to make sure the .project and .cproject are where I expect them to be.

Setting up a Project

The ways I could set up a project include:

  • starting new – either using a makefile or using managed makefiles.  I decided to not go this route because the nRF51 SDK has many, many header and libraries.  Plus, I do not have the skills to masterfully set up a makefile – or in the case of a managed makefile – set all the parameters.
  • starting from an example – this is my preferred way to start. 
  • starting from a project – doable, but I could only find one project and then there is a step that involves tweaking the file paths in the .project path.  Also, it brings in more libraries and the code is more complex than other examples.  Ideally, the nRF51 SDK would come with both Keil Packs and Eclipse Projects for each example.

Starting From an Example

note: modifying Makefile.posix must be done every time an updated nRF51 SDK is used.

The very first step is to update Makefile.posix file located within the<filepath to root of SDK folders> /components/toolchain/gcc directory.  Update the variables to reflect the GNU version that is installed.  The version of GNU I am using is 4.9.3.  Here’s the contents of my updated file:

GNU_INSTALL_ROOT := /usr/local/gcc-arm-none-eabi-4_9-2015q1
GNU_VERSION := 4.9.3
GNU_PREFIX := arm-none-eabi

I’ll use the ble_app_template example from the SDK.  Here are the steps to set up the example within a folder of a workspace.  Doing the steps in this ordered ensured the most success:

  • go to the workspace folder in the file system.  For example, my Eclipse workspace folder is /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51
  • create a folder that will be the same name as what you will name the project in Eclipse.  In this example, I named the folder test2 E.G.: /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/test2
  • copy the config folder, Makefile and ble_app_template_gcc_nrf51.ld from the folder where it resides in the nRF51 SDK E.G.: /Users/margaret/Documents/nRF51_SDK_8.1.0/examples/ble_peripheral/ble_app_template/pca10028/s110/armgcc into the project folder created in the second step
  • create a folder to hold main.c.  I created one called Application.  Then copy the main.c program from the examples into the folder.
  • in Eclipse create a new project that starts with the Makefile (i.e.: Makefile Project with Existing Code).  On the “New Project” Dialog box, browse to the project folder you created above.  ALSO choose Cross ARM GCC under Toolchain for Indexer settings.  When I did not select the toolchain, CDT did not discover the gcc include files which is an important step that happens in a bit.  

Pasted Image 7 22 15 9 18 AM

However, later on debugging won’t work because an environment variable, which is a prefix to the gdb client isn’t set.  SO the environment variable cross-prefix needs to be set to arm-none-eabi-  since the gdb client is named arm-none-eabi-gdb.exe.  To set the cross-prefix variable, go into project properties->C/C++ Build/Build Variables, check the box Show system variables and scroll down to the cross_prefix.  While setting cross-prefix also check cross-make to make sure it is set to make.

Pasted Image 7 22 15 1 01 PM

  • Don’t enter a project name but browse to the folder within the workspace that will contain the project files E.G.: test2.
  • check that the .project and .cproject have been created within the project folder within the filesystem.  If they are not there, Eclipse does not think the project is contained in that folder.
  • check to make sure the build command = make.  I was having challenges getting the Build command to use the Makefile.  It turned out make was not being called.  Check Project -> Properties->C/C++ Build->Builder Settings  Build command is make.  If not, uncheck the Use default build command and type in make.
makeinbuildcommand
 
  • set up CDT to discover the toolchain paths and compiler settings.  This will add the gcc include files to the project and associate the makefile with CDT so the project uses the makefile.  Vidar describes how to do this in this post under “Enable auto discovery of symbols, include paths and compiler settings.”  I’m putting the steps here just in case the post goes away.
    • Enter project properties -> C/C++ General->Preprocessor Include Paths,etc.->Providers
    • Click on CDT GCC Build Output Parser and change the compiler command pattern from (gcc)|([gc]++)|(clang) to (arm-none-eabi-gcc)|([gc]++)|(clang)
    • Click apply button
    • Click on CDT Built-in Compiler Settings Cross ARM and replace ${COMMAND} with arm-none-eabi-gcc
    • Click apply button.
    • if the CDT discovered the gcc paths, there will now be an include folder within the project folder that include the gcc paths. I’m using gcc version 4.9.3.   Six include paths were located.
  • Make changes to the Makefile:
    • Add the following variables near the top of the Makefile and set the path to whatever you used. Mine are shown as examples:
      PROJECT_PATH = /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/test2
      SDK_PATH = /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/nrf51_sdk
    • update TEMPLATE_PATH e.g.: TEMPLATE_PATH = $(SDK_PATH)/components/toolchain/gcc
    • Do a Find/Replace all for Find: ../../../../../../..  Replace All: $(SDK_PATH)
    • Delete the line:  INC_PATHS += -I$(SDK_PATH)/../components/drivers_nrf/config
    • Modify the line: INC_PATHS  = -I../../../config  to the location of the config folder within the project: -I$(PROJECT_PATH)/config
    • change the path to main.c within the C_SOURCE_FILES variable from: ../../../main.c \ to $(PROJECT_PATH)/Application/main.c .  I put the main.c within the Application folder.
    • change the location of the INC_PATHS for the config directory to  -I$(PROJECT_PATH)/config
    • if the makefile compiles bsp.c, change the path to bsp.c within the C_SOURCE_FILES variable from: $(SDK_PATH)/bsp/bsp.c \ to $(SDK_PATH)/examples/bsp/bsp.c \
    • change the bsp path to:  INC_PATHS += -I$(SDK_PATH)/examples/bsp
    • set up the options for debugging.  Vidar covers this in his tutorial post.  I repeat his steps here:
      • Open makefile from project explorer and find the ‘-O3’ option in the CFLAGS variable and replace it with ‘-O0’. This will turn off compiler optimization.
      • Add ‘-g3’ option to CFLAGS to produce debugging information.
      • Link time optimization is then turned off by removing “CFLAGS += -flto -fno-builtin”
      • CDT has a build output parser that can parse include paths and symbols from the build output. To use this tool we need the Makefile to print the compiler commands.Search for the $(NO_ECHO)$(CC) $(CFLAGS) $(INC_PATHS) -c -o $@ $< line in the makefile.Add @echo arm-none-eabi-gcc $(CFLAGS) $(INC_PATHS) -c -o $@ $< on the line above to echo the symbols and include paths used when compiling the source files.
    • Replace this section of the Makefile from:

finalize: genbin genhex echosize

 

genbin:

@echo Preparing: $(OUTPUT_FILENAME).bin

$(NO_ECHO)$(OBJCOPY) -O binary $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).bin

 

## Create binary .hex file from the .out file

genhex: 

@echo Preparing: $(OUTPUT_FILENAME).hex

$(NO_ECHO)$(OBJCOPY) -O ihex $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).hex

 

echosize:

-@echo “”

$(NO_ECHO)$(SIZE)$(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out

-@echo “”

 

clean:

$(RM)$(BUILD_DIRECTORIES)

 

cleanobj:

$(RM)$(BUILD_DIRECTORIES)/*.o

 

flash: $(MAKECMDGOALS)

@echo Flashing: $(OUTPUT_BINARY_DIRECTORY)/$<.hex

nrfjprog –reset –program $(OUTPUT_BINARY_DIRECTORY)/$<.hex

 

## Flash softdevice

flash_softdevice: 

@echo Flashing: s110_softdevice.hex

nrfjprog –reset –program $(SDK_PATH)/../components/softdevice/s110/hex/s110_softdevice.hex

to: 

#don’t need .bin so I took it out.

#finalize: genbin genhex echosize

finalize: genhex echosize

 

#genbin:

#@echo Preparing: $(OUTPUT_FILENAME).bin

#$(NO_ECHO)$(OBJCOPY) -O binary $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).bin

 

## Create binary .hex file from the .out file

genhex: 

@echo Preparing: $(OUTPUT_FILENAME).hex

$(NO_ECHO)$(OBJCOPY) -O ihex $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out $(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).hex

 

echosize:

-@echo “”

$(NO_ECHO)$(SIZE)$(OUTPUT_BINARY_DIRECTORY)/$(OUTPUT_FILENAME).out

-@echo “”

 

clean:

$(RM)$(BUILD_DIRECTORIES)

 

cleanobj:

$(RM)$(BUILD_DIRECTORIES)/*.o

 

#############################################################################

# Set up variables to run SEGGER’s JLINK utility

#############################################################################

JLINK_OPTS = -device nrf51422_xxac 

JLINK = JLinkExe $(JLINK_OPTS)

#############################################################################

# Target: erase

# remove all binaries from the nRF51

#############################################################################

erase: erase.jlink

$(JLINK) erase.jlink

erase.jlink:

printf “erase\nr\nexit\n” > erase.jlink

#############################################################################

# Target: flash_softdevice

# load the s110 softdevice onto the nRF51

#############################################################################

flash_softdevice: flash_softdevice.jlink

$(JLINK) flash_softdevice.jlink

flash_softdevice.jlink:

printf “loadfile /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/nrf51_sdk/components/softdevice/s110/hex/s110_softdevice.hex\nr\ng\nexit\n” >flash_softdevice.jlink

#############################################################################

# Target: flash

# load app onto the nRF51

#############################################################################

flash: flash.jlink

$(JLINK) flash.jlink

flash.jlink:

printf “loadfile /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/EC_Test_1/_build/nrf51422_xxac_s110.hex\nr\ng\nexit\n” > flash.jlink

      change the loadfile path under flash.jlink to match the path of the project.  E.g.: above has EC_Test_1.  Replace this with whatever the project name is, e.g.: test2

    • now it’s time to load up the include file paths in Eclipse so Eclipse can help with source line debugging. 
      • open up the Makefile and copy the INC_PATHS lines to an editor.  E.g.:
        INC_PATHS += -I$(PROJECT_PATH)/SimpleTimer/config
        INC_PATHS += -I$(SDK_PATH)/examples/bsp
        INC_PATHS += -I$(SDK_PATH)/components/RTT
        INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/nrf_soc_nosd
        INC_PATHS += -I$(SDK_PATH)/components/device
        INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/hal
        INC_PATHS += -I$(SDK_PATH)/components/libraries/button
        INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/timer
        INC_PATHS += -I$(SDK_PATH)/components/libraries/util
        INC_PATHS += -I../..
        INC_PATHS += -I$(SDK_PATH)/components/libraries/simple_timer
        INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/timer
        INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/common
        INC_PATHS += -I$(SDK_PATH)/components/toolchain
        INC_PATHS += -I$(SDK_PATH)/components/libraries/timer
        INC_PATHS += -I$(SDK_PATH)/components/drivers_nrf/gpiote
        INC_PATHS += -I$(SDK_PATH)/components/toolchain/gcc

 

      add these paths to Project Properties -> C/C++ General->Paths and Symbols-> GNU C
  • add make targets erase, flash_softdevice, flash
  • run the erase make target (assuming the flash on the nRF51 DK needs to be erased)
  • run the flash_softdevice make target (assuming the flash on the nRF51 DK does not have the s110 soft device)
  • run the flash make target to load the binary onto the nRF51 DK
I covered debugging in this post.  Since then, I’ve bumbled a few times setting up the Debug Configuration correctly.  Make sure to create the debug configuration under the GDB SEGGER J-Link Debugging folder.  
 

Pasted Image 7 23 15 9 51 AM

 
If you get an error message about file not found, most likely this is caused because the arm-none-eabi-gdb.exe client is not being used.  Check the build variables noted above as well as the Debug Configuration.
 

If You Get A Build Error Like This..

.

‘RTC0_INSTANCE_INDEX’ undeclared here (not in a function)

          .instance_id =  RTC##id##_INSTANCE_INDEX

 where there is a ##id## you most likely did not copy the config folder into the workspace and/or change the INC_PATHS for the config to point to the config folder in the workspace.  I addressed these steps above.  I mention them here because I’ve forgotten to do this a few times.
 

Whew!

Well, I got this far from repetition until I *think* I had somewhat of an idea what was going on within the Eclipse environment.  I’ll share anything else I learn. 
 
 
Thanks for reading this far.  Please find many things to smile about.

Even MORE Time with the Othermill

Tags

 

Thanks to Those That Went Before

I’m starting by thanking Matt Berggren for his excellent post on how an experienced miller approaches and uses the Othermill.  His post made me feel like I was his apprentice.

While I am at it, as I wrote this post I went through support responses provided by Simone at Othermill.  Simone provided exceptional support.  She really knows the details of using an Othermill and is blessed with the ability of exceptional communication skills and understanding.  Thank you.

Updates on Techniques

Pumped up with my previous success milling a pH BoB, I boldly moved on to milling an EC BoB.

After a day of getting to Gerbers and the Othermill carving away for hours on end, I FAILED to make a usable board.  

EC BoB from Othermill

I attribute the reason to the failure to not vacuuming up the remnants of previous runs.  This caused the FR1 not to be as securely taped to the base plate as it should be.  Also, it is much more difficult to get a good track when all copper is removed from around the track.  So what is a better way?  I went back to an excellent post on milling on the Othermill done by Matt Berggren.

These are the “tips and tricks” I have been learning through more time using the Othermill.

Set the Trace Clearance to Larger than the Default

Up until now, my experience using BoBs I made with the Othermill favored setting the trace clearance at a large value (e.g.: 2 in) so that all the extra copper is removed.  I discussed increasing the trace clearance  in this post.  Search for bit breaker :-) .  When I used the default trace clearance, I was getting copper traces connected that shouldn’t be.  Then I reread Matt’s post and am finding better luck setting a trace clearance of 0.055in.  The BoB looks more like what Matt did:

MattsPCBOthermill

 

 Put Less on a BoB

Another challenge I have is due to my (lack of) skill level.  I was having a low level of success milling chips that had spacing of 10 mils – the smallest spacing recommended.  Either the copper was torn, or I could not reflow the chip successfully on what was chiseled out.  I plan to get better at this, but for now I broke up the prototype such that some of the chips – and all that have 10 mill spacing between pads – are broken out onto SMT -> DIP adapters.

MosfetDIP

 I had bought a bunch of different sizes when I first started using this technique in order to use the same chip I use on the PCB to prototype on a breadboard.

Constantly Check if Mill bit is Broken

A big cost of using an Othermill is constantly feeding it mill bits.  I find it all too easy to break mill bits up to 1/8”.  The other challenge is being able to look at a 1/100” drill bit and seeing that it is broken.  Even under magnification there are times when I just can’t tell.  I bet this is due to my skill level.  Thus I assume my ability to see that the 1/100” mill bit is broken will become more obvious.

Have a Supply of Mill Bits on Hand

I wanted to try mill bits other than those offered on the Othermill web site.  I decided to by 1/32” 1/100” and 1/8” from 
Think & Tinker, LTD (see Pricebits.com

I ended up buying three each of:

  • EM3E8-0100-15V
  • MN208-0313-006F
  • MN208-1250-019F
I haven’t received these yet.  If I was to order again, I would go with Matt’s recommendation and instead of buying 1/8” – which I was using to remove all unused copper – I would have bought a 1/64” with the trace clearance set at .055”.

Always vacuum Out the Dust

The FR1 sheet needs to be as fitted as possible.  The first picture shows the results when I didn’t vacuum out before starting a run.

 

 

That’s it for now.  I wanted to document additions to my list of Othermill “what works for me”.  Perhaps these tips will be useful for you.

 

 

Thanks for reading this far.  Please find many things to smile about.

More Time With the Othermill

Tags

,

This is a short post so that I can collect my thoughts on my recent experience with the Othermill.

I’m starting to test the firmware for the Ladybug Blue Lite.  The Ladybug Blue Lite is an expansion to the Ladybug offering.  The Ladybug Blue Lite differs from the Ladybug Shield in the following ways:

  • the Arduino and BLE shield are replaced with the nRF51822.  Instead of stackable shields there is one PCB.
  • it does not adjust pumps.  It only updates pH and EC readings.  I found many instances where I just wanted the pH and EC.
  • it runs on a LiPo battery.  I wanted to be able to put the Ladybug Blue Lite around my outside garden.

My goal is to get to reading a pH value and then an EC value using the same circuitry I am designing within the Kicad schematic.  In order to reach the first goal of getting a pH reading, I built a PCB using my Othermill.

OthermillpHBoB

Areas where my skills at using the Othermill have evolved after this effort:

  • I clear off non-copper areas.  I found that when I did not do this I would get shorts.  I used the technique I discussed in this post.
  • while the pitch between the pins of the MCP6242 requires a 1/100” mill, longer tracks should be at least 40 mils.  It was too easy for the track to get an opening when smaller.
  • Use a spacing of 5.08mm (twice 2.54mm header board spacing) when using headers.  This way, the headers can have more copper around the hole.  It is all too easy to burn off the copper when the copper around the hole is small.  Besides, it is difficult to cleanly mill and have enough copper at the 2.54mm spacing.
PadSizeForOthermillHeaders
  • I found using a hotter setting (say 350˚C) on the reflow gun and solder and lightly touching the solder was a better technique than soldering at a lower temperature. The less time putting heat on the copper the better chance I was having with the copper strips not coming off.
  • Ugh.  Still challenged by the name extension for the Gerber outline file.  Kicad creates a file with a .gbr extension.  I keep forgetting to change the extension to .gko.  I use an Automator script to rename..however I’m not having much luck getting the script to change the extension name (even though after Googling how to do this it should be ….grrrr…ummm…obvious).

Areas where I am left wondering:

  • Using the 1/100” mill – even though I used a 1/8” and then a 1/32” prior – takes a very long time.  A lot of what gets milled are small areas that don’t need to be.  I wish Otherplan allowed me to “cut out” those areas that don’t need the 1/100” mill.  For example, in this case the 1/100” mill was only needed to cut out the copper between the pins of the MCP6242.
  • At some point “soon” I should get high(er) quality mills in the sizes that make the most sense for making this type of PCB.  I’m thinking most likely a 1/8”, 1/32” and 1/100”.  Right now the UI of the Otherplan supports 3 mills on a project.  I believe there is a way to get into the files and add more.  Perhaps that’s something I should look into.  I’d like to minimize the 1/100” by adding a 1/64”.
  • I wonder if there is FR-1 boards that have a “better” copper layer such that it is more difficult for the copper tracks to get pulled up.
  • I wonder if there is a spray that I could apply to the board so that there is a protective layer above the copper as there is on the PCBs I get from OSH Park.

 

That’s it for now…on to seeing if the prototype works!

Using Eclipse to Program the nRF51822

In a previous post I discussed using Arm’s Keil IDE and Nordic’s nRF81 DK to program the nRF51822.  I was right in assuming this would be the fastest way (for me given I had no experience with any of the supported IDEs).  I wasn’t satisfied with the Keil IDE because:

  • the IDE is Windows only.  I use a Mac.  While I can run the IDE in Windows within a Parallels VM, the look and keyboard interactions are tedious and quite frankly far less than a delightful experience.  I found myself mentally switching between how to do things on the Mac versus on Windows.  Also, the look of the Windows is far from crystal clear, looking more like a bitmapped image of a Window than something I am directly interacting with.
  • the IDE will eventually cost a lot.  While exploration up to 32K is free, I did not like the feeling this ceiling is looming.  In addition, support is extremely important AND expensive for the company to provide.  They deserve some amount of money for the IDE.  I would not get the level of support I would want with this proprietary program.  I also didn’t like paying for a Windows version when I am a Mac user.

I decided to give the Eclipse IDE a try.   The forums appear vibrant and accepting, similar to the Arduino community that has been so supportive.

Benefits of the Eclipse IDE

  • it is written in Java which means I can run it “natively” on my Mac. 
  • it is a mature environment. The Eclipse Wikipedia entry states the initial release was in November, 2001.
  • it has a lot of smart people organized around evolving the IDE and releasing stable releases.  Mars was release on June 24 (which – oh my! is today!).  The numbers are impressive.  This Eclipse newsletter notes: The Mars release is the expression of the effort of 379 committers and 351 contributors working on approximately 65 million lines of code for seventy nine separate open source projects.
  • it is powerful.  I was able to build, flash the nRF51 DK, and do source line debugging all from within the Eclipse IDE.  I guess the 65 million lines of code are being (mostly?) productive.
  • it seems to be used by a vibrant, large, and ever growing community.  The wikipedia entry lists several programming languages that are supported.  I am anectodally seeing a growing use of the Eclipse IDE by Universities as well as folks like me. 

Concerns with using Eclipse

  • While there is vision around concepts of  a Workspace, Project, and Perspective which provide strong visual to set the context of how to interact with the Eclipse IDE, there are usually several ways of doing something which gets confusing and it is incredibly rich.  This is not surprising given the incredible amount of contributions and the number of years Eclipse has been around. 
  • There is a loose coupling between Eclipse and the C/C++ environment.  For example, a workspace can either have a managed Makefile or a Makefile.  I am using a Makefile.  When I update the Eclipse IDE with an #include path, the source path in the Makefile is not updated.  I have to remember to update the Makefile to fix symbol unresolved errors.
The above is not a comprehensive list.  Just some ideas that come to my mind.  What I want to focus on is what it takes to set up an Eclipse environment on the Mac for programming the nRF51822.

The Goal

The goal of this post is to provide enough information such that the Mars version of Eclipse and the nRF51 SDK (current version 8.1) can build, flash to the chip, and debug apps written to the nRF822’s cortex-m0 µC and BLE hardware.
 

Thanks to Those That Went Before

  • The Eclipse team for bringing us a well done, freely available IDE that enables us to embrace more micro controller options.
  • Vidar Berg – a Nordic employee – gave excellent answers to support questions I posted in Nordic’s developer support. He is also the author of the post “Development with GCC and Eclipse” that served as the backbone to this post.
  • Nguyen Hoan Hoang – a consultant who is active on the Nordic DevZone – tirelessly answered my questions.  He is inspirational in his knowledge and exceptionally giving in his feedback.  I found his web site to be very helpful.

Non-Goal

It is not the goal to redo the tutorials of others.  The challenge I had with existing tutorials included:
  • there are enough differences in setting up the Eclipse environment on the Mac versus Windows that I found myself having to switch between tutorials and figure out what steps were relevant to installing the environment on the Mac.
  • there are different combinations of how folks wish to program for the nRF51822.  Some stick with the GCC.  I wanted to use the Eclipse IDE because for me it is easier to use Eclipse’s debugging environment and more efficient to fix bugs as well as write code.
  • there are different ways to build a binary in Eclipse – with a Makefile and opting for Eclipse to figure it out (managed Makefile).  I didn’t understand this concept at first…which got me confused for awhile.
  • some of the recommendations within the tutorials didn’t make sense to me and ultimately were not the best way to do things.  This too took me awhile to figure out :-).
Yet, there is A LOT of great info on getting the Eclipse environment.  I will refer to these sections instead of redoing them here.  

Setting up the Eclipse IDE + GNU ARM Tools + nRF51 SDK on a Mac

 Vidar’s tutorial, Development with GCC and Eclipse, was the most helpful post on getting set up.  I call Vidar out by name because during my process I had several questions I directed to Nordic support.  Vidar’s answers where consistently excellent / useful.  I had no context of what all these steps meant.  It took me several times to gain this context before I succeeded in all the task.  The tutorial:
  • installs the Kepler version of the Eclipse IDE.  The current version is Mars (this link shows the progression of Eclipse releases from 2.0 through June, 2015).  Since the Eclipse IDE is evolving, before installing the Eclipse IDE gain familiarity with the current version…accommodations will need to be made for steps that have changed based on the evolution of the Eclipse IDE.
  • is more approachable to someone running Windows instead of the Mac.  The recommendations for the Mac are sparse and not specific.  

Steps

With that said, on with the steps
  • Download the tools Vidar recommends( note: before installing the tools read the bullets below).  Given installation on a Mars – as Vidar points out – the GnuWin32 and nRF-tools aren’t part of the Mac environment.  Also check about the latest versions.  Posts are dated the minute they are published.  Perhaps the nRF51 SDK is on version 25…right now the latest is 8.1.  As of this writing the latest Eclipse version is Mars (link here.  The download is “Eclipse IDE for C/C++ Developers” ).  
  • Install Eclipse.  When installing, the IDE asks for a Workspace.  Workspaces are a concept that needs to be understood.  There is an article from 2005 that helped me better understand the role Workspaces serve within Eclipse.  While the post uses old Windows UI, the properties, concepts, and the first two recommendations still ring true.  After several attempted installs of Eclipse, what I would recommend is to link the workspace to a folder that represents projects of the same category.   So for example, /Users/happyday/documents/nRF51822 might be mine.  Do not link your workspace to a path where the Eclipse app is installed.  If you link the workspace to a path within the folder structure where Eclipse is installed source code, binaries…your hard work!…will most likely be deleted.
  • Open Eclipse.   If the error:  “Version 1.6.0_65 of the JVM is not suitable for this product.” occurs, follow the directions posted in this Stack Exchange post. I got this error the first time I tried to open Eclipse on my Mac.  After googling how to fix it, the advice in the Stack Exchange post fixed the problem.
  • Install the nRF51 SDK. Unzip the nRF51 SDK into a folder under the workspace folder.  Following the example, /Users/happyday/documents/nRF51822/nRF51_sdk might be mine.
  • Install the GNU tools.  The Mac version of the GNU tools for ARM processors is compressed using tar.  The file will decompress when clicked on.  Place the folder structure below /usr/local .  This is the assumed path for the GNU tools.  Install the GNU Arm Eclipse plug-in from here.  You might also want to change some of the default preferences as discussed in this link.
  • Install the J-Link tools.  The nRF51 DK includes a J-Link OB chip.  This is wonderful because J-Link can be used to flash the nRF51 as well as do source line debugging.  Here is the link to instructions and files needed for installation.  I added JLink’s executable path(in my case this was /Applications/SEGGER/JLink)  to the $PATH variable following so that terminal would find it no matter what folder i was in.  This is not necessary if running JLink commands within the Eclipse IDE.  However, occasionally I would go to a terminal window and try out a JLink command.
  • Install the EmbSysRegView plug-in.  As noted on the EmbSysRegView project page: “EMBedded SYStems REGister VIEW is an Eclipse Plugin which is designed for monitoring and modifying memory values of embedded devices. Therefore it offers a structured display of the special functions registers (SFR). While debugging, the register values are presented in the Hexadecimal (HEX) and Binary (Bin) column of the view.”  Instructions for installing the plug-in are under the “Install and Use” tab.  Installing EmbSysRegView has a tricky step on Macs.  In Vidar’s instructions, he states under setting up Eclipse: “To install the register view file for Nordic Semiconductor devices copy file nrf51.xml from ../nRF51 SDK/SVD/ to the folder.”  On the Mac, this file needs to be copied within Eclipse.app.  On my Mac, Eclipse is installed in /Applications/Eclipse.  Within the file manager, I right-click on Eclipse.App and choose “Show Package Contents”

Eclipse_App_Package_Contents

now follow Vidar’s instructions on where to copy the nrf51.xml file and the additional Eclipse set up steps to get the registers showing within the Eclipse IDE.

Building a BLE Peripheral App

As most software folks know, the instructions to compile and build an app are usually contained within a Makefile.  A feature of IDEs like Arduino, XCode, Eclipse and others is creating and updating compile and build commands without having to be an expert in the arcane options and commands of a makefile.

Eclipse features both the ability for the IDE to manage compiling and linking as well as using a makefile.  It all depends how a project is created.  If the choice is to use an existing makefile – it is up to you to keep the makefile source and include files in sync.  I’ll touch on this in a bit.

Creating a Managed MakeProject

When creating an empty C project, all the options that would be in a makefile are set in the Eclipse Tool settings.

EclipseToolSettingsDialog

UI to set compiler and link settings when there is no makefile

I tried using a managed makefile through an example from a web site labeled “Embedded System Development”  I ended up not going down this path because it would take me longer than I like to understand all the options since I am less familiar with the C tool chain.  I’ll eventually have to have a better grasp on compiling and building, but for now I will rely on makefiles that have been written by Nordic engineers specific for the nRF51 DK.

Using an Existing Makefile

I ended up successfully debugging a BLE app on the nRF51822 by starting with a makefile.  The Tool Settings dialog looks different when a Makefile is used.

ToolSettingWithMakefileInEclipse

Tool Settings when this is a makefile

Starting with an SDK Example

I started with the Makefile and Linkfile located at …/examples/ble_peripheral/ble_app_template/pca10028/s110/armgcc To create a project named EC_Test:

  • created folder named EC_Test
  • copied the Makefile file and ble_app_templage_gcc_nrf51.ld files from the ble_app_template…/armgcc location into EC_Test
  • copied the ble_app_template main.c into EC_Test
  • renamed ble_app_template_gcc_nrf51.ld EC_test.ld so the linker file felt like it was part of the EC_Test project
  • created a new project in Eclipse that starts off with a Makefile: File -> New -> Makefile Project With Existing Code
  • pointed to the folder EC_Test (it contains the makefile).  
  • Edited the Makefile
    • searched for ble_app_template_gcc_nrf51.ld and change to EC_test.ld
    • changed PROJECT_NAME to EC_Test
    • removed the bin target (I discuss later why only the .hex file is needed).
    • corrected path locations:
      • added a variable SDK_PATH, e.g.:  SDK_PATH = /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/nrf51_sdk
      • changed the variable TEMPLATE_PATH, e.g.:  TEMPLATE_PATH = $(SDK_PATH)/components/toolchain/gcc
      • found/replaced ../../../../../.. with $(SDK_PATH)
  • Added a folder to the Eclipse project to hold main.c :  focus the cursor on the project name, EC_Test,  then right-click and choose New->Folder (I named my folder src)
  • Moved main.c into the src folder.
Note: the Makefile requires additional modifications which are discussed below.

Adding Source and Include Files

Remember: When you add an include/source file to the Eclipse project, the paths (i.e.:  INC_PATHS and C_SOURCE_FILES must also be added to the makefile.

Because Eclipse does not use a makefile, in order to take full advantage of the Eclipse IDE  (who doesn’t want source level debugging AND printf’s???), adding/deleting source and include files from the project is a two step process.  The include files needs to be added to the IDE and also to the makefile.  To add the include files, I click on the project folder and then type in ⌘i to bring up the Path dialog box.

Adding Include Files in Eclipse

The makefiles include with the SDK examples are optimized.  Follow Vidar’s “Edit Makefile” steps (found in this post) to add in instructions to make to include debug information and be a bit more verbose.

When I was building a program to test the nRF51 DK talking to an ADS1015, I started with the ble_app_template project that comes with the nRF51 SDK.  The makefile listed a lot of include files that main.c was dependent on.  Since Eclipse does not get info from the makefile, I wanted to tell Eclipse about it.  Here is a screen shot of the list of include files:

EclipseIncludeFiles

This help file describes the UI that lets you tell Eclipse the path to include files.  I found the fastest way to add include files was to:

  • get the list of include paths from the makefile.  These are listed using the INC_PATHS variable:
INC_PATHS += -I../../../../../../components/drivers_nrf/uart
  • get the path to the nRF51 SDK into the clipboard.  Since I have Alfred installed, I took advantage of Alfred’s ability to create snippets so that I could quickly past the path to the SDK and then attach the rest.  In my case, the snippet is “${workspace_loc}/../../Nordic/nRF51/nrf51_sdk/“
  • click to add an include, past the snippet, then write in the rest of the path.  In the example above, the rest of the path is “/components/drivers_nrf/uart
New source and/or include file added to the Eclipse IDE must be added to the makefile by adding the include’s path as an additional INC_PATHS +=  and C_SOURCE_FILES += in order to compile and build the app.

Loading the App Onto nRF51

A BLE app requires two binaries to be loaded onto the nRF51:

  • The SoftDevice library provided by Nordic that handles calls and replies from BLE.  I use the library for a peripheral, s110_softdevice.hex.  This file comes with the nRF51 SDK in the …/components/softdevice/s110/hex/ folder.
  • The app.
The SoftDevice need only be loaded once.  Every time the app is rebuilt it needs to be reloaded onto the nRF51.  JLink – a utility that comes when the SEGGER tools are installed (see above) – is used on the Mac to load binaries.  First thing is to figure out what commands and how to format them so the JLink utility does what we want.  The second thing is to figure out how to integrate loading into Eclipse.

Loading Binaries

Since I program on a Mac, I’d rather use a native Mac program than Nordic’s nRFgo Studio to copy the app’s binary onto the nRF51.  SEGGER provides the JLinkExe utility (I also call JLinkExe JLink) when the SEGGER tools are installed (installing the SEGGER tools was outlined as an earlier step).  I ended up creating makefile targets (link to makefile overview) that evolved from this post on programming the nRF51 DK + Eclipse on a Mac.  I start this section going through concepts that were new to me and then discuss how the flash.jlink file integrates within the makefile so that loading the app becomes part of Eclipse.

The Device Name

When starting JLink, a mandatory parameter is the name of the device.  As noted within a Nordic support reply message I received: Regarding device types, the xxAC variant has 32K of RAM as opposed to xxAA with 16K (overview overview of chip variants can be found in the compatibility matrix.) 

For example, if you open a terminal window and then type in (assuming the JLink utilities can be found):

JLinkExe -device nrf51422_xxac

(note: the nRF51 DK uses the nrf51422 and has 32K of RAM)

The JLink utility starts up with the correct device info for the nRF51 DK.  Now that the JLink utility is running, the files are loaded.  But I got confused on what file format to load a .bin or a .hex?  After all – the makefile examples seemed to make both binaries.  Why?

Use the HEX file format

Some of the makefiles I looked at built both a .hex and a .bin file.  I asked Nordic support this question and got the answer:

I do not see any reasons for using the .bin files instead of .hex files unless creating an image for doing DFU. The Keil projects does for instance not output a .bin file by default.
.hex files specifies which addresses the data/code shall be loaded into, whereas .bin files use byte padding to get the correct address offsets when there are gaps between data/code and it does not contain info about the start address.  So with .hex files Jlink will now exactly where to load data.  

The important point (besides curiosity in trying DFU which I’ll do at some point) was the .hex files contained the address of where the data and code should be loaded.

Copy Binaries to the nRF51 DK

 I found two commands the JLink utility uses for loading a binary:

  • loadbin:  loadbin takes a .bin formatted file.  I started using the loadbin command because many of the posts used this command.  The challenge I had was ensuring I had the right address (i.e.: loadbin <filename.bin> <address to load binary at>
  • loadfile: loadfile takes a .hex formatted file.  I prefer using loadfile because there is no need to specify an address.
For example, after starting the JLinkexe utility as noted above, the command to load a binary would look like:
loadfile _build/nrf51422_xxac_s110.hex

here the name of the app is nrf51422_xxac_s110.hex.  Hmm…I was using the name that came with the makefile.  Pretty darn ugly.

Make a Flash Programming Target

I integrated flash programming into Eclipse by creating three flash target within the makefile.  First, setting up the variables:

JLINK_OPTS = -device nrf51422_xxac 

JLINK = JLinkExe $(JLINK_OPTS)

then setting up the targets:

#############################################################################

# Target: erase

# remove all binaries from the nRF51

#############################################################################

erase: erase.jlink

$(JLINK) erase.jlink

erase.jlink:

printf “erase\nr\nexit\n” > erase.jlink

#############################################################################

# Target: flash_softdevice

# load the s110 softdevice onto the nRF51

#############################################################################

flash_softdevice: flash_softdevice.jlink

$(JLINK) flash_softdevice.jlink

flash_softdevice.jlink:

printf “loadfile /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/nrf51_sdk/components/softdevice/s110/hex/s110_softdevice.hex\nr\ng\nexit\n” >flash_softdevice.jlink

#############################################################################

# Target: flash

# load app onto the nRF51

#############################################################################

flash: flash.jlinka

$(JLINK) flash.jlink

flash.jlink:

printf “loadfile /Users/margaret/Documents/EHAL/ARM/Nordic/nRF51/nrf51_sdk/examples/ble_peripheral/ble_app_template/pca10028/s110/armgcc/_build/nrf51422_xxac_s110.hex\nr\ng\nexit\n” > flash.jlink

(the paths and filenames are relative to what I have. You will need to change these).  There is no need to reload the s110 softdevice once loaded so I broke out loading the softdevice from loading the app.

Run Target from Eclipse

The menu item – Make Target – off the project in the navigator:

Run a Target in Eclipse UI

is where to start.  First, let Eclipse know about the target in the makefile by going to the Create… dialog and entering the target name.  Then run the target commands by going to the Build… dialog and choosing the target to run.  

Debugging

 Assuming the changes to the makefile were made as discussed above, source line debugging is straightforward.  Just explore Run -> Debug Configurations to get going.  It is a little bit trickier is getting printf() to work.

printf()

There seem to be several ways to get to writing out to a serial port.  The one that worked for me was SEGGER’s Real Time Terminal (RTT) utility.   Noted in this overview:  “…SEGGER_RTT_printf(), which can be used to print formatted strings via RTT, directly, without a standard library.”  There is a Nordic Devzone post where it is states RTT is the way to go for real time apps that use BLE.

Include the RTT library within the Eclipse IDE and the makefile (see adding Source and Include Files above).  The library for the 8.1 nRF51 SDK is located at …/components/RTT .

Include SEGGER_RTT.h to the project by writing  #include”SEGGER_RTT.h” near the top of the main.c file in the project

 Include printf() statements, e.g.:  SEGGER_RTT_printf(0,“The raw ADC value is: %d\n”,rawADC);

Open a Terminal window

Start the RTT client, e.g.: jlinnkrttclient

Note: sometimes the RTT client couldn’t connect.  While using options that help these utilities (e.g.: -if SWD and -device nrf51422_xxac) might help clarify any confusion, I found my challenge with connecting to be because the port was busy.  For example, if I started the RTT client before starting the debug session (which uses the SWD port).  At this point, I could use Unixy commands (which I am no expert in) to kill the JLink apps running on the Mac, or start over and unplug/plug in the nRF51 DK from the USB port.  This post gives some detail on SEGGER’s tools for the Mac  

 A Note on Navigation

To use commands like those for search, make sure to highlight the scope you wish to search on.  For example, searching on the project can be done by highlighting/left clicking on the project within the navigator.  Then when telling the Search -> C/C++ to search for a string (e.g.: *SCL* to search for occurrences of scl and SCL within the files) the radio button for all files within the project is available.

That’s It For Now

I now have a very rudimentary understanding of how to use Eclipse.  Nothing is very difficult, however there is A LOT of pieces that need to come together.  I am thrilled that I can have an IDE that runs on the Mac.  I am thrilled to have source level debugging.  The folks on the Eclipse team have done amazing work that has benefited so many of us.  Time to get coding.

 

 

Thanks for reading this far. 

 

Follow

Get every new post delivered to your Inbox.