Kinetis Microcontrollers Knowledge Base

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

Kinetis Microcontrollers Knowledge Base

Discussions

Sort by:
Introduction The K32L3A60VPJ1AT MCU is a next generation Kinetis dual core device.  This device brings processing and multi-tasking capabilities that legacy Kinetis devices did not support.  In addition, the K32L3A60VPJ1AT offers improved power consumption and security features.   Some important aspects of these security features lie in a nonvolatile information register (IFR) memory region and how this region is programmed.  The IFR memory region is a memory space with restricted access separate from the main array and is comprised of an erasable IFR region and a non-erasable IFR region.  The non-erasable IFR region contains the program once identifier and the version identifier.  The erasable IFR region holds the flash security, flash options, mass erase enable, and other such features that governs how the device behaves.  In legacy Kinetis devices, certain fields of the main flash array (flash addresses 0x400 - 0x40F) configured the IFR at boot time.  In the K32L3A60VPJ1AT however, the IFR memory region is no longer controlled in this manner.  This presents challenges when trying to configure these settings.  The purpose of this document is to explain how these settings can be changed and provide some options of how to make these changes.   IFR Field Programming Process The first step in configuring the IFR fields is understanding how the these fields are programmed via the hardware. IFR fields are programmed using a special flash command called the Program Index Command. Once programmed, the flash configuration values cannot be reprogrammed without first erasing these fields.  The only way to erase these values is via a mass erase.  This provides security in that the IFR values cannot be changed without erasing the user code as well.  In addition, changes to the user code image cannot affect the bootloader operation, ensuring that a secure boot function can be executed.  The procedure for writing the erasable IFR values is described here:   Write FCCOB0 with the Program Index command (0x43). Write FCCOB1 with the Index to be programmed. The possible Indexes are listed in Erasable IFR Map table (table 16.4.1.2 in the K32L3A6 reference manual). Write FCCOB2 and FCCOB3 with 0x00 as they are not used with this command.  Write FCCOB4 - FCCOBB with the desired value.  (Note that not all of the indexes use all of the FCCOB fields.  Be sure to consult the Erasable IFR Map table for which FCCOB fields are used for the index you are programming). NOTE:  For 2 byte IFR fields that map to 2 bit wide register bit fields (i.e., SEC0, FSLACC, MEEN, and KEYEN fields which map to the FSEC register bit fields), the lower FCCOB register maps to the LSB of the bit field and the upper FCCOB register maps to the MSB of the bit field.  For example, to write 0b'10 to the FSEC field, FCCOB6 should be written to 0xFF and FCCOB7 should be written to 0x00 before executing the Flash command.  Write 0x70 to the Flash status register (FSTAT) to clear any errors that might have been present from the last flash command. (Note that this command MUST be a byte write.) Write 0x80 to the Flash status register (FSTAT) to initiate the programmed flash command. Poll the FSTAT register until the CCIF bit field (bit field 7) is one ('1').  (Note that it may not be possible in your scripting language to do this, or it may just be easier to simply wait for the flash command to finish executing. In these cases, wait significantly longer than the typical Program Index command completion time of 110us.)   After the IFR has been programmed, the IFR should be read back to verify that it completed correctly.  The process for this is as follows:   Write FCCOB0 with the Read Index command (0x41). Write FCCOB1 with the Index to be read.  The possible Indexes are listed in Erasable IFR Map table (table 16.4.1.2 in the K32L3A6 reference manual). Write FCCOB2 - FCCOBB with 0. The results will be stored in FCCOB4 - FCCOBB so, these should be cleared to ensure correct results are received. Write 0x70 to the Flash status register (FSTAT) to clear any errors that might have been present from the last flash command. Note that this command MUST be a byte write. Write 0x80 to the Flash status register (FSTAT) to initiate the programmed flash command. Poll the FSTAT register until the CCIF bit field (bit field 7) is one ('1').  (Note that it may not be possible in your scripting language to do this, or it may just be easier to simply wait for the flash command to finish executing. In these cases, wait significantly longer than the maximum Read Index command completion time of 35us.)   When using the Program Index Command, you must know which index you want to modify to create the correct flash commands.  The index list can be found in the IFR descriptions section of the Flash chapter in the K32L3A60VPJ1AT reference manual.     There are several different options for programming the FOPT fields. These options are: Using the Kinetis Flash Tool  Using blhost Debugger script Subroutine in user software   Option #1: Kinetis Flash Tool Using the Kinetis Flash Tool is likely the most convenient method to change the IFR values.  The Kinetis Flash Tool uses either the UART or USB protocol to interface with the K32L3A6 bootloader and write the IFR fields desired. One of the biggest advantages for the Kinetis Flash Tool is that it provides a graphical interface for users to easily program the IFR fields. The following figure is a picture of the Kinetis Flash Tool and highlights the important input controls and tabs to be used when programming the IFR fields:     This field is the Port set box.  It selects the interface (UART or USB) to be used when communicating to the bootloader.  This box also allows for configuration of the interface.  Consult the K32L3A6 reference manual for default configurations.   This is the Flash Utilities tab.  Select this tab to see the controls shown in this image.  This is the Index input field.  The Index of the IFR to program should be entered here.  This is the Hex digits field.  This value will be programmed at the IFR Index indicated in the Index field. The value here should be in hex format WITHOUT the preceding "0x".  Note that this will write to the FCCOBs in descending order.  For example, to write 0b'10 to the KEYEN field, FFFFFF00 should be written to the Hex digits field. Refer to the programming process outlined in the IFR Field Programming Process in this document for more information.    This is the Byte Count field.  This tells the utility how many bytes to program and must be the byte count of that IFR field.  Consult the Erasable IFR Map table in the reference manual for the value of the specific IFR index to be programmed.   This is the Program button.  After all of the fields have been filled out, click this button to program the desired IFR location.    Option #2: BLHOST The MCUBoot package also includes a command line executable to interface with the bootloader.  This tool, blhost, can be used to program the IFR fields as well.  The "flash-program-once" command should be used to program the desired IFR location.  The syntax of this command is as follows:   flash-program-once <index> <byteCount> <data>   So for example, if you want to program the FOPT IFR field (record index 0x84) with 0xFFFFF3FF, the correct syntax using this command would be   flash-program-once 0x84 4 FFFFF3FF   After programming, the "flash-read-once" command can be used to read back and verify the programmed IFR field(s).  Below is an example using the previous IFR locations   flash-read-once 0x84 4   Below is a full example of erasing the device, programming the FOPT IFR, and reading the FOPT IFR back from the command line using blhost.     When Programming two byte fields, blhost orders the bytes in descending FCCOBx order (just like the Kinetis Flash Tool).  The blhost utility also requires the input to be 4 or 8 byte aligned, but the flash-program-once command only uses the last 2 bytes.  The upper 4 bytes can be padded with 0's or F's. For example, to write the KEYEN field such that the KEYEN bit field is 0b'10, the command would be as follows: flash-program-once 0x83 4 FFFFFF00 Below is a full example of using the blhost command line to erase the device, program the KEYEN IFR, read the KEYEN IFR back, and evaluate the FSEC bit field using the Attach to Running Target function in a debugger.     After executing a pin reset and attaching to the running target:     Option #3: Debugger Script A simple debugger script is another convenient way to write the IFR values.  Debugger scripts are executed in the background of the debug session initiation process (therefore are hidden operations from the user) and typically can be edited easily using any text editor.  However, it can be cumbersome to change the value because this generally must done manually with each programming by the user. With that in mind, it is a good idea to have different connect scripts for different configurations   The first step in using a debugger script is writing a debugger script.  The capabilities and syntax of a debugger script are dependent on your toolchain. For the purposes of this document, we will focus on MCUXpresso IDE.  MCUXpresso IDE uses the PokeXX and PeekXX (where XX is 8, 16, or 32 depending on whether you want to byte access, half-word or word access to the desired register) commands, which are debugger agnostic. So the same commands that work on a device will continue to work whether you are debugging with a JLink or CMSIS-DAP, or whatever other debugger you are using. Below is an example of a MCUXpresso connect script which writes the FOPT register and then reads it back for printing to the debug log.    5140 REM ====================Program FOPT=================================== 5150 Poke32 this 0x40023004 0x43840000 5160 REM Stuff FCCOB registers with desired FOPT value 5170 Poke32 this 0x40023008 v% 5171 s% = Peek32 this 0x40023008 5172 Print "New Val ";~s% 5180 Poke32 this 0x4002300c 0x00000000 5180 Poke8 this 0x40023000 0x70 5190 Poke8 this 0x40023000 0x80 5200 wait 1000 6000 REM ================== Read FOPT ===================================== 6001 REM Now read the FOPT back 6010 Poke32 this 0x40023004 0x41840000 6020 Poke32 this 0x40023008 0x00000000 6030 Poke32 this 0x4002300c 0x00000000 6040 Poke8 this 0x40023000 0x70 6050 Poke8 this 0x40023000 0x80 6060 wait 1000 6070 s% = Peek32 this 0x40023008 6080 Print "New FOPT Val ";~s%   Note in the above script that v% is the desired FOPT value and it has been defined in sections of the script not shown (at line 164).    162 REM This is the value to be written to the FOPT 164 v% = 0xfffff3ff   After the script is written, MCUXpresso must be told to use the connect script.  This is done in the Debug Configurations window.  Assuming a debug configuration has already been created, click on the arrow next to the green bug icon and select Debug Configurations.       In the resulting dialog box, select the debug configuration you want to use, and select the Linkserver Debug tab.  In the Connect Script field, point MCUXpresso to the location of your connect script.       That's all that needs to be done in the IDE. The selected debug configuration should now be using the script which was written.     Some debuggers will allow standalone command line running of a script, such as a JLink debugger.  As the JLink is one of the more popular external debuggers that we encounter, an example of programming using this script has been provided below.     // Now Program the FOPT w4 0x40023004, 0x43840000 // The 43 selects the Program Index command. The 84 selects the FOPT IFR field. // Stuff the FCCOB registers (4-7) with the FOPT value we want to write. // ** (Boot Settings) ** w4 0x40023008, 0xfffff3ff // Write 0xFFFF_1FFF to boot the M4 from internal Flash. Asserting the NMI pin will force booting from the ROM. // Write FCCOB registers 8-B with dummy values. w4 0x4002300c, 0x00000000 // Write the FSTAT register to clear any errors that could have been present. w1 0x40023000, 0x70 // Launch the flash command. w1 0x40023000, 0x80 // Wait for the flash command to finish. Sleep 1 // Now Read the FOPT back w4 0x40023004, 0x41840000 // The 43 selects the Program Index command. The 84 selects the FOPT IFR field. // Stuff the FCCOB registers (4-7) with the FOPT value we want to write. // ** (Boot Settings) ** w4 0x40023008, 0x00000000 // Write 0xFFFF_F1FF to boot the M0+ from internal Flash. Asserting the NMI pin will force booting from the ROM. // Write FCCOB registers 8-B with dummy values. w4 0x4002300c, 0x00000000 // Write the FSTAT register to clear any errors that could have been present. w1 0x40023000, 0x70 // Launch the flash command. w1 0x40023000, 0x80 // Wait for the flash command to finish. Sleep 1 // Read the memory back to verify the FOPT settings that should be present after reset. mem32 40023000,4     Option #4: Subroutine in User Software Occasionally the requirements of your system will prevent implementation of any of the above methods to program the IFR values.  In these cases, you may need to implement your own subroutine to program the IFR.  The procedure to do this is essentially the same as in the debugger script methods, just written in code instead of an external script.  The flash drivers provided in the SDK aid in this process.  One key to remember is that you likely will need to erase the entire flash.  So this subroutine and flash drivers should be placed in RAM memory.  The SDK flash drivers also operate a little differently from the Kinetis flash tool and blhost.  The FCCOB registers will be loaded in ascending order.  For example, to write 0b'10 to the SEC0 bit field in the FSEC register, the command would be: result = FLASH_ProgramOnce(&s_flashDriver, 0x80, ifr2write, 0x2); where ifr2write is an array defined as uint8_t ifr2write[2] = {0x00, 0xFF}; The above will result in 0x00 being loaded to FCCOB6 and 0xFF being loaded to FCCOB7 and SEC0 will then be 0b'10 on the reset after the command is successfully executed.   Conclusion In summary, the IFR registers are nonvolatile information registers that govern certain behaviors of the K32L3A MCU.  The IFR is dividing into an erasable IFR space and non-erasable IFR space, both of which are not a part of the main flash array.  Programming these values requires the use of special flash commands and requires that these values haven't been previously written since the last mass erase.  There are, in general, four different methods of programming the FOPT register settings.  The four methods are:   Kinetis Flash Tool BLhost command line interface Debugger script  User software subroutine   Each method has its advantages, therefore, you should pick the one that meets your needs and is most convenient. However with any of the methods chosen, the IFR values must not have been programmed before writing erasable IFR fields. It is best to perform a mass erase (which can be done using any of the methods presented in this document) before attempting to program any IFR fields.     
View full article
A vulnerability (CVE-2022-22819) has been identified on select NXP processors by which a malformed SB2 file header sent to the device as part of an update or recovery boot can be used to create a buffer overflow. The buffer overflow can then be used to launch various exploits. Refer to the attached bulletin for more information.   09/26/2022 - Bulletin updated to include fix datecode information. 11/01/2022 - Bulletin updated with clarification that mixed datecodes are RT600 only.    
View full article
Ezportdl:programming Kinetis EZport by MCU-Link Pro NXP launched MCU-Link Pro at the end of 2021. This is a new debugger probe that has been significantly upgraded based on mcu-link. The focus of the upgrade is to add current measurement, analog signal monitoring, USB to SPI and I2C bridge working modes, and on-board lpc804 for peripheral simulation. Among these upgrades, USB to SPI and I2C are more interesting and practical. Combining Kinetis EZport and flashloader, MCU-Link Pro can directly download programs to the target chip, instead of bridge through another board as before. This project uses the USB to SPI function of MCU-Link Pro to download programs to kinetis K series chips through EZport. In addition to the Kinetis series, many ColdFire chips of NXP also support EZport. In addition, since EZport interface protocol is compatible with SPI NOR flash, this project can also directly burn SPI NOR flash chip. This can also be used when debugging or producing i.mxRT500 and i.mxRT600. MCU Link Pro can easily use USB to SPI and I2C interfaces because NXP provides LIBUSBSIO library. Readers can download this library and related documents from NXP's official website. Here is a brief introduction. The SUB to SIO function is only an optional additional function for lpclink2 and MCU-Link Pro devices. Its main function is to provide CMSIS-DAP debugging interface for the target microcontroller, and provide virtual serial communication using USB VCOM class. The following figure is the process framework of LIBUSBSIO. As shown in the figure, LIBUSBSIO library can support SPI, I2C and GPIO.   The core of LIBUSBSIO communication is SPI_Transfer (lpc_handle hspi, spi_xfer_t * xfer) function. This function can send and receive 1024 bytes at most at one time. In addition, LIBUSBSIO library can set baud rate, clock polarity, sampling edge, and frame length. The following figure shows the output waveform captured by the logic analyzer.   Command Description WREN Write Enable WRDI Write Disable RDSR Read Status Register READ Flash Read Data SP Flash Section Program SE Flash Sector Erase BE Flash Bulk Erase RESET Reset Chip WRFCCOB Write FCCOB Registers dl Image download fw Write/read any sequence to SPI port   This project has been tested on FRDM-K64F.    Hardware connection: MCU-Link pro            FRDM-K64F J19-2        ---       R75-1(flying a line is needed) J19-3        ---       J1-8 J19-4        ---       J11-1 J19-5        ---       J1-12    Connect ground together.   Operation steps: Before power on, R75-1 should be connected to ground, then connect openSDA USB. Thus, K64 will enter EZport status. Read memory ezportdl read [address] [number]   Write enable ezportdl wren Write flash, which can write up to 1000 byte. ezportdl sp [length] [data]   Program a binary file into flash Ezportdl dl [address] [file name] [flag]  address means start address flag=0 means erase flash before programming, flag=1 means don’t erase the flash.     Project download address: https://github.com/jophpan/ezportdl/tree/master  
View full article
Explore the Features of MCU-Link Pro MCU-Link Pro is the latest ARM Cortex-M series core debugger tool from NXP. Some of its features are inherited from the past LPC-LINK2. But overall it was redesigned and introduced and many new features. These additional functions make the MCU-Link Pro a very powerful debugging tool. This article will explore these features and highlight interesting and useful ones. These functions mainly include the following aspects. SWD+SWO Measurement of current and power consumption MCU-Link pro is supported by new blhost LIBUSBSIO library for windows, Ubuntu Linux and MacOS A secondary chip LPC804 on the board   SWD+SWO MCU-Link Pro support SWD only. It doesn’t support JTAG. The main controller in the board is LPC55S69. There is level shifter circuit between the port and LPC55S69. It makes the board can debug the target board work at 1.2V to 5V. It has reference voltage trace circuit which is use to trace the target board voltage. It can trace the port automatically, needn’t any settings. The MCU-Link Pro also can supply 1.8V/3.3V to target board. The maximum current is 350mA. This is done by connecting J6 and selecting by J5. The maximum speed of SWO is 9.6Mbit/s. Same as <PC-Link, MCU-Link Pro support CMSIS-DAP and Jlink firmware. These firmware are kept updating. The latest firmware of CMSIS-DAP is 2.25. If the firmware version is old, there will be a message jump out telling customer to update it when connecting to computer. We can see that the new version gives two VCOM while the original version only have one. The new VCOM use J26-4 and J26-5.   Measurement of current and power consumption MCU-Link pro provides a very interesting real-time function of current and voltage measurement. It can capture a burst of samples of target current usage, the target supply voltage, the shield current, the analog input, the debug interface reference voltage, or target power consumption at up to 100ksps. This information is displayed in a graph and can also be exported for further analysis. The average value of the collected data can also be displayed. And based on the current and voltage data, the power consumption can also be calculated and displayed in the graph. The place where the red line is drawn in the figure below is the real-time voltage at the time point of the mouse.     What's more interesting is that the energy measurement function does not need to activate the debug to capture the data, it is offline and has a separate data channel. But it can also be linked to a session if such a debug session exists. This is very useful in debugging various low-power applications. Not only can you see the power consumption change under each step of the command, but you can also check the power change rule for a long period of time when the system is running. Since there is a separate data channel, you can even debug the program in KEIL and watch the current change in MCUXpresso. The MCU Link Pro has two current measurement configurations, each with a maximum measurable current. This is to achieve maximum measurement accuracy for a variety of different objectives. There are two automatically controlled ranges in each configuration to provide greater precision. The automatic switching from low current measurement to high current measurement is completely controlled by hardware.   Each time the MCU Link Pro is powered up, the measurement circuit calibrates itself. There is no need to disconnect/reconnect the MCU Link Pro before calibration due to transistors are used to isolate the sensing circuit from the target power supply to avoid contention and to ensure a known voltage is applied to the system during calibration. At high sample rates, the MCUXpresso IDE may not capture all data, so the sample rate may need to be adjusted using the configuration options in the energy measurement configuration settings in the tool. If the target current exceeds the maximum current in the selected range, the measurement will saturate and cut off and will therefore be inaccurate. Blhost and MCU-Link Pro The most noteworthy feature of MCU Link Pro is its USB to SPI and I2C bridge functions. This enables the computer to send I2C and SPI signals directly through USB. This powerful function is not ignored by blhost. The new version of blhost can support this function and adds a new command parameter '- L'. Specifically, '- L SPI' refers to the SPI interface and '- L I2C' refers to the I2C interface. The following figure shows the test results on frdm-k64f. It can be clearly seen in the figure that the SPI interface of MCU Link Pro can communicate with mcuboot firmware on k64 and download the encryption program to flash.     In addition to KINETIS, this function is most suitable for i.mxrt600 and I mxRT500。 These two chips have serial ISP mode. User can download the program to ram through SPI or I2C port, and then directly let it run. Many users have this usage. In the previous examples in SDK, a board of twr-kv46 or twr-k65 or frdm-kl25 was required to receive command and data from UART and translate them to SPI and I2C command. Since there is no direct interface on these boards, flying wires are required, which is very troublesome. Moreover, NXP only provides firmware for these three boards. If you want to use other chips or boards, you must also transplant firmware. It's also very troublesome. But with MCU Link Pro, it's all very easy and pleasant.   LIBUSBSIO library In order to better expand the use of bridge functions, NXP has provided libusbsio library. By calling this library, you can realize the USB to SPI \ I2C \ GPIO function in your own application. I also made the upper computer tools for writing kinetis ezport and flash according to the libusbsio library provided by NXP. I introduced this point in detail in my last article. Interested friends can read my article. It is not enough to provide libraries based on windows and Linux. NXP also provides a python library. This library is based on python3 and can be installed through the following command >pip install libusbsio Its usage is not described in detail in the libusbsio documentation. Here is a general introduction. The following is an initialization procedure of libusbsio. import logging import logging.config from libusbsio import * # enable basic console logging logging.basicConfig() # load DLL from default directory sio = LIBUSBSIO(loglevel=logging.DEBUG) # the main code # calling GetNumPorts is mandatory as it also scans for all connected USBSIO devices numports = sio.GetNumPorts() print("SIO ports = %d" % numports) if numports > 0 and sio.Open(0): print("LIB version = '%s'" % sio.GetVersion()) print("SPI ports = %d" % sio.GetNumSPIPorts()) print("Max data size = %d" % sio.GetMaxDataSize()) if(sio.GetNumSPIPorts() > 0): spi = sio.SPI_Open(1000000, portNum=0, dataSize=8, preDelay=100) if spi: data, ret = spi.Transfer(spi_ssel[0], spi_ssel[1], b"Hello World") sio.Close() else: print("No USBSIO device found") ​ Line 9 is to open an instance of libusbsio library; Line 14: get the number of usbsio ports of all USB bridges; Lines 18, 19 and 20 are the read version information, the number of SPI ports and the maximum size of SPI cache; Line 22: open an SPI interface; Line 24, start transmitting data. It can be seen from the above that the use process is relatively simple. And these commands are very similar to those of the C language library.   A secondary controller LPC804 on the board There is also an lpc804 on the MCU Link Pro board. This chip is confusing here. What is it for? Its UART, SPI and I2C ports are all connected for external use. But what's the use? One conceivable application is that the UART port is connected to the newly added vcom2, and then the SPI or I2C works in the slave mode. As a listener, it monitors the SPI or I2C bus to be debugged and displays it on the computer terminal. The LPC804 debug interface is the same as the debugging port of MCU Link Pro. Maybe the board itself is a development board, so that users can develop lpc804 programs? In addition, its UART-ISP port is connected to a UART port of LPC55S69. It seems that in the future, it can communicate with each other through the new version of CMSIS-DAP firmware. Let's look forward to new ways of playing in the future.
View full article
RNN on FRDM_K64 to denoise 1 Introduction Ordinary MCUs are limited by resources, and it is difficult to do some complex deep learning. But although it is difficult, it can still be done. Last time we used CNN for handwritten recognition. This time ,we use RNN for audio noise reduction. This audio noise reduction uses fixed-point noise reduction. MFCC and gain are used as input parameters for training. MFCC, Mel frequency cepstrum, is often used for audio feature extraction. RNN achieves the effect of noise reduction by adjusting the gain of different frequencies.        2 Experiment 2.1 Required tools: frdm-k64, python 3.7, Pip, IAR   2.2 Download the source code of deep learning framework, https://github.com/majianjia/nnom This is a pure C framework that does not   rely on hardware structure. Transplantation is very convenient     2.3 we select the example ‘bubble’ to add the Inc, port and Src folders in NNoM to the project, as shown in the figure                        Figure 1 Open the file ‘port.h’ . The definitation of NNOM_LOG is changed to PRINTF (__ VA_ ARGS__ ), Open the ICF file, and change the heap size to 0x5000, define symbol__ size_ heap__ = 0x5000; Malloc, which is used in this library, allocates memory from here. If it is small, it can't run the network   2.4 Transplant fatfs file system to bubble project   Figure 2   2.5 In the bubble.c file, add the following header file        #include "nnom_port.h" #include "nnom.h" #include "weights.h" #include "denoise_weights.h" #include "mfcc.h" #include "wav.h" #include "equalizer_coeff.h"   2.6 Find main.c under ‘examples\rnn-denoise’ in the framework, ‘main_arm.c’ is used for stm32. Main.c is provided for windows running routines. This code is used to open the audio file, then run the network, and finally generate noise reduction. Use this code to facilitate experiments. The APIs for file operations in this file need to be manually changed to MCU APIs. I added a progress bar display. You can refer the attachment.   2.7 This example also uses DSP, so DSP support needs to be turned on, as shown in the figure                                                           Figure 3 2.8 Add macro                                             Figure 4   2.9 After the compilation is passed, download the program. The tested wav needs to be named ‘sample.wav’, and the mcu will reduce noise for it. Finnally mcu will generate ‘filtered_sample.wav’. Plug the SD card into the computer to listen to the noise-reduced audio. This is serial message.     Figure 5 This is the audio analyzed by the audio analysis software. The upper part is the noise reduced, and the lower part is the original sound. You can see that a lot of noise has been suppressed.                                                    Figure 6 3 training Through the above steps, we have achieved audio noise reduction, so how is this data trained? You need to install tensorflow, and keras. How to train is written in the README_CN.md file. This tensorflow needs to install the gpu version, and needs to install cuda. Please check the installation yourself. 3.1 Download the voice data https://github.com/microsoft/MS-SNSD and put it under the MS-SNSD folder 3.2 Use pip3 to install the tools in requirements.txt 3.3 Run ‘noisyspeech_synthesizer.py’ directly, this will report an error. You need to change the 15-line float to int type. 3.4 Run the script gen_dataset.py to generate mfcc and gain 3.5 Run main.py, this will generate the noise file of the sample, which can be used for testing and also generated ‘weights.h,denoise_weights.h,equalizer_coeff.h’ PS: Put the code in directory 'boards/frdmk64f/demo_app'. 'sample.zip' has the noise wave.
View full article
  CNN on FRDM_K64 1 Introduction Limited by resources, ordinary MCU is difficult to do some complex deep learning. However, although it is difficult, it can still be done. CNN, convolutional neural network, is a kind of deep learning algorithm, which can be used to solve the classification task. After the implementation of CNN, ordinary MCU can also be used as edge computing device. Next, we introduce how to run CNN on frdm-k64 to recognize handwritten numbers. The size of digital image is 28x28. 28x28 image as input for CNN will output a 1x10 matrix. There are few deep learning libraries written for MCU on the Internet. Even if there are, there will be various problems. NNoM framework is easy to transplant and apply, so we use it     2 Experiment 2.1 Required tools: frdm-k64, python 3.7, Pip, IAR, tcp232   2.2 Download the source code of deep learning framework, https://github.com/majianjia/nnom This is a pure C framework that does not rely on hardware structure. Transplantation is very convenient   2.3  we select the example ‘bubble’ to add the Inc, port and Src folders in NNoM to the project, as shown in the figure          Figure 1 Open the file ‘port.h’ . The definitation of NNOM_LOG is changed to PRINTF (__ VA_ ARGS__ ), Open the ICF file, and change the heap size to 0x5000, define symbol__ size_ heap__ = 0x5000; Malloc, which is used in this library, allocates memory from here. If it is small, it can't run the network   2.4 From the download framework, go into ‘mnist-simple/mcu’, which has trained file ‘weights.h’, and randomly generated handwritten image file, ‘image.h’. Add these two files to the project   2.5 Add headfile to ‘bubble.c’        #include "nnom_port.h" #include "nnom.h" #include "weights.h" #include "image.h"   2.6 Delete the original code, add the following code   nnom_model_t *model; const char codeLib[] = "@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'.   "; /*******************************************************************************  * Code  ******************************************************************************/ void print_img(int8_t * buf) {     for(int y = 0; y < 28; y++)        {         for (int x = 0; x < 28; x++)               {             int index =  69 / 127.0 * (127 - buf[y*28+x]);                      if(index > 69) index =69;                      if(index < 0) index = 0;             PRINTF("%c",codeLib[index]);                      PRINTF("%c",codeLib[index]);         }         PRINTF("\r\n");     } }   // Do simple test using image in "image.h" with model created previously. void mnist(char num) {        uint32_t predic_label;        float prob;        int32_t index = num;        PRINTF("\nprediction start.. \r\n");               // copy data and do prediction        memcpy(nnom_input_data, (int8_t*)&img[index][0], 784);        nnom_predict(model, &predic_label, &prob);          //print original image to console        print_img((int8_t*)&img[index][0]);               PRINTF("\r\nTruth label: %d\n", label[index]);        PRINTF("\r\nPredicted label: %d\n", predic_label);        PRINTF("\r\nProbability: %d%%\n", (int)(prob*100)); }   int main(void) {     uint8_t ch;     /* Board pin, clock, debug console init */     BOARD_InitPins();     BOARD_BootClockRUN();     BOARD_InitDebugConsole();     /* Print a note to terminal */     model = nnom_model_create();        // dummy run        model_run(model);     PRINTF("\r\nwhich image to distinguish? 0-9 \r\n");     for(uint8_t i=0; i<10; i++)     {         print_img((int8_t*)&img[i][0]);     }     while(1)     {         PRINTF("\r\nwhich image to distinguish? 0-9 \r\n");         ch = GETCHAR();         if((ch >'9') || ch < '0')         {             continue;         }         PRINTF("\r\n");         mnist(ch-'0');     } }   An error will be reported when compiling ‘weights.h’, due to lack of few parameters. In layer [1], layer [4], layer [7], you need to add ‘division (1,1)’ after ‘stride (1,1)’. In this way, the compilation passes.   2.7 As a result, open the serial port software. At the beginning, the terminal will print out a variety of handwritten digital pictures, and then enter a number, The corresponded picture will be recognized.                                                    Figure 2 When we input ‘8’, the recognition is the handwriting '9'                        Figure 3   The ‘Truth label’ corresponds to IMG9_LABLE in ‘image.h’ and ‘Predicted label’ is the prediction results   3 training Through the above steps, we have realized a simple handwritten numeral recognition. Next, we will introduce ‘weights.h’. How to generate the weight model here? The image data here are all from MNIST digital set. How can we make a handwritten number for MCU to recognize?   3.1 Under ‘nnom-master\examples\mnist-simple’, there is a ‘mnist_ simple.py’. You need to run it to generate ‘weights.h’ and ‘image.h’. To run this, you need to install tensorflow, keras and so on. When you run it, you can use pip to install what is missing The network operation process is as shown in the figure                               Figure 4 Conv2d-> convolution operation, Maxpool-> pooling. The meaning of convolution operation is to extract the features of the image. Pooling is a bit like compressing data, which can reduce the running space. 28x28 input and output a 1x10 matrix, representing the possibility of 0-9   3.2 We can use the ‘Paint’ program of WIN to adjust the canvas to 28x28, write numbers on it and save it in PNG format. I wrote a ‘4’     Figure 5   Change the code as following.   nnom_model_t *model; uint8_t temp[28*28]={0}; const char codeLib[] = "@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'.   "; /*******************************************************************************  * Code  ******************************************************************************/ void print_img(int8_t * buf) {     for(int y = 0; y < 28; y++)        {         for (int x = 0; x < 28; x++)               {             int index =  69 / 127.0 * (127 - buf[y*28+x]);                      if(index > 69) index =69;                      if(index < 0) index = 0;             PRINTF("%c",codeLib[index]);                      PRINTF("%c",codeLib[index]);         }         PRINTF("\r\n");     } }     void mnist_pic(uint8_t *temp) {        float prob;     uint32_t predic_label;        PRINTF("\nprediction start.. \r\n");          // copy data and do prediction        memcpy(nnom_input_data, (int8_t*)temp, 784);        nnom_predict(model, &predic_label, &prob);          //print original image to console        print_img((int8_t *)temp);        PRINTF("\r\nPredicted label: %d\n", predic_label);        PRINTF("\r\nProbability: %d%%\n", (int)(prob*100)); }   int main(void) {     /* Board pin, clock, debug console init */     BOARD_InitPins();     BOARD_BootClockRUN();     BOARD_InitDebugConsole();     /* Print a note to terminal */     model = nnom_model_create();        // dummy run        model_run(model);     while(1)     {         PRINTF("\r\n Send picture by serial\r\n");            DbgConsole_ReadLine(temp,784);         PRINTF("\r\n Got picture\r\n");           mnist_pic(temp);     } }   3.3 Then use pic2mnist.py(see the attachment), run this script with CMD and enter 'Python pic2mnist.py 1. PNG ', 1. PNG is the image to be parsed, and then ‘content.txt’ will be generated. The file contains the data of the picture. Send the data to the MCU through the serial port. Note that ‘Send as Hex’ should be checked. Similarly, the handwritten picture will be displayed first, and then the picture will be recognized.                                                           Figure 6   We can see that '4' was identified
View full article
Introduction: User can modify the value of internal reference clock (IRC) by using the Trimming capability of programmers. There are two registers (ICS_C3 & ICS_C4) which contains the trim and fine trim value of the clock respectively. By default, devices comes with a factory programmed trim value which is automatically passed into these registers during reset initialization when not in debug mode. These registers needs to be passed with appropriate value to get the required clock value. This is where programmers come into picture, they generally have this capability to find the accurate trim and fine trim values corresponding to required clock. Now the catch here is that the customized trim value calculated by programmers is stored at reserved flash location (eg-0x000003ff and 0x000003fe for Ke02) and have to be copied to trim registers( ICS_C3 &C4) manually by user during code initialization. Using this process to trim the clock will give accurate desired clock without any deviation between samples. Process:  There is an option in programmers( like I am using PEmicro Multilink universal ) for enabling and setting the trimming feature in the configuration settings as below:         After enabling this option, the programmer will find the best trim value and store at internal memory location:   0x03ff for trim internal reference value 0x03fe for fine trim internal reference value   Below snapshot from console shows this process:       Next step is to copy these value from above locations and pass it to ICS_C3 and ICS_C4 manually in our application code. You can use below code lines:       ICS->C3 = *((uint8_t*) 0x03FF); // trim internal reference clock     ICS->C4 |= ICS_C4_SCFTRIM(*((uint8_t*) 0x03FE)); // fine trim internal reference clock   What the above logic does is – it copies the trimmed value from the factory programmer into your ICS register – not dependent on a constant value but dynamically update the register as per the tuning happening by the programmer. Similar option is there in production programmers also to take care of the trimming process. Note: Above steps are with reference to KE series, for other series user has to check the device reference manual for specific address and register. Also attaching a code with the above mentioned things added into it for ease of understanding. Thanks Madhur
View full article
The following document explains how to load an encrypted image using the MCUBoot in the K64. Download the SDK for the K64, be sure that the MCU boot feature is selected:  https://mcuxpresso.nxp.com/en/welcome   For the example, I’m going to use gpio_led_output. To prepare the example to work with the bootloader, you would need to do the following steps: In properties / C/C++ Build / MCU settings set the start of the flash to the 0xa000, this to preserve the bootloader section:   Generate the binary for the image to load.   Write BCA Create the BCA, this field is to manage the different features of the bootloader. To generated this, the KinetisFlashTool can be used, you can find it in the following link. In this options you need to add the tag, the peripheral used to communicate and a timeout to have a time frame to call again the bootloader without need to call it from your application. Click OK and save the BCA to the image.     The bootloader can be found in the SDK examples: In the bootloader_config.h, change the BL_FEATURE_ENCRYPTION_KEY_ADDRESS for outside the code you would load, in this case, I'm going to use the 0xF000: #define BL_FEATURE_ENCRYPTION_KEY_ADDRESS 0xf000 // NOTE: this address must be 4-byte aligned. After this, load the firmware to the MCU. For the next steps we will need to generate the secure file: Generate the AES 128 key using the elftosb Command: elftosb.exe -V -d -f kinetis -n 1 – K 128 -o SBKEK.key Create the SB image from the binary, Use option -k to pass the key generated before Use option -f to define the device. Device must be kinetis to be able to use a 128 AES key Use option -c to load the bd file. (see attached) Use option -o to define the output Command: elftosb.exe -V -d -f kinetis -c (bd file path) -k (key path) -o (output path).sb2 (image path)   Now we will load the image using the blhost: Erase the memory section to load the program and reset the device  Command: blhost.exe -p COMx – flash-erase-region 0xa000 0x10000 Program AES key, same as generated in the previous step. Load it to the memory section previously defined in the  BL_FEATURE_ENCRYPTION_KEY_ADDRESS and confirm that the key was loaded correctly: Command: blhost.exe -p COMx – write-memory 0xF000 “{{key generated}}” Command: blhost.exe -p COMx –read-memory 0xF000 16   Load the SB file Command: blhost.exe -p COMx – receive-sb-file (path encrypted sb file) After a reset the application should run correctly.                
View full article
  This document describes the different source clocks and the main modules that manage which clock source is used to derive the system clocks that exists on the Kinetis devices. It’s important to know the different clock sources available on our devices, modifying the default clock configuration may have different purposes since increasing the processor performance, achieving specific baud rates for serial communications, power saving, or simply getting a known base reference for a clock timer. The hardware used for this document is the following: Kinetis:  FRDM-K64F Keep in mind that the described hardware and management clock modules in this document are a general overview of the different platforms and the devices listed above are used as a reference example, some terms and hardware modules functionality may vary between devices of the same platform. For more detailed information about the device hardware modules, please refer to your specific device Reference Manual. Kinetis platforms The Kinetis devices have a main module called Multipurpose Clock Generator (MCG) this module controls which clock source is used to derive the system clocks. A high-level description diagram is shown below: Figure 1. Multipurpose Clock Generator External clock sources can provide a frequency signal as the System oscillator module or the RTC oscillator module, also the MCG module has internal clock generators that the System integration module (SIM) manages, the SIM module provides module-specific clock gating to allow granular shutoff of modules. For more detailed information about the SIM module, refer to “Chapter 12. System Integration Module(SIM)” from the K64 Sub-Family Reference Manual.  The following clock diagram shows all the multiplexers, dividers, and clock gates that can be controlled by the MCG, however, we will focus on the external and internal clock sources and the MCG outputs. Figure 2. Oscillators,  MCG and SIM modules At ‘MCGOUTCLK’ line, the primary clocks for the system are generated, the circuitry provides fixed clock dividers for the Core clock, Bus clock, FlexBus clock, and the Flash Clock. This allows for trade-offs between performance and power dissipation. It’s important to know that the MCG has 9 states of operation shown in the following figure.    Figure 3. MCG operation states In the previous image, the arrows indicate the permitted MCG state transitions, for example, if the current MCG state is BLPI(Bypassed Low Power Internal) and the desired state is BLPE(Bypassed Low Power External) the shortest and allowed path to follow is first switch to FBI(FLL Bypassed Internal) then to FBE(FLL Bypassed External), and finally to the BLPE MCG state. These switching mode restrictions exist due to certain MCG configuration bits that must be changed to properly move from one mode to another. For example, in the K64 family, the MCG state after a power-on reset is FEI(FLL Engaged Internal) mode, the MCGOUTCLK is derived from the FLL clock that is controlled by the 32kHz Internal Reference Clock (IRC), the following table shows the output frequency values for this specific MCG state. Source Frequency MCGOUTCLK 20.97MHZ Core/System clocks 20.97MHz Bus clock 10.48MHz FlexBus clock 6.99MHz Flash clock 4.19MHz Table 1. K64 default MCG configuration after reset: FEI (FLL Engaged Internal) The following image shows the blocks used for the FEI state using Clocks Tool from MCUXpresso IDE. Figure 4. View of FEI state from Clock Tools For more detailed information, refer to “Chapter 25. Multipurpose Clock Generator (MCG)” from the K64 Sub-Family Reference Manual.  External Clock Sources     System oscillator The System Oscillator module is a crystal oscillator. The module, in conjunction with an external crystal or resonator, generates a reference clock for the MCU.  Supports 32 kHz crystals (Low Range mode) and supports 3–8 MHz, 8–32 MHz crystals and resonators (High Range mode) For more detailed information, refer to Chapter 26. Oscillator(OSC) at K64 Sub-Family Reference Manual.   RTC oscillator The RTC oscillator module, in conjunction with an external crystal, generates a reference clock source of 1Hz and 32.768KHz, supports 32 kHz crystals with very low power. For more detailed information, refer to Chapter 27. RTC Oscillator(OSC32K) at K64 Sub-Family Reference Manual.   Internal Clock Sources    IRC oscillators Internal clock driven by the Fast Internal Reference (FIR) @4MHz or the Slow Internal Reference (SIR) @32kHz.  IRC internal oscillator Internal 48 MHz oscillator that can be used as a reference to the MCG and also may clock some on-chip modules. PLL Phase-locked loop circuit that in conjunction with an external clock source can achieve higher and stable frequencies.   FLL Frequency-locked loop circuit that in conjunction with an internal/external clock source provides module-specific clock and achieves higher frequencies. Modifying MCG state from FEI to FBI state If the current system clock does not fit with our timing requirements we can modify it by changing the state of the MCG module, in this case, if the user requires a lower system clock frequency @32.7KHz(Slow IRC) or @4MHz(Fast IRC) instead @21MHz(FLL Engaged Internal ‘FEI’ default state) and a low power option of the MCG module, the FLL Bypass Internal (FBI) state is an option to reach these requirements. 1.1 Configure MCG mode The FBI state allows us to use the Fast IRC together with its frequency divider achieving frequencies between 31.25KHz to 4MHz, for this example the final core clock is @2MHz. Follow the next steps to change to the FBI state and select a 2MHz clock using the Clock-Tools tool from MCUXpresso IDE.        At the MCUXpresso QuickStart Panel select MCUXpresso Config Tools >> Open Clocks Figure 5. Open Config Tools        At the left top of the screen select the MCG mode to “FBI(FLL Bypassed Internal)” Figure 6. Selection of MCG Mode        Select the frequency divider block(FCRDIV) right-click on it and select “Edit settings of: FCRDIV” Figure 7. FCRDIV block        Modify the divider value from 1 to 2. Figure 8. FCRDIV divider value        Finally, the next image shows how the MCG state and the new yellow paths get modified. The Core and system clocks are @2MHz. Figure 9. FBI MCG state @2MHz 1.2 Export clock configuration to the project After you complete the clock configuration, the Clock Tool will update the source code in clock_config.c and clock_config.h, including all the clock functional groups that we created with the tool. In the previous example, we configured the MCG state to FBI mode, this is translated to the following instructions in source code: “CLOCK_SetInternalRefClkConfig();” and “CLOCK_SetFbiMode();”  Figure 10. Source code view of FBI MCG configuration Another way to change the MCG state is by directly modifying the internal MCG registers. The blocks shown in the following image need to be modified to switch from the default FEI state to the FBI state. Figure 11. Blocks in FEI state to modify at MCG registers Note. MCG registers can only be written in supervisor mode. The ARM core runs in privileged(supervisor mode) out of reset, it is controlled by [nPRIV] bit in CONTROL core register. For more detailed information visit the Cortex-M4 ARM Documentation Reference Manual.        Internal Reference Source Multiplexor (IREFS), selects the reference source clock for the FLL.  1 is written to C1[IREFS]. The slow internal reference is selected.        PLL Select  Multiplexor(PLLS) Controls whether the PLL or FLL output is selected. 0 is written to C6[PLLS] The FLL output is selected as the MCG source, the PLL is disabled.        Clock Source Select Multiplexor(CLKS), selects the clock source for the MCGOUTCLK  line. 01 is written to C1[CLKS].  The internal reference clock is selected at the CLKS multiplexor.        Fast Clock Internal Reference Divider(FCRDIV), selects the Fast Internal Reference Clock divider, the resulting frequency can be in the range of 31.25KHz to 4MHz. 001 is written to SC[FCRDIV]. The dividing factor is 2 since the desired frequency is @2MHz and the source clock is @4MHz.        Internal Reference Clock Select (IRCS). Selects between the fast or slow internal reference clock source.  x is written to C2[IRCS]. Write 0 for Slow IRC or 1 for Fast IRC.        Finally, to enable the low power when neither the PLL nor FLL are used, a register in C2[LP] is modified. x is written to C2[LP]. Enable, or Disable the PLL & FLL in all the bypass modes.     This is translated to the following instructions in source code in “CLOCK_SetInternalRefClkConfig();” and “CLOCK_SetFbiMode();” functions:  Figure 12. Source code view of Internal MCG Registers Note. C1, C2, C6, and SC registers are part of the internal MCG control registers.  References K64 Sub-Family Reference Manual Also visit LPC's System Clocks   
View full article
Status: Text content finished, to be updated with more diagrams/images. Foreword First of all. I have to say sorry for my English since I am not a native speaker. This is my personal experience of using AN2295 on FRDM-KL25Z. I want to share it with anyone who is suffering on it, since the existing release from FSL has not been fully tested on FRDM-KL25Z. (I can figure it out in many ways.) Components and Documents The lastest microcontrollers always have some kind of bootloader to download user code to the flash memory. FSL does offer many bootloaders for various mcu families. Among them, AN2295 bootloader is general purpose for most of these families, since it only requires an UART, either hardware or software UART. To make AN2295 work for a system, the developer must have three components: AN2295 bootloader running on target MCU, such as MKL25Z128VLK4 on FRDM-KL25Z. AN2295 bootloader PC software, such as win_hc08sprg, or my Python script, FcBootloaderProgrammer.py AN2295 aware user application, which has different start address and special treatment for interrupt vector. UPDATE Add an2295_programmer_v1.zip for Python source code, you need install PySerial module for Python 2.5. Please visit following URLs to download necessary files: http://www.freescale.com/files/microcontrollers/doc/app_note/AN2295.pdf http://cache.freescale.com/files/32bit/doc/app_note/AN4767.pdf Software for AN2295 AN4767: UART Boot Loader Design on the Kinetis E Series, a more detail application note for AN2295 on E series. Updates for AN2295 on IAR 6.5 The released AN2295 has to be updated before downloading into FRDM. When you open the AN2295_Kinetis project in IAREW, please update the following setup items in IDE. In workspace window, switch to Kinetis L Debug configuration. Right click on AN2295_Kinetis and open Options. Click on Debugger category. In Setup tab, select PE micro in driver, disable Run to options. (The release leaves it as simulator, and run to __main, which makes warning report when you downloading code to debugger) In Download tab, enable Verify download, Use flash loader, Override default .board file, with $TOOLKIT_DIR$\config\flashloader\Freescale\FlashKLxx128K.board . Disable Attach to program. In PE micro, select interface type as OpenSDA - USB In Tools|Options (IDE Options)|stack, you can leave Stack points not valid until program reaches as blank (Optional). In AN2295_LinkerFile.icf, change IntVectTable_end__ from 0x0000003f to 0x000003FF (Optional). In bootloader_cfg.h, enable AN2295_FRDM_KL25Z_cfg.h, comment out AN2295_TWR_KL25Z_cfg In AN2295_FRDM_KL25Z_cfg.h, #define BOOT_UART_BAUD_RATE 57600 Add gpio.h/gpio.c to project, in case you want use them to indicate something. We can conclude a fact that AN2295 is good enough as a bootloader as it has been tested on many MCU families. The source code is open, so you can custom it for your own specific purposes. However, you have to do it yourself. The reason why I change baudrate is I found VCP of OpenSDA will decode 0x00 send from KL25Z as 0x80. So I slow down to 57600. Updates for PC software of AN2295 Unfortunately, The released (10.0.12.0) and updated (10.0.16.0) versions of bootloader PC software don't work with my bootloader. The released PC software of AN2295 is open source as well. However I am not fan for VC++. I prefer to make my own programmer software with Python, as knows as a clue language. The programmer software is released in Windows exe format and works in command line prompt. The user don't bother to install Python/PySerial and other components. The programmer software works in a simple single shot mode, blank/erase/program actions. D:\an2295_programmer>FcBootloaderProgrammer --help ################################################## FC Booloader Programmer v0.1-alpha - An alternative to Freescale AN2295 on FRDM-KL25Z   by Allan K Liu (C)2013 Ennovation LLC ################################################## Usage: -h, --help      :       This help information -p, --port      :       Serial port setting in Windows, like COMxx, depends on your VCP installation -b, --baudrate  :       Baud rate setup from 9600/19200/38400/57600/152000 -f, --file      :       Hex file (Motorola SREC or Intel HEX) -a, --action    :       Action [blank/erase/program] -d, --debug     :       Debug [0:1]    In order to make it simpler, I prepare some DOS batch files. blank.bat FcBootloaderProgrammer --port=COM14 --baudrate=57600 --debug=1 --action=blank erase.bat FcBootloaderProgrammer --port=COM14 --baudrate=57600 --debug=1 --action=erase program.bat FcBootloaderProgrammer --port=COM14 --baudrate=57600 --debug=1 --action=program --file=DemoApp_4_FC_Bootloader_freedom.srec You can disable debug output optionally. Updates for Demo projects for AN2295 on IAR6.5 In order to run a user application with AN2295 bootloader, you have to rebuild it with modified linker script. You can generate a new app based upon Kinetis L Sample Codeby following steps. Run make_new_project.exe under Kinetis L Sample Code\kl25_sc_rev5\klxx-sc-baremetal\build\iar\ Offer a new project name "DemoApp_4_FC_Bootloader_freedom" according to prompt. Open it in IAR IDE. Change project linker options, enable Override default with $PROJ_DIR$\..\config files\Pflash_32KB_AN2295.icf, the modified linker file. The major changes are: define symbol __ICFEDIT_region_ROM_start__ = 0x00001000; define symbol __ICFEDIT_region_ROM_end__   = 32*1024; define exported symbol __VECTOR_TABLE      = 0x00001000; define symbol __code_start__ = 0x00001410; After building the project, we can get similiar srec file, which has start address at 0x1000. S027000044656D6F4170705F345F46435F426F6F746C6F616465725F66726565646F6D2E7372656331 S1131000F80B0020111400002929000029290000F0 S11310102929000029290000292900002929000084 ...... S1132B200A0D4B4C300000000A0D4B4C31000000E4 S1132B300A0D4B4C320000000A0D4B4C33000000D0 S10B2B400A0D4B4C34000000A7 S9032B0DC4 According to SREC file format, its filename: DemoApp_4_FC_Bootloader_freedom.srec. And it ranges from 0x1000 to 0x1B47 Finally, run "erase.bat", then "blank.bat", then "program.bat" Simple! Other Useful Tools During my development for AN2295, I used some other tools, especially PC terminal to monitor events/traffics on RS232/UART. Future Development Here are my plan. More images for step on step. Smart detection for OpenSDA VCP port. Mutli-thread GUI for bootloader tool. More labs to reduce unnecessary empty flash space on VECTOR_TABLE and flash_protection_table in user application. Support cryptographic/authentication in bootloader to support licenses.
View full article
Introduction This document provides guidance to program or store code in FlexNVM memory available in KW36 MCU to use it as P-Flash memory. This article uses as the starting point, an example imported from the connectivity software stack. Software Requirements 1. FRDM-KW36 SDK 2.2.0.   2. MCUXpresso IDE. Hardware Requirements 1. FRDM-KW36 board. Programming KW36 FlexNVM Example The objective is to explain how to place a linker input section, variable or function into the FlexNVM memory. Before starting, the developer must know that the GNU linker cannot automatically place code or data across two separate memory regions, so the developer must analyze and manually choose what sections will be placed at which memory (P-Flash or FlexNVM) to get the most efficient way to use the total memory size. This example will use the "bare-metal" version of the heart rate sensor project (included in the SDK), however, the same steps apply for the "freertos" version. 1. Select "Import SDK example(s)..." option in the "Quickstart Panel" window. Next, choose the FRDM-KW36 board. Click the "Next" button. 2. Expand "wireless_examples/bluetooth/hrs" folders and select bm project with the checkbox beside. Click the "Finish" button. 3. Replace the "MKW36Z512xxx4_connectivity.ld" file by "MKW36_connectivity_dflash_use.ld" (attached to this document) into the source folder in the workspace. 4. Open the "Project/Properties" window and select "C/C++Build/Settings". Next, go to the "MCU Linker/Managed Linker Script" perspective and edit the linker script name to "MKW36_connectivity_dflash_use.ld". Click "Apply and Close" button.   Placing input sections in FlexNVM memory It is possible to program specific input sections in the FlexNVM memory following the next steps: 1. Open the "MKW36_connectivity_dflash_use.ld" linker file. 2. Search for output sections. In this example, we will edit the "text" section to save it in the FlexNVM array. Replace the "m_text" memory by "FLEX_NVM" memory as in the following picture.  3. Debug the project using the CMSIS-DAP debugger. 4. Open the memory perspective. Add a new memory monitor with the "green plus icon" in the 0x1000_0000 address. Verify the expected results.      Placing variables and functions in FlexNVM memory Also, it is possible to place a specific function or variable in the Flex NVM using attributes as follows: 1. Open the "MKW36_connectivity_dflash_use.ld" linker file. 2. Search for output sections. In this example, we will create a new section placed at Flex NVM address range. The name of this output section is "text_Flash2". Edit the linker file as shown below.   3. Create your functions and variables using the following section attribute (in this example we will open and place the text below in the "fsl_os_abstraction_bm.c" file under framework/OSAbstraction/Source folder in the workspace): __attribute__ ((section(".d_flash_array"))) const uint32_t const_data_table[10] = {0,1,2,3,4,5,6,7,8,9}; __attribute__ ((section(".text.$FLEX_NVM"))) void delay (void) {        volatile uint32_t i = 0;        for (i = 0; i < 800000; i++)        {               __asm("NOP");        }        }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 4. Use your own variables and functions in the code (this step is only for testing purpose, this is important to prevent any optimizations performed by the compiler). In this example, we will use the delay function and the const array in the main function located at "fsl_os_abstraction_bm.c" file. uint32_t Use_Array; uint32_t index;     for (index = 0; index < 10; index++) {        delay();        Use_Array = const_data_table[10]; }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 5. Debug the project using the CMSIS-DAP debugger. 6. Open the memory perspective. Add a new memory monitor with the "green plus icon" in the 0x1000_0000 address. Verify the expected results.
View full article
    以DMA方式通过UART发送数据应该是工程应用中很常用的一种方式了,尤其是在需要频繁发送数据或者数据包长度较大的场合,如果使用传统的UART查询或者中断方式发送和接收数据,对CPU资源的占用将是极大的浪费,带操作系统的应用还好些,如果是纯粹的前后台程序有时不能容忍的,所以DMA方式是很恰当的选择。而本篇以Kinetis L系列为例介绍一下以DMA方式通过UART端口发送长数据包,当然不同于K系列复杂强大的eDMA功能,L系列的DMA模块配置起来还是比较简单的。 测试平台:IAR6.7 + KL26 FRDM 测试代码:FRDM-KL26Z_SC\FRDM-KL26Z_SC_Rev_1.0\klxx-sc-baremetal\build\iar\uart0_dma        其实KL26的官方sample code中是自带uart0_dma例程的,但是实现的功能只是将UART口接收到的每一个字节的数据通过DMA方式再发送出去(即环形缓冲),这样用来作为一个功能演示的demo是可以,但是往往我们需要的是将某缓冲区的数据以DMA方式发送出去或者将接收到的数据以DMA方式写到某缓冲区这样的功能,为此我们就需要在原有的例程上进行修改从而达到我们的应用目的,这里给出几点需要修改的地方,并做了相关注释(整个工程见最后附件): 1)定义待发送缓冲区: /* array to be sended */ uint8 testdata[]={"\nFreescale Kinetis KL26\n"}; 2)设置DMA源地址: #define DMA0_DESTINATION  0x4006A007    /* the memory adress of UART0_D register */ #define DMA0_SOURCE_ADDR  (uint32)testdata    /* define the source data array address */ 3) 在DMA0_init()函数中修改发送数据包的长度: DMA_SAR0 = DMA0_SOURCE_ADDR;    //Set source address to UART0_D REG DMA_DSR_BCR0 = DMA_DSR_BCR_BCR(sizeof(testdata));    //Set BCR to know how many bytes to transfer DMA_DCR0 &= ~(DMA_DCR_SSIZE_MASK | DMA_DCR_DSIZE_MASK);    //Clear source size and destination size fields 4)添加源地址自动加1功能,因为之前的环形缓冲方式只是单字节数据,所以不需要源地址递增,但是由于我们这次需要发送整个数据包,所以这里我们就需要将源地址递增功能打开,而具体递增1,2还是4则取决于发送数据的最小单位(8bit,16bit or 32bit): /* Set DMA as follows: Source size is 8-bit size Destination size is 8-bit size Cycle steal mode External requests are enabled source address increments 1 automatically */ DMA_DCR0 |= (DMA_DCR_SSIZE(1) | DMA_DCR_DSIZE(1) | DMA_DCR_CS_MASK | DMA_DCR_ERQ_MASK | DMA_DCR_EINT_MASK | DMA_DCR_SINC_MASK); 5)配置DMAMUX通道为UART0 TX即发送通道(通道号为3),因为我们需要的是UART0_TX触发DMA传送: DMA_DAR0 = DMA0_DESTINATION;    //Set source address to UART0_D REG DMAMUX0_CHCFG0 = DMAMUX_CHCFG_SOURCE(3);    //Select UART0 TX as channel source DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;    //Enable the DMA MUX channel 6)在UART0_DMA_init()函数中修改UART0发送缓冲区为空时即触发DMA发送: void UART0_DMA_init(void) { UART0_C2 &= ~(UART0_C2_TE_MASK | UART0_C2_RE_MASK);  //Disable UART0 UART0_C5 |= UART0_C5_TDMAE_MASK;                      // Turn on DMA request(Transmit) for UART0 UART0_C2 |= (UART0_C2_TE_MASK | UART0_C2_RE_MASK);  //Enable UART0 } 7)在DMA发送完成中断服务函数中禁掉DMA通道,实现单次发送,即每个数据包发送完成之后即停止发送,否则不禁掉的话会一直触发DMA发送,造成串口堵塞: void DMA0_IRQHandler(void) {  /* Create pointer & variable for reading DMA_DSR register */ volatile uint32_t* dma_dsr_bcr0_reg = &DMA_DSR_BCR0; uint32_t dma_dsr_bcr0_val = *dma_dsr_bcr0_reg; if (((dma_dsr_bcr0_val & DMA_DSR_BCR_DONE_MASK) == DMA_DSR_BCR_DONE_MASK)      | ((dma_dsr_bcr0_val & DMA_DSR_BCR_BES_MASK) == DMA_DSR_BCR_BES_MASK)      | ((dma_dsr_bcr0_val & DMA_DSR_BCR_BED_MASK) == DMA_DSR_BCR_BED_MASK)      | ((dma_dsr_bcr0_val & DMA_DSR_BCR_CE_MASK) == DMA_DSR_BCR_CE_MASK)) { DMA_DSR_BCR0 |= DMA_DSR_BCR_DONE_MASK;                //Clear Done bit DMA_DSR_BCR0 = DMA_DSR_BCR_BCR(sizeof(testdata));      //Reset BCR dma0_done = 1; } /* once the array complete the transfer, then disable the DMA channel.*/ DMAMUX0_CHCFG0 &= ~DMAMUX_CHCFG_ENBL_MASK; }        将上述代码做完相应修改即可实现单次将内存缓冲区数据以DMA方式通过UART0发送出去,效果如下。此外,如果想周期性触发或者条件性触发,则只需再相应位置添加“DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;”这句代码即可打开通道,然后立即会触发UART0_TX发送数据,然后待数据包发送完之后再次停止等待下次使能。 另外,关于DMA的传输速度的话,因为其独立占用一条自己的总线,其搬运时钟为系统时钟(即coreclock/Systemclock),相比于总线上的传输速度,本例程中整个数据包的发送时间主要是取决于UART串口的波特率*数据包长度。 附件为修改好后的完整工程:
View full article
As we know, uC/OS –II is a scalable, ROMable, preemptive real-time kernel that manages multiple tasks and it has been ported to more than 45 CPU architectures.  In this article, you can learn the steps of porting uC/OS –II to MAPS-22. Downloading uC/OS-II source code and application project To obtain the μC/OS-II source code and projects, simply point your favorite browser to: www.Micrium.com/Books/Micrium-uCOS-II. You will be required to register. This means that you’ll have to provide information about yourself. Download and execute the following file: Micrium-Book-uCOS-II-TWR-K53N512.exe. Fig 1 shows the directory structure created by this executable. All files are placed under the \Micrium directory. There are two main sub-directories: \Examples and \Software and they are described below. Fig 1 Directories and Files μC/OS-II is fairly easy to use once it is understood exactly which source files are needed to make up a μC/OS-II-based application. Fig 2 shows the μC/OS-II architecture and its relationship with hardware. Of course, in addition to the timer and interrupt controller, hardware would most likely contain such other devices as Universal Asynchronous Receiver Transmitters (UARTs), Analog to Digital Converters (ADCs), Ethernet controller(s) and more. Fig 2 F2-(1) The application code consists of project or product files. For convenience, these are simply called app.c and app.h, however an application can contain any number of files that do not have to be called app.*. The application code is typically where one would find the main(). F2-(2) The Board Support Package (BSP) code needed by μC/OS-II is typically quite simple and generally, μC/OS-II only requires that you initialize a periodic interrupt source which is used for time delays and timeouts. This functionality can be placed in a file called bsp.c along with its corresponding header file, bsp.h. Semiconductor manufacturers often provide library functions in source form for accessing the peripherals on their CPU or MCU. These libraries are also part of the BSP. F2-(3) This is the μC/OS-II processor-independent code. This code is written in highly portable ANSI C. F2-(4) This is the μC/OS-II code that is adapted to a specific CPU architecture and is called a port. F2-(5) Configuration files are used to define μC/OS-II features (os_cfg.h) to include in the application, specify the size of certain variables and data structures expected by μC/OS-II, such as idle task stack size and tick rate among others. Below is a summary of all directories and files involved in a μC/OS-II-based project (Fig 3). The“<-Cfg” on the far right indicates that these files are typically copied into the application directory and edited based on the project requirements. Fig 3 Porting Steps 1. Copy uC/OS-II source code to ~\MAPSK22_SC\Libraries which includes peripheral driver files, startup code and devices header 2. Copy os_cfg.h, app_cfg.h which reside in ~\Micrium-Book-uCOS-II-TWR-K53N512\Micrium\Examples\Freescale\TWR-K53N512\(project name) to ~\MAPSK22_SC\Project\MAPSK22\1-Template\src Summary: configuration files os_cfg.h, app_cfg.h should be adapt to the specific requirements of the application code 3. Copy lib_def.h which resides in ~\Micrium\Software\uC-LIB to ~\MAPSK22_SC\Libraries\drivers\K\inc 4. Adds systick timer initialization function in system_MK22F51212.c void SystemTickInit (void) {   uint32_t cpu_clk_freq;   uint32_t cnts;   cpu_clk_freq = SystemCoreClock;    cnts  = cpu_clk_freq / (uint32_t)OS_TICKS_PER_SEC;            OS_CPU_SysTickInit(cnts);     5. Modify the interrupt vector 6. Create uC/OS-II group in the workspace, then add the uC/OS-II source code and os_cfg.h, app_cfg.h 7. Add application code in the main.c and please check the attachment. 8. Modify the Include Directories    Run the uC/OS-II application After build the modified application code, then run it on MAPS-K22 board(Fig 4). Fig 4 You can find the LED3 and LED4 flash every 2s, however for the LED1 and LED2, it’s 1s. And some informations’re illustrated in the Hyper Terminal (Fig 5)
View full article
Introduction With the growth of the Internet of Things (IoT), more and more applications are incorporating the use of sensors while also requiring power efficiency and increased performance.  A popular interface for these sensors is the I2C protocol. The I2C bus is a great protocol that is a true multi-master protocol and allows for each bus to contain many devices.  As the performance demand of the application grows, so will the speed of the I2C bus as it will be necessary to get more data from the sensors and/or at a faster rate.  Many applications may already have a need to operate an I2C bus at 400 kHz or more.  Higher data rates means the MCU core will need to spend more time servicing the I2C transactions.  The DMA module is one good way to free up the core in order to let it tend to other aspects of the application.  This can add much needed or much desired performance to applications.  Especially applications that may be using small, power efficient, single core MCUs. It may seem like an easy, straight-forward task to add I2C reads from a sensor to an application.  However I2C is a time sensitive protocol and consequently, so is the I2C peripherals on MCUs.  It is important to understand the time requirements and how to overcome them. The recommended approach is to use DMA to transfer the received I2C data to the desired buffer in your application software.  This document is going to outline how to setup your DMA and provide an example of how to do this for a KW40 device using the Kinetis SDK version 1.3.  The KW40 is being targeted because this is a small, power efficient MCU that incorporates a radio for your wireless applications and as such, it is likely that your application could need this DMA approach.  The KSDK version 1.3 is being targeted because this version of the SDK does not currently support DMA transactions for the I2C peripheral. Understanding the Kinetis I2C peripheral module Before getting into the specifics of creating a DMA enabled I2C driver, it is important to understand some basics of the Kinetis I2C peripheral module.  This module handles a lot of the low-level timing.  However the I2C registers must be serviced in a timely manner to operate correctly.  Take the case of a master reading data from a typical I2C sensor as shown in the diagram below. In the diagram above, the red lines indicate points in the transaction where software or DMA needs to interact with the I2C peripheral to ensure the transaction happens correctly.  To begin a transaction the core must change the MST bit which puts a start bit on the bus (marked by symbol ST).  Immediately following this, the core should then also write the target slave's address (Device Address) including the read/write bit (R/W).  Once this transaction is complete, the I2C will issue an interrupt and then the core should write the register address to be read from. Upon completion of that being put on the bus, the I2C will issue another interrupt and the master should then put a repeated start (SR) on the bus as well as the slave's address again.  Now the slave will send data to the master (once the master begins the transaction by issuing a dummy read of the I2C data register).  In the standard configuration, the I2C peripheral will automatically send the NAK or AK depending on the configuration of the TXAK bit in the I2C peripheral.  Because of this automation, it is important that this bit be handled properly and is configured one frame in advance. Furthermore, to ensure that the NAK bit is sent at the appropriate time, the TXAK bit must be set when the second to last byte is received.  The timing of this configuration change is very important to ensuring that the transaction happens properly. This document will describe how to use DMA to receive the data.  The DMA will be configured before the transaction begins and will be used to receive the data from the slave.  The document will also discuss options to handle proper NAK'ing of the data to end the transaction. Writing a DMA I2C master receive function The first step in adding DMA capability to your SDK driver is to create a new receive function with an appropriate name.  For this example, the newly created receive function is named I2C_DRV_MasterReceiveDataDMA.  To create this function, the I2C_DRV_MasterReceive function (which is called for both blocking and non-blocking) was copied and then modified by removing the blocking capability of the function. Then in this function, after the dummy read of the IIC data register that triggers the reception of data, the DMA enable bit of the I2C control register is written. /*FUNCTION********************************************************************** * * Function Name : I2C_DRV_MasterReceiveDataDMA * Description   : Performs a non-blocking receive transaction on the I2C bus *                 utilizing DMA to receive the data. * *END**************************************************************************/ i2c_status_t I2C_DRV_MasterReceiveDataDMA(uint32_t instance,                                                const i2c_device_t * device,                                                const uint8_t * cmdBuff,                                                uint32_t cmdSize,                                                uint8_t * rxBuff,                                                uint32_t rxSize,                                                uint32_t timeout_ms) {     assert(instance < I2C_INSTANCE_COUNT);     assert(rxBuff);       I2C_Type * base = g_i2cBase[instance];     i2c_master_state_t * master = (i2c_master_state_t *)g_i2cStatePtr[instance];             /* Return if current instance is used */     OSA_EnterCritical(kCriticalDisableInt);         if (!master->i2cIdle)     {         OSA_ExitCritical(kCriticalDisableInt);         return kStatus_I2C_Busy;     }         master->rxBuff = rxBuff;     master->rxSize = rxSize;     master->txBuff = NULL;     master->txSize = 0;     master->status = kStatus_I2C_Success;     master->i2cIdle = false;     master->isBlocking = true;     OSA_ExitCritical(kCriticalDisableInt);             while(I2C_HAL_GetStatusFlag(base, kI2CBusBusy));     I2C_DRV_MasterSetBaudRate(instance, device);         /* Set direction to send for sending of address. */     I2C_HAL_SetDirMode(base, kI2CSend);       /* Enable i2c interrupt.*/     I2C_HAL_ClearInt(base);     I2C_HAL_SetIntCmd(base, true);       /* Generate start signal. */     I2C_HAL_SendStart(base);       /* Send out slave address. */     I2C_DRV_SendAddress(instance, device, cmdBuff, cmdSize, kI2CReceive, timeout_ms);       /* Start to receive data. */     if (master->status == kStatus_I2C_Success)     {         /* Change direction to receive. */         I2C_HAL_SetDirMode(base, kI2CReceive);                 /* Send NAK if only one byte to read. */         if (rxSize == 0x1U)         {         I2C_HAL_SendNak(base);         }         else         {         I2C_HAL_SendAck(base);         }                 /* Dummy read to trigger receive of next byte in interrupt. */         I2C_HAL_ReadByte(base);                 /* Now set the DMA bit to let the DMA take over the reception. */         I2C_C1_REG(I2C1) |= I2C_C1_DMAEN_MASK;                 /* Don't wait for the transfer to finish. Exit immediately*/     }     else if (master->status == kStatus_I2C_Timeout)     {         /* Disable interrupt. */         I2C_HAL_SetIntCmd(base, false);                 if (I2C_HAL_GetStatusFlag(base, kI2CBusBusy))         {         /* Generate stop signal. */         I2C_HAL_SendStop(base);         }                 /* Indicate I2C bus is idle. */         master->i2cIdle = true;     }         return master->status; } After writing the DMA driver, a DMA specific transfer complete function must be implemented. This is needed in order for the application software to signal to the driver structures that the transfer has been completed and the bus is now idle. In addition, the DMA enable bit needs to be cleared in order for other driver functions to be able to properly use the IIC peripheral. void I2C_DRV_CompleteTransferDMA(uint32_t instance) {     assert(instance < I2C_INSTANCE_COUNT);     I2C_Type * base = g_i2cBase[instance];     i2c_master_state_t * master = (i2c_master_state_t *)g_i2cStatePtr[instance];         I2C_C1_REG(base) &= ~(I2C_C1_DMAEN_MASK | I2C_C1_TX_MASK);     I2C_C1_REG(base) &= ~I2C_C1_MST_MASK;;        /* Indicate I2C bus is idle. */     master->i2cIdle = true; } DMA Configuration Next, the application layer needs a function to configure the DMA properly, and a DMA callback is needed to properly service the DMA interrupt that will be used as well as post a semaphore. But before diving into the specifics of that, it is important to discuss the overall strategy of using the DMA in this particular application. After every transaction, the data register must be serviced to ensure that all of the necessary data is received.  One DMA channel can easily be assigned to service this activity.  After the reception of the second to last data byte, the TXAK bit must be written with a '1' to ensure that the NAK is put on the bus at the appropriate time. This is a little trickier to do.  There are three options: A second dedicated DMA channel can be linked to write the I2C_C1 register every time the I2C_D register is serviced.  This option will require a second array to hold the appropriate values to be written to the I2C_C1 register.  The following figure illustrates this process. The second DMA channel can be linked to write the I2C_C1 register after the second to last data byte has been received.  This option would require that the primary DMA channel be set to receive two data bytes less than the total number of desired data bytes.  The primary DMA channel would also need to be re-configured to receive the last two bytes (or the application software would need to handle this).  However this could be a desirable programming path for applications that are memory constrained as it reduces the amount of memory necessary for your application. The primary DMA channel can be set to receive two data bytes less than the total number of desired data bytes and the core (application software) can handle the reception of the last two bytes.  This would be a desirable option for those looking for a simpler solution but has the drawback that in a system where the core is already handling many other tasks, there may still be issues with writing the TXAK bit on time. This example will focus on option number 1, as this is the simplest, fully automatic solution.  It could also easily be modified to fit the second option as the programmer would simply need to change the number of bytes to receive by the primary DMA and add some reconfiguration information in the interrupt to service the primary DMA channel. DMA Channel #1 The first DMA channel is configured to perform 8-bit  transfers from the I2C data register (I2C_D) to the buffer to hold the desired data.  This channel should transfer the number of desired bytes minus one.  The final byte will be received by the core.  Other DMA configuration bits that are important to set are the cycle steal bit, disable request bit, peripheral request bit (ERQ), interrupt on completion of transfer (EINT), and destination increment (DINC).  It also important to configure the link channel control to perform a link to channel LCH1 after each cycle-steal transfer and LCH1 should be configured for the channel that will transfer from memory to the I2C control register (I2C_C1).  The first DMA channel is configured as shown below. // Set Source Address (this is the UART0_D register       DMA_SAR0 = (uint32_t)&I2C_D_REG(base);             // Set BCR to know how many bytes to transfer       // Need to set to desired size minus 1 because the last will be manually       // read.        DMA_DSR_BCR0 = DMA_DSR_BCR_BCR(destArraySize - 1);             // Clear Source size and Destination size fields.        DMA_DCR0 &= ~(DMA_DCR_SSIZE_MASK                     | DMA_DCR_DSIZE_MASK                     );       // Set DMA as follows:       //     Source size is byte size       //     Destination size is byte size       //     D_REQ cleared automatically by hardware       //     Destination address will be incremented after each transfer       //     Cycle Steal mode       //     External Requests are enabled       //     Interrupts are enabled       //     Asynchronous DMA requests are enabled.       //     Linking to channel LCH1 after each cycle steal transfer       //     Set LCH1 to DMA CH 1.        DMA_DCR0 |= (DMA_DCR_SSIZE(1)             // 1 = 8-bit transfers                    | DMA_DCR_DSIZE(1)           // 1 = 8-bit transfers                    | DMA_DCR_D_REQ_MASK                    | DMA_DCR_DINC_MASK                    | DMA_DCR_CS_MASK                    | DMA_DCR_ERQ_MASK                    | DMA_DCR_EINT_MASK                    | DMA_DCR_EADREQ_MASK                    | DMA_DCR_LINKCC(2)          // Link to LCH1 after each cycle-steal transfer                    | DMA_DCR_LCH1(1)            // Link to DMA CH1                    );       // Set destination address       DMA_DAR0 = (uint32_t)destArray; DMA Channel #2 The second DMA channel, which is the linked channel, should be configured to perform 8-bit transfers that transfer data from an array in memory (titled ack_nak_array in this example) to the I2C control register (I2C_C1).  This channel should also disables requests upon completion of the entire transfer, and enable the cycle-steal mode.  In this channel, the source should be incremented (as opposed to the destination as in the first channel). This channel is configured as shown below: // Set Source Address (this is the UART0_D register       DMA_SAR1 = (uint32_t)ack_nak_array;             // Set BCR to know how many bytes to transfer       // Need to set to desired size minus 1 because the last will be manually       // read.       DMA_DSR_BCR1 = DMA_DSR_BCR_BCR(destArraySize - 1);             // Clear Source size and Destination size fields.        DMA_DCR1 &= ~(DMA_DCR_SSIZE_MASK                     | DMA_DCR_DSIZE_MASK                     );             // Set DMA as follows:       //     Source size is byte size       //     Destination size is byte size       //     D_REQ cleared automatically by hardware       //     Destination address will be incremented after each transfer       //     Cycle Steal mode       //     External Requests are disabled       //     Asynchronous DMA requests are enabled.       DMA_DCR1 |= (DMA_DCR_SSIZE(1)             // 1 = 8-bit transfers                    | DMA_DCR_DSIZE(1)           // 1 = 8-bit transfers                    | DMA_DCR_D_REQ_MASK                    | DMA_DCR_SINC_MASK                    | DMA_DCR_CS_MASK                    | DMA_DCR_EADREQ_MASK                    );             // Set destination address       DMA_DAR1 = (uint32_t)&I2C_C1_REG(base); Once the DMA channels are initialized, the only action left is to configure the interrupts, enable the channel in the DMA MUX, and create the semaphore if it has not already been created.  This is done as shown below. //Need to enable the DMA IRQ       NVIC_EnableIRQ(DMA0_IRQn);       //////////////////////////////////////////////////////////////////////////       // MUX configuration       // Enables the DMA channel and select the DMA Channel Source        DMAMUX0_CHCFG0 = DMAMUX_CHCFG_SOURCE(BOARD_I2C_DMAMUX_CHN); //DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(0x31); //0xb1;       DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;             /* Create semaphore */       if(semDmaReady == NULL){         semDmaReady = OSA_EXT_SemaphoreCreate(0);       } Finally, the DMA initialization function also initializes the ack_nak_array.  This is only necessary when implementing the first DMA strategy.  The second DMA strategy would only need to write a single value at the correct time.  The array initialization for strategy #1 is shown below.  Note that the values written to the array are 0xA1 plus the appropriate value of the TXAK bit.  By writing 0xA1, it is ensured that the I2C module will be enabled in master mode with the DMA enable bit set. // Initialize Ack/Nak array       // Need to initialize the Ack/Nak buffer first       for( j=0; j < destArraySize; j++)       {           if(j >= (destArraySize - 2))           {               ack_nak_array[j] = 0xA1 | I2C_C1_TXAK_MASK;           }           else           {               ack_nak_array[j] = 0xA1 & (~I2C_C1_TXAK_MASK);           }       } DMA Interrupt Handler Now a DMA interrupt handler is required.  A minimum of overhead will be required for this example as the interrupt handler simply needs to service the DONE bit and post the semaphore created in the initialization.  The DMA interrupt handler is as follows: void DMA0_IRQHandler(void) {     // Clear pending errors or the done bit     if (((DMA_DSR_BCR0 & DMA_DSR_BCR_DONE_MASK) == DMA_DSR_BCR_DONE_MASK)         | ((DMA_DSR_BCR0 & DMA_DSR_BCR_BES_MASK) == DMA_DSR_BCR_BES_MASK)         | ((DMA_DSR_BCR0 & DMA_DSR_BCR_BED_MASK) == DMA_DSR_BCR_BED_MASK)         | ((DMA_DSR_BCR0 & DMA_DSR_BCR_CE_MASK) == DMA_DSR_BCR_CE_MASK))     {         // Clear the Done MASK and set semaphore, dmaDone         DMA_DSR_BCR0 |= DMA_DSR_BCR_DONE_MASK;         //dmaDone = 1;         OSA_SemaphorePost(semDmaReady);     } } Using your newly written driver function Once all of these items have been taken care of, it is now time for the application to use the functions. It is expected that the DMA will be initialized before calling the DMA receive function.  After the first call, the DMA can be re-initialized every time or could simply be reset with the start address of the arrays and byte counter (this is the minimum of actions that must be performed).  Then the application should ensure that the transaction happened successfully.   Upon a successful call to the I2C_DRV_MasterReceiveDataDMA function, the application should wait for the semaphore to be posted.  Once the semaphore posts, the application software should wait for the Transfer Complete flag to become set.  This ensures that the application does not try to put a STOP signal on the bus before the NAK has been physically put on the bus.  If the STOP signal is attempted out of sequence, the I2C module could be put in an erroneous state and the STOP signal may not be sent.  Next, the I2C_DRV_CompleteTransferDMA function should be called to send the STOP signal and to signal to the driver structures that the bus is idle.  At this point, the I2C transaction is now fully complete and there is still one data byte that hasn't been transferred to the receive buffer.  It is the application's responsibility to perform one last read of the Data register to receive the last data byte of the transaction. /* Now initialize the DMA */    dma_init(BOARD_I2C_INSTANCE, Buffer, ack_nak_buffer, FXOS8700CQ_READ_LEN); //Init DMAMUX       returnValue = I2C_DRV_MasterReceiveDataDMA(BOARD_I2C_INSTANCE, &slave,                                                   cmdBuff, 1, Buffer, FXOS8700CQ_READ_LEN, 1000); if (returnValue != kStatus_I2C_Success)    {        return (kStatus_I2C_Fail);    } /* Wait for the DMA transaction to complete */    OSA_SemaphoreWait(semDmaReady, OSA_WAIT_FOREVER);       /* Need to wait for the transfer to complete */ for(temp=0; temp<250; temp++)     {         if(I2C_HAL_GetStatusFlag(base, kI2CTransferComplete))         {             break;         }     }       /* Now complete the transfer; this includes sending the I2C STOP signal and       clearing the DMA enable bit */    I2C_DRV_CompleteTransferDMA(BOARD_I2C_INSTANCE);       // Once the Transfer is complete, there is still one byte sitting in the Data    // register.      Buffer[11] = I2C_D_REG(g_i2cBase[BOARD_I2C_INSTANCE]); Conclusion To summarize, as consumers demand more and more power efficient technology with more and more functionality, MCU product developers need to cram more functionality in small power efficient MCUs.  Relying on DMA for basic data transfers is one good way to improve performance of smaller power efficient MCUs with a single core. This can be particularly useful in applications where an MCU needs to pull information from and I2C sensor.  To do this, there are three methods of implementing an I2C master receive function in your SDK 1.3 based application. Use two DMA channels.  The first to transfer from the I2C Data register to the destination array.  A second dedicated DMA channel can be linked to write the I2C_C1 register every time the I2C_D register is serviced. Use two DMA channels.  The first to transfer from the I2C Data register to the destination array. The second DMA channel can be linked to write the I2C_C1 register only after the second to last data byte has been received. Use a single DMA channel can be set to receive two data bytes less than the total number of desired data bytes and the core (application software) can handle the reception of the last two bytes. The recommendation of this document is to implement the first or second option as these are fully automatic options requiring the least intervention by the core.
View full article
I have definitely experienced some of the growing pains of using the Kinetis tools as they have underdone some changes.  I started tinkering with KDS last year when KSDK and MQX were separate packages.  I didn't mess around with it much, other than to prove that I could toggle some GPIO.  I then got more serious with KDS 2.0 and KSDK 1.1 when MQX was integrated into the installer.  I started with simple projects, and eventually got a pretty good demo put together that incorporated ethernet (using lwIP), RS485, Modbus TCP/RTU, motion control, and barcode reading.  Unfortunately, at that time there were some small issues with the KSDK 1.1 which prevented us from being able to easily write applications in C++.  I definitely think better in C++ than in C, so this was a bummer. I was quite excited when the C++ issues were fixed in KSDK 1.2.  So now I need to port my application from C to C++.  At this point, I am faced with two hurdles: Directly porting my currently-working application (written for KDS 2 / KSDK 1.1) doesn't work.  I have written some posts here about it and could use some help solving those problems. lwIP project that was working in KDS 2.0 with MQX and PEx no longer works in KDS 3.0 How do you force PEx to be totally C++ compatible? Adding HardFault handlers in KDS3/KSDK1.2? Figuring out how to call into C++ wrappers This post is about #2, where I believe I have a usable solution.  It's basically covered in Re: How to call C functions that use "restrict" keyword from C ?  but with a small twist or two.  I am currently using KDS 3, KSDK 1.2, and my project requires MQX Standard as well as Processor Expert.When you create a project like mine, you will likely go through the following basic steps: Create new project Enable KSDK and Processor Expert Change osa from BareMetal to MQX Change MQX from Lite to Standard Disable DbgCs1 Enable new fsl_uart in MQX settings and disable its pins Add OS_Task components and other PEx components Specify your CPU type in the C++ compiler settings, as shown below Generate code In addition to main.c, after you generate code, you'll also end up with os_tasks.c.  Your PEx components will have C code added to the Generated Code folder.  At this point, it should be possible to wrap components in C++ classes.  Tonight, I ran a simple test where I wanted one of my MQX tasks to blink an LED.  The LED blink code was wrapped in a simple C++ class, and in order to be able to create the C++ object to call into, you have to call it from C++ code! The solution ends up being pretty simple.  Rename main.c to main.cpp, and rename os_tasks.c to os_tasks.cpp.  Then generate code again.  Click on your Sources folder and hit F5.  You will see that main.c and os_tasks.cpp reappear, because they get re-created.  Right click on each of them and click Resource Configuration -> Exclude from Build. Click Select All, then Close.  This will prevent those files from being compiled.  Note that if you add more OS_Task components, you will need to manually update os_tasks.cpp accordingly. At this point, it's very simple to create a wrapper class and call it.  I wrote one called DebugLed.cpp: #include <DebugLed.h> #include "Cpu.h" #include "gpio_comp.h" namespace Peripherals { DebugLed::DebugLed() {   // TODO Auto-generated constructor stub } DebugLed::~DebugLed() {   // TODO Auto-generated destructor stub } void DebugLed::BlinkGreen() {   GPIO_DRV_SetPinOutput( LEDRGB_GREEN);   OSA_TimeDelay(150);                 /* Example code (for task release) */   GPIO_DRV_ClearPinOutput( LEDRGB_GREEN);   OSA_TimeDelay(150);                 /* Example code (for task release) */ } } /* namespace Peripherals */ (hopefully all of the code shows up when I post this!  I don't see all of it in the preview) Then you can instantiate the DebugLed object before the while(1) in your OS_Task: void Blink_task(os_task_param_t task_init_data) {   /* Write your local variable definition here */   Peripherals::DebugLed led; #ifdef PEX_USE_RTOS   while (1) { #endif     /* Write your code here ... */    led.BlinkGreen(); #ifdef PEX_USE_RTOS     } #endif    } /* END os_tasks */ #ifdef __cplusplus }  /* extern "C" */ #endif Build, debug, and set a breakpoint on your call into your C++ object, and it should hit it! It's a lot easier than I thought it would be.  I figured there would be more manual labor involved with the code generation aspect of it, but it seems to basically boil down to two files, and you don't even need to disable code generation for any of the PEx components, which means you can still use the GUI to change settings if necessary (even though manually changing the header is just as simple). When I get to the office tomorrow, I'll probably start wrapping more complex peripherals, but I really need to figure out the HardFault problem with my lwIP project.  If you have any suggestions, please visit my post: Adding HardFault handlers in KDS3/KSDK1.2? and comment if you can.  I hope my first document here on the Freescale Community was helpful to someone here!
View full article
Hello Kinetis community, High accuracy metering is an essential feature of an electronic power meter application. Metering accuracy is a most important attribute because inaccurate metering can result in substantial amounts of lost revenue. Moreover, inaccurate metering can also undesirably result in overcharging to customers.  The common sources of metering inaccuracies, or error sources in a meter, include the sensor devices, the sensor conditioning circuitry, the Analog FrontEnd (AFE), and the metering algorithm executed either in a digital processing engine or a microcontroller In this post you will find the description of the implementation of a Two Phase Power Meter firmware featuring Kinetis KM34 device using a metering algorithm known as Filter Based Algorithm. The showed firmware was implemented on the hardware design described in the Reference Manual DRM149 (the software implementation shown in the DRM149 is using a more complex metering algorithm called Fast Fourier Transform). Firmware Design The firmware implements the basic functions for an e-meter application such as: Power meter calibration: Performs power meter calibration and stores calibration parameters. Data processing: Read digital values form the AFE and performs scaling. Calculation of quantities: Calculates billing and non-billing quantities HMI control: Updates LCD with the new values and transitions to new LCD screen. Powe Meter Calibration The calibration task runs whenever a non-calibrated power meter is connected to the mains. First it checks if the calibration flag is already stored in the microcontroller flash, if not, then it runs the calibration. More detail about the calibration process can be found in the DRM143 document. The function: int16 CONFIG_CalcCalibData (tCONFIG_FLASH_DATA *ptr)‍‍‍ is in charge of calculating the calibration data and store the calibration flag in the flash. You can refer to the next code section for the complete usage and definition.   /* if calibration data were collected then calibration parameters are       */   /* calculated and saved to flash                                            */   if (CONFIG_CalcCalibData ((tCONFIG_FLASH_DATA *)&ramcfg) == TRUE)     CONFIG_SaveFlash ((tCONFIG_FLASH_DATA *)&ramcfg, ramcfg.flag);‍‍‍‍ The calibration task terminates by storing calibration gains, offsets and phase shift into the flash and by resetting the microcontroller device. Data Processing Reading the phase voltage and phase current samples from the analog front-end (AFE) occurs periodically every 166.6 μs. This task runs on the highest priority level (Level 0) and is triggered asynchronously when the AFE result registers receive new samples. The task reads the phase voltage and phase current samples from the AFE result registers, scales the samples to the full fractional range, and writes the values to the temporary variables for use by the calculation task. While kWh values is being calculated every 6000 Hz the remaining quantities: kVARh, QAVG, PAVG, URMS and IRMS are being calculates every 1500 Hz (666.6 μs). This is according to the decimation factor and to reduce the CPU usage since those values are not needed every AFE sample. The meter definition used for this application can be found in the meterlib2ph_cfg.h files, this file was generated using the Filter-Based Metering Algorithms Configuration Tool, for more information on how to use the tool you can refer to the AN4265 document. This is the Meter configuration: Here you can see the expected error behavior against Frequency: The configuration of the AFE module and channels is the following:   /* Current Phase 1                                                          */   AFE_ChInit (CH0,               AFE_CH_SWTRG_CCM_PGAOFF_CONFIG(DEC_OSR1024),                    0,               PRI_LVL1,               (AFE_CH_CALLBACK)NULL);     /* Current Phase 2                                                          */   AFE_ChInit (CH1,               AFE_CH_SWTRG_CCM_PGAOFF_CONFIG(DEC_OSR1024),               0,               PRI_LVL1,               (AFE_CH_CALLBACK)NULL);     /* Voltage Phase 1                                                          */   AFE_ChInit (CH2,               AFE_CH_SWTRG_CCM_PGAOFF_CONFIG(DEC_OSR1024),                    0,               PRI_LVL1,               (AFE_CH_CALLBACK)NULL);     /* Voltage Phase 2                                                          */     AFE_ChInit (CH3,               AFE_CH_SWTRG_CCM_PGAOFF_CONFIG(DEC_OSR1024),               0,               PRI_LVL1,               (AFE_CH_CALLBACK)afech3_callback);     /* AFE Initialization @ 6 KHz                                               */   AFE_Init    (AFE_MODULE_RJFORMAT_CONFIG(AFE_PLL_CLK, AFE_DIV2, 12.288e6));  ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ As you can see only the CH3, which is measuring the Voltage phase 2, is configured with interrupt. During the CH3 callback the remaining channels are sampled to get the 4 values at the same time and performs scaling: /* measurements callback @ 6000 Hz                                            */ static void afech3_callback (AFE_CH_CALLBACK_TYPE type, int32 result) {   static int cnt_1 = 0;     if (type == COC_CALLBACK)   {      /* Current and Voltage reading                                            */     u24_sample[0] = AFE_ChRead (CH2) << U_SCALE;            /* Voltage 1 reading ... */     i24_sample[0] = AFE_ChRead (CH0) << I_SCALE;            /* Current 1 reading ... */     u24_sample[1] = AFE_ChRead (CH3) << U_SCALE;            /* Voltage 2 reading ... */     i24_sample[1] = AFE_ChRead (CH1) << I_SCALE;            /* Current 2 reading ... */     . . . . }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ Calculation of Quantities Within the CH3 interrupt the metering algorithm functions are called to process the data of the active energy, the calculation task scales the samples using calibration offsets and calibration gains obtained during the calibration phase: METERLIB2PH_ProcSamples removes DC bias from phase voltage and phase current samples together with performing an optional sensor phase shift correction. METERLIB2PH_CalcWattHours recalculates active energy using new voltage and current samples. CONFIG_UpdateOffsets updates offset of the phase voltage and current measurements conditionally. The CH3 calls the next software interrupt, auxcalc_callback where non-billing quantities are calculated: METERLIB2PH_CalcVarHours recalculates reactive energy. METERLIB2PH_CalcAuxiliary recalculates URMS, IRMS, PAVG, QAVG and S auxiliary quantities. You can find more information about the Filter-Based Algorithm function in the AN4265 document.   HMI Control The display_callback task is called every 3 Hz by the auxcalc_callback and is executed on the lowest priority. For this application it update the clock data structure, refresh the watchdog timer and call some metering algorithm functions to read the values of the billing and non-billing quantities: METERLIB2PH_ReadResultsPh1 reads URMS, IRMS, PAVG, QAVG and S auxiliary quantities from phase 1. METERLIB2PH_ReadResultsPh2 reads URMS, IRMS, PAVG, QAVG and S auxiliary quantities from phase 2. A timer interrupt is used to update the the LCD content every 1500 ms. static void lptmr_callback (void) {   lcd_all_off();   /* update menu index                                                    */   menu_fcn[menu_idx]();   if ((++menu_idx) >= DIM(menu_fcn))   {     menu_idx = 0;   } }‍‍‍‍‍‍‍‍‍‍ Results The two-phase hardware has been calibrated using the test equipment ELMA8303. During accuracy calibration and testing, the power meter measured electrical quantities generated by the test bench, calculated active and reactive energies, and generated pulses on the output LEDs; each generated pulse was equal to the active and reactive energy amount kWh (kVARh)/imp3. The deviations between pulses generated by the power meter and reference pulses generated by test equipment defined the measurement accuracy.   The next figure shows the calibration protocol of the power meter. The protocol indicates the results of the power meter calibration performed at 25 °C. The accuracy and repeatability of the measurement for various phase currents and angles between phase current and phase voltage are shown in these graphs. The first graph indicates the accuracy of the active and reactive energy measurement after calibration. The x-axis shows variation of the phase current, and the y-axis denotes the average accuracy of the power meter computed from five successive measurements The second graph (on the bottom) shows the measurement repeatability; i.e. standard deviation of error of the measurements at a specific load point.  You will find attached a ZIP file containing the IAR source code of the application and a PDF file showing the complete results of the protocol. I hope the information helps. Regards, Adrian Cano
View full article
Abstract         Kinetis M series MCU is Freescale’s Metrology microcontrollers based on ARM Cortex M0+ cores. The SPI module of KM provides for full-duplex, synchronous, serial communication between the MCU and peripheral devices, it also has programmable 8 or 16 bit data transmission length, 64bit FIFO mode for data transfers, DMA transmit and receive features, single wire bidirectional mode, etc. This document is mainly use the KM34Z256VLQ7 SPI module realize the erase, program and read operation in external flash MX25L6404EM2I, it also gives sample code of the detail command external flash operation, and at last, print the testing code via UART. 1.SPI pin assignment and basic code (1) SPI pin assignment SPI signal Pin name Description SPI_SS PTD1 Slave select SPI_SCK PTD2 SPI serial clock SPI_MOSI PTD3 Master data out, slave data in SPI_MISO PTD4 Master data in, slave data out External flash MX25L6404EM2I circuit: (2) SPI initialization   SPI initialization configuration the SPI pin, SPI module baud, master or slave mode, module enable, etc. the code just as following: SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK|SIM_SCGC4_SPI1_MASK;                             SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK; void serial_flash_init(void) {     PORTD_PCR1 &= ~PORT_PCR_MUX_MASK;            PORTD_PCR1 |= PORT_PCR_MUX(1) |0X03;                                           //Use PTD1 as SPI0_SS  // configure it as the GPIO     PORTD_PCR2 &= ~PORT_PCR_MUX_MASK;     PORTD_PCR2 |= PORT_PCR_MUX(3) |0X03;                                           //Use PTD2 as SPI0_SCK     PORTD_PCR3 &= ~PORT_PCR_MUX_MASK;     PORTD_PCR3 |= PORT_PCR_MUX(3) |0X03;                                           //Use PTD3 as SPI0_MOSI      PORTD_PCR4 &= ~PORT_PCR_MUX_MASK;     PORTD_PCR4 = PORT_PCR_MUX(3) |0X03;                                            //Use PTD4 as SPI0_MISO     GPIOD_PDDR |=  0X02; // SS pin output     GPIOD_PDOR |=  0X02; //  SS pin high     SPI0_C1 |= SPI_C1_MSTR_MASK; // SPI0 master mode                             SPI0_BR = 0x43;  //SPPR = 4, SPR = 3, bps div = (SPPR+1)*2^(SPR+1) = 80, baudrate= 24Mhz/80=300khz     SPI0_C1 |= SPI_C1_SSOE_MASK;                              SPI0_C1 &= (~SPI_C1_CPHA_MASK);  // clock polarity     SPI0_C1 &= (~SPI_C1_CPOL_MASK);  //clock phase     SPI0_C1 &= (~SPI_C1_LSBFE_MASK);  // LSB:most significant     SPI0_C1 &= (~SPI_C1_SPIE_MASK);                  //Disable RX interrrupt      SPI0_C1 &= (~SPI_C1_SPTIE_MASK);         //Disable the transmit interrupt     SPI0_C2 |= SPI_C2_MODFEN_MASK;             SPI0_C1 |= SPI_C1_SPE_MASK;  // enable SPI module } (3) One byte transfer code uint8 hal_spi_transfer_one_byte(uint8 v) {    int dummy =0;    char buff=0;    while ((SPI0_S & SPI_S_SPTEF_MASK) == 0)  // wait for transmit buffer empty    {                 dummy++;     }    dummy = SPI0_S;    SPI0_DL = v;   // send one byte to transmit buffer    while ((SPI0_S & SPI_S_SPRF_MASK) == 0); // wait ready buffer full    buff = SPI0_DL;  // read one received byte    return buff;         // return the received byte   } 3 Code realization for external flash operation command     At first, refer to the external flash program / erase flow, then I will give the according command code realization one by one. Take flash sector erase flow as an example, the according code is : void hal_spi_dev_flash_erase_sector(uint8 addr) { write_enable();      // WREN command spi_wait(WEL);     // RDSR command and wait WEL=1 hal_spi_transfe_start();    // enable CS pin , CS=0 hal_spi_transfer_one_byte(CMD_SECTOR_ERASE);   // erase one sector (4KByte)command hal_spi_transfer_one_byte(addr>>16);  // address hal_spi_transfer_one_byte(addr>>8); hal_spi_transfer_one_byte(addr>>0); hal_spi_transfe_stop();  // disable CS pin, CS=1 spi_wait(WIP);    // RDSR command and wait WIP=0;   } (1)Write enable (WREN) command : 0X06 static void write_enable(void) {     hal_spi_transfe_start();  // enable CS pin , CS=0     hal_spi_transfer_one_byte(CMD_WRITE_EN);  // Send WREN command     hal_spi_transfe_stop();  // disable CS pin, CS=1   } (2)Read status register (RDSR) sequence: 0X05 static void spi_wait(uint8 CMD) { if(CMD == WEL) while(get_sr()&0x02 != 0x02); // wait until WEL bit =1 else if(CMD == WIP) while(get_sr()&0x01 != 0x00); // wait until WIP bit =0 } static uint8 get_sr(void) {     uint8 v;     hal_spi_transfe_start(); // enable CS pin , CS=0     hal_spi_transfer_one_byte(CMD_GET_SR);  // Send RDSR command     v = hal_spi_transfer_one_byte(0x00); // read states register data     hal_spi_transfe_stop();    // disable CS pin, CS=1     return v;   } (3) Sector erase (SE) sequence: 0X20 hal_spi_transfe_start();    // enable CS pin , CS=0 hal_spi_transfer_one_byte(CMD_SECTOR_ERASE);   // erase one sector (4KByte)command hal_spi_transfer_one_byte(addr>>16);  // address hal_spi_transfer_one_byte(addr>>8); hal_spi_transfer_one_byte(addr>>0);   hal_spi_transfe_stop();  // disable CS pin, CS=1 (4) Page program (PP) sequence : 0x02 #define PAGE_SIZE 256       hal_spi_transfe_start();// enable CS pin , CS=0 hal_spi_transfer_one_byte(CMD_PROGRAM); //send flash program command hal_spi_transfer_one_byte(addr>>16); // flash page base address hal_spi_transfer_one_byte(addr>>8); hal_spi_transfer_one_byte(addr>>0); for(i=0;i<(PAGE_SIZE-1);i++) // send program data to the flash page hal_spi_transfer_one_byte(buf[i]); hal_spi_transfer_one_byte(buf[i]);   hal_spi_transfe_stop();// disable CS pin, CS=1 (5) Read at higher Speed(FAST_READ) Sequence: 0X0B void hal_spi_dev_flash_read_page(uint8 addr, char *buf) { int i; hal_spi_transfe_start();  // enable CS pin , CS=0 hal_spi_transfer_one_byte(CMD_READ); // read command hal_spi_transfer_one_byte(addr>>16);  // base address hal_spi_transfer_one_byte(addr>>8); hal_spi_transfer_one_byte(addr>>0); hal_spi_transfer_one_byte(0x00); // dummy byte for(i=0;i<(PAGE_SIZE-1);i++)    // read data back from the flash buf[i] = hal_spi_transfer_one_byte(0x00); buf[i] = hal_spi_transfer_one_byte(0x00); hal_spi_transfe_stop();  // disable CS pin, CS=1   } 4 Experimental result The test code function is to realize one sector (4KB) erasing, then read one page (256Byte) and print it out, after that, program one page , read and print it out to check the data. (1)The main function code is : static char buf[256]; int i; serial_flash_init();  // SPI initialization hal_spi_dev_flash_erase_sector(0); // erase one sector(4KByte) printf("reading page...\n"); hal_spi_dev_flash_read_page(0,buf); // read one page(256Byte) print_buf(buf,PAGE_SIZE);  // print the read data out printf("programing a page...\n"); for(i=0;i<256;i++) buf[i] = i;     // define the data which will write to the flash hal_spi_dev_flash_program_page(0,buf); // write 256BYTE to the flash page0 printf("clearing buffer..\n"); for(i=0;i<256;i++)    // clear buff buf[i] = 0; printf("reading page...\n"); hal_spi_dev_flash_read_page(0,buf); // read the page0 data out print_buf(buf,PAGE_SIZE);  // print the read data out   printf("demo end.\n"); (2) print test data reading page... 0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  0xFF,0xFF,0xFF,0xFF,  programing a page... clearing buffer.. reading page... 0x0,0x1,0x2,0x3,  0x4,0x5,0x6,0x7,  0x8,0x9,0xA,0xB,  0xC,0xD,0xE,0xF,  0x10,0x11,0x12,0x13,  0x14,0x15,0x16,0x17,  0x18,0x19,0x1A,0x1B,  0x1C,0x1D,0x1E,0x1F,  0x20,0x21,0x22,0x23,  0x24,0x25,0x26,0x27,  0x28,0x29,0x2A,0x2B,  0x2C,0x2D,0x2E,0x2F,  0x30,0x31,0x32,0x33,  0x34,0x35,0x36,0x37,  0x38,0x39,0x3A,0x3B,  0x3C,0x3D,0x3E,0x3F,  0x40,0x41,0x42,0x43,  0x44,0x45,0x46,0x47,  0x48,0x49,0x4A,0x4B,  0x4C,0x4D,0x4E,0x4F,  0x50,0x51,0x52,0x53,  0x54,0x55,0x56,0x57,  0x58,0x59,0x5A,0x5B,  0x5C,0x5D,0x5E,0x5F,  0x60,0x61,0x62,0x63,  0x64,0x65,0x66,0x67,  0x68,0x69,0x6A,0x6B,  0x6C,0x6D,0x6E,0x6F,  0x70,0x71,0x72,0x73,  0x74,0x75,0x76,0x77,  0x78,0x79,0x7A,0x7B,  0x7C,0x7D,0x7E,0x7F,  0x80,0x81,0x82,0x83,  0x84,0x85,0x86,0x87,  0x88,0x89,0x8A,0x8B,  0x8C,0x8D,0x8E,0x8F,  0x90,0x91,0x92,0x93,  0x94,0x95,0x96,0x97,  0x98,0x99,0x9A,0x9B,  0x9C,0x9D,0x9E,0x9F,  0xA0,0xA1,0xA2,0xA3,  0xA4,0xA5,0xA6,0xA7,  0xA8,0xA9,0xAA,0xAB,  0xAC,0xAD,0xAE,0xAF,  0xB0,0xB1,0xB2,0xB3,  0xB4,0xB5,0xB6,0xB7,  0xB8,0xB9,0xBA,0xBB,  0xBC,0xBD,0xBE,0xBF,  0xC0,0xC1,0xC2,0xC3,  0xC4,0xC5,0xC6,0xC7,  0xC8,0xC9,0xCA,0xCB,  0xCC,0xCD,0xCE,0xCF,  0xD0,0xD1,0xD2,0xD3,  0xD4,0xD5,0xD6,0xD7,  0xD8,0xD9,0xDA,0xDB,  0xDC,0xDD,0xDE,0xDF,  0xE0,0xE1,0xE2,0xE3,  0xE4,0xE5,0xE6,0xE7,  0xE8,0xE9,0xEA,0xEB,  0xEC,0xED,0xEE,0xEF,  0xF0,0xF1,0xF2,0xF3,  0xF4,0xF5,0xF6,0xF7,  0xF8,0xF9,0xFA,0xFB,  0xFC,0xFD,0xFE,0xFF,    demo end From the print data, we can find the code can realize flash sector erasing , flash program and flash data read out, and the test result is correct. The following wave is the page read data out after flash page program. The attachment is the testing code.
View full article
The SPI bus has the capability of addressing multiple slave devices by a single master. The Kinetis L series of devices feature either an 8-bit or 16-bit capable SPI module; however, there is only one dedicated CS/SS signal per instance of the module. Of course this signal is muxed to a few pin locations on the device. Unfortunately, there are not that many pins with the CS/SS muxing and they are most likely they are not near to each other physically. A solution to this issue is to use GPIO as CS/SS lines. This way you can take advantage of the SPI bus protocol and the Kinetis L series IOPORT interface (also known as FGPIO on Kinetis L). The Cortex-M0+ allows accesses to the IOPORT to occur in parallel with any instruction fetches; therefore, these accesses will complete in a single cycle. Core vs. SPI I'm sure many who have tried to use GPIO as CS/SS have written code similar to this pseudo code, I know I have: while(1) {      set_cs_low;      send_byte;      set_cs_high; } Logically this makes sense, but on an oscilloscope you will see the GPIO CS/SS line toggling at irregular intervals and out of sync with the SPI transfers. This is due to the nature of the 'send_byte' function or instruction. Simply transmitting a data packet will not prevent the core from waiting for the transmission to complete. The core will move on from writing data to the SPI data register, and execute the next instruction. If you have a core operating at 48 MHz and you are performing, at most depending on instance, 24 MHz SPI transfers the core will always move onto the next instruction before the data has left the module. The code must either implement a delay or wait for the transmission to complete. Incorporating an accurate delay can be tricky and can be interrupted by any interrupts occurring during the delay process. A more robust solution is to wait for the transmission to complete. However, there appears to be no Transmit Complete Flag (TCF) in the L-Series SPI module. The Solution Fortunately, there is a way to wait for transmit complete. Software must wait for the SPI read buffer full flag (SPRF) to be set in the SPI status register (SPIx_S) after writing data to the SPI data register (SPIx_D) . When the SPRF bit is set, software must read the SPIx_D. This procedure will ensure that the core does not move onto GPIO toggling, or other instructions, until the data has left the SPI module. The following function demonstrates how to write the above procedure in C using SPI0 and PTD0 as the CS/SS line: uint8_t SPI_send(uint8_t spiWrite) {     uint8_t spiRead;                        //Variable for storing SPI data     FGPIOD_PCOR |= (1 << 0);                //Toggle CS/SS line low     while(!(SPI0_S & SPI_S_SPTEF_MASK))     {         __asm("NOP");     }                                       //Wait for SPI transmit empty flag to set     SPI0_D = spiWrite;                            //Write data to SPI     while(!(SPI0_S & SPI_S_SPRF_MASK))     {         __asm("NOP");     }                                       //Wait for receive flag to set     spiRead = SPI0_D;                       //Read the SPI data register     FGPIOD_PSOR |= (1 << 0);                //Toggle CS/SS line high     return spiRead; } Please note that the GPIO CS/SS toggling need not be in the function. It should work just as well if the GPIO CS/SS toggles occur before and after the function is call, just remove the FGPIO instructions from the function and place them outside. I hope this document proves useful to those of you designing multiple slave SPI buses around Kinetis L series parts.
View full article