Wireless Connectivity Knowledge Base

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

Wireless Connectivity Knowledge Base

Discussions

Sort by:
Introduction In some applications, is it necessary to keep updated the software running in many MCU's that take part in the system, fortunately, Over The Air Programming, it's a custom Bluetooth LE service developed to send "over the air" software updates for the KW MCU series. FRDM-KW36 SDK already provides the "otap_client" software, that can be used together with the "otap_bootloader" such as it is described in the following community post: Reprogramming a KW36 device using the OTAP Client Software to reprogram the KW36. This example can be modified to store code for another MCU and later send the software update to this device as depicted in the figure below. This post guides you on modifying the OTAP client software to support software updates for other MCU's. Preparing the OTAP client software The starting point of the following modifications is supposing that there is no need to perform over the air updates for the KW36 MCU, so the use of the "otap_bootloader" is obsolete and will be removed in this example. In other words, KW36 will be programmed only with the "otap_client" code. Open the MCUXpresso settings window (Project->Properties->"C/C++ Build->MCU settings") and configure the following fields. Save the changes. For external storage: For internal storage: Locate the "app_preinclude.h" file, and set the storage method, as follows: For external storage: #define gEepromType_d       gEepromDevice_AT45DB041E_c For internal storage: #define gEepromType_d        gEepromDevice_InternalFlash_c Locate the "main_text_section.ldt" linker script into the "linkscripts" folder, and delete it from the project.  Search in the project for "OTA_SetNewImageFlag();" and "ResetMCU();" functions in the "otap_client.c" file (source->common->otap_client->otap_client.c) and delete or comment. (For reference, there are 4 in total). Locate the following code in "OtaSupport.h" (framework->OtaSupport->Interface) and delete or comment. extern uint16_t gBootFlagsSectorBitNo;‍‍‍‍‍‍ void OTA_SetNewImageFlag(void);‍‍‍‍‍‍‍ Locate the following code in "OtaSupport.c" (framework->OtaSupport->Source) and delete or comment. extern uint32_t __BootFlags_Start__[]; #define gBootImageFlagsAddress_c ((uint32_t)__BootFlags_Start__)‍‍‍‍‍‍‍‍‍‍‍‍ #if !gEnableOTAServer_d || (gEnableOTAServer_d && gUpgradeImageOnCurrentDevice_d) /*! Variables used by the Bootloader */ #if defined(__IAR_SYSTEMS_ICC__) #pragma location = "BootloaderFlags" const bootInfo_t gBootFlags = #elif defined(__GNUC__) const bootInfo_t gBootFlags __attribute__ ((section(".BootloaderFlags"))) = #elif defined(__CC_ARM) volatile const bootInfo_t gBootFlags __attribute__ ((section(".BootloaderFlags"))) = #else #error "Compiler unknown!" #endif { {gBootFlagUnprogrammed_c}, {gBootValueForTRUE_c}, {0x00, 0x02}, {gBootFlagUnprogrammed_c}, #if defined(CPU_K32W032S1M2VPJ_cm4) && (CPU_K32W032S1M2VPJ_cm4 == 1) {PLACEHOLDER_SBKEK}, {BOOT_MAGIC_WORD} #endif }; #endif /* !gEnableOTAServer_d || (gEnableOTAServer_d && gUpgradeImageOnCurrentDevice_d) */‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ uint16_t gBootFlagsSectorBitNo; gBootFlagsSectorBitNo = gBootImageFlagsAddress_c/(uint32_t)((uint8_t*)FSL_FEATURE_FLASH_PFLASH_BLOCK_SECTOR_SIZE);‍‍‍‍ gBootFlagsSectorBitNo = gBootImageFlagsAddress_c/(uint32_t)((uint8_t*)FSL_FEATURE_FLASH_PAGE_SIZE_BYTES);‍‍‍‍ void OTA_SetNewImageFlag(void) { #if (gEepromType_d != gEepromDevice_None_c) && (!gEnableOTAServer_d || (gEnableOTAServer_d && gUpgradeImageOnCurrentDevice_d)) /* OTA image successfully written into the non-volatile storage. Set the boot flag to trigger the Bootloader at the next CPU Reset. */ union{ uint32_t value; uint8_t aValue[FSL_FEATURE_FLASH_PFLASH_BLOCK_WRITE_UNIT_SIZE]; }bootFlag; #if defined(CPU_K32W032S1M2VPJ_cm4) && (CPU_K32W032S1M2VPJ_cm4 == 1) uint8_t defaultSBKEK[SBKEK_SIZE] = {DEFAULT_DEMO_SBKEK}; #endif uint32_t status; if( mNewImageReady ) { NV_Init(); bootFlag.value = gBootValueForTRUE_c; status = NV_FlashProgramUnaligned((uint32_t)&gBootFlags.newBootImageAvailable, sizeof(bootFlag), bootFlag.aValue); if( (status == kStatus_FLASH_Success) && FLib_MemCmpToVal(gBootFlags.internalStorageAddr, 0xFF, sizeof(gBootFlags.internalStorageAddr)) ) { bootFlag.value = gEepromParams_StartOffset_c + gBootData_ImageLength_Offset_c; status = NV_FlashProgramUnaligned((uint32_t)&gBootFlags.internalStorageAddr, sizeof(bootFlag), bootFlag.aValue); } #if defined(CPU_K32W032S1M2VPJ_cm4) && (CPU_K32W032S1M2VPJ_cm4 == 1) if( status == kStatus_FLASH_Success ) { /* Write the default SBKEK for secured OTA */ status = NV_FlashProgramUnaligned((uint32_t)&gBootFlags.sbkek, SBKEK_SIZE, defaultSBKEK); } #endif if( status == kStatus_FLASH_Success ) { mNewImageReady = FALSE; } } #endif }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍   At this point, the FRDM-KW36 can receive and store any image for any MCU and can request a further software update from the OTAP server device.    Adding API's to reprogram the "MCU X" on OTAP client software Once the software update has been downloaded from the OTAP Server into the OTAP Client, the developer should request the software update from the OTAP Client to the "MCU X" through a serial protocol such as UART, SPI, CAN, etc. You should develop the API's and the protocol according to the requirements for your system to send the software update to the "MCU X" (as well as the bootloader for the MCU X). The handling your protocol can be integrated into the OTAP client code replacing "ResetMCU()" (The same code removed in step 4) in the code by "APISendSoftwareUpdateToMCUX()" for instance, since at this point the image was successfully sent over the air and stored in the memory of the KW36. 
View full article
The image below shows the different types of devices in a Thread Network. Router Routers provide routing services to network devices. Routers also provide joining and security services for devices trying to join the network. Routers are not designed to sleep. Routers can downgrade their functionality and become REEDs (Router-eligible End Devices). A Router can become a Leader and start a Thread network. Border Router A Border Router is a type of Router that provides connectivity from the 802.15.4 network to adjacent networks on other physical layers (for example, Wi-Fi and Ethernet). Border Routers provide services for devices within the 802.15.4 network, including routing services for off-network operations. There may be one or more Border Routers in a Thread Network. The Border Router also serves as an interface point for the Commissioner when the Commissioner is on a non-Thread Network; it requires a Thread interface and may be combined in any device with other Thread roles except the Joiner. Leader A Router or Border Router can assume a Leader role for certain functions in the Thread Network. This Leader is required to make decisions within the network. For example, the Leader assigns Router addresses and allows new Router requests. The Leader role is elected and if the Leader fails, another Router or Border Router assumes the Leader role. It is this autonomous operation that ensures there is no single point of failure. Router-eligible End Device REEDs have the capability to become Routers but due to the network topology or conditions these devices are not acting as Routers. These devices do not generally forward messages or provide joining or security services for other devices in the Thread Network. The Thread Network manages REEDs becoming Routers if necessary without user interaction. Sleepy End Device Sleepy end devices are host devices. They communicate only through their Parent Router and cannot forward messages for other devices References: Thread Whitepapers available at http://threadgroup.org 
View full article
The MCU in the KW40/30Z has various available very low power modes. In these power modes, the chip goes to sleep to save power, and it is not usable during this time (it can however receive different kinds of interruptions that could wake it up). The very low power modes supported by the microcontroller are: The KW40Z connectivity software stack has 6 predetermined deep sleep modes. These deep sleep modes have different configurations for the microcontroller low power mode, the BLE Link Layer state and in which ways the device can be awaken. These predetermined DSM (Deep Sleep Modes) are: * VLLS0 if DCDC is bypassed. VLLS1 with either Buck or Boost. ** Available in Buck mode only. Having said that, if you want the lowest possible consumption by the MCU, while also being able to wake up your application automatically with a timer (achieved with VLSS1), there is no DSM available. You can, however, create your own Deep Sleep Mode with low power timers enabled. Please note that VLSS1 has the lowest possible consumption when using a DCDC converter. When in bypass mode, the lowest possible consumption is achieved with VLSS0. To create your Deep Sleep Mode, you should start with the function that will actually handle the board going into deep sleep. This should be done in the PWR.c file, along with the rest of the DSM handler functions. This function is quite similar to the ones already made for the other deep sleep modes. Link layer interruptions, timer settings and the low power mode for the MCU are handled here. /* PWR.c */ #if (cPWR_UsePowerDownMode) static void PWR_HandleDeepSleepMode_7(void) {   #if cPWR_BLE_LL_Enable   uint16_t bleEnabledInt; #endif   uint8_t clkMode;   uint32_t lptmrTicks;   uint32_t lptmrFreq;     PWRLib_MCU_WakeupReason.AllBits = 0;   #if cPWR_BLE_LL_Enable    if(RSIM_BRD_CONTROL_BLE_RF_OSC_REQ_STAT(RSIM)== 0) // BLL in DSM   {     return;   bleEnabledInt = PWRLib_BLL_GetIntEn();   PWRLib_BLL_ClearInterrupts(bleEnabledInt);      PWRLib_BLL_DisableInterrupts(bleEnabledInt); #endif     if(gpfPWR_LowPowerEnterCb != NULL)   {     gpfPWR_LowPowerEnterCb();   }     /* Put the device in deep sleep mode */ #if cPWR_DCDC_InBypass    PWRLib_MCU_Enter_VLLS0(); #else   PWRLib_MCU_Enter_VLLS1(); #endif     if(gpfPWR_LowPowerExitCb != NULL)   {     gpfPWR_LowPowerExitCb();   }   #if cPWR_BLE_LL_Enable    PWRLib_BLL_EnableInterrupts(bleEnabledInt);        #endif      PWRLib_LPTMR_ClockStop();   } #endif /* #if (cPWR_UsePowerDownMode) */ ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Remember to add this function to the deep sleep handler function array: /* PWR.c */ const pfHandleDeepSleepFunc_t maHandleDeepSleep[]={PWR_HandleDeepSleepMode_1,                                                     PWR_HandleDeepSleepMode_2,                                                     PWR_HandleDeepSleepMode_3,                                                     PWR_HandleDeepSleepMode_4,                                                     PWR_HandleDeepSleepMode_5,                                                     PWR_HandleDeepSleepMode_6,                                                     PWR_HandleDeepSleepMode_7                                                    }; ‍‍‍‍‍‍‍‍‍‍ This function should allow your device to go to sleep. It does the strictly necessary things before the device goes to sleep: disables link layer interruptions, gets the configuration for the low power timer and it starts the timer. Please note that when the board is in either Buck or Boost DCDC mode, only VLSS1 is supported. When the device is in bypass mode, VLSS0 can be chosen. Now that the deep sleep handler is done, there are some changes that have to be made to have a proper execution. In the PWR_Configuration.h file, for example, there is an error message when the parameter cPWR_DeepSleepMode is larger than 6 (the default DSM modes), but, since you have added a new deep sleep mode, this number should be changed to 7: #if (cPWR_DeepSleepMode > 7 )  // default: 6   #error "*** ERROR: Illegal value in cPWR_DeepSleepMode" #endif ‍‍‍ Other changes that have to be made are the Low Leakage Wake Up unit and the deep sleep mode configurations. To change the LLWU configuration, you should add a case for the new deep sleep mode in the PWRLib_ConfigLLWU() function: /* PWRLib.c */ void PWRLib_ConfigLLWU( uint8_t lpMode ) {   switch(lpMode)   {   case 1:     LLWU_ME = gPWRLib_LLWU_WakeupModuleEnable_BTLL_c | gPWRLib_LLWU_WakeupModuleEnable_LPTMR_c;   break;   case 2:     LLWU_ME = gPWRLib_LLWU_WakeupModuleEnable_BTLL_c;   break;   case 3:     LLWU_ME = gPWRLib_LLWU_WakeupModuleEnable_LPTMR_c | gPWRLib_LLWU_WakeupModuleEnable_DCDC_c;   break;   case 4:   case 5:     LLWU_ME = gPWRLib_LLWU_WakeupModuleEnable_DCDC_c;    break;   case 6:     LLWU_ME = 0;   break;   case 7: /* The new deep sleep mode can be awaken through a Low Power Timer timeout */     LLWU_ME = gPWRLib_LLWU_WakeupModuleEnable_LPTMR_c;   break;   } }  } #endif /* #if (cPWR_UsePowerDownMode) */ ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Once this case has been added, you should change the function that calls PWRLib_ConfigLLWU(), PWRChangeDeepSleepMode(): /* PWR.c */ bool_t PWR_ChangeDeepSleepMode (uint8_t dsMode) { #if (cPWR_UsePowerDownMode)   if(dsMode > 7) //Since you’ve added an extra DSM, this is now 7 (default: 6)   {      return FALSE;   }    PWRLib_SetDeepSleepMode(dsMode); PWRLib_ConfigLLWU(dsMode); #if (cPWR_BLE_LL_Enable)    PWRLib_BLL_ConfigDSM(dsMode);   PWRLib_ConfigRSIM(dsMode); #endif    return TRUE;   #else   return TRUE; #endif  /* #if (cPWR_UsePowerDownMode) */ } ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Now, since you’ll be using a Low Power Timer, you should modify the maLPModeUseLPTMR[] constant in the PWRLib.c file, indicating that you will use a low power timer: /* PWRLib.c */ const uint8_t maLPModeUseLPTMR[]={0,1,1,1,0,0,1,1}; //We add the last 1. default: {0,1,1,1,0,0,1} ‍‍‍ You should add a case for your new low power mode in the PWRLib_ConfigRSIM(). Here you will handle the BLE link layer whilst the device is in low power mode. This function can be found in the PWRLib.c file: /* PWRLib.c */ void PWRLib_ConfigRSIM( uint8_t lpMode ) {   switch(lpMode)   {   case 1:   case 2:       RSIM_BWR_CONTROL_STOP_ACK_OVRD_EN(RSIM, 0);       RSIM_CONTROL |= RSIM_CONTROL_BLE_RF_OSC_REQ_EN_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_INT_EN_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_INT_MASK;     break;   case 3:   case 4:   case 5:       RSIM_CONTROL &= ~(RSIM_CONTROL_STOP_ACK_OVRD_EN_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_EN_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_INT_EN_MASK);       RSIM_CONTROL |= RSIM_CONTROL_BLE_RF_OSC_REQ_INT_MASK;     break;   case 6:       RSIM_CONTROL &= ~(RSIM_CONTROL_STOP_ACK_OVRD_EN_MASK  | RSIM_CONTROL_BLE_RF_OSC_REQ_INT_EN_MASK);       RSIM_CONTROL |= RSIM_CONTROL_BLE_RF_OSC_REQ_INT_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_EN_MASK;     break;   case 7: //@PNN       RSIM_CONTROL &= ~(RSIM_CONTROL_STOP_ACK_OVRD_EN_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_EN_MASK | RSIM_CONTROL_BLE_RF_OSC_REQ_INT_EN_MASK);       RSIM_CONTROL |= RSIM_CONTROL_BLE_RF_OSC_REQ_INT_MASK;     break;   } } ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Your low power mode awaken by a low power timer should now be ready. To change the deep sleep mode and the time the device will be in deep sleep mode before it is awaken, use these functions in your application: PWR_ChangeDeepSleepMode(7);                                     /* Change deep sleep mode */ PWR_SetDeepSleepTimeInMs(YOUR_DEEP_SLEEP_TIME_IN_MS);           /* Time the device will spend on deep sleep mode */ PWR_AllowDeviceToSleep();                                      /* Allows the device to go to deep sleep mode */ PWR_DisallowDeviceToSleep();                                   /* Does not allows the device to go to deep sleep mode */ ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
View full article
Introduction The FRDM-KW36 includes an RSIM (Radio System Integration Module) module with an external 32 MHz crystal oscillator. This clock source reference is mainly intended to supply the Bluetooth LE Radio peripheral, but it can be used as the main clock source of the MCU as well. This oscillator includes a set of programmable capacitors to support crystals with different load capacitance needs. Changing the value of these capacitors can modify the frequency the oscillator provides, that way, the central frequency can be tuned to meet the wireless protocol standards. This configurable capacitance range is from C1: 5.7pF - C2: 7.1pF to C1: 22.6pF - C2: 28.2pF and it is configured through the BB_XTAL_TRIM field at the ANA_TRIM. The KW36 comes preprogrammed with a default load capacitance value. However, since there is variance in devices due to tolerances and parasite effects, the correct load capacitance should be checked by verifying that the optimal central frequency is attained.  You will need a spectrum analyzer to measure the central frequency. To find the most accurate value for the load capacitance, it is recommended to use the Connectivity Test demo application. Adjusting Frequency Example Program the KW36 Connectivity Test software on the device. This example can be found in wireless_examples -> genfsk -> conn_test folder from your SDK package. Baremetal and FreeRTOS versions are available. In case that FRDM-KW36 board is being used to perform the test, you should move the 10pF capacitor populated in C55 to C57, to direct the RF signal on the SMA connector. Connect the board to a serial terminal software. When you start the application, you will be greeted by the NXP logo screen:  Press the enter key to start the test. Then press "1" to select "Continuous tests": Finally, select "6" to start a continuous unmodulated RF test. At this point, you should be able to measure the signal in the spectrum analyzer. You can change the RF channel from 0 to 127 ("q" Ch+ and "w" Ch- keys), which represents the bandwidth from 2.360GHz to 2.487GHz, stepping of 1MHz between two consecutive channels. To demonstrate the trimming procedure, this document will make use of channel 42 (2.402GHz) which corresponds to the Bluetooth LE channel 37. In this case, with the default capacitance value, our oscillator is not exactly placed at the center of the 2.402GHz, instead, it is slightly deflected to 2.40200155 GHz, as depicted in the following figure: The capacitance can be adjusted with the "d" XtalTrim+ and "f" XtalTrim- keys. Increasing the capacitance bank means a lower frequency. In our case, we need to increase the capacitance to decrease the frequency. The nearest frequency of 2.402 GHz was 2.40199940 GHz  Once the appropriate XTAL trim value has been found, it can be programmed as default in any Bluetooth LE example, changing the mXtalTrimDefault constant located in the board.c file: static const uint8_t mXtalTrimDefault‍ = 0x36;‍‍‍
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 AN12180 Power Consumption Analysis - FRDM-KW36 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) 2 different deep sleep modes (LLS3 or VLLS2) Different Tx output power (0dBm, +3.5dBm or +5dBm) 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 typical batteries.
View full article
Regarding to the "Reprogramming a KW36 device using the OTAP Client Software" and "Reprogramming a KW35 device using the OTAP Client Software" documents, there are some additional steps to debug the OTAP client software in the specific case when you use MCUXpresso together with a P&E micro debug probe. Just before to program the OTAP client project (the second software), the user must do the following: Open the "Debug Configurations" view clicking on the green bug as depicted below. Go to the "Debugger" perspective and search the "Advanced Options" button. Enable the "Preserve this range (Memory Range 0)" checkbox, and edit the textbox "From: 0" To: 1fff" for the KW36 device or "From: 0 To: 3fff" for the KW35 device. After to flash the device, disconnect and connect again. If everything it's OK, the RGB LED must blink (If you are using an FRDM board). Then, test the demo as described in the document.
View full article
Wireless communication systems require several different components or parts to achieve reliable systems. Components like the antenna, radio and XTAL are all key elements in wireless communication. Here however, the XTAL will be discussed. In the Kinetis W series, for example, the XTAL used for wireless operation is usually the oscillator also used as a core clock. Now, while this external oscillator is connected to the MCU, it is also connected to an internal programmable capacitor bank. What is the purpose of these capacitor banks? To allow frequency trimming. And why would you want to trim the frequency provided by this oscillator? Well, to properly adjust the central frequency to where it should be operating. This option exists because not every design is going to be the same: not the same PCB, not the same components, not the same manufacturing process. Thus, having the option to adjust the frequency provided by the external oscillator allows to any possible device to operate under the same conditions is essential. Let’s say your design is using a 32 MHz external oscillator, but because of the conditions of your whole design, the operating frequency ends up being slightly different. Now, if this design transmits over the air through 802.15.4, there could be some consequences to this slight shift in frequency. This capture shows a transmission made without being centered in the desired channel. This signal should be centered exactly on 2405 MHz, as specified by IEEE 802.15.4 channel 11. As you may see, in this case the frequency is actually centered on 2405.0259 MHz. Trimming these capacitors to change the frequency obtained from the oscillator can help to adjust error. In this case, the frequency was adjusted so that it was centered in the central frequency of the desired channel, to prevent any possible mistakes while transmitting to other devices. Once the XTAL is trimmed, the signal is effectively centered on 802.15.4 channel 11's frequency, 2405 MHz. Both transmit and receive are affected by incorrect frequency trim. Receiver performance is degraded when either (or both) of the transmitting or receiving stations have a frequency offset. And if both transmitting and receiving stations have frequency offsets in opposite directions the result is the receiver experiences the sum of the frequency offsets. Now, when trimming the frequency of a design, there are two possibilities: That the board layout design, board manufacturing and component selection have repeatable values of resistance, capacitance and inductance, resulting in a stable XTAL trim – The components and manufacturing process of the board are reliable enough, allowing you to characterize the XTAL trim during the system development and then use it every board during production. That the design and component selection do not result in a stable XTAL trim – If there is considerable variation between different boards of the same design or components used in the board manufacturing, you would need to implement a XTAL trim procedure during the production process, and somehow program that trim value into the device's NVM. For evaluation purposes, a manual adjustment could be done to a single device, modifying the corresponding XTAL trim register, and then including said adjustment in the evaluation application. The two posts linked explain how to modify and use the SMAC Connectivity Test demo to find the proper XTAL trim for KW40Z and KW41Z.
View full article
In addition of the Design Guideline, PCB hardware package find here the design in check list to build sucessfully your own PCB
View full article
This patch fixes some minor issues with the Connectivity Software v1.0.2 when working with the Kinetis BLE Toolbox application for smartphones. Following issues are fixed. BLE OTAP Application: Fixes application failing to download the new image when the previous image upload has been interrupted due a disconnection. BLE Wireless UART: Fixes MTU exchange issue causing some characters not bein shown in the smartphone application in iOS and Android. Hybrid BLE + Thread console: Fixes MTU exchange issue causing some characters not bein shown in the smartphone application console in iOS and Android. Make sure the Connectivity Software version 1.0.2 is installed in your computer before proceeding to install this application.
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
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
With the release of the Bluetooth LE core erratum 10734, two new Host test cases (SM/SLA/KDU/BI-01-C and SM/MAS/KDU/BI-01-C) were added to the Test Case Reference List (TCRL) and are active since 24-Jan-19. This has an impact on new product qualifications based on Component (Tested) QDIDs that used an older TCRL when the test cases for this erratum were not required. Products that rely on NXP HOST QDIDs have 2 options for covering the erratum 10734 in order to complete the qualification: NXP provides a new qualification/QDID that includes these 2 tests. This is scheduled for later this year for QN908x, KW35/36 and KW41/31 products. NXP provides the test evidence/logs for these 2 tests and the test house reviews them before completing the product qualification. Right now, option 2 can be followed using the test evidence/logs provided by NXP. Later in the year, option 1 can be followed with an updated QDID. To obtain the test evidence/logs, please submit a support request.
View full article
During last week I receive a question about identify a way to notice when the 32kHz oscillator is ready to be able to use it as clock source. In other words, when the 32kHz oscillator is stable to be used as reference clock for the MCG module. Then, system can switch over it. Kinetis devices starts up using its internal clock which is called FEI mode, then, the applications change to a different mode which require an external clock source (i.e. FEE mode). Normally, we have two options which are: Main reference clock. Talking specifically to KW devices, there is the 32MHz external crystal for the main reference clock. The 32kHz external crystal for the 32kHz oscillator which is driven through the RTC registers.  For the first option, there is a register which could be monitored to know when oscillator is ready (RSIM_CONTROL_RF_OSC_READY). So, it can be polled to notice when the oscillator is up and ready.  About the second option, there is no “OSCINIT” bit for the 32 kHz oscillator. However, there is a way to know when the 32kHz Oscillator is running, it is to simply enable the RTC OSC, and configure the RTC to count. After doing that immediately poll (read) the RTC_TPR register and check it until it is greater than 4096. It will roll over once it reaches 32767 so, it is important to the poll this register in a loop doing nothing else or the register could potentially be read when it is less than 4096 but had already rolled over. Once it is greater than 4096, it can be determined that oscillator is running well. If the RTC is not required, the counter can be disabled. Now that the 32kHz clock is available, the application can switch to FEE mode. So, in order to perform the above description, I modified the “CLOCK_CONFIG_EnableRtcOsc()” as shown next: static void CLOCK_CONFIG_EnableRtcOsc(uint32_t capLoad) {        rtc_config_t rtc_basic_config;        uint32_t u32cTPR_counter=0;        /* RTC clock gate enable */     CLOCK_EnableClock(kCLOCK_Rtc0);     if ((RTC->CR & RTC_CR_OSCE_MASK) == 0u)     {       /* Only if the Rtc oscillator is not already enabled */       /* Set the specified capacitor configuration for the RTC oscillator, "capLoad" parameter shall be set          to the value specific to the customer board requirement*/       RTC_SetOscCapLoad(RTC, capLoad);          /*Init the RTC with default configuration*/       RTC_GetDefaultConfig(&rtc_basic_config);       RTC_Init(RTC, &rtc_basic_config);       /* Enable the RTC 32KHz oscillator */       RTC->CR |= RTC_CR_OSCE_MASK;       /* Start the RTC time counter */       RTC_StartTimer(RTC);             /* Verify TPR register reaches 4096 counts */       while(u32cTPR_counter < 4096)       {          u32cTPR_counter= RTC->TPR;       }       /* 32kHz Oscillator is ready. Based on the application requirements, it can let the RTC enabled or disabled.           In this case, we can disable RTC since it is not needed by this application */       RTC_Deinit(RTC);     }     /* RTC clock gate disable  since RTC is not needed anymore*/     CLOCK_DisableClock(kCLOCK_Rtc0); }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Then, using the above function, it can be noticed when the 32kHz oscillator is ready to be used. Hope this helps....
View full article
OVERVIEW This document shows how to include the PowerLib to enable low power functionality in connectivity software projects that does not include it. It shows step by step instructions on how to import, configure and use this module. ADD POWER LIBRARY INTO A NEW PROJECT Once you have installed the “Connectivity Software” package, browse for the extracted files (typically located in C:\Freescale\KW40Z_Connectivity_Software_1.0.0). In this location search for the LowPower folder, then copy and paste it into your new project folder. Open your IAR project and create a new group called “Low Power”. Inside this group add two new groups called “Interface” and “Source”. In the Windows explorer, open the LowPower folder copied in the previous step. Drag and drop the contents of the "Interface" folder to the "Interface" group in IAR. Do the same for the "Source" folder. You can also use the option "Add Files" in the group menu to add the files. Note: Do not copy the “PWR_Platform.c” and “PWR_Platform.h” files. Once you have copied the files in their respective folders, you need to add the paths of these files in the project environment. Right click on the project name and select "Options". In Options go to “C/C++Compiler”, select “Preprocessor” and click on the red square. The next window will appear. Click on <Click to add>  to open the windows explorer. Navigate to the folder PowerLib/Interface in your project to add the "Interface" folder path. Repeat this step with the "Source" folder. HOW TO CONFIGURE LOW POWER To use low power in your project you need to define the following macros in the “app_preinclude.h” file: /* 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              4 cPWR_UsePowerDownMode enables the necessary functions to use low power in your project. cPWR_BLE_LL_Enable configures the link layer to work in doze mode when in low power, and cPWR-DeepSleepMode defines the deep sleep mode the MCU will enter when the low power function is executed. There are the six different modes that can be used.   Mode 1: MCU/Radio low power modes:         MCU in LLS3 mode.         BLE_LL in DSM.       Wakeup sources:       GPIO (push button) interrupt using LLWU module.        BLE_LL wake up interrupt(BLE_LL reference clock reaches wake up instance register)  using LLWU module.              - BTE_LL wakeup timeout: controlled by the BLE stack(SoC must be awake before next BLE action).              - BTE_LL reference clock source:   32Khz oscillator              - BTE_LL reference clock resolution:     625us                            Mode 2: MCU/Radio low power modes:         MCU in LLS3 mode.         BLE_LL in DSM.       Wakeup sources:         GPIO (push button) interrupt using LLWU module.         BLE_LL wake up interrupt(BLE_LL reference clock reaches wake up instance register)  using LLWU module.                - BTE_LL wakeup timeout: cPWR_DeepSleepDurationMs by default. Use PWR_SetDeepSleepTimeInMs  to change it at run time. Maximum timeout is 40959 ms. BLE suppose to be idle.                - BTE_LL reference clock source:   32Khz oscillator                - BTE_LL reference clock resolution:     625us   Mode  3: MCU/Radio low power modes:         MCU in LLS3 mode.         BLE_LL in idle.       Wakeup sources:        GPIO (push button) interrupt using LLWU module.        DCDC PowerSwitch - available in buck mode only.        LPTMR interrupt using LLWU module           - LPTMR wakeup timeout: cPWR_DeepSleepDurationMs by default. Use PWR_SetDeepSleepTimeInMs to change it at run time. Maximum timeout is 65535000 ms (18.2 h).           - LPTMR clock source:   32Khz oscillator           - LPTMR resolution:     modified at run time to meet timeout value. Mode 4: MCU/Radio low power modes:         MCU in VLLS0/1 mode(VLLS0 if DCDC bypassed/ VLLS1 otherwise ).        BLE_LL in idle.       Wakeup sources:        GPIO (push button) interrupt using LLWU module.         DCDC PowerSwitch - available in buck mode only. Mode 5: MCU/Radio low power modes:        MCU in VLLS2 (4k Ram retention (0x20000000- 0x20000fff)).        BLE_LL in idle.       Wakeup sources:         GPIO (push button) interrupt using LLWU module.         DCDC PowerSwitch - available in buck mode only.   Mode 6: MCU/Radio low power modes:         MCU in STOP.       Wakeup sources:         GPIO (push button) interrupt using LLWU module.         DCDC PowerSwitch - available in buck mode only.         LPTMR wakeup timeout: cPWR_DeepSleepDurationMs by default. Use PWR_SetDeepSleepTimeInMs to change it at run time. Maximum timeout is 65535000 ms (18.2 h).          - LPTMR clock source:   32Khz oscillator           - LPTMR resolution:     modified at run time to meet timeout value.           - LPTMR resolution:     modified at run time to meet timeout value.         Radio interrupt LL or 802.15.4         UART Configuring Wakeup Source The PowerLib software includes preconfigured wakeup methods for low power. These methods are described below and a couple of examples are included. From Reset: Comming from Reset From PSwitch_UART: Wakeup by UART interrupt From KeyBoard: Wakeup by TSI/Push button interrupt From LPTMR: Wakeup by LPTMR timer interrupt From Radio:  Wakeup by RTC timer interrupt From BLE_LLTimer:  Wakeup by BLE_LL Timer DeepSleepTimeout:  DeepSleep timer overflow. SleepTimeout: Sleep timer overflow. Configure Module Wakeup using LPTMR This example explains how to configure the third deep sleep mode using the LPTMR as wakeup source. The desired low power mode must be configured in the file app_preinclude.h. /* Default Deep Sleep Mode*/ #define cPWR_DeepSleepMode            3 On the same file, the macro cPWR_DeepSleepDurationMs macro must be added. It defines the time the MCU will be in low power mode before being waken by the low power timer. By default it it set to 10 seconds (10000 milliseconds). #define cPWR_DeepSleepDurationMs     10000 This defines the time that the device will remain asleep by default. The PWR_SetDeepSleepTimeInMs function can be used to change this period at run time. Consider that the maximum time period is 65535000 ms (18.2 hours). PWR_SetDeepSleepTimeInMs(10000); Also the deep sleep mode can be changed at run time with the following function. PWR_ChangeDeepSleepMode(3); For further power reduction, all the modules not in use must be turned off . To run in this mode, all the timers except the LPTMR must be turned off. The device enters in low power mode with the following code lines in the main application. PWR_SetDeepSleepTimeInMs(cPWR_DeepSleepDurationMs); PWR_ChangeDeepSleepMode(3); PWR_AllowDeviceToSleep(); Configure GPIO (Push Button) wakeup. In the “PWRLib.c” file, find the “PWRLib_Init” function. It contains the code to initialize the LLWU pins to be used for wakeup. Chip configuration Reference Manual chapter contains information on which LLWU pins are tied to GPIOs on the MCU. For this example LLWU pins 6 and 7 (which are tied to PTA18 and PTA19 in the MCU) are used.   LLWU_PE1 = 0x00;   LLWU_PE2 = LLWU_PE2_WUPE7(0x03) | LLWU_PE2_WUPE6(0x03);   LLWU_PE3 = 0x00;   LLWU_PE4 = 0x00; Since the LLWU pin sources work as GPIO interrupts, the propper ports in the MCU must be configured. Following code shows howthese pins are configured in the MCU.   /* PORTA_PCR18: ISF=0,MUX=1 */   PORTA_PCR18 = (uint32_t)((PORTA_PCR18 & (uint32_t)~(uint32_t)(                                                                 PORT_PCR_ISF_MASK |                                                                   PORT_PCR_MUX(0x06)                                                                     )) | (uint32_t)(                                                                                     PORT_PCR_MUX(0x01)                                                                                       ));   PORTA_PCR19 = (uint32_t)((PORTA_PCR19 & (uint32_t)~(uint32_t)(                                                                 PORT_PCR_ISF_MASK |                                                                   PORT_PCR_MUX(0x06)                                                                     )) | (uint32_t)(                                                                                     PORT_PCR_MUX(0x01)                                                                                       )); Once the pins have been defined, it is neccesary to configure them as Keyboard inputs for the Power Lib. Go to "PWRLib.h" and find the next define: #define  gPWRLib_LLWU_KeyboardFlagMask_c (gPWRLib_LLWU_WakeupPin_PTA18_c | gPWRLib_LLWU_WakeupPin_PTA19_c ) In this define you must place the pins that were configured previously as wakeup sources. Using Low Power in the Project When you define "cPWR_UsePowerDownMode"  in app_preinclude.h, it automatically creates a task in "ApplMain.c" called "App_Idle_Task". When executed by the OS scheduler, this task verifies if the device can go to sleep. This statement is always false unless the next function is called. PWR_AllowDeviceToSleep(); This function indicates the program that the device can enter in low power and will execute the neccesary code to enter in the power mode configured at that time. Note: Before you allow the device to sleep, disable all uneccessary modules and turn off all leds. When the device is ready to enter in low power (all the application layers allows it and the device is in an iddle state) function PWR_EnterLowPower() must be called. This function will enter the MCU into the selected low power mode. On the HID example this is done into the iddle task as shown below. #if (cPWR_UsePowerDownMode) static void App_Idle(void) {     PWRLib_WakeupReason_t wakeupReason;         if( PWR_CheckIfDeviceCanGoToSleep() )     {         /* Enter Low Power */         wakeupReason = PWR_EnterLowPower(); #if gFSCI_IncludeLpmCommands_c         /* Send Wake Up indication to FSCI */         FSCI_SendWakeUpIndication(); #endif #if gKeyBoardSupported_d              /* Woke up on Keyboard Press */         if(wakeupReason.Bits.FromKeyBoard)         {             KBD_SwitchPressedOnWakeUp();             PWR_DisallowDeviceToSleep();         } #endif                  if(wakeupReason.Bits.DeepSleepTimeout)         {           Led1On();           for(;;)           {}         }     } } #endif /* cPWR_UsePowerDownMode */ PWR_CheckIfDeviceCanGoToSleep() function checks that all the application layers are agree on entering in low power mode (checking that PWR_DisallowDeviceToSleep() function hasn't been called). If everything is ok, function PWR_EnterLowPower() enters the device in low power and waits for a wakeup event.
View full article
As know, FSK and OOK are the modulation types that can be configured in the radio by setting the bits 4-3 from the RegDataModul register, as shown in below picture taken from Reference Manual:                                                          A common inquire you could have is: what modulation should I use? Let's first understand how these modulations work. FSK: Frequency Shift Keying is a modulation type that uses two frequencies, for 0 and 1. In a spectrum analyzer we can see a spectrum similar to the next picture, where the frequency for 0's is separated from the central frequency with FDev, and same case for the frequency for the 1's: OOK: On Off Keying is a modulation type that represents a logic 1 with the presence of the carrier frequency and a logic 0 with the absence of it. In a spectrum analyzer we can see a spectrum similar to the next picture, where the central frequency represents a logic 1. We can not see a logic 0 in the spectrum due to it's represented as the absence of power. Then what modulation should I use? FSK is most commonly used because is more spectral efficient so has better sensitivity. In the other hand, OOK modulation is commonly used in applications where the frequency accuracy can not be guaranteed. It also helps in conserving battery power due to the power absence for the logic 0's. Regards, Luis Burgos.
View full article
This document describes the implementation of the Connected Home Gateway for the Internet of Things (IoT) and its controller implemented in a Smart device (tablet) running Android OS. The gateway is intended to serve as a communication bridge between WiFi/Ethernet and ZigBee Protocol, making every ZigBee-enabled device accessible and controllable from any smart device with Wi-Fi capabilities such as a smart phone or tablet. This will remove the need of having a ZigBee transceiver in every mobile device attempting to control the house appliances. In general, users will be able to: Remote control of Home Appliances using ZigBee protocol Any WiFi-enabled device could control the appliances without a ZigBee transceiver Achieve bi-directional communication between users and appliances Real system implementation would require a powerful MCU to manage all WiFi/Ethernet communication and a second MCU to manage all ZigBee communications. The Kinetis K60 and KW24 were selected among the different options available.
View full article
This video shows how to load the Open SDA software from PE micro to the TWR-KW2x in order to debug applications using USB port and without needing external JTAG debuggers. Required downloads: TWR-KW2x Board Support Package:Kinetis KW2x Tower System Modules|Freescale PE Micro - Open SDA: P&E Microcomputer Systems
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