Wireless Connectivity Knowledge Base

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Wireless Connectivity Knowledge Base

Discussions

Sort by:
Introduction HCI Application is a Host Controller Interface application which provides a serial communication to interface with the KW40/KW41/KW35/KW36/QN9080 BLE radio part. It enables the user to have a way to control the radio through serial commands. The format of the HCI Command Packet it’s composed of the following parts:     Each command is assigned a 2 byte Opcode which it’s divided into two fields, called the OpCode Group Field (OGF) and OpCode Command Field (OCF). The OGF uses the upper 6 bits of the Opcode, while the OCF corresponds to the remaining 10 bits. The OGF of 0x3F is reserved for vendor-specific debug commands. The organization of the opcodes allows additional information to be inferred without fully decoding the entire Opcode. For further information regarding this topic, please check the BLUETOOTH SPECIFICATION Version 5.0 | Vol 2, Part E, 5.4 EXCHANGE OF HCI-SPECIFIC INFORMATION.   Adding HCI Custom Commands Example This document will guide you through the implementation of custom HCI commands in the KW36. For this example, we will include the following set of custom commands: 01 50 FC 00 – This command is to send a continuous unmodulated wave using a defined channel and output power (default: frequency 2.402GHz and PA_POWER register set to 0x3E).  01 4F FC 00 – This command is to stop the continuous unmodulated wave and configure the radio in Bluetooth LE mode again. This way you can continue sending adopted HCI commands. 01 00 FC 00 – Set the Channel 0 Freq 2402 MHz 01 01 FC 00 – Set the Channel 19 Freq 2440 MHz 01 02 FC 00 – Set the Channel 39 Freq 2480 MHz 01 10 FC 00 – Set the PA_POWER 1 01 11 FC 00 – Set the PA_POWER 32 01 12 FC 00 – Set the PA_POWER 62 The changes described in the following sections were based on the HCI Black Box SDK example (it is located at wireless_examples -> bluetooth -> hci_bb)   Changes in hci_transport.h The "hci_transport.h" file is located at bluetooth->hci_transport->interface folder. Include the following macros in ''Public constants and macros" #define gHciCustomCommandOpcodeUpper (0xFC50) #define gHciCustomCommandOpcodeLower (0xFC00) #define gHciInCustomVendorCommandsRange(x) (((x) <= gHciCustomCommandOpcodeUpper) && \ ((x) >= gHciCustomCommandOpcodeLower))‍‍‍‍‍‍‍‍ Declare a function to install the custom command as follows: void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler);‍   Changes in hcit_serial_interface.c The "hci_transport.h" file is located at bluetooth->hci_transport->source folder. Add the following in "Private memory declarations" static hciTransportInterface_t mCustomTransportInterface = NULL;‍ Modify the Hcit_SendMessage function as follows: static inline void Hcit_SendMessage(void) { uint16_t opcode = 0; /* verify if this is an event packet */ if(mHcitData.pktHeader.packetTypeMarker == gHciEventPacket_c) { /* verify if this is a command complete event */ if(mHcitData.pPacket->raw[0] == gHciCommandCompleteEvent_c) { /* extract the first opcode to verify if it is a custom command */ opcode = mHcitData.pPacket->raw[3] | (mHcitData.pPacket->raw[4] << 8); } } /* verify if command packet */ else if(mHcitData.pktHeader.packetTypeMarker == gHciCommandPacket_c) { /* extract opcode */ opcode = mHcitData.pPacket->raw[0] | (mHcitData.pPacket->raw[1] << 8); } if(gHciInCustomVendorCommandsRange(opcode)) { if(mCustomTransportInterface) { mCustomTransportInterface( mHcitData.pktHeader.packetTypeMarker, mHcitData.pPacket, mHcitData.bytesReceived); } } else { /* Send the message to HCI */ (void)mTransportInterface(mHcitData.pktHeader.packetTypeMarker, mHcitData.pPacket, mHcitData.bytesReceived); } mHcitData.pPacket = NULL; mPacketDetectStep = mDetectMarker_c; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Develop the function to install the custom command as follows:   void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler) { OSA_InterruptDisable(); mCustomTransportInterface = mCustomInterfaceHandler; OSA_InterruptEnable(); }‍‍‍‍‍‍   Changes in hci_black_box.c This is the main application file, and it is located at source folder. Include the following files to support our HCI custom commands #include "hci_transport.h" #include "fsl_xcvr.h"‍‍ Define the following macros which represent the opcode for each custom command #define CUSTOM_HCI_CW_ON (0xFC50) #define CUSTOM_HCI_CW_OFF (0xFC4F) #define CUSTOM_HCI_CW_SET_CHN_0 (0xFC00) /*Channel 0 Freq 2402 MHz*/ #define CUSTOM_HCI_CW_SET_CHN_19 (0xFC01) /*Channel 19 Freq 2440 MHz*/ #define CUSTOM_HCI_CW_SET_CHN_39 (0xFC02) /*Channel 39 Freq 2480 MHz*/ #define CUSTOM_HCI_CW_SET_PA_PWR_1 (0xFC10) /*PA_POWER 1 */ #define CUSTOM_HCI_CW_SET_PA_PWR_32 (0xFC11) /*PA_POWER 32 */ #define CUSTOM_HCI_CW_SET_PA_PWR_62 (0xFC12) /*PA_POWER 62 */ #define CUSTOM_HCI_CW_EVENT_SIZE (0x04) #define CUSTOM_HCI_EVENT_SUCCESS (0x00) #define CUSTOM_HCI_EVENT_FAIL (0x01)‍‍‍‍‍‍‍‍‍‍‍ Add the following application variables static uint16_t channelCC = 2402; static uint8_t powerCC = 0x3E; uint8_t eventPacket[6] = {gHciCommandCompleteEvent_c, CUSTOM_HCI_CW_EVENT_SIZE, 1, 0, 0, 0 };‍‍‍‍‍‍ Declare the handler for our custom commands bleResult_t BleApp_CustomCommandsHandle(hciPacketType_t packetType, void* pPacket, uint16_t packetSize);‍ Find the "main_task" function, and register the handler for the custom commands through "Hcit_InstallCustomCommandHandler" function. You can include it just after BleApp_Init(); /* Initialize peripheral drivers specific to the application */ BleApp_Init(); /* Register the callback for the custom commands */ Hcit_InstallCustomCommandHandler((hciTransportInterface_t)&BleApp_CustomCommandsHandle); /* Create application event */ mAppEvent = OSA_EventCreate(TRUE); if( NULL == mAppEvent ) { panic(0,0,0,0); return; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Develop the handler of our custom commands as follows: bleResult_t BleApp_CustomCommandsHandle(hciPacketType_t packetType, void* pPacket, uint16_t packetSize) { uint16_t opcode = 0; if(gHciCommandPacket_c == packetType) { opcode = ((uint8_t*)pPacket)[0] | (((uint8_t*)pPacket)[1] << 8); switch(opcode) { /*@CC: Set Channel */ case CUSTOM_HCI_CW_SET_CHN_0: /*@CC: Set Channel 0 Freq 2402 MHz */ channelCC=2402; break; case CUSTOM_HCI_CW_SET_CHN_19: /*@CC: Channel 19 Freq 2440 MHz*/ channelCC=2440; break; case CUSTOM_HCI_CW_SET_CHN_39: /*@CC: Channel 39 Freq 2480 MHz */ channelCC=2480; break; /*@CC: Set PA_POWER */ case CUSTOM_HCI_CW_SET_PA_PWR_1: /*@CC: Set PA_POWER 1 */ powerCC=0x01; break; case CUSTOM_HCI_CW_SET_PA_PWR_32: /*@CC: Set PA_POWER 32 */ powerCC=0x20; break; case CUSTOM_HCI_CW_SET_PA_PWR_62: /*@CC: Set PA_POWER 62 */ powerCC=0x3E; break; /*@CC: Generate a Continuous Unmodulated Signal ON / OFF */ case CUSTOM_HCI_CW_ON: /*@CC: Generate a Continuous Unmodulated Signal when pressing SW3 */ XCVR_DftTxCW(channelCC, 6); XCVR_ForcePAPower(powerCC); break; case CUSTOM_HCI_CW_OFF: /*@CC: Turn OFF the transmitter */ XCVR_ForceTxWd(); /* Initialize the PHY as BLE */ XCVR_Init(BLE_MODE, DR_1MBPS); break; default: eventPacket[5] = CUSTOM_HCI_EVENT_FAIL; break; } eventPacket[3] = (uint8_t)opcode; eventPacket[4] = (uint8_t)(opcode >> 8); eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS; Hcit_SendPacket(gHciEventPacket_c, eventPacket, sizeof(eventPacket)); } else { return gBleUnexpectedError_c; } return gBleSuccess_c; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Testing Custom HCI Commands Using NXP Test Tool 12 To test HCI Black Box software, we need to install NXP Test Tool 12, from the NXP Semiconductors | Automotive, Security, IoT official web site. Once you have installed Test Tool, attach the FRDM-KW36 board to your PC and open the serial port enumerated in the start page clicking twice on the icon. Then, select "Raw Data" checkbox and type any of our custom commands, for instance, "01 01 FC 00" (Set the Channel 19 Freq 2440 MHz). Shift out the command clicking on the "Send Raw..." button. You will see the HCI Tx and Rx in the right upper corner of your screen
View full article
Introduction This document guides to load a new software image in a KW41 device through Over The Air Programming bootloader. Also, are explained the details of how to set up the client software to change the storage method of the image. Software Requirements IAR Embedded Workbench IDE or MCUXpresso IDE Download both, SDK FRDM-KW41Z and SDK USB-KW41Z. Hardware Requirements FRDM-KW41Z board OTAP Memory Management During the Update Process The KW41 has a 512KB Program Flash with a flash address range from 0x0000_0000 to 0x0007_FFFF.     The OTAP application splits the flash into two independent parts, the OTAP Bootloader, and the OTAP Client. The OTAP Bootloader verifies if there is a new image available at the OTAP Client to reprogram the device. The OTAP Client software provides the Bluetooth LE custom service needed to communicate the OTAP Client device with the OTAP Server that contains the new image file (The OTAP Server device could be another FRDM-KW41Z connected to a PC with Test Tool or a Smartphone with IoT Toolbox app). Therefore, the OTAP Client device needs to be programmed twice, first with the OTAP Bootloader, then with the Bluetooth LE application supporting OTAP Client. The mechanism created to have two different software coexisting in the same device is storing each one in different memory regions. This functionality is implemented by the linker file. In the KW41 device, the bootloader has reserved a 16 KB slot of memory from 0x0000_0000 to 0x0003_FFFF, thus the left memory is reserved among other things, by the OTAP Client demo. To create a new image file for the client device, the developer needs to specify to the linker file that the code will be built with an offset of 16 KB since the first addresses must be reserved for the OTAP Bootloader. In connection state, the OTAP server sends the image packets (known as chunks) to the OTAP Client device via Bluetooth LE. The OTAP Client device can store these chunks, in first instance, at the external SPI flash or the On-Chip Flash. The destination of the code is selectable in the OTAP Client software. When the connection has finished and all chunks were sent from the OTAP Server to the OTAP Client device, the OTAP Client software writes information, such as the source of the image update (external flash or internal flash) in a portion of memory known as Bootloader Flags and then resets the MCU to execute the OTAP Bootloader code. The OTAP Bootloader reads the Bootloader Flags to get the information needed to program the device and triggers a commando to reprogram the MCU with the new application. Due to the new application was built with an offset of 16 KB, the OTAP Bootloader programs the device starting from the 0x0000_4000 address and the OTAP Client application is overwritten by the new image, therefore, after the device has been reprogrammed through this method, cannot be programmed a second time as same. Finally, the OTAP Bootloader triggers a command to start the execution of the new code automatically.     Preparing the Software to Test the OTAP Client for KW41Z Device Using IAR Embedded Workbench Program the OTAP Bootloader on the FRDM-KW41Z. Program the OTAP Bootloader software from the project included in the SDK FRDM-KW41Z at the following path, or you can simply drag and drop the pre-built binary from the following path.           OTAP Bootloader Project:          <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\framework\bootloader_otap\bm\iar\bootloader_otap_bm.eww            OTAP Bootloader pre-built binary:            <SDK_2.2.0_FRDM-KW41Z_download_path>\tools\wireless\binaries\bootloader_otap_frdmkw41z.bin   Open the OTAP Client project included in the SDK FRDM-KW41Z located in the following path.          <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\otap_client_att\freertos\iar\otap_client_att_freertos.eww   Customize the OTAP Client software to select the storage method. Locate the app_preinclude.h header file inside the source folder at the workspace. To select the External Flash storage method, set the "gEepromType_d" define to "gEepromDevice_AT45DB041E_c"                      To select the Internal Flash storage method, set the "gEepromType_d" define to "gEepromDevice_InternalFlash_c"   Configure the linker flags. Open the project options window (Alt + F7). In "Linker->Config" window, locate the "Configuration file symbol definitions" pane. To select the External Flash storage method, remove the "gUseInternalStorageLink_d=1" linker flag To select the Internal Flash storage method, add the "gUseInternalStorageLink_d=1" linker flag     Load the OTAP Client software on the FRDM-KW41Z board (Ctrl + D). Stop the debug session (Ctrl + Shift + D). The default linker configurations of the project allow the OTAP Client application to be stored with the proper memory offset.   Preparing the Software to Test the OTAP Client for KW41Z Device Using MCUXpresso IDE Program the OTAP Bootloader on the FRDM-KW41Z. Program the OTAP Bootloader software from the project included in the SDK FRDM-KW41Z at the following path, or you can simply drag and drop the pre-built binary from the following path.           OTAP Bootloader Project:          wireless_examples->framework->bootloader_otap->bm            OTAP Bootloader pre-built binary:            <SDK_2.2.0_FRDM-KW41Z_download_path>\tools\wireless\binaries\bootloader_otap_frdmkw41z.bin   Click on "Import SDK examples(s)" option in the "Quickstart Panel" view. Click twice on the frdmkw41z icon.     Open the OTAP Client project included in the SDK FRDM-KW41Z located in the following path.wireless_examples->bluetooth->otap_client_att->freertos     Customize the OTAP Client software to select the storage method. Locate the app_preinclude.h header file inside the source folder at the workspace. To select the External Flash storage method, set the "gEepromType_d" define to "gEepromDevice_AT45DB041E_c"                      To select the Internal Flash storage method, set the "gEepromType_d" define to "gEepromDevice_InternalFlash_c"   Configure the linker file. To select the External Flash storage method, are not required any changes in the project from this point. You can skip this step. To select the Internal Flash storage method, search the linker file located in the SDK USB-KW41Z at the following path and replace instead of the default linker file at the source folder in the OTAP Client project. You can copy (Ctrl + C) the linker file from SDK USB-KW41Z and paste (Ctrl + V) on the workspace directly. A warning message will be displayed, select "Overwrite".           Linker file at the SDK USB-KW41Z:        <SDK_2.2.0_USB-KW41Z_download_path>\boards\usbkw41z_kw41z\wireless_examples\bluetooth\otap_client_att\freertos\MKW41Z512xxx4_connectivity.ld     Save the changes in the project. Select "Debug" in the "Quickstart Panel". Once the project is already loaded on the device, stop the debug session.   Creating an S-Record Image File for FRDM-KW41Z OTAP Client in IAR Embedded Workbench Open the connectivity project that you want to program using the OTAP Bootloader from your SDK FRDM-KW41Z. This example will make use of the glucose sensor project, this is located at the following path. <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\glucose_sensor\freertos\iar\glucose_sensor_freertos.eww   Open the project options window (Alt+F7). In Linker->Config window, add the following linker flag in the “Configuration file symbol definitions” textbox.         gUseBootloaderLink_d=1     Go to the “Output Converter” window. Deselect the “Override default" checkbox, expand the “Output format” combo box and select Motorola S-records format. Click the OK button.     Rebuild the project. Search the S-Record file (.srec) in the following path.<SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\glucose_sensor\freertos\iar\debug   Creating an S-Record Image File for FRDM-KW41Z OTAP Client in MCUXpresso IDE Open the connectivity project that you want to program using the OTAP Bootloader from MCUXpresso IDE. This example will make use of the glucose sensor project, this is located at the following path.        wireless_examples->bluetooth->glucose_sensor->freertos   Search the linker file located in the SDK FRDM-KW41Z at the path below and replace instead of the default linker file at the source folder in the Glucose Sensor project. You can copy (Ctrl + C) the linker file from SDK FRDM-KW41Z and paste (Ctrl + V) on the workspace directly. A warning message will be displayed, select "Overwrite".          Linker file at the SDK FRDM-KW41Z:        <SDK_2.2.0_FRDM-KW41Z_download_path>\boards\frdmkw41z\wireless_examples\bluetooth\otap_client_att\freertos\MKW41Z512xxx4_connectivity.ld     Open the new "MKW41Z512xxx4_connectivity.ld" linker file. Locate the section placement of the figure below and remove the "FILL" and the "BYTE" statements.         Build the project. Deploy the “Binaries” icon in the workspace. Click the right mouse button on the “.axf” file. Select the “Binary Utilities/Create S-Record” option. The S-Record file will be saved at “Debug” folder in the workspace with “.s19” extension.     Testing OTAP Client Demo Using IoT Toolbox App Save the S-Record file created with the steps in the last section in your smartphone at a known location. Open the IoT Toolbox App and select OTAP demo. Press “SCAN” to start scanning for a suitable advertiser. Press the “SW4” button on the FRDM-KW41Z board to start advertising. Create a connection with the found device. Press “Open” and search the S-Record file. Press “Upload” to start the transfer. Once the transfer is complete, wait a few seconds until the bootloader has finished programming the new image. The new application will start automatically. 
View full article
This guide will show a way to set up and enable an I2C Serial Interface to send a string of data instances using one of the Wireless Bluetooth SDK examples and the Serial Manager API.
View full article
I got a question related to best practices to configure a GPIO if the pin is not used. To make it short, the recommendation is to leave the GPIO floating on the PCB and leave the GPIO in its "Default" state as shown in the Signal Multiplexing table in the Reference Manual. The Default state is either “Disabled” or an analog function.   Some Kinetis devices have analog only pins (PGAx/ADCx) while most have GPIO pins with analog functions (PTx/ADCx) or digital GPIO pins   Unused pins, whether analog only or GPIO, should be left floating. Analog only pins do not have input buffers that will cause shoot-through currents when the input floats. GPIO pins with analog functions default to analog functions, which disables the digital input buffer – no shoot-through current.   The digital GPIO pins default to "Disabled", which disables the input buffers - no shoot-through currents with floating inputs.   Finally, unused pins shall not be tied to VDD or VSS. Hence, when designing your board and there are some unused pins, leave them floating on the PCB and then make sure that the software leaves the GPIO in its Default state in the MUX register. 
View full article
Introduction The MTU (Maximum Transmission Unit) in Bluetooth LE, is an informational parameter that indicates to the remote device, the maximum number of bytes that the local can handle in such channel, for example, the ATT_MTU for KW36 is fixed in 247 bytes. A few applications require to have long characteristics defined in the GATT database, and sometimes the length of the characteristic exceeds the MTU negotiated by the client and server Bluetooth LE devices. For this scenario, the Bluetooth LE specification defines a procedure to write and read the characteristic of interest. In summary, it consists in perform multiple writes and reads on the same characteristic value, using specific commands. For the "write long characteristic value" procedure, these commands are ATT_PREPARE_WRITE_REQ and ATT_EXECUTE_WRITE_REQ. For the "read long characteristic value" procedure, these commands are ATT_READ_REQ and ATT_READ_BLOB_REQ. This document provides an example of how to write and read long characteristic values, from the perspective of Client and Server devices.   APIs to Write and Read Characteristic Values Write Characteristic Values The GattClient_WriteCharacteristicValue API is used to perform any write operation. It is implemented by the GATT Client device. The following table describes the input parameters. Input Parameters Description deviceId_t deviceId Device ID of the peer device. gattCharacteristic_t * pCharacteristic Pointer to a gattCharacteristic struct type. This struct must contain a valid handle of the characteristic value in the "value.handle" field. The handle of the characteristic value that you want to write is commonly obtained after the service discovery procedure.  uint16_t valueLength This value indicates the length of the array pointed by aValue. const uint8_t * aValue Pointer to an array containing the value that will be written to the GATT database. bool_t withoutResponse If true, it means that the application wishes to perform a "Write Without Response", in other words, when the command will be ATT_WRITE_CMD or ATT_SIGNED_WRITE_CMD. bool_t signedWrite If withoutResponse and signedWrite are both true, the command will be ATT_SIGNED_WRITE_CMD. If withoutResponse is false, this parameter is ignored. bool_t doReliableLongCharWrites This field must be set to true if the application needs to write a long characteristic value. const uint8_t * aCsrk If withoutResponse and signedWrite are both true, this pointer must contain the CSRK to sign the data. Otherwise, this parameter is ignored.   Read Characteristic Values The GattClient_ReadCharacteristicValue API is used to perform read operations. It is implemented by the GATT Client device. The following table describes the input parameters. Input Parameters Description deviceId_t deviceId Device ID of the peer device. gattCharacteristic_t * pIoCharacteristic Pointer to a gattCharacteristic struct type. This struct must contain a valid handle of the characteristic value in the "value.handle" field. The handle of the characteristic value that you want to write is commonly obtained after the service discovery procedure. As well, the "value.paValue" field of this struct, must point to an array which will contain the characteristic value read from the peer. unit16_t maxReadBytes The length of the characteristic value that should be read. This API takes care of the long characteristics, so there is no need to worry about a special parameter or configuration. The following sections provide a functional example of how to write and read long characteristics. This example was based on the temperature collector and temperature sensor SDK examples. The example also shows how to create a custom service at the GATT database and how to discover its characteristics.   Bluetooth LE Server (Temperature Sensor) Modifications in gatt_uuid128.h Define the 128 bit UUID of the "custom service" which will be used for this example. Add the following code: /* Custom service */ UUID128(uuid_service_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01) UUID128(uuid_char_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in gatt_db.h Define the characteristics of the "custom service", for this example, our service will have just one characteristic, it can be written or read, and it has a variable-length limited to 400 bytes (remember that the ATT_MTU of KW36 is 247 byte, so with this length, we ensure long writes and reads). Add the following code: PRIMARY_SERVICE_UUID128(service_custom, uuid_service_custom) CHARACTERISTIC_UUID128(char_custom, uuid_char_custom, (gGattCharPropWrite_c | gGattCharPropRead_c)) VALUE_UUID128_VARLEN(value_custom, uuid_char_custom, (gPermissionFlagWritable_c | gPermissionFlagReadable_c), 400, 1)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in app_preinclude.h One of the most important considerations to write and read long characteristics is the memory allocation needed for this. You must increment the current "AppPoolsDetails_c" configuration, the "_block_size_" and "_number_of_blocks_". Please ensure that "_block_size_" is aligned with 4 bytes. Once you have found the configuration that works in your application, please follow the steps in Memory Pool Optimizer on MKW3xA/KW3xZ Application Note, to found the best configuration without waste memory resources. For this example, configure "AppPoolsDetails_c" as follows: /* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/ #define AppPoolsDetails_c \ _block_size_ 264 _number_of_blocks_ 8 _eol_‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Bluetooth LE Client (Temperature Collector) Modifications in gatt_uuid128.h Define the 128 bit UUID of the "custom service" which will be used for this example. Add the following code: /* Custom service */ UUID128(uuid_service_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01) UUID128(uuid_char_custom, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x01, 0x01, 0xFF, 0x01)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in temperature_collector.c 1. Define the following variables at the "Private type definitions" section: typedef struct customServiceConfig_tag { uint16_t hService; uint16_t hCharacteristic; } customServiceConfig_t; typedef struct appCustomInfo_tag { tmcConfig_t tempClientConfig; customServiceConfig_t customServiceClientConfig; }appCustomInfo_t; typedef enum { mCustomServiceWrite = 0, mCustomServiceRead }customServiceState_t;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 2. Add two arrays of 400 bytes, one to send and the other to receive the data from the server in "Private memory declarations" section: /* Dummy array for custom service */ uint8_t mWriteDummyArray[400]; uint8_t mReadDummyArray[400];‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 3. Define a new function in "Private functions prototypes" section, to write and read the characteristic value: static void BleApp_SendReceiveCustomService (customServiceState_t state);‍‍‍‍ 4. Locate the "BleApp_Config" function, add the following code here to fill the "mWriteDummyArray" with a known pattern before to write our custom characteristic. static void BleApp_Config(void) { uint16_t fill_pattern; /* Fill pattern to write long characteristic */ for (fill_pattern = 0; fill_pattern<400; fill_pattern++) { mWriteDummyArray[fill_pattern] = (uint8_t)fill_pattern; } /* Configure as GAP Central */ BleConnManager_GapCommonConfig(); ... ... }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 5. Locate the "BleApp_StoreServiceHandles" function. Modify this function to include our custom service in the service discovery procedure. This is to save the handle of the custom characteristic since it is used by GattClient_WriteCharacteristicValue and GattClient_ReadCharacteristicValue APIs. static void BleApp_StoreServiceHandles ( gattService_t *pService ) { uint8_t i,j; if ((pService->uuidType == gBleUuidType128_c) && FLib_MemCmp(pService->uuid.uuid128, uuid_service_temperature, 16)) { /* Found Temperature Service */ mPeerInformation.customInfo.tempClientConfig.hService = pService->startHandle; for (i = 0; i < pService->cNumCharacteristics; i++) { if ((pService->aCharacteristics[i].value.uuidType == gBleUuidType16_c) && (pService->aCharacteristics[i].value.uuid.uuid16 == gBleSig_Temperature_d)) { /* Found Temperature Char */ mPeerInformation.customInfo.tempClientConfig.hTemperature = pService->aCharacteristics[i].value.handle; for (j = 0; j < pService->aCharacteristics[i].cNumDescriptors; j++) { if (pService->aCharacteristics[i].aDescriptors[j].uuidType == gBleUuidType16_c) { switch (pService->aCharacteristics[i].aDescriptors[j].uuid.uuid16) { /* Found Temperature Char Presentation Format Descriptor */ case gBleSig_CharPresFormatDescriptor_d: { mPeerInformation.customInfo.tempClientConfig.hTempDesc = pService->aCharacteristics[i].aDescriptors[j].handle; break; } /* Found Temperature Char CCCD */ case gBleSig_CCCD_d: { mPeerInformation.customInfo.tempClientConfig.hTempCccd = pService->aCharacteristics[i].aDescriptors[j].handle; break; } default: ; /* No action required */ break; } } } } } } else if ((pService->uuidType == gBleUuidType128_c) && FLib_MemCmp(pService->uuid.uuid128, uuid_service_custom, 16)) { /* Found Custom Service */ mPeerInformation.customInfo.customServiceClientConfig.hService = pService->startHandle; if (pService->cNumCharacteristics > 0U && pService->aCharacteristics != NULL) { /* Found Custom Characteristic */ mPeerInformation.customInfo.customServiceClientConfig.hCharacteristic = pService->aCharacteristics[0].value.handle; } } else { ; } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 6. Develop the "BleApp_SendReceiveCustomService" as shown below. This function is used to write and read the custom characteristic values using long operations. Focus your attention in this function, here is the example of how to use GattClient_WriteCharacteristicValue and GattClient_ReadCharacteristicValue APIs to write and read long characteristic values. Note that the "characteristic" struct was filled before to use the last APIs, with the handle of our custom characteristic and a destination address to receive the value read from the peer. Note that the "doReliableLongCharWrites" field must be TRUE to allow long writes using GattClient_WriteCharacteristicValue.  static void BleApp_SendReceiveCustomService (customServiceState_t state) { bleResult_t bleResult; gattCharacteristic_t characteristic; /* Verify if there is a valid peer */ if (gInvalidDeviceId_c != mPeerInformation.deviceId) { /* Fill the characteristic struct with a read destiny and the custom service handle */ characteristic.value.handle = mPeerInformation.customInfo.customServiceClientConfig.hCharacteristic; characteristic.value.paValue = &mReadDummyArray[0]; /* Try to write the custom characteristic value */ if(mCustomServiceWrite == state) { bleResult = GattClient_WriteCharacteristicValue(mPeerInformation.deviceId, &characteristic, (uint16_t)400, &mWriteDummyArray[0], FALSE, FALSE, TRUE, NULL); /* An error occurred while trying to write the custom characteristic value, disconnect */ if(gBleSuccess_c != bleResult) { (void)Gap_Disconnect(mPeerInformation.deviceId); } } /* Try to read the custom characteristic value */ else { bleResult = GattClient_ReadCharacteristicValue(mPeerInformation.deviceId, &characteristic, (uint16_t)400); /* An error occurred while trying to read the custom characteristic value, disconnect */ if(gBleSuccess_c != bleResult) { (void)Gap_Disconnect(mPeerInformation.deviceId); } } } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 7. Modify the "BleApp_GattClientCallback" as shown below. In this function, we implement the "BleApp_SendReceiveCustomService" which writes or reads the characteristic depending on the input parameter "state". The expected behavior of this example is, first, write the 400-byte pattern contained in the mWriteDummyArray to our custom characteristic value, just after to write the characteristic descriptor of the temperature service (which is indicated by this callback in the gGattProcWriteCharacteristicDescriptor_c event). When the write has been executed successfully, it is indicated in this callback, by the "gGattProcWriteCharacteristicValue_c" event, therefore, here we can execute our function to read the characteristic value. Then "gGattProcReadCharacteristicValue_c" event is triggered if the read has been completed, here, we compare the value written with the value read from the GATT server and, if both are the same, the green RGB led should turn on indicating that our long characteristic has been written and read successfully, otherwise, the GATT client disconnects from the GATT server.   static void BleApp_GattClientCallback( deviceId_t serverDeviceId, gattProcedureType_t procedureType, gattProcedureResult_t procedureResult, bleResult_t error ) { if (procedureResult == gGattProcError_c) { attErrorCode_t attError = (attErrorCode_t)(uint8_t)(error); if (attError == gAttErrCodeInsufficientEncryption_c || attError == gAttErrCodeInsufficientAuthorization_c || attError == gAttErrCodeInsufficientAuthentication_c) { /* Start Pairing Procedure */ (void)Gap_Pair(serverDeviceId, &gPairingParameters); } BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcError_c); } else { if (procedureResult == gGattProcSuccess_c) { switch(procedureType) { case gGattProcReadCharacteristicDescriptor_c: { if (mpCharProcBuffer != NULL) { /* Store the value of the descriptor */ BleApp_StoreDescValues(mpCharProcBuffer); } break; } case gGattProcWriteCharacteristicDescriptor_c: { /* Try to write to the custom service */ BleApp_SendReceiveCustomService(mCustomServiceWrite); } break; case gGattProcWriteCharacteristicValue_c: { /* If write to the custom service was completed, try to read the custom service */ BleApp_SendReceiveCustomService(mCustomServiceRead); } break; case gGattProcReadCharacteristicValue_c: { /* If read to the custom service was completed, compare write and read buffers */ if(FLib_MemCmp(&mWriteDummyArray[0], &mReadDummyArray[0], 400)) { Led3On(); } else { (void)Gap_Disconnect(mPeerInformation.deviceId); } } break; default: { ; /* No action required */ break; } } BleApp_StateMachineHandler(serverDeviceId, mAppEvt_GattProcComplete_c); } } /* Signal Service Discovery Module */ BleServDisc_SignalGattClientEvent(serverDeviceId, procedureType, procedureResult, error); }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Modifications in app_preinclude.h One of the most important considerations to write and read long characteristics is the memory allocation needed for this. You must increment the current "AppPoolsDetails_c" configuration, the "_block_size_" and "_number_of_blocks_". Please ensure that "_block_size_" is aligned with 4 bytes. You can know when the current configuration of pools do not satisfy the application requirements if the return value of either "GattClient_WriteCharacteristicValue" or "GattClient_ReadCharacteristicValue " is "gBleOutOfMemory_c" instead of "gBleSuccess_c" (If it is the case, the device will disconnect to the peer according to the code in step 6 in "Modifications in temperature_collector.c"). Once you have found the configuration that works in your application, please follow the steps in Memory Pool Optimizer on MKW3xA/KW3xZ Application Note, to found the best configuration without waste memory resources. For this example, configure "AppPoolsDetails_c" as follows: /* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/ #define AppPoolsDetails_c \ _block_size_ 112 _number_of_blocks_ 6 _eol_ \ _block_size_ 256 _number_of_blocks_ 3 _eol_ \ _block_size_ 280 _number_of_blocks_ 2 _eol_ \ _block_size_ 432 _number_of_blocks_ 1 _eol_‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Please let us know any question regarding this topic.
View full article
This guide describes the hardware for the KW38 minimum BoM development board. The KW38 Minimum BoM development board is configurable, low-power, and cost-effective evaluation and development board for application prototyping and demonstration of the KW39A/38A/37A/39Z/38Z family of devices. The KW38 is an ultra-low-power, highly integrated single-chip device that enables Bluetooth Low Energy (Bluetooth LE) or Generic FSK (at 250, 500 and 1000 kbps) for portable, extremely low-power embedded systems. The KW38 integrates a radio transceiver operating in the 2.36 GHz to 2.48 GHz range supporting a range of GFSK, an ARM Cortex-M0+ CPU, up to 512 KB Flash and up to 64 KB SRAM, Bluetooth LE Link Layer hardware and peripherals optimized to meet the requirements of the target applications. MKW38 device is also available on the FRDM-KW38 Freedom Development Board. For more information about the FRDM-KW38 Freedom Development Board, see the FRDM-KW38 Freedom Development Board User's Guide (document FRDMKW38ZUG available in the NXP Connectivity website also).
View full article
This document describes the Persistent Data Manager (PDM) module which handles the storage of stack context data and application data in Non-Volatile Memory (NVM). For the KW41Z devices, this memory is internal Flash and this document will therefore refer to Flash. Tip: In this document, a cold start refers to either a first-time start or a re-start without memory (RAM) held. A warm start refers to a re-start with memory held (for example following sleep with memory held). 1.    Overview If the data needed for the operation of a network node is stored only in on-chip RAM, this data is maintained in memory only while the node is powered and will be lost during an interruption to the power supply (e.g. power failure or battery replacement). This data includes context data for the network stack and application data. In order for the node to recover from a power interruption with continuity of service, provision must be made for storing essential operational data in Non-Volatile Memory (NVM), such as Flash. This data can then be recovered during a re-boot following power loss, allowing the node to resume its role in the network. The storage and recovery of operational data in KW41Z Flash can be handled using the Persistent Data Manager (PDM) module, as described in the rest of this document, which covers the following topics: Initializing the PDM module - see Section 2 Managing data in Flash - see Section 3 PDM features like record searching by record ID – see Section 4 The PDM can be used with ZigBee PRO and IEEE802.15.4 wireless networking protocols. 2.    Initializing the PDM and Building a File System Using the Kinetis NVM framework requires that the user must register the necessary data sets for NVM writing. This is done by calling function NVM_RegisterDataSet(). This function registers the given data set to be written in the NVM_TABLE section from Flash. The PDM module must be initialized by the application following a cold or warm start, irrespective of the PDM functionality used (e.g. context data storage or counter implementation). PDM initialization is performed using the function PDM_eInitialise(). This function requires the following information to be specified: The number of Flash sectors to be used by PDM (a zero value means use all segments) Once the PDM_eInitialise() function has been called, the PDM module builds a file system in RAM containing information about the sectors that it manages in Flash. The PDM reads the header data from each Flash sector and builds the file system. Application records are grouped and initialized in function InitAplRecords(), while network stack records are grouped and initialized in function InitNwkRecords(). For ZigBee PRO, the PDM is used in its most general form, as described above. 3.    Managing Data in Flash This section describes use of the PDM module to persist data in Flash in order to provide continuity of service when the KW41Z device resumes operation after a cold start or a warm start without memory held. Data is stored in Flash in terms of ‘records’. A record occupies at least one Flash sector but may be larger than a sector and occupy multiple sectors. Any number of records of different lengths can be created, provided that they do not exceed the Flash capacity. The records are created automatically for stack context data and by the application (as indicated in Section 3.1) for application data. Each record is identified by a unique 16-bit value which is assigned when the record is created - for application data, this identifier is user-defined. The stack context data which is stored in Flash includes the following: Application layer data: AIB members, such as the EPID and ZDO state Group Address table Binding table Application key-pair descriptor Trust Centre device table Network layer data: NIB members, such as PAN ID and radio channel Neighbor table Network keys Address Map table On performing a KW41Z cold start or warm start without RAM held, the PDM must be initialized in the application as described in Section 2. If this is the first ever cold start, there will be no stack context data or application data preserved in the Flash. If it is a cold or warm start following previous use (such as after a reset), there should be stack context data and application data preserved in the Flash. On start-up, the PDM builds a file system in RAM and scans the Flash for valid data. If any data is found, it is incorporated in the file system. Saving and recovering application data in Flash are described in the subsections below. 3.1   Saving Data to Flash       Application data and stack context data are saved from RAM to Flash as described below.       Note: During a data save, if the Flash needs to be defragmented and purged, this will be performed automatically resulting in all records being re-saved.     Application data           You should save application data to Flash when important changes have been made to the data in RAM. Application data in RAM can be saved to an individual record           in Flash using the function PDM_eSaveRecordData(). A buffer of data in RAM is saved to a single record in Flash (a record may span multiple Flash sectors).          The records are created when calling PDM_eInitialise(). These records are traced by a unique 16-bit identifier assigned by the application - this identifier is subsequently          used to reference the record. The value used must not clash with those used by the NXP libraries - the ZigBee PRO stack libraries use values above 0x8000.          Subsequently, in performing a re-save to the same record (specified by its 16-bit identifier), the original Flash sectors associated with the record will be overwritten but          only the sector(s) containing data changes will be altered (if no data has changed, no write will be performed). This method of only making incremental saves improves          the occupancy level of the size-restricted Flash.     Stack Context Data          The NXP ZigBee PRO stack automatically saves its own context data from RAM to Flash when certain data items change. This data will not be encrypted. 3.2   Recovering Data from Flash       Application data and stack context data are loaded from the Flash to RAM as described below.     Application Data             During a cold start or a warm start without memory held, once the PDM module has been initialized (see Section 2.2), PDM_eReadDataFromRecord() must be called             for each record of application data in Flash that needs to be copied to RAM.     Stack Context Data             The function PDM_eReadDataFromRecord(), described above, is not used for records of stack context data. Loading this data from the Flash to RAM is handled             automatically by the stack (provided that the PDM has been initialized). 3.3   Deleting Data in Flash         All records (application data and stack context data) in the Flash can be deleted using the function PDM_vDeleteAllDataRecords().          Caution: You are not recommended to delete records of ZigBee PRO stack context data by calling PDM_vDeleteAllDataRecords() before a rejoin of the same secured          network. If these records are deleted, data sent by the node after the rejoin will be rejected by the destination node since the frame counter has been reset on the source          node. For more information and advice, refer to the “Application Design Notes” appendix in the ZigBee 3.0 Stack User Guide. 4.    PDM Features PDM offers a function that can be used to search for a specific record by using the 16-bit record ID. This function is called PDM_GetNVMTableEntry() and the required parameters are the record ID and an output pointer for the found entry. Another available PDM feature is providing a mechanism to safely write the data to NVM. This is done by calling the function PDM_vCompletePendingOperations(), which calls the appropriate NVM function that is used to complete all writings to NVM before any other operation. As an example, user can use this function to make sure that the data is written to the NVM before a reset.
View full article
FRDM-KW36 Software Development Kit (SDK) includes drivers and examples of FlexCAN module for KW36 which can be easily configured for a custom communication. For example, if user want to change the default baud rate from FlexCAN driver demo examples then the only needed change is the default value on "config->baudRate" and "config->baudRateFD" from "FLEXCAN_GetDefaultConfig" function (See Figure 1). Segments within a bit time will be automatically configured to obtain the desired baud rate. By default, demos are configured to work with CAN FD communication. Figure 1. FRDM-KW36's default baudrate from flexcan_interrupt_transfer driver example Even so, there are cases where segments within a bit time are not well configured and it's necessary that user configure segments manually. An example occurs by setting the maximum FD baud rate "3.2MHz" using the 32MHz xtal or "2.6MHz" using a 26MHz xtal where demo reports an error. See Figure 2. Figure 2. Error by setting maximum baud rate When this error occurs, the fix is on setting the timing config parameters correctly by including the definition of SET_CAN_QUANTUM on application source file (see Figure 3) and then declare and initialize the timing config parameters shown in Figure 4. Figure 3. SET_CAN_QUANTUM define Figure 4. Custom timing config parameters For this example we are going to show how to calculate timing config parameters in an scenario where a CAN FD communication is used with baud rate of 500kHz on nominal phase and 3.2MHz on FD phase. See Figure 5.  To do it, we need to calculate Time Quanta and value of segments within the bit time.    Figure 5. Custom CAN FD baudrate KW36 Reference Manual in chapter "37.4.8.7 Protocol timing" shows the segments within a bit time for CAN nominal phase configured in "CAN_CTRL1" register (see Figure 6), and segments for FD phase configured in CAN_FDCBT register (see Figure 7). Figure 6. Segment within a bit time for CAN nominal phase Figure 7. Segment within a bit time for CAN FD phase Before calculating the value of segments, first we need to calculate the Time Quanta which is the atomic number of time handled by the CAN engine. The formula to calculate Time Quanta is shown in Figure 8 taken from KW36 Reference Manual. Figure 8. Time Quanta Formula CANCLK can be selected by CLKSRC bits on CAN_CTRL1 register as shown in Figure 9, where the options are Peripheral clock=20MHz or Oscillator clock (16MHz if using 32MHz xtal or 13MHz if using 26MHz xtal). The recomiendation is to use the Oscillator clock due to peripheral clock can have jitter that affect communication.  Figure 9. CAN clocks To select the Oscillator clock, search for flexcanConfig.clkSrc definition and set it to kFLEXCAN_ClkSrcOsc as shown in Figure 10. Figure 10. CANCLK selection Next step is selecting the PRESDIV value for nominal phase and FPRESDIV for FD phase. You have to select the right value to achieve the TQ needed to obtain the configured baudrate. For this example, let's set FPRESDIV value to 0 and PRESDIV value to 3. TQ calculation for nominal phase: TQ = (PRESDIV + 1) / CANCLK = (3 + 1) / 16000000 = 0.00000025 TQ calculation for FD phase: TQ = (FPRESDIV + 1) / CANCLK = (0 + 1) / 16000000 = 0.0000000625 The bit rate, which defines the rate of CAN message is given by formula shown in Figure 11 taken from KW36 Reference Manual. Figure 11. CAN Bit Time and Bit Rate Formulas With this info and with our TQ calculated, we can deduce that we need: For Nominal phase: 8 = Number of Time Quanta in 1 bit time For FD phase: 5 = Number of Time Quanta in 1 bit time Now, let's define the value of segments. For nominal phase: Bit Time =  (number of Tq in 1 bit time) x Tq CAN Bit Time = (1 + (PROPSEG + PSEG1 + 2) + (PSEG2 + 1) ) x Tq CAN Bit Time = (1 + (1 + 2  + 2) + (1 + 1) ) x Tq = 8 x 0.00000025 =  Baud rate = 1/ CAN Bit Time = 500KHz For FD phase: CAN Bit Time = (number of Tq in 1 bit time) x Tq CAN Bit Time = (1 + (FPROPSEG + FPSEG1 + 1) + (FPSEG2 + 1) ) x Tq CAN Bit Time = (1 + (0 + 1 + 1) + (1 + 1) ) x Tq = 5 x Tq =  0.0000003125 Bit Rate = 1/CAN Bit Time = 1 / 0.0000003125 =  3.2MHz To finish, just update the calculated values on your firmware on flexcanConfig.timingConfig structure.  Notes: FRDM-KW36 Software Development Kit (SDK) can be downloaded from MCUXpresso webpage. FlexCAN driver examples are located in path: "SDK_2.2.0_FRDM-KW36\boards\frdmkw36\driver_examples" from your downloaded FRDM-KW36 SDK. Take in consideration that not all the baud rates are achievables and will depend on the flexcan clock and segment values used.
View full article
Introduction This post guides you on migrating from MKW36Z512VHT4 to MKW36A512VFT4 MCUs. This example will make use of the "beacon" SDK example. SDK Download and Install 1- Go to MCUXpresso web page: MCUXpresso Web Page 2- Log in with your registered account. 3- Search for the "KW36A" device. Then click on the suggested processor and click on "Build MCUXpresso SDK"       4- The next page will be displayed. Select “All toolchains” in the “Toolchain / IDE” box and provide a name to identify the package. Then click on "Download SDK".     5- Accept the license agreement. Wait a few minutes until the system gets the package into your profile. Download the SDK clicking on "Download SDK Archive" as depicted in the following figure.     6- If MCUXpresso IDE is used, drag and drop the KW36A SDK zip folder in “Installed SDK’s” perspective to install the package.     At this point, you have downloaded and installed the SDK package for the KW36A device.   Software Migration in MCUXpresso IDE 1- Import the "beacon" example on the MCUXpresso workspace. Click on “Import SDK examples(s)…” option, a new window will appear. Then select "MKW36Z512xxx4" and click on the FRDM-KW36 image. Click on the "Next >" button.     2- Search beacon and select your project version (bm or freertos).     3- Go to Project/Properties. Expand C/C++ Build/MCU settings and select MKW36A512xxx4 MCU. Click Apply and Close button to save the configuration.     4- Rename MKW36Z folders as MKW36A, clicking the right mouse button and selecting "Rename". These are the following:   framework/DCDC/Interface -> MKW36Z framework/DCDC/Source -> MKW36Z framework/LowPower/Interface -> MKW36Z framework/LowPower/Source -> MKW36Z framework/XCVR -> MKW36Z4     5- Open the Project/Properties window in MCUXpresso IDE. Go to C/C++ Build/Settings and select MCU C Compiler/Includes folder in the Tool Settings window. Edit all paths related to MKW36 MCU, in according to MKW35 folders before created. The results must look similar as shown below:   ../framework/LowPower/Interface/MKW36A ../framework/LowPower/Source/MKW36A ../framework/DCDC/Interface/MKW36A ../framework/XCVR/MKW36A4     6- Select MCU Assembler/General folder in Tool Settings. Edit the paths related to MKW36 MCU. The results must look similar as shown below:   ../framework/LowPower/Interface/MKW36A ../framework/LowPower/Source/MKW36A ../framework/DCDC/Interface/MKW36A ../framework/XCVR/MKW36A4     7- Go to Project/Properties. Expand MCU C Compiler/Preprocessor window. Edit "CPU_MKW36Z512VHT4" and "CPU_MKW36Z512VHT4_cm0plus" symbols, rename it as "CPU_MKW36A512VFT4" and "CPU_MKW36A512VFT4_cm0plus" respectively. Save the changes.     8- Go to the workspace. Delete “fsl_device_registers, MKW36Z4, MKW36Z4_features, system_MKW36Z4.h and system_MKW36Z4.c” files located at CMSIS folder. Then, unzip the MKW35Z SDK package and search for “fsl_device_registers, MKW36A4, MKW36A4_features, system_MKW36A4.h and system_MKW36A4.c” files into this folder at the following paths:   <SDK_folder_root>/devices/MKW36A4/fsl_device_registers.h <SDK_folder_root>/devices/MKW36A4/MKW36A4.h <SDK_folder_root>/devices/MKW36A4/MKW36A4_features.h <SDK_folder_root>/devices/MKW36A4/system_MKW36A4.h <SDK_folder_root>/devices/MKW36A4/system_MKW36A4.c     9- Overwirte the “startup_mkw36z4.c” (located inthe startup folder) by the "startup_mkw36a4.c" located in the following path <SDK_folder_root>/devices/MKW36A4/mcuxpresso/startup_mkw36a4.c. You can simply drag and drop on the startup folder, and remove the older one.     10- Open "fsl_device_registers.h" file in CMSIS folder. Add"defined(CPU_MKW36A512VFT4)" in the following code (line 18 of the file):   /* * Include the cpu specific register header files. * * The CPU macro should be declared in the project or makefile. */ #if (defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VFT4) || defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36A512VFT4))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   11- Open "ble_config.h" file in bluetooth->host->config folder. Add "defined(CPU_MKW36A512VFT4)" in the following code (line 146 of the file):   /* The maximum number of BLE connection supported by platform */ #if defined(CPU_QN9080C) #define MAX_PLATFORM_SUPPORTED_CONNECTIONS 16 #elif (defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36A512VFT4) || \ defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW35A512VFP4) || \ defined(CPU_K32W032S1M2CAx_cm0plus) || defined(CPU_K32W032S1M2VPJ_cm0plus) || \ defined(CPU_K32W032S1M2CAx_cm4) || defined(CPU_K32W032S1M2VPJ_cm4) || \ defined(CPU_MKW38A512VFT4) || defined (CPU_MKW38Z512VFT4) || defined(CPU_MKW39A512VFT4) || \ defined(CPU_MKW37A512VFT4) || defined(CPU_MKW37Z512VFT4))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   12- Open "ble_controller_task.c" file in source->common folder. Add "defined(CPU_MKW36A512VFT4)" in the following code (line 272 of the file):    #elif (defined(CPU_MKW35A512VFP4) || defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VFT4) ||\ defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4)) /* Select BLE protocol on RADIO0_IRQ */ XCVR_MISC->XCVR_CTRL = (uint32_t)((XCVR_MISC->XCVR_CTRL & (uint32_t)~(uint32_t)( XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_MASK )) | (uint32_t)( (0UL << XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_SHIFT) ));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   13-Build the project.   At this point, the project is already migrated.   Software Migration in IAR Embedded Workbench IDE 1- Open the beacon project located in the following path: 2- Select the project in the workspace and press Alt + F7 to open project options.   3- In the General Options/Target window click the icon next to the device name and select the appropriate device NXP/KinetisKW/KW3x/NXP MKW36A512xxx4, then click the OK button.   4- Create a new folder with the name MKW36A at following paths: <SDK_root>/middleware/wireless/framework_5.4.6/DCDC/Interface <SDK_root>/middleware/wireless/framework_5.4.6/DCDC/Source <SDK_root>/middleware/wireless/framework_5.4.6/LowPower/Interface <SDK_root>/middleware/wireless/framework_5.4.6/LowPower/Source <SDK_root>/middleware/wireless/framework_5.4.6/XCVR     5- Copy all files inside MKW36Z folders located at the above paths and paste in MKW36A folders.     6- Select the beacon project in the workspace and press Alt+F7 to open project options window. In C/C++ Compiler/Preprocessor window, rename the paths related to MKW36Z folders to MKW36A folders. Rename the CPU_MKW36Z512VHT4 macro as CPU_MKW36A512VFT4 in the defined symbols text box. The results must look similar as shown below: Click the OK button. $PROJ_DIR$/middleware/wireless/framework_5.4.2/LowPower/Interface/MKW36A $PROJ_DIR$/../../../../../../../devices/MKW36A4/drivers $PROJ_DIR$/../../../../../../../middleware/wireless/framework_5.4.2/DCDC/Interface/MKW36A $PROJ_DIR$/../../../../../../../middleware/wireless/framework_5.4.2/XCVR/MKW36A4 $PROJ_DIR$/../../../../../../../devices/MKW36A4 $PROJ_DIR$/../../../../../../../devices/MKW36A4/utilities     7- Expand the startup folder, select all files, click the right mouse button and select the “Remove” option. Click the right mouse button on the folder and select “Add/Add files”. Add the startup_MKW36A4.s located at this path: <SDK_root>/devices/MKW36A4/iar/startup_MKW36A4.s Also, add system_MKW36A4.c and system_MKW36A4.h into the startup folder. Both files are located at the next path: <SDK_root>/devices/MKW36A4   8- Open "ble_config.h" file in bluetooth->host->config folder. Add "defined(CPU_MKW36A512VFT4)" in the following code: /* The maximum number of BLE connection supported by platform */ #if defined(CPU_QN9080C) #define MAX_PLATFORM_SUPPORTED_CONNECTIONS 16 #elif (defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36A512VFT4) || \ defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW35A512VFP4) || \ defined(CPU_K32W032S1M2CAx_cm0plus) || defined(CPU_K32W032S1M2VPJ_cm0plus) || \ defined(CPU_K32W032S1M2CAx_cm4) || defined(CPU_K32W032S1M2VPJ_cm4) || \ defined(CPU_MKW38A512VFT4) || defined (CPU_MKW38Z512VFT4) || defined(CPU_MKW39A512VFT4) || \ defined(CPU_MKW37A512VFT4) || defined(CPU_MKW37Z512VFT4))‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   9- Open "ble_controller_task.c" file in source->common folder. Add "defined(CPU_MKW36A512VFT4)" in the following code: #elif (defined(CPU_MKW35A512VFP4) || defined(CPU_MKW35Z512VHT4) || defined(CPU_MKW36A512VFP4) || defined(CPU_MKW36A512VFT4) ||\ defined(CPU_MKW36A512VHT4) || defined(CPU_MKW36Z512VFP4) || defined(CPU_MKW36Z512VHT4)) /* Select BLE protocol on RADIO0_IRQ */ XCVR_MISC->XCVR_CTRL = (uint32_t)((XCVR_MISC->XCVR_CTRL & (uint32_t)~(uint32_t)( XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_MASK )) | (uint32_t)( (0UL << XCVR_CTRL_XCVR_CTRL_RADIO0_IRQ_SEL_SHIFT) ));‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   10-Build the project.   At this point, the project is already migrated.
View full article
This document provides the calculation of the Bluetooth Low Power consumption linked to the setting of the Kinetis.   The Power Profile Calculator is build to provide the power consumption of your application. It's a mix between real measurements in voltage and temperature. The process is not taken into account which may create some variation.   DISCLAIMER: This excel workbook is provided as an estimation tool for NXP customers and is based on power profile measurements done on a set of randomly selected parts. A specific part may exhibit deviation from the nominal measurements used on this tool.   This document is the summary of all the information available in the AN12459 Power Consumption Analysis - FRDM-KW38 available in the NXP web page.   Several parameters could be fill-in: Buck or bypass mode (DCDC) Supply Voltage (2.4V to 3.6V) Temperature (-40°C to +105°C) Processor configuration (20MHz, 32MHz or 48MHz) 10 different deep sleep modes Different Tx output power (0dBm, +3.5dBm or +5dBm) Data rate (1Mbps, 2Mbps, 500kbps, 125kbps) Possibility to set the Advertising interval, connection interval, scan interval and active scan windows duration Fix the Bluetooth Packet sizes in Advertising and Connection  Tx/Rx payload.   One optional information is to provide an idea of the duration life time on 9 typical batteries.
View full article
Introduction When a software update is requested by an OTAP Client (a device that receives a software update, commonly Bluetooth LE Peripheral) from the OTAP Server (a device that sends a software update, commonly Bluetooth LE Central), you may want to preserve some data previously acquired, such as bonding information, trimming values for the system oscillators, or probably NVM data for your application. This document guides you in performing OTAP updates preserving the flash data content of your interest. This document is intended for developers familiarized with OTAP custom Bluetooth LE service, for more information, you can take a look at the following post: Reprogramming a KW36 device using the OTAP Client Software.   OTAP Header and Sub-elements OTAP Protocol implements a format for the software update that is composed of a header and a defined number of sub-elements. The OTAP Header describes general information about the software update and it has a defined format shown in the following figure. For more information about the header fields, you can go to 11.4.1 Bluetooth Low Energy OTAP header chapter of the Bluetooth Low Energy Application Developer's Guide document included in the SDK at <SDK_2.2.X_FRDM-KW36_Download_Path>\docs\wireless\Bluetooth                              Each Sub-element contains information for a specific purpose. You could implement your proprietary fields for your application (For more information about sub-element fields, you can go to 11.4.1 Bluetooth Low Energy OTAP header chapter of the Bluetooth Low Energy Application Developer's Guide document included in the SDK at <SDK_2.2.X_FRDM-KW36_Download_Path>\docs\wireless\Bluetooth). OTAP includes the following sub-elements: Image File Sub-element Value Field Lenght (bytes) Description Upgrade Image  Variable This sub-element contains the actual binary executable image which is copied into the flash memory of the OTAP Client device. The maximum size of this sub-element depends on the target hardware. Sector Bitmap 32 This sub-element contains a sector bitmap of the flash memory of the target device which tells the bootloader which sectors should be overwritten and which leave intact. The format of this field is the least-significant bit first for each byte with the least significant bytes and bits standing for the lowest memory sections of the flash.  Image File CRC 2 This is a 16-bit CRC calculated over all elements of the image file except this field itself. This element must be the last sub-element in an image file sent over the air.   OTAP Sector Bitmap Sub-element Field The KW36 Flash is partitioned into: One 256 KB Program Flash (P-Flash) array divided into 2 KB sectors with a flash address range from 0x0000_0000 to 0x0003_FFFF. One 256 KB FlexNVM array divided in 2 KB sectors, flash address ranges from 0x1000_0000 to 0x1003_FFFF with an Alias memory with address range 0x0004_0000 to 0x0007_FFFF. The Bitmap sub-element is 256 bits of length, in terms of the KW36 flash, each bit represents a 2KB sector covering the address range from 0x0 - 0x0007_FFFF (P-Flash to FlexNVM Alias address range), where 1 means that such sector should be erased and 0 means that such sector should be preserved. The Bitmap field is used by the OTAP Bootloader to obtain the address range which should be erased before programming the KW36 with the software update, so it must be configured before sending a software update to leave intact the address range of memory that contain data of your interest and erase only the address range that will be overwritten by the software update.        For example: Suppose that a developer wants to preserve the address range between 0x7D800 - 0x7FFFF and the address range between 0x0 - 0x1FFF, and the left memory must be erased. The address range between 0x7D800 - 0x7FFFF corresponds to the 5 top flash sectors and the address range between 0x0 - 0x1FFF is the lowest 4 sectors. So, it means that bits between 256 and 252 (256, 255, 254, 253 and 252) and bits between 4 and 1 (4,3,2 and 1) should be set to 0, that way OTAP Bitmap for this example is: 0x07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0   Configuring OTAP Bitmap to Protect an Address Range with NXP Test Tool Download and install Test Tool for Connectivity products in NXP's web site Open NXP Test Tool 12 software on your PC. Go to "OTA Updates -> OTAP Bluetooth LE" Then load your image file for the software update clicking on the "Browse..." button (NXP Test Tool only accepts .bin and .srec files). You can configure the OTAP Bitmap selecting the "Override sector bitmap" checkbox and changing the default value by your new bitmap value. Once you have configured the bitmap, select "Save...".   Then, a window will be displayed to select the destination to save the .bleota file. Provide a name to identify this file. You can use this file with IoT Toolbox App for Android and iOS to update the software using OTAP. This new .bleota file contains the bitmap that tells to the OTAP Bootloader which sectors will be erased and which sectors will be preserved.          
View full article
This document describes a simple process for enabling the user controls the radio through serial commands. Hardware requirements: • FRDM-KW41Z/QN902x board or a board programmed with HCI black box application. Software requirements: • Test Tool 12 application. It can be downloaded from the NXP web page. • HCI Black Box binary.   Running Demo 1. Load the board with hci_black_box example. 2. Open the Test Tool 12 software 3. Set up the correct Serial Configuration. If there were no changes in the application the default configuration will correspond to the one showed in the following figure. 4. Double click on the active device that you want to test, this will open the COM port in the command console. 5. Set the command set to the BLE_HCI.xml. This file has a list of the HCI commands that the user can send to the device, some of the commands have some options to be configured if necessary or some data to be filled. 6. To make easier the use of frequent commands, there is the option to add a shortcut to the command and the chosen behavior will be added to the panel. 7. Once you add the shortcut or choose the command or your preference, just double click over it and the tool will send the command to the device. In this case, we will send a reset on the board, this command does not receive any extra parameters, data or need any extra configuration.   8. If successful there will be a response or acknowledge of the behavior that will be shown in the right panel. Hope it helps. Regards, Mario
View full article
Introduction The FRDM-KW36 includes an RTC module with a 32 kHz crystal oscillator. This module generates a 32 kHz clock source for the MCU whilst running on very low power mode. This oscillator includes a set of programmable capacitors used as the C LOAD . Changing the value of these capacitors can modify the frequency the oscillator provides. This configurable capacitance ranges from 0 pF (capacitor bank disabled) to 30 pF in steps of 2 pF. These values are obtained by combining the enabled capacitors. The values available are 2 pF, 4 pF, 8 pF, and 16 pF. Any combination of these four can be done. It is recommended that these internal capacitors are disabled if the external capacitors are available (clearing SC2P, SC4P, SCS8, and SC16 bits in RTC Control Register SFR). To adjust the frequency provided by the oscillator, you must first be able to measure the frequency. Using a frequency counter would be ideal, as it provides a more precise measurement than an oscilloscope. You will also need to output the oscillator frequency. To output the oscillator frequency, using any of the Bluetooth demo applications as an example, you should do the following: Adjusting Frequency Example This example will make use of the Heart Rate Sensor demo (freertos version) from the Connectivity Software Stack and assumes that the developer has the knowledge of import or open projects from the SDK to IDE. Open or clone the Heart Rate Sensor project from your SDK. Find the board.c and board.h files in the board folder at the workspace.                                                                                Declare a void function on the board.h file as shown below. This function will be in order to mux the RTC clock out to the PTB3 and be able to measure the frequency.  /* Function to mux PTB3 to RTC_CLKOUT */ void BOARD_EnableRtcClkOut (void);‍‍ Develop the BOARD_EnableRtcClkOut function inside the board.c file as below. void BOARD_EnableRtcClkOut(void) { /* Enable PORTB clock gating */ CLOCK_EnableClock(kCLOCK_PortB); /* Mux the RTC_CLKOUT to PTB3 */ PORT_SetPinMux(PORTB, 3u, kPORT_MuxAlt7); /* Select the 32kHz reference for RTC_CLKOUT signal */ SIM->SOPT1 |= SIM_SOPT1_OSC32KOUT(1); } Call the BOARD_EnableRtcClkOut function in hardware_init function just after BOARD_BootClockRUN (board.c file). Find clock_config.c file in the board folder at the workspace. Add the following defines at the top of the file. #define RTC_OSC_CAP_LOAD_0 0x0U /*!< RTC oscillator, capacitance 0pF */ #define RTC_OSC_CAP_LOAD_2 0x2000U /*!< RTC oscillator, capacitance 2pF */ #define RTC_OSC_CAP_LOAD_4 0x1000U /*!< RTC oscillator, capacitance 4pF */ #define RTC_OSC_CAP_LOAD_6 0x3000U /*!< RTC oscillator, capacitance 6pF */ #define RTC_OSC_CAP_LOAD_8 0x800U /*!< RTC oscillator, capacitance 8pF */ #define RTC_OSC_CAP_LOAD_10 0x2800U /*!< RTC oscillator, capacitance 10pF */ #define RTC_OSC_CAP_LOAD_12 0x1800U /*!< RTC oscillator, capacitance 12pF */ #define RTC_OSC_CAP_LOAD_14 0x3800U /*!< RTC oscillator, capacitance 14pF */ #define RTC_OSC_CAP_LOAD_16 0x400U /*!< RTC oscillator, capacitance 16pF */ #define RTC_OSC_CAP_LOAD_18 0x2400U /*!< RTC oscillator, capacitance 18pF */ #define RTC_OSC_CAP_LOAD_20 0x1400U /*!< RTC oscillator, capacitance 20pF */ #define RTC_OSC_CAP_LOAD_22 0x3400U /*!< RTC oscillator, capacitance 22pF */ #define RTC_OSC_CAP_LOAD_24 0xC00U /*!< RTC oscillator, capacitance 24pF */ #define RTC_OSC_CAP_LOAD_26 0x2C00U /*!< RTC oscillator, capacitance 26pF */ #define RTC_OSC_CAP_LOAD_28 0x1C00U /*!< RTC oscillator, capacitance 28pF */ #define RTC_OSC_CAP_LOAD_30 0x3C00U /*!< RTC oscillator, capacitance 30pF */ Search the CLOCK_CONFIG_EnableRtcOsc call to a function inside the BOARD_BootClockRUN function (also in the clock_config.c file), and edit the argument by any of the defines above. Finally, disable the low power options and led support in the "preinclude.h" file located in the source folder of the project: #define cPWR_UsePowerDownMode 0 #define gLEDSupported_d 0 At this point, you can measure in PTB3 and play with the frequency adjust using your frequency counter. Each time that the board is programmed, you need to perform a POR to get the correct measure. The following table was obtained from an FRDM-KW36 board rev B and it can be used as a reference to adjust the frequency. Please note that the capacitance is not only composed of the enabled internal capacitance, but also the parasitic capacitances found in the package, bond wires, bond pad, and the PCB traces. So, while the reference measurements given below should be close to the actual value, you should also make measurements with your board, to ensure that the frequency is trimmed specifically to your board and layout.   Enabled Capacitors CLOAD Capacitance Definition Frequency - 0pF RTC_OSC_CAP_LOAD_0 (bank disabled) 32772.980Hz SC2P 2pF RTC_OSC_CAP_LOAD_2 32771.330Hz SC4P 4pF RTC_OSC_CAP_LOAD_4 32770.050Hz SC2P, SC4P 6pF RTC_OSC_CAP_LOAD_6 32769.122Hz SC8P 8pF RTC_OSC_CAP_LOAD_8 32768.289Hz SC2P, SC8P 10pF RTC_OSC_CAP_LOAD_10 32767.701Hz SC4P, SC8P 12pF RTC_OSC_CAP_LOAD_12 32767.182Hz SC2P, SC4P, SC8P 14pF RTC_OSC_CAP_LOAD_14 32766.766Hz SC16P 16pF RTC_OSC_CAP_LOAD_16 32766.338Hz SC2P, SC16P 18pF RTC_OSC_CAP_LOAD_18 32766.038Hz SC4P, SC16P 20pF RTC_OSC_CAP_LOAD_20 32765.762Hz SC2P, SC4P, SC16P 22pF RTC_OSC_CAP_LOAD_22 32765.532Hz SC8P, SC16P 24pF RTC_OSC_CAP_LOAD_24 32765.297Hz SC2P, SC8P, SC16P 26pF RTC_OSC_CAP_LOAD_26 32765.117Hz SC4P, SC8P, SC16P 28pF RTC_OSC_CAP_LOAD_28 32764.940Hz SC2P, SC4P, SC8P, SC16P 30pF RTC_OSC_CAP_LOAD_30 32764.764Hz
View full article
Introduction This document is to guide how to modify the OTAP Client software to the Low Power module. The starting point of this document is the OTAP Client example in the FRDM-KW36 SDK v2.2.2.   Deep Sleep Modes This section provides a base to understand how the developer should change between DSM1 (Deep Sleep Mode 1) and DSM3 (Deep Sleep Mode 3). The DSM6 does not need to be started by the developer, instead, the controller configures this mode automatically and returns to the latest mode after finished the radio activity.   DSM1 This low-power mode was designed to be used when the BLE stack is active, in other words when the LL is in advertising, scanning, or connection states. In this mode, the MCU enters LLS3 and BLE Link Layer enters deep sleep. The SoC wakes up from this mode by the on-board switches, by LPTMR timeout, or by BLE Link Layer wake-up interrupt (BLE_LL reference clock reaches wake up instance register) using LLWU module. The LPTMR timer is used to measure the time that the MCU spends in deep sleep to synchronize low-power timers at wakeup.   DSM3 This low-power mode was designed to be used when all stacks enabled for this platform are idle, in other words, when the LL stop advertising, scanning, or connection. In this mode, the MCU enters LLS3 and all enabled link layers remain idle. All RAM is retained. The SoC wakes up from this mode by the on-board switches, by DCDC power switch (when DCDC is in buck mode), or by LPTMR timeout using LLWU module. The LPTMR timer is also used to measure the time that MCU spends in deep sleep to synchronize low-power timers at wakeup.   DSM6 This low-power mode was developed to save some power while the radio is on. Its most common use case is with the radio in Rx waiting for a packet. Upon receiving the packet the radio wakes up the MCU. In this mode, the MCU enters STOP mode and the radio maintains its state. Any module capable of producing an interrupt can wake up the MCU, such as on-board switches, DCDC power switch (when DCDC is in buck mode), LPTMR timeout, Radio Interrupt, UART, and so on. The LPTMR timer is also used to measure the time that the MCU spends in deep sleep to synchronize low-power timers at wakeup.   For more information about DSM modes, you can inspect the “Connectivity Framework Reference Manual” chapter 3.15 Low-power library, it provides full information of Low Power modes and the usage on the NXP stack. It is available in your SDK at <FRDM-KW36 SDK root>\docs\wireless\Common.   Modifications on the Software In order to add low power on the OTAP Client (switching between DSM1, DSM3, and DSM6) two files must be modified: - app_preinclude.h - otap_client_att.c The following sections explain these changes.   app_preinclude.h This file is intended to contain the definitions that manage the behavior of the application. To include and enable the Low Power module you must add (or modify if the macro is already defined in this file) the following preprocessor directives.   1. Modify the AppPoolsDetails as following. /* Defines pools by block size and number of blocks. Must be aligned to 4 bytes.*/ #define AppPoolsDetails_c \ _block_size_ 32 _number_of_blocks_ 6 _eol_ \ _block_size_ 64 _number_of_blocks_ 4 _eol_ \ _block_size_ 88 _number_of_blocks_ 3 _eol_ \ _block_size_ 248 _number_of_blocks_ 2 _eol_ \ _block_size_ 312 _number_of_blocks_ 1 _eol_ \ _block_size_ 392 _number_of_blocks_ 1 _eol_‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 2. Set “cPWR_UsePowerDownMode” to 1 and keep the following directives in the “Framework Configuration” section as shown below. /* Check Low Power Timer */ #define cPWR_CheckLowPowerTimers 1 /* Enable/Disable Low Power Timer */ #define gTMR_EnableLowPowerTimers 1 /* Enable/Disable PowerDown functionality in PwrLib */ #define cPWR_UsePowerDownMode 1 /* Enable/Disable BLE Link Layer DSM */ #define cPWR_BLE_LL_Enable 1 /* Default Deep Sleep Mode*/ #define cPWR_DeepSleepMode 3 /* Enable/Disable MCU Sleep During BLE Events */ #define cMCU_SleepDuringBleEvents 1 /* Default deep sleep duration in ms */ #define cPWR_DeepSleepDurationMs 30000 /* Number of slots(625us) before the wake up instant before which the hardware needs to exit from deep sleep mode. */ #define cPWR_BLE_LL_OffsetToWakeupInstant 3 /* Enables / Disables the DCDC platform component */ #define gDCDC_Enabled_d 1 /* Default DCDC Mode used by the application */ #define APP_DCDC_MODE gDCDC_Mode_Buck_c /* Default DCDC Battery Level Monitor interval */ #define APP_DCDC_VBAT_MONITOR_INTERVAL 600000‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 3. Add the following directives in the “BLE Stack Configuration” section. Create the “Auto Configuration” section to disable LED support whenever Low Power is enabled. /*! ********************************************************************************* * BLE Stack Configuration ********************************************************************************** */ /* Time between the beginning of two consecutive advertising PDU's */ #define mcAdvertisingPacketInterval_c 0x02 /* 1.25 msec */ /* Offset to the first instant register. */ #define mcOffsetToFirstInstant_c 0x00 /* 625usec */ /*! ********************************************************************************* * Auto Configuration ********************************************************************************** */ /* Disable LEDs when enabling low power */ #if cPWR_UsePowerDownMode || gMWS_UseCoexistence_d #define gLEDSupported_d 0 #endif #if gMWS_UseCoexistence_d #undef gKBD_KeysCount_c #define gKBD_KeysCount_c 1 #endif‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 4. Modify the “Memory Pools Configuration” section as follows. /* Enable RNG seed storage in Flash */ #define gRngSeedStorageAddr_d ((uint32_t)FREESCALE_PROD_DATA_BASE_ADDR + 1024) /* Enable XCVR calibration storage in Flash */ #define gPreserveXcvrDacTrimValue_d 1 #define gXcvrDacTrimValueSorageAddr_d ((uint32_t)FREESCALE_PROD_DATA_BASE_ADDR + 1040) /* Application Connection sleep mode */ #define gAppDeepSleepMode_c 1 /* Application RAM usage configuration */ #define cPWR_RamRetentionInVLLS 2 /* 32K */ /* Disable unused LowPower modes */ #define cPWR_EnableDeepSleepMode_1 1 #define cPWR_EnableDeepSleepMode_2 0 #define cPWR_EnableDeepSleepMode_3 1 #define cPWR_EnableDeepSleepMode_4 0 #define cPWR_EnableDeepSleepMode_5 0 #define cPWR_EnableDeepSleepMode_7 0 #define cPWR_EnableDeepSleepMode_8 0 /* Warm-boot sequence will use the default stack which is used by ISRs on FreeRTOS */ #define USE_WARMBOOT_SP 0‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   otap_client_att.c This is the main source file at the application level. Here are managed all the procedures that the device performs, before, during, and after to create a connection. This allows you to get the state of the device any instant and, hence, the dedicated low power APIs that rule the application must be implemented here, in the callbacks contained in this file, for an easier switching among the low power states.   1. Include “PWR_Configuration.h” header in “Include” section: #if (cPWR_UsePowerDownMode) #include "PWR_Interface.h" #include "PWR_Configuration.h" #endif‍‍‍‍‍‍‍‍‍‍‍‍ 2. Locate the “BleApp_Config” function. This function is executed once, after POR (Power on reset) during the device setup. Here you can change the deep sleep mode to DSM3 and allow the device to sleep using “PWR_ChangeDeepSleepMode” and “PWR_AllowDeviceToSleep” APIs. When the device has finished the initialization, it goes to sleep since all stacks are idle. See the following example. static void BleApp_Config(void) { #if defined(MULTICORE_APPLICATION_CORE) && (MULTICORE_APPLICATION_CORE == 1) if (GattDbDynamic_CreateDatabase() != gBleSuccess_c) { panic(0,0,0,0); return; } #endif /* MULTICORE_APPLICATION_CORE */ /* Common GAP configuration */ BleConnManager_GapCommonConfig(); /* Register stack callbacks */ (void)App_RegisterGattServerCallback (BleApp_GattServerCallback);‍‍‍‍‍‍‍‍‍‍‍‍‍ mAdvState.advOn = FALSE; /* Start services */ basServiceConfig.batteryLevel = BOARD_GetBatteryLevel(); (void)Bas_Start(&basServiceConfig); (void)Dis_Start(&disServiceConfig); if (OtapClient_Config() == FALSE) { /* An error occurred in configuring the OTAP Client */ panic(0,0,0,0); } /* Allocate application timer */ appTimerId = TMR_AllocateTimer(); mBatteryMeasurementTimerId = TMR_AllocateTimer(); #if (cPWR_UsePowerDownMode) #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(cPWR_DeepSleepMode); PWR_AllowBlackBoxToSleep(); #endif PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); PWR_AllowDeviceToSleep(); #else PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); PWR_AllowDeviceToSleep(); #endif #endif }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 3. Locate the “BleApp_Start” function. This function is executed just after wake up by pressing the LLWU SW3 button. This action will trigger the advertising, so, you must change the deep sleep mode to DSM1 using “PWR_ChangeDeepSleepMode” API since the BLE stack is active. See the following example. void BleApp_Start(void) { Led1On(); if (mPeerDeviceId == gInvalidDeviceId_c) { /* Device is not connected and not advertising*/ if (!mAdvState.advOn) { #if gAppUseBonding_d if (gcBondedDevices > 0) { mAdvState.advType = whiteListAdvState_c; } else { #endif mAdvState.advType = advState_c; #if gAppUseBonding_d } #endif #if (cPWR_UsePowerDownMode) #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(gAppDeepSleepMode_c); #endif #else PWR_ChangeDeepSleepMode(gAppDeepSleepMode_c); #endif #endif BleApp_Advertise(); } } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 4. Locate the “BleApp_AdvertisingCallback” function. This function is executed every time the advertising state changes. Change the deep sleep mode to DSM3 when “mAdvState.advOn” is false, in other words, when the device stops advertising. If you stop the advertising either using an application timer or a user button, KW36 will go to sleep until you start advertising again (pressing LLWU SW3 button), saving power when all stacks are idle. See the following example. static void BleApp_AdvertisingCallback (gapAdvertisingEvent_t* pAdvertisingEvent) { switch (pAdvertisingEvent->eventType) { case gAdvertisingStateChanged_c: { mAdvState.advOn = !mAdvState.advOn; if(mAdvState.advOn) { LED_StopFlashingAllLeds(); Led1Flashing(); } #if (cPWR_UsePowerDownMode) else { #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(cPWR_DeepSleepMode); #endif #else PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); #endif } #endif } break; case gAdvertisingCommandFailed_c: { Led2On(); panic(0,0,0,0); } break; default: ; /* For MISRA compliance */ break; } }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 5. Locate “BleApp_ConnectionCallback” function. It is executed every time the connection state changes. In “gConnEvtConnected_c” add the following code to change to DSM1, since the BLE stack is active. case gConnEvtConnected_c: { /* Advertising stops when connected */ mAdvState.advOn = FALSE; (void)TMR_StopTimer(appTimerId); /* Subscribe client*/ mPeerDeviceId = peerDeviceId; (void)Bas_Subscribe(&basServiceConfig, peerDeviceId); (void)OtapCS_Subscribe(peerDeviceId); OtapClient_HandleConnectionEvent (peerDeviceId); /* Start battery measurements */ (void)TMR_StartLowPowerTimer(mBatteryMeasurementTimerId, gTmrLowPowerIntervalMillisTimer_c, TmrSeconds(mBatteryLevelReportInterval_c), BatteryMeasurementTimerCallback, NULL); #if (cPWR_UsePowerDownMode) #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(gAppDeepSleepMode_c); PWR_AllowBlackBoxToSleep(); #endif #else PWR_ChangeDeepSleepMode(gAppDeepSleepMode_c); PWR_AllowDeviceToSleep(); #endif #else /* UI */ LED_StopFlashingAllLeds(); Led1On(); #endif } break;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ In “gConnEvtDisconnected_c” add the following code to change to DSM3, since all stacks are idle. case gConnEvtDisconnected_c: { /* Unsubscribe client */ mPeerDeviceId = gInvalidDeviceId_c; (void)Bas_Unsubscribe(&basServiceConfig, peerDeviceId); (void)OtapCS_Unsubscribe(); /* UI */ LED_StopFlashingAllLeds(); Led1Flashing(); Led2Flashing(); Led3Flashing(); Led4Flashing();‍‍‍‍‍‍‍‍‍‍‍‍ OtapClient_HandleDisconnectionEvent (peerDeviceId); #if (cPWR_UsePowerDownMode) /* Go to sleep */ #if MULTICORE_APPLICATION_CORE #if gErpcLowPowerApiServiceIncluded_c PWR_ChangeBlackBoxDeepSleepMode(cPWR_DeepSleepMode); #endif #else PWR_ChangeDeepSleepMode(cPWR_DeepSleepMode); #endif #else /* Restart advertising*/ BleApp_Start(); #endif } break;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   Power Consumption Profile of OTAP Client This section explains the behavior of the power consumption profile along the time. We can differ when DSM1 or DSM3 are used depending on the device state. If the device needs to advertise or is in connection state, it will use DSM1 because this sleep mode can predict when the device needs to handle the communication with others and wake up automatically through the BLE Link Layer wakeup interrupt. On the other hand, when no actions are in progress, it will use DSM3 and the wake up depends entirely on the LLWU SW3 button in this example. On the other hand, the DSM6 puts the MCU in STOP mode during the transmission and reception in BLE events, it does not need to be started manually, instead, the controller configures this mode automatically and returns to DSM1 mode after finished the radio activity.   The APIs that change the deep sleep mode to DSM1 are: • BleApp_Start: It starts advertising. • BleApp_ConnectionCallback – gConnEvtConnected_d: It notifies when the MCU has been connected to a peer device.   The APIs that change the deep sleep mode to DSM3 are: • BleApp_Config: It takes part of the initialization procedure after POR. All tasks are idle, the device is waiting for the LLWU SW3 button to wake up and start advertising. • BleApp_AdvertisingCallback – mAdvState is off: The device has to stopped advertising, so the MCU is idle. • BleApp_ConnectionCallback – gConnEvtDisconnected_d: It notifies when the device has been disconnected, so the MCU is idle.   Please let us know any questions or comments regarding this topic.
View full article