2017年5月30日 星期二

nRF52 in SDK12 : activate the UART function

There are few articles about nRF52 SDK12, and UART print is a basic function for developers.

This example is modified from ble_app_template
located in : nRF5_SDK_12.2.0_f012efa\examples\ble_peripheral\ble_app_template

Board:nRF52832/ SDK12/s132

STEP 0. project config
I create a project folder "ble_app_uart_porting_file"
located in : nRF5_SDK_12.2.0_f012efa\examples\ble_peripheral\ble_app_uart_porting_file

add  these files into nrf_library
"app_uart_fifo.c"
"app_fifo.c"
"retarget.c"
located in nRF5_SDK_12.2.0_f012efa\components\libraries\uart

and remove "app_uart.c"

next  include path to project option

STEP 1. modify "sdk_config"
I suggest first open sdk_config.h

add these about line  3004
// <h> nRF_Libraries 
//==========================================================
// <q> APP_FIFO_ENABLED  - app_fifo - Software FIFO implementation
 

#ifndef APP_FIFO_ENABLED
#define APP_FIFO_ENABLED 1
#endif

then add these about line 3080
// <e> APP_UART_ENABLED - app_uart - UART driver
//==========================================================
#ifndef APP_UART_ENABLED
#define APP_UART_ENABLED 1
#define APP_UART_DRIVER_INSTANCE 0
#endif

then add these about line 3506

// <q> RETARGET_ENABLED  - retarget - Retargeting stdio functions
 

#ifndef RETARGET_ENABLED
#define RETARGET_ENABLED 1
#endif

and then, go to  Configuration wizard
open nrf_libraries and check :

 then open nrf_drivers and check


for your UART configuration, I suggest to select in configuration wizard - nrf_drivers
in this example, I select as




































but if you prefer to add code , please add in sdk_config.h Text Editor
about line  2724

// <e> UART_ENABLED - nrf_drv_uart - UART/UARTE peripheral driver
//==========================================================
#ifndef UART_ENABLED
#define UART_ENABLED 1
#endif
#if  UART_ENABLED
// <o> UART_DEFAULT_CONFIG_HWFC  - Hardware Flow Control
 
// <0=> Disabled 
// <1=> Enabled 

#ifndef UART_DEFAULT_CONFIG_HWFC
#define UART_DEFAULT_CONFIG_HWFC 0
#endif

// <o> UART_DEFAULT_CONFIG_PARITY  - Parity
 
// <0=> Excluded 
// <14=> Included 

#ifndef UART_DEFAULT_CONFIG_PARITY
#define UART_DEFAULT_CONFIG_PARITY 0
#endif

// <o> UART_DEFAULT_CONFIG_BAUDRATE  - Default Baudrate
 
// <323584=> 1200 baud 
// <643072=> 2400 baud 
// <1290240=> 4800 baud 
// <2576384=> 9600 baud 
// <3862528=> 14400 baud 
// <5152768=> 19200 baud 
// <7716864=> 28800 baud 
// <10289152=> 38400 baud 
// <15400960=> 57600 baud 
// <20615168=> 76800 baud 
// <30801920=> 115200 baud 
// <61865984=> 230400 baud 
// <67108864=> 250000 baud 
// <121634816=> 460800 baud 
// <251658240=> 921600 baud 
// <268435456=> 57600 baud 

#ifndef UART_DEFAULT_CONFIG_BAUDRATE
#define UART_DEFAULT_CONFIG_BAUDRATE 30801920
#endif

// <o> UART_DEFAULT_CONFIG_IRQ_PRIORITY  - Interrupt priority
 

// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 
// <4=> 4 
// <5=> 5 
// <6=> 6 
// <7=> 7 

#ifndef UART_DEFAULT_CONFIG_IRQ_PRIORITY
#define UART_DEFAULT_CONFIG_IRQ_PRIORITY 7
#endif

// <q> UART_EASY_DMA_SUPPORT  - Driver supporting EasyDMA
 

#ifndef UART_EASY_DMA_SUPPORT
#define UART_EASY_DMA_SUPPORT 1
#endif

// <q> UART_LEGACY_SUPPORT  - Driver supporting Legacy mode
 

#ifndef UART_LEGACY_SUPPORT
#define UART_LEGACY_SUPPORT 1
#endif

// <e> UART0_ENABLED - Enable UART0 instance
//==========================================================
#ifndef UART0_ENABLED
#define UART0_ENABLED 1
#endif
#if  UART0_ENABLED
// <q> UART0_CONFIG_USE_EASY_DMA  - Default setting for using EasyDMA
 

#ifndef UART0_CONFIG_USE_EASY_DMA
#define UART0_CONFIG_USE_EASY_DMA 1
#endif

#endif //UART0_ENABLED
// </e>

// <e> UART_CONFIG_LOG_ENABLED - Enables logging in the module.
//==========================================================
#ifndef UART_CONFIG_LOG_ENABLED
#define UART_CONFIG_LOG_ENABLED 0
#endif
#if  UART_CONFIG_LOG_ENABLED
// <o> UART_CONFIG_LOG_LEVEL  - Default Severity level
 
// <0=> Off 
// <1=> Error 
// <2=> Warning 
// <3=> Info 
// <4=> Debug 

#ifndef UART_CONFIG_LOG_LEVEL
#define UART_CONFIG_LOG_LEVEL 3
#endif

// <o> UART_CONFIG_INFO_COLOR  - ANSI escape code prefix.
 
// <0=> Default 
// <1=> Black 
// <2=> Red 
// <3=> Green 
// <4=> Yellow 
// <5=> Blue 
// <6=> Magenta 
// <7=> Cyan 
// <8=> White 

#ifndef UART_CONFIG_INFO_COLOR
#define UART_CONFIG_INFO_COLOR 0
#endif

// <o> UART_CONFIG_DEBUG_COLOR  - ANSI escape code prefix.
 
// <0=> Default 
// <1=> Black 
// <2=> Red 
// <3=> Green 
// <4=> Yellow 
// <5=> Blue 
// <6=> Magenta 
// <7=> Cyan 
// <8=> White 

#ifndef UART_CONFIG_DEBUG_COLOR
#define UART_CONFIG_DEBUG_COLOR 0
#endif

#endif //UART_CONFIG_LOG_ENABLED
// </e>

#endif //UART_ENABLED
// </e>

STEP 2.  add code in main.c


#include "app_uart.h"

#define UART_TX_BUF_SIZE                256                                         
#define UART_RX_BUF_SIZE                256
#define UART_MAX_DATA_LEN  (20)   


void uart_event_handle(app_uart_evt_t * p_event)
{

    static uint8_t data_array[UART_MAX_DATA_LEN]; 
    static uint8_t index = 0;
    uint32_t       err_code;

    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
}


static void uart_init(void)
{
    uint32_t                     err_code;
    const app_uart_comm_params_t comm_params =
    {
        RX_PIN_NUMBER,
        TX_PIN_NUMBER,
        RTS_PIN_NUMBER,
        CTS_PIN_NUMBER,
        APP_UART_FLOW_CONTROL_DISABLED,
        false,
        UART_BAUDRATE_BAUDRATE_Baud115200
    };

    APP_UART_FIFO_INIT( &comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_handle,
                       APP_IRQ_PRIORITY_LOWEST,
                       err_code);
    APP_ERROR_CHECK(err_code);
}


int main(void)
{
    uint32_t err_code;
    bool     erase_bonds;

    // Initialize.  
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
    uart_init();   
    printf("system start\r\n");
 
    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    //timers_init();
    buttons_leds_init(&erase_bonds);
    ble_stack_init();
    peer_manager_init(erase_bonds);
    if (erase_bonds == true)
    {
        NRF_LOG_INFO("Bonds erased!\r\n");
    }
    gap_params_init();
    advertising_init();
    services_init();
    conn_params_init();

    // Start execution.
    NRF_LOG_INFO("Template started\r\n");
    application_timers_start();
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
    APP_ERROR_CHECK(err_code);
    

    // Enter main loop.
    for (;;)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            power_manage();
        }
    }
}

and the result print by UART will be like :
(I used SSCOM. a UART print console on PC )




























NOTE:
1. in SDK12, the developer must assign UART, PWM,WDT...etc in sdk_config.h
otherwise, even they include all the paths and files needed, they still can not open UART.


reference:
https://devzone.nordicsemi.com/question/107836/undefined-symbol-problem-app_uart_get/

http://gaiger-programming.blogspot.tw/2016/12/nrf51-make-printf-works-well.html?m=1

2017年5月28日 星期日

nRF52 fstorage (1):a simple example

There are few examples about SDK12, and that is the reason why I wrote this.

the pstorage function(persistent storage)  of nRF5X has been changed as fstorage in SDK12.
the APIs have been changed. So I write the example for demonstrating how to adopt those .

Board :nRF52832/SDK12/s132

STEP 0.
I create the project file "ble_app_fstoarge_test_file" in example\ble_peripheral
And the example is coopied from  nRF5_SDK_12.2.0_f012efa\examples\ble_peripheral\ble_app_template


project config:
please include all the files in this folder
nRF5_SDK_12.2.0_f012efa\components\libraries\fds

include
"fds.h"
"fds.c"
"fstorage.h"
"fds.c"

and do not forget to paste the path to project option



STEP 1.
the line begins about 3124 in "sdk_config.h" should be modified as  following for fds purpose.
("sdk_config.h" is located in nRF5_SDK_12.2.0_f012efa\examples\ble_peripheral\ble_app_fstorage_test_file\pca10040\s132\config)

add these codes to test fstorage

// <e> FDS_ENABLED - fds - Flash data storage module
//==========================================================
#ifndef FDS_ENABLED
#define FDS_ENABLED 1
#endif
#if  FDS_ENABLED
// <o> FDS_OP_QUEUE_SIZE - Size of the internal queue. 
#ifndef FDS_OP_QUEUE_SIZE
#define FDS_OP_QUEUE_SIZE 4
#endif

// <o> FDS_CHUNK_QUEUE_SIZE - Determines how many @ref fds_record_chunk_t structures can be buffered at any time. 
#ifndef FDS_CHUNK_QUEUE_SIZE
#define FDS_CHUNK_QUEUE_SIZE 8
#endif

// <o> FDS_MAX_USERS - Maximum number of callbacks that can be registered. 
#ifndef FDS_MAX_USERS
#define FDS_MAX_USERS 8
#endif

// <o> FDS_VIRTUAL_PAGES - Number of virtual flash pages to use. 
// <i> One of the virtual pages is reserved by the system for garbage collection.
// <i> Therefore, the minimum is two virtual pages: one page to store data and
// <i> one page to be used by the system for garbage collection. The total amount
// <i> of flash memory that is used by FDS amounts to @ref FDS_VIRTUAL_PAGES
// <i> @ref FDS_VIRTUAL_PAGE_SIZE * 4 bytes.

#ifndef FDS_VIRTUAL_PAGES
#define FDS_VIRTUAL_PAGES 3
#endif

// <o> FDS_VIRTUAL_PAGE_SIZE  - The size of a virtual page of flash memory, expressed in number of 4-byte words.
 

// <i> By default, a virtual page is the same size as a physical page.
// <i> The size of a virtual page must be a multiple of the size of a physical page.
// <1024=> 1024 
// <2048=> 2048 

#ifndef FDS_VIRTUAL_PAGE_SIZE
#define FDS_VIRTUAL_PAGE_SIZE 1024
#endif

#endif //FDS_ENABLED
// </e>

// <e> FSTORAGE_ENABLED - fstorage - Flash storage module
//==========================================================
#ifndef FSTORAGE_ENABLED
#define FSTORAGE_ENABLED 1
#endif
#if  FSTORAGE_ENABLED
// <o> FS_QUEUE_SIZE - Configures the size of the internal queue. 
// <i> Increase this if there are many users, or if it is likely that many
// <i> operation will be queued at once without waiting for the previous operations
// <i> to complete. In general, increase the queue size if you frequently receive
// <i> @ref FS_ERR_QUEUE_FULL errors when calling @ref fs_store or @ref fs_erase.

#ifndef FS_QUEUE_SIZE
#define FS_QUEUE_SIZE 4
#endif

// <o> FS_OP_MAX_RETRIES - Number attempts to execute an operation if the SoftDevice fails. 
// <i> Increase this value if events return the @ref FS_ERR_OPERATION_TIMEOUT
// <i> error often. The SoftDevice may fail to schedule flash access due to high BLE activity.

#ifndef FS_OP_MAX_RETRIES
#define FS_OP_MAX_RETRIES 3
#endif

// <o> FS_MAX_WRITE_SIZE_WORDS - Maximum number of words to be written to flash in a single operation. 
// <i> Tweaking this value can increase the chances of the SoftDevice being
// <i> able to fit flash operations in between radio activity. This value is bound by the
// <i> maximum number of words which the SoftDevice can write to flash in a single call to
// <i> @ref sd_flash_write, which is 256 words for nRF51 ICs and 1024 words for nRF52 ICs.

#ifndef FS_MAX_WRITE_SIZE_WORDS
#define FS_MAX_WRITE_SIZE_WORDS 1024
#endif

#endif //FSTORAGE_ENABLED
// </e>

the "sdk_config.h" is first time added in SDK12, almost every function must config in this file.


STEP 2.
example code:
add these functions to test fstorage
about  line 706

/*the definition for fstorage*/
static volatile uint8_t write_flag=0;
static uint8_t fs_callback_flag; 
#define NUM_PAGES 4
#define PAGE_SIZE_WORDS 256

static uint32_t data;
static uint32_t flash_data[4];

/**@brief Function for the Power manager.
 */
static void power_manage(void)
{
    uint32_t err_code = sd_app_evt_wait();

    APP_ERROR_CHECK(err_code);
}


static void fs_evt_handler(fs_evt_t const * const evt, fs_ret_t result)
{
    if (result != FS_SUCCESS)
    {
        bsp_indication_set(BSP_INDICATE_FATAL_ERROR);
    }
  else
  {
    NRF_LOG_INFO("fstorage command successfully completed\n\r");    
    fs_callback_flag = 0;
  }
}


static void fstorage_test(void)
{
 PRINTF("fstorage_test\r\n");
 
 FS_REGISTER_CFG(fs_config_t fs_config) =
 {
  .callback  = fs_evt_handler, // Function for event callbacks.
  .num_pages = NUM_PAGES,      // Number of physical flash pages required.
  .priority  = 0xFE            // Priority for flash usage.
 };

 fs_ret_t ret = fs_init();
 if (ret != FS_SUCCESS)
 {
  bsp_indication_set(BSP_INDICATE_FATAL_ERROR);
 }
  
 // Erase one page (256*4).
 NRF_LOG_INFO("Erasing a flash page at address 0x%X\r\n", (uint32_t)fs_config.p_start_addr);  
 
 fs_callback_flag = 1;
 ret = fs_erase(&fs_config, fs_config.p_start_addr, 1, NULL);
 if (ret != FS_SUCCESS)
 {
  bsp_indication_set(BSP_INDICATE_FATAL_ERROR);
 }
 while(fs_callback_flag == 1)  { power_manage(); }
 
 //Read the first 4 words of the page
 NRF_LOG_INFO("Data read from flash address 0x%X: \r\n", (uint32_t)fs_config.p_start_addr);  
  
 for(int i=0; i<4; i++)
 {
  flash_data[i] = *(fs_config.p_start_addr + i);
  NRF_LOG_INFO("%X \r\n", flash_data[i]);    
 } 
  
  /*agatha*/  
 NRF_LOG_INFO("from agatha\r\n"); 
 
 data = 0x19950225;
 NRF_LOG_INFO("Writing data 0x%X to address 0x%X\r\n", data, (uint32_t)fs_config.p_start_addr );
 
 fs_callback_flag = 1;
 ret = fs_store(&fs_config, fs_config.p_start_addr, &data, 1, NULL); 
 if (ret != FS_SUCCESS)
 {
  bsp_indication_set(BSP_INDICATE_FATAL_ERROR);
 }
 while(fs_callback_flag == 1)  { power_manage(); }
 
 //Read the first 4 words of the page
 NRF_LOG_INFO("Data read from flash address 0x%X: \r\n", (uint32_t)fs_config.p_start_addr);  
    
 flash_data[2] = *(fs_config.p_start_addr);
 NRF_LOG_INFO("%X ", flash_data[2]);     
}


/**@brief Function for application main entry.
 */
int main(void)
{
 PRINTF("DEVICE START \r\n");
 
 uint32_t err_code;
    bool     erase_bonds;

    // Initialize.
    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    
    ble_stack_init();
 
    peer_manager_init(erase_bonds);
    if (erase_bonds == true)
    {
        NRF_LOG_INFO("Bonds erased!\r\n");
    }
    gap_params_init();
    advertising_init();
 
    services_init();
    conn_params_init();
 
  // Start execution.
    NRF_LOG_INFO("Proximity Start!\r\n");
    advertising_start();
  
  /*the example function of  fstorage*/
  PRINTF("start fstorage test\r\n");
  fstorage_test();
  

    // Enter main loop.
    for (;;)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            power_manage();
        }   
    }
}


these code demo how to :
initialize fstorage ->clear the code page  -> write at specific address (0x79000) ->read the address

and I define
the number of code pages and page_size should be a multiple of 4.

/*the definition for fstorage*/
static volatile uint8_t write_flag=0;
static uint8_t fs_callback_flag; 
#define NUM_PAGES 4
#define PAGE_SIZE_WORDS 256

static uint32_t data;
static uint32_t flash_data[4];

and the result print in segger_RTT will be like this :


NOTES:

1.this article only shows  the most basic way to use fstorage, and how to prevent the conflict of
power_manage and  fstorage. 

2.I register the space and clear/ write at the same time, so the space for data to store is limited .

3.I did not define the start code page , so the fstorage will start at 0x79000 (in default)
but the start point is able to be assigned by yourself.

try to assign start point in 
fs_config.p_start_addr

your could change the address 0x79000 as 0x78000 to observe the storaging address be entailed changed.

4.KNOWN insuffience :I have not implement how to use fstorage in interrupt (press button, UART...etc). Maybe I should write an example  relate to fstorage and interrupt ?

2017年4月19日 星期三

nRF5X: prevent bootloader from erased after programming

Board : nRF52832_xxac / SDK11/s132  (but not only these have the problem)

After add DFU service in application, we have to program bootloader. In nRFgo , the IROM must be look like this :


but after you compile and program your application again, and open nRFgo next time , you might see :
and that means your bootloader is erased by application.

That's because a mistake of project setting of application



this is project setting of SDK11/ble_app_template
the start point + size = 0x1c000 + 0x64000 = 0x7f000 = the end point.
So, the application is end at 0x7f000 in default.

But in the project setting of SDK 11/bootloader/ dual_bank_dfu_s132
It shows that bootloader start at 0x7a000.

So, if you program application again , it is necessary to erase bootloader for application space.


And the solution I suggest you is :


change application size  0x62000 ->0x5e000


0x1c000+ 0x5d000 = 0x79000 to prevent bootloader from erased after programming


Note:
let the application size smaller than the start point of bootloader for 0x2000 is more stable.

Reference :
https://devzone.nordicsemi.com/question/100609/sdk-12-bootloader-erased-after-programming/

nRF5x: how to use UICR as your signature

board: nRF52832_xxac_s132/ SDK11

After developing a firmware, you can use UICR as a mark to use .
because UICR can not be erased by DFU-OTA

STEP 1
copy file "uicr_config.h" at:
C:\..\nRF5x_SDK_11.0.0\examples\peripheral\uicr_config
to your project file.









STEP 2
open your project in IDE
add this in main.c :


//power by Agatha Kuan
//add uicr
#include "uicr_config.h"
then compile your project first.

STEP 3
open uicr_config.h
I add my family name like this:


// const uint32_t UICR_CLENR0    __attribute__((at(0x10001000))) __attribute__((used)) = 0xFFFFFFFF;
// const uint32_t UICR_RBPCONF   __attribute__((at(0x10001004))) __attribute__((used)) = 0xFFFFFFFF;
// const uint32_t UICR_XTALFREQ  __attribute__((at(0x10001008))) __attribute__((used)) = 0xFFFFFFFF;
const uint32_t UICR_ADDR_0x04 __attribute__((at(0x10001004))) __attribute__((used)) = 0x6e61754b;
//const uint32_t UICR_ADDR_0x80 __attribute__((at(0x1000108c))) __attribute__((used)) = 0x69617354;
//const uint32_t UICR_ADDR_0x84 __attribute__((at(0x10001084))) __attribute__((used)) = 0x6e656843;
//const uint32_t UICR_ADDR_0x88 __attribute__((at(0x10001090))) __attribute__((used)) = 0x53334955;
// const uint32_t UICR_ADDR_0x8C __attribute__((at(0x1000108C))) __attribute__((used)) = 0xFFFFFFFF;
// const uint32_t UICR_ADDR_0x90 __attribute__((at(0x10001090))) __attribute__((used)) = 0xFFFFFFFF;
// const uint32_t UICR_ADDR_0x94 __attribute__((at(0x10001094))) __attribute__((used)) = 0xFFFFFFFF;
// const uint32_t UICR_ADDR_0x98 __attribute__((at(0x10001098))) __attribute__((used)) = 0xFFFFF
then compile , program to your nRF52

STEP 4
open command line, type :


:: power by Agatha Kuan  2017.04.18

.\nrfjprog.exe --family NRF52 --memrd 0x10001004 --n 4

it shows:














be careful that the ascii shows in Little Endian
parameter "--n" write the length to read, "4" means 4 bytes.

Reference:
https://devzone.nordicsemi.com/documentation/nrf51/4.4.1/html/group__uicr__config__example.html

nRF52 avoid "DfuTarg": how to prevent from staying stuck in bootloader mode without starting the application

board: nRF52832_xxac_s132 nordic / SDK 11

While you try to add DFU  OTA service and flash softdevice, application ,and bootloader to your nRF52 (by nRFgo or command line tools),  you might see its BLE Advertising name as "Dfutarg". Like this :



and that means your application does not start. The device stays stuck in bootloader.

There are 2 ways to solve this problem:

First solution: program by command line tools and write memory

1. go to
C:\..\Nordic Semiconductor\nrf5x\bin
open command line, and write :


:: power by Agatha Kuan 20170419

.\nrfjprog.exe --family NRF52 --eraseall
.\nrfjprog.exe --family NRF52 --program %your_softdevice_name%
.\nrfjprog.exe --family NRF52 --program %your_application_name%
.\nrfjprog.exe --family NRF52 --program %your_bootloader_name%
.\nrfjprog.exe --family NRF52 --memwr 0x7f000 --val 1
.\nrfjprog.exe --family NRF52 --reset

then after it reset , the application will start.

Second solution : add patch file 

I provide you a patch file to force the device to start application after reset.
please download by this link :
https://drive.google.com/open?id=0ByqJaZSFoV7Hb190d1l4UjNzb28

and program by command line tools. Open command line and write:



:: power by Agatha Kuan 20170419

.\nrfjprog.exe --family NRF52 --eraseall
.\nrfjprog.exe --family NRF52 --program %your_softdevice_name%
.\nrfjprog.exe --family NRF52 --program %your_application_name%
.\nrfjprog.exe --family NRF52 --program %your_bootloader_name%
.\nrfjprog.exe --family NRF52 --program patch.hex
.\nrfjprog.exe --family NRF52 --reset

Note:
1.the patch.hex file can only use on nRF52832 SDK11
2.these 2 solution does not work after reset or DFU
3.if you want to use the first solution on nRF51, please see  here
(it is 0x3FC00 for nrf51)



Reference:

https://devzone.nordicsemi.com/question/22056/combining-sd-dfu-and-application-hex-and-programming/
https://devzone.nordicsemi.com/question/37880/initdat-file-for-dfu-gets-crc-error-correct-order-nrf-beacon-help/
https://devzone.nordicsemi.com/question/70738/bootloader-does-not-start-application/

2017年2月8日 星期三

J-Link as UART- Logging Output for nRF51/52

I use SEGGER RTT to replace UART logging .

STEP 1.
Download J-Link RTT Viewer and install it.
Download the RTT library files from SEGGER site and unzip them into a folder of your BLE SDK:

nRF51_SDK_10.0.0\nRF51_SDK_10.0.0\components\RTT










STEP 2.
Add the folder to  your project.

STEP 3.
add the RTT folder path to config your Project

STEP 4. my example code for main function printf


#include "SEGGER_RTT.h"

//SEGGER Delay
static void _Delay(int period) {
  int i = 100000*period;
  do { ; } while (i--);
}

int main(void)
{
    uint32_t err_code;
    bool erase_bonds;

    // Initialize.
    timers_init();
    buttons_leds_init(&erase_bonds);
    ble_stack_init();
    device_manager_init(erase_bonds);
    gap_params_init();
    advertising_init();
    services_init();
    conn_params_init();

    // Start execution.
    application_timers_start();
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
    APP_ERROR_CHECK(err_code);

    // Enter main loop.
    for (;;)
    {
        power_manage();
    SEGGER_RTT_SetTerminal(0);
    SEGGER_RTT_WriteString(0, "Hello from SEGGER!\r\n");
    _Delay(100);
   
    SEGGER_RTT_TerminalOut(1,"terminalOut 1 \r\n");
    _Delay(100);
   
   int terminalNumber=2;
    SEGGER_RTT_SetTerminal(terminalNumber);
    SEGGER_RTT_printf(0, "terminal = %u \r\n",terminalNumber);
    _Delay(100);
   
    SEGGER_RTT_WriteString(0, "terminal 2\r\n");
    _Delay(100);
   
    SEGGER_RTT_TerminalOut(1,"terminalOut from 1 \r\n");
    _Delay(100);    
    }
}
then rebuild.

 If nothing is configured, all data is sent to Terminal 0.

The Teminal to output all following via Write(), WriteString() or printf() can be set with SEGGER_RTT_SetTerminal() .

Output of only one string via a specific Terminal can be done with SEGGER_RTT_TerminalOut(). I could set the parameter to specify which terminal would be the output target.

STEP 5. Open J-Link RTT Viewer

While I want 2 threads( maybe one is main thread, the other one is  interrupt thread) to print in the same terminal, the output message might be disorder or  tattered.

First, config your channel

press "OK".
press File-> connect


the main function output will be like :

STEP 6. my example code for button interrupt printf

add the following code demonstrating a button interrupt

//Add SEGGER_RTT
#include "SEGGER_RTT.h"

void bsp_event_handler(bsp_event_t event)
{
    uint32_t err_code;
    switch (event)
    {
        case BSP_EVENT_SLEEP:
            sleep_mode_enter();
            break;

        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle,
                       BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        case BSP_EVENT_WHITELIST_OFF:
            err_code = ble_advertising_restart_without_whitelist();
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;
 
// add button interrupt      
 case BSP_EVENT_KEY_0:
     break;       

        default:
            break;
    }
}


// add button interrupt handler
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    uint32_t err_code;

    switch (pin_no)
    {
        case BSP_BUTTON_0:
  if(0 != button_action)
  {       
   SEGGER_RTT_TerminalOut(3,"BUTTON ACTION \r\n"); 
   button_action = 1; 
  } 
        
  if(1 == button_action)
  {
   LEDS_ON(BSP_LED_1_MASK);
     
   SEGGER_RTT_TerminalOut(3,"BUTTON on \r\n");
   break;
  }
  else
  {    
   LEDS_OFF(BSP_LED_1_MASK); 
    
   SEGGER_RTT_TerminalOut(3,"BUTTON off \r\n");
   break;
  }
    
   
        default:
            APP_ERROR_HANDLER(pin_no);
            break;
    }
}



/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds)
{
    bsp_event_t startup_event;

#if (0)
    uint32_t err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS,
                                 APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), 
                                 bsp_event_handler);
#else
  uint32_t err_code = bsp_init(BSP_INIT_NONE,
                                 APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), 
                                 bsp_event_handler);
 static app_button_cfg_t buttons[] =
    {
        {BSP_BUTTON_0, false, BUTTON_PULL, button_event_handler}
    };

    err_code = app_button_init(buttons, sizeof(buttons) / sizeof(buttons[0]),
                               APP_TIMER_TICKS(300, APP_TIMER_PRESCALER));
    APP_ERROR_CHECK(err_code);
 app_button_enable();
#endif
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}
then rebuild.


Now,  press button 0 (for pin define, please open  pca100xx.h)













the output will be

























You can try what will happen if 2 threads(interrupt and main thread) print in same terminal.

while you press down, it shows "BUTTON ACTION" and "BUTTON on".
And while you release the button, it shows "BUTTON off"




APPENDIX.
0.For more example, please check the following web site:

https://www.segger.com/jlink-rtt.html#RTT_Example_Code
http://blog.blecentral.com/2015/06/22/rtt_debugging/
http://dl.btc.pl/kamami_wa/j_link_edu_segger.pdf


1.J-Link RTT has function to input , just like what UART logging has done.

2017年1月10日 星期二

nRF5x: How to Set Version Checking in the DFU Zip File

This article is to provide a security solution for DFU.

My application is based on Heart rate Service 
( s110 with dfu in Nordic SDK)

C:\..\..\..\nRF51_SDK_10.0.0\nRF51_SDK_10.0.0_dc26b5e\examples\ble_peripheral\ble_app_hrs

the firmware can check if the DFU .zip file is authorized by manifest.json 
(in.zip ,usually called INIT file in Nordic Website).

The PASSWORD could be set in the zip file parameters while packed, and you should modify the bootloader code, to consist with the parameters. 



step 0.my environment:

  • nRF51822DK xxac
  • softdevice s110 8.0.0
  • Keil_v5 
  • nrf SDK 10.0.0
  • bootloader_s110      <--will be changed by me in step 2
  • master control panel 3.10.0.8

  • Android 6.0.1
  • windows 7
  • nrf Connect (for android) <--download the latest version
  • Samsung J7 prime

step 1.set password in .zip file

There are 3 options to place my password: device version, device type and application version.



To simply my example, , I adopt the "application version" as my password column at first .

(About how to adopt device revision or device type ,see Note.3)


password :0x03734301

cd C:\Program Files (x86)\Nordic Semiconductor\Master Control Panel\3.10.0.14\nrf\
nrfutil.exe dfu genpkg --nRF51422_xxac_s110.bin --application-version 0x03734301 --dev-revision 0xFFFF --dev-type 0xFFFF --sd-req 0x4f,0x5a,0x64 app_version_password.zip


add the .zip file to smartphone

step 2.customized your bootloader

copy bootloader folder  from:
C:\\nRF51_SDK_10.0.0\nRF51_SDK_10.0.0_dc26b5e\examples\dfu\bootloader

rename it as bootloader_with_password

copy dfu_init_template.c from nRF51_SDK_10.0.0_dc26b5e\components\libraries\bootloader_dfu 

modift the project setting to depend on the dfu_init_template.c 
which be inside folder bootloader_with_password, instead of the one in components\libraries\bootloader_dfu .

In dfu_init_template.c

#include "dfu_init.h"
#include <stdint.h>
#include <string.h>
#include "dfu_types.h"
#include "nrf_error.h"
#include "crc16.h"

#define DFU_INIT_PACKET_EXT_LENGTH_MIN      2                       
#define DFU_INIT_PACKET_EXT_LENGTH_MAX      10                      

static uint8_t m_extended_packet[DFU_INIT_PACKET_EXT_LENGTH_MAX];   
static uint8_t m_extended_packet_length;                            

#define APP_VERSION_PASSWORD                  ((uint32_t)0x03734301)

uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len)
{
    uint32_t i = 0;
    
    
    if (init_data_len < sizeof(dfu_init_packet_t))
    {
        return NRF_ERROR_INVALID_LENGTH;
    }

    // Current template uses clear text data so they can be casted for pre-check.
    dfu_init_packet_t * p_init_packet = (dfu_init_packet_t *)p_init_data;

    m_extended_packet_length = ((uint32_t)p_init_data + init_data_len) -
                   (uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len];
    
    if (m_extended_packet_length < DFU_INIT_PACKET_EXT_LENGTH_MIN)
    {
        return NRF_ERROR_INVALID_LENGTH;
    }

    if (((uint32_t)p_init_data + init_data_len) < 
        (uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len])
    {
        return NRF_ERROR_INVALID_LENGTH;
    }

    memcpy(m_extended_packet,
           &p_init_packet->softdevice[p_init_packet->softdevice_len],
           m_extended_packet_length);

/** [DFU init application version] */
    // To support application versioning, this check should be updated.
    // This template allows for any application to be installed. However, 
    // customers can place a revision number at the bottom of the application 
    // to be verified by the bootloader. This can be done at a location 
    // relative to the application, for example the application start 
    // address + 0x0100.
/** [DFU init application version] */
    
    // First check to verify the image to be transfered matches the device type.
    // If no Device type is present in DFU_DEVICE_INFO then any image will be accepted.
  

    /*agatha: set application version check*/ 
    if ((p_init_packet->app_version!= APP_VERSION_PASSWORD))
    {
        return NRF_ERROR_INVALID_DATA;
    }/*app_version check*/ 
    
    //First check 
    if ((DFU_DEVICE_INFO->device_type != DFU_DEVICE_TYPE_EMPTY) &&
   (p_init_packet->device_type != DFU_DEVICE_INFO->device_type))
    {
        return NRF_ERROR_INVALID_DATA;
    }
   
    // Second check to verify the image to be transfered matches the device revision.
    
    if ((DFU_DEVICE_INFO->device_rev != DFU_DEVICE_REVISION_EMPTY) &&
        (p_init_packet->device_rev != DFU_DEVICE_INFO->device_rev))
    {
        return NRF_ERROR_INVALID_DATA;
    }

    // Third check: Check the array of supported SoftDevices by this application.
    // If the installed SoftDevice does not match any SoftDevice in the list then an
    // error is returned.
    while (i < p_init_packet->softdevice_len)
    {
        if (p_init_packet->softdevice[i] == DFU_SOFTDEVICE_ANY ||
            p_init_packet->softdevice[i++] == SOFTDEVICE_INFORMATION->firmware_id)
        {
            return NRF_SUCCESS;
        }
    }
    
    // No matching SoftDevice found - Return NRF_ERROR_INVALID_DATA.
    return NRF_ERROR_INVALID_DATA;
}


uint32_t dfu_init_postvalidate(uint8_t * p_image, uint32_t image_len)
{
    uint16_t image_crc;
    uint16_t received_crc;
    
    
    // calculate CRC from active block.
    image_crc = crc16_compute(p_image, image_len, NULL);

    // Decode the received CRC from extended data.    
    received_crc = uint16_decode((uint8_t *)&m_extended_packet[0]);

    // Compare the received and calculated CRC.
    if (image_crc != received_crc)
    {
        return NRF_ERROR_INVALID_DATA;
    }

    return NRF_SUCCESS;
}

The structure  p_init_packet  would pass the password from INIT file, in here, modify the code to check its value consisted with true password .

Rebuild bootloader.hex and application. Then use nRFgo Studio to program:
  • softdevice
  • application
  • bootloader (be changed by you)




step 3.test 


open Nordic nRF connecter app

connect your board

press the DFU button above

press "Distribution packet" and select the .zip file we made in STEP 1
start DFU
After DFU , reset the board

if you add the zip file that the parameter  --application-version is incorrect, your smartphone will soon disconnect to device .


Additional notes:


0. This implement check the number of application version to achieve "set a password to avoid unknown .zip file ".


1. In my experience,Samsung smartphone has a higher probability to DFU failed.

(whether using which smartphone app)

2. nRF connect ,the app I used , has some problem in DFU mode and connect the device after DFU. 


3. I suggest not to check dev_type and dev_rev,but use application version ,because application version isn't used in normal bootloader.


However, if you want to check device type, that is feasible :


add in bootloader
/*agatha: how to check  device type*/
#define DEV_TYPE_PASSWORD  0x1618  
  
   if ((p_init_packet->device_type != DEV_TYPE_PASSWORD))
    {
        return NRF_ERROR_INVALID_DATA;
    }
  
#if (0)
   
    if ((DFU_DEVICE_INFO->device_type != DFU_DEVICE_TYPE_EMPTY) &&
   (p_init_packet->device_type != DFU_DEVICE_INFO->device_type))
    {
        return NRF_ERROR_INVALID_DATA;
    }
#endif   
and make the .zip file


cd C:\Program Files (x86)\Nordic Semiconductor\Master Control Panel\3.10.0.14\nrf\
nrfutil.exe dfu genpkg --nRF51422_xxac_s110.bin --application-version 0x03734301 --dev-revision 0xFFFF --dev-type 0x1618 --sd-req 0x4f,0x5a,0x64 app_version_password.zip

you could adopt device_type and/or device_revision as further checking.


3. This method above can work in SDK11 , when the chip is nRF52 with softdevice s132.
But that can not be adopted to SDK12  and s232, s332.