i.MX RT Crossover MCUs Knowledge Base

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

i.MX RT Crossover MCUs Knowledge Base

Discussions

Sort by:
Frequently, we receive questions related to the DQS pins present on i.MXRT whether how to use it and what it is exactly the function of this as well as why it is important to use it. The goal of this document is answering common questions and expose the most common mistakes when connecting this pin. Lets start defining why DQS signal is helpful for memory interfaces; DQS stands as data strobe and it is the clock signal for the data lines used to solve an issue during the memory read. The controller must first transmit the clock to memory, where it arrives x ns later, then the memory sends data bits to the controller and this takes x nanoseconds. There is a clock skew, which limits how fast you can transmit. On iMXRT family it is present on FlexSPI and SEMC interfaces where you can connect multiple memories, it also allows to have multiple configurations as not all memories provide DQS signal on the memory. The next section will detail the particular configuration on each memory interface, RT1170 data will be used but information on RT10xx family is also applicable on this.  SEMC SEMC has two configurations for DQS pad, on DQSMD register.   For DQSMD = 0: We do not have an exact maximum/minimum for the achievable frequency, we only know that when DQSMD we will not reach the maximum SEMC frequency on SDRAM. There could be variations on the frequency on this mode. It is impossible to run at the max 200MHz 1 and meet this input timing spec on datasheet, so the clock frequency needs to be decreased to ensure you still meet timing., this depends on the data output delay spec for the memory that is being used. For DQSMD = 1: As the signal delay is calculated in DQS pad, 200MHz 1 frequency can be achieved on this mode, please consider that the pin needs to be floating or apply extra capacitance on special cases which will be discussed below. As SDRAM device don't output DQS signal, so it take DQS pad as loopback and measure signals delay, and take this delay to compensate and get the correct data strobe point, this can cover most application case and get the good performance, however, if external signal delay is big, it has the complicated topology and long trace, so it can't take DQS pad delay to compensate external SDRAM signal delay. There are two methods to adjust the delay, the first one is using Delay Chain Control Register(DCCR) while the other one is adding capacitance to the DQS pad; unfortunately there is no formula to calculate the register value and capacitance as this is related to SDRAM signal layout, different layout will get the different signal delay. There are some particular cases where more than 3 SDRAMs were added to RT1xx, since the combined memories capacitance exceeded the pad capacitance there were issues using the memory at the max speed; this was solved by adding extra capacitance to DQS pin.   FlexSPI FlexSPI DQS pins behaves similarly as the one we found on SEMC with some difference on the available configuration and maximum speeds. For FlexSPI device there are three different modes of configuration controlled by the RXCLKSRC field on MCR0 register. RXCLKsrc=0x0 (Internal dummy read strobe and internal loopback) In this mode DQS pin not used so an alternative option for this pin can be configured, however the achieved frequency is the lowest as the timings for highest speeds cannot be achieved.   RXCLKsrc=0x1 (Internal dummy read strobe and loopback from DQS pad) In this mode FlexSPI uses DQS pin and it must be configured for the FlexSPI function, it is not an option to use it for a different purpose in this mode. The internally generated read strobe is sent to the DQS pin and is sampled at the pin to match more closely the data pin timings. The timing for sampling with an internal dummy read strobe loopback is very similar to the timing for loopback from pad but it can achieve a higher frequency than loopbacking internally however not the highest one. Similarly to the described on the SEMC side, there are some special cases where signal delay is big, the design has a complicated topology or long traces were the solution is adding extra capacitance to DQS pad, As this is dependent of design there is no formula to calculate the needed capacitance.   RXCLKsrc=0x3 (Flash-memory-provided read strobe) In this mode DQS signal is provided by the connected memory, this mode allows maximum frequency for the memory however only certain memories provide this signal. The FlexSPI controller delays the read strobe for one half cycle of the serial root clock (with DLL), then samples read data with the delayed strobe. Conclusion On i.mxRT family commonly uses external memories to execute code or access important data where good performance on the device is needed. To optimize the access speed of the memory DQS signal is always needed as it may limit the speed rate. 1 Please consult the device specific datasheet for detailed rates.   
View full article
Compared with the RT10xx series, the i.MX RT117x has an additional M4 core, which makes multi-core collaboration possible. The general practice of multi-core operation is to run in independent program data space and communicate through a shared memory space. For example, in the official SDK routine, the M7 code runs in Flash, while the M4 code runs in SRAM, and they communicate from each other via a shared SRAM space, which can ensure the maximum performance. However, during the development stage, customers may need to put both the M7 and M4 codes in external SDRAM for debugging. Although this will affect some performance, it will not perform too many erase and write operations on the flash, which also has certain practical significance.
View full article
 This article provides a generic introduction related to the cryptographical algorithms and HW acceleration. By using i.MX RT117x with related hands-on examples, it aims at helping NXP customers to quickly understand how to use and make a well decision regarding the selection of cryptographic algorithms to use in their products and systems. Note: TL, DR. If the reader has the basic knowledge of the cryptography, please skip to chapter 3. A cryptographic accelerator is a co-processor designed specifically to perform computationally intensive cryptographic operations, there are different names from different chip manufactures. For NXP, ‘CASPER’ on LPC55xx series, but ‘DCP’ or ‘CAAM’ for i.MX and i.MX RT. i.MX RT Name Features i.MXRT10xx   DCP (Data Co-Processor) Symmetric Engines: AES-128 Hash Engines: SHA-1, SHA-256   i.MXRT11xx CAAM  (Cryptographic Acceleration and Assurance Module) Symmetric Engines: AES 128, 192, 256; 3DES, DES; PKHA: RSA, ECDSA,DH,ECDH  Hash Engines: SHA-1, SHA-2, MD5, HMAC Random Number Generation It shows cryptographic features and benchmark performance with 2 examples: Features: CAAM usage in mbedTLS. Performance: Benchmark of HW acceleration or software only   CAAM Features Key Function APIs JobRing0 kCAAM_Sha256 kCAAM_HmacSha1/sha224/384/512 kCAAM_Aes_cbc-128/192/256 RunShaExamples(base,&caamHandle); RunHmacExamples(base,&caamHandle); RunAesCbcExamples(base,&caamHandle); JobRing1 kCAAM_Aes_gcm RunAesGcmExamples(base,&caamHandle); JobRing2 kCAAM_Aes_cbc RunAesCbcExamples(base,&caamHandle); JobRing3 kCAAM_Aes_gcm kCAAM_RNG kCAAM_Red-Block kCAAM_Black-Block kCAAM_CRC RunAesGcmExamples(base,&caamHandle); RunRngExample(base, &caamHandle); RedBlobExample(base, &caamHandle); BlackBlobExample(base, &caamHandle); RunCrcExamples(base, &caamHandle);   Key words: Cryptography, Cryptographic HW Acceleration, i.MX RT   
View full article
Porting JLink RTT to RT595 Porting JLink RTT to RT595         1. Introduction         2. RTT (Real-Time Terminal)         3. Porting                  Steps for Porting         4. Conclusion 1. Introduction For most beginners learning MCU or embedded systems, the first step often involves simple tasks like "lighting up an LED" or a "Hello World" program. Today, we will discuss a topic closely related to "Hello World." Serial output is a highly effective debugging tool, allowing developers to monitor program states, interact with the program, and diagnose issues. This is a familiar friend to anyone engaged in embedded development. The most common approach, as seen in NXP SDK examples, uses a UART peripheral for logging: Initialize the MCU's UART: Configure clock frequency, pin multiplexing, pin settings, baud rate, etc. Open a serial tool on the PC and configure the correct baud rate. Use the UART driver in the project to enable serial logging. This is the simplest and most widely used method for serial output. However, what if the precious UART resource is already occupied? Here's a great alternative: porting SEGGER's RTT (Real-Time Terminal) driver and using the JLink RTT functionality for logging. The biggest advantage of this approach is conserving UART resources! Next, let’s explore the powerful capabilities of JLink RTT. 2. RTT (Real-Time Terminal) RTT, developed by SEGGER, is a real-time terminal solution for interactive communication in embedded applications. Beyond conserving UART resources, RTT offers significant advantages over semi-hosting methods provided by tools like MCUXpresso IDE. RTT allows for high-speed bidirectional data transfer between the MCU and the host without compromising real-time performance. Key features of JLink RTT include: Low Overhead: Efficient data transfer mechanisms ensure minimal impact on target system performance. Real-Time Capability: Developers can output debugging information or receive data from the target system in real time without halting execution. Flexibility: Supports multiple channels for transmitting different types of data, such as debugging logs and performance metrics. OS Independence: Unlike traditional printf debugging methods, RTT can be used on embedded systems without an operating system. JLink RTT typically pairs with JLink debuggers and SEGGER's development tools, providing powerful support for debugging and tracking embedded systems. To try out this functionality, a JLink debugger is essential. Using the classic RT595-EVK as an example, we will demonstrate how to port RTT. 3. Porting The development environment includes the MCUXpresso IDE and the hello_world project from the SDK. The SDK version is not critical. Steps for Porting Locate RTT Resources According to SEGGER's official documentation, RTT resources can be found in the JLink installation directory:   C:\Program Files\SEGGER\JLink\Samples\RTT Copy Required Files Copy the following files to the source folder of the hello_world project: SEGGER_RTT_Syscalls_GCC.c SEGGER_RTT_Conf.h SEGGER_RTT_printf.c SEGGER_RTT.c SEGGER_RTT.h Copy these source files to the source folder of the hello_world project:   Integrate into Project If using Keil or IAR, you may need to add header file dependencies. However, since the RTT files are placed directly in the MCUXpresso project’s source folder, you only need to call the relevant RTT functions in hello_world.c.   Initialize and Configure Buffers Add the following code to initialize RTT and create up/down buffers: SEGGER_RTT_Init(); uint8_t rx_buffer[32], tx_buffer[32]; SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", rx_buffer, sizeof(rx_buffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", tx_buffer, sizeof(tx_buffer), SEGGER_RTT_MODE_NO_BLOCK_SKIP); SEGGER_RTT_SetTerminal(0); SEGGER_RTT_printf(0, "hello world\r\n"); Use RTT for sending: SEGGER_RTT_SetTerminal(0); SEGGER_RTT_printf(0, "hello world\r\n"); Here, after we port the file and add the RTT operation to the source code of hello_world, the code part is ready to be completed. Use JLink RTT Viewer Launch the JLink RTT Viewer program, select the appropriate device number, run the program, and open "Terminal 0" to view the output.   4. Conclusion Compared to traditional UART-based logging, utilizing the debugger’s built-in RTT functionality reduces peripheral usage and eliminates the need for UART initialization and configuration. With JLink, RTT is essentially plug-and-play, providing convenient and fast logging and interaction. In addition to basic functionality, SEGGER offers advanced features such as changing font colors. Explore more on SEGGER's official website: SEGGER RTT Documentation   For Chinese version and demo project, please check this link: https://www.nxpic.org.cn/module/forum/forum.php?mod=viewthread&tid=803638&fromuid=3253523
View full article
Updating Firmware via USB DFU Based on RT1170 Updating Firmware via USB DFU Based on RT1170         Development Environment         Preparing dfu-util                  Steps to Prepare dfu-util         Running the Demo                  Using Prebuilt Firmware from SDK                  Using Custom Firmware Performing microcontroller (MCU) firmware upgrades in the field without the aid of external programming tools is a necessary feature. For MCUs that support USB device controllers, the USB Device Firmware Update (DFU) class offers a solution. the USB_DFU bootloader requires only a PC and a USB cable. The RT series also provides this feature. In the case of the RT1170, for example, a DFU project is provided in the SDK under the USB class. The project is based on the MCUXpresso IDE. by running the dev_dfu_freertos_cm7 project in the SDK, the RT1170 will be enumerated as a dfu device, and after connecting it to the Host PC via another USB cable, the user can use the “dfu-util” utility to download a firmware to this device. Development Environment Software Environment: SDK Version: 2.15.000 IDE: MCUXpresso IDE Demo Project: dev_dfu_freertos_cm7 Host Software: dfu-util Download link: dfu-util For Windows 64-bit: Download dfu-util-0.9-win64.zip   Hardware Environment: Board: RT1170-EVKB   Preparing dfu-util dfu-util is used to download Firmware to a DFU device, but it does not add CRC32 to the Firmware. Since the DFU demo in the SDK verifies the CRC32 to ensure the Firmware written to Flash is free from bit errors, modifications to the dfu-util source code are necessary. Steps to Prepare dfu-util Install Dependencies sudo apt-get build-dep libusb-1.0-0 dfu-util sudo apt-get install gcc-mingw-w64-x86-64 Download dfu-util and libusb Source Code git clone https://git.code.sf.net/p/dfu-util/dfu-util git clone https://github.com/libusb/libusb.git Modify CRC Code in Source Modify the dfu_store_file function in dfu_file.c to add CRC32 to the Firmware suffix. /* write suffix, if any */ if (write_suffix) {     uint8_t dfusuffix[DFU_SUFFIX_LENGTH];     dfusuffix[0] = file->bcdDevice & 0xff;     dfusuffix[1] = file->bcdDevice >> 8;     dfusuffix[2] = file->idProduct & 0xff;     dfusuffix[3] = file->idProduct >> 8;     dfusuffix[4] = file->idVendor & 0xff;     dfusuffix[5] = file->idVendor >> 8;     dfusuffix[6] = file->bcdDFU & 0xff;     dfusuffix[7] = file->bcdDFU >> 8;     dfusuffix[8] = 'U';     dfusuffix[9] = 'F';     dfusuffix[10] = 'D';     dfusuffix[11] = DFU_SUFFIX_LENGTH;     /*crc = dfu_file_write_crc(f, crc, dfusuffix,     DFU_SUFFIX_LENGTH - 4);*/     dfusuffix[12] = crc;     dfusuffix[13] = crc >> 8;     dfusuffix[14] = crc >> 16;     dfusuffix[15] = crc >> 24;     crc = dfu_file_write_crc(f, crc, dfusuffix +     12, 4); }   Build libusb mkdir -p build cd libusb-1.0.24 ./autogen.sh PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure --host=x86_64-w64-mingw32 --prefix=$PWD/../build make make install cd .. Build dfu-util cd dfu-util-0.11 ./autogen.sh PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure --host=x86_64-w64-mingw32 --prefix=$PWD/../build make make install cd .. After these steps, the newly built tool will be located in the /build/bin folder.   Open cmd for Windows. Run the following command with the new dfu-suffix.exe and CRC32 will be added to the Firmware. dfu-suffix.1 exe -a your_Firmware   Running the Demo Using Prebuilt Firmware from SDK The SDK provides a prebuilt Firmware binary (dev_hid_mouse_bm.bin) that already includes CRC32. Follow these steps: Use MCUXpresso IDE to flash the dev_dfu_freertos_cm7 demo to the EVKB board.     Connect the board to the Host PC via USB.   In the USB Device Descriptor, we find the Vendor ID and Product ID:     Run the following command to download the Firmware: dfu-util.exe -d <your_vid:pid> -D <your_Firmware> After downloading, the DFU demo will verify the CRC32 and execute the new Firmware in RAM. The device will be enumerated as a USB mouse, moving in a rectangular pattern on the screen. Using Custom Firmware When using custom Firmware, ensure that the image is loaded at the correct address (e.g., 0x10000). If the offset is incorrect, the DFU demo will fail to load the Firmware, even if the CRC check passes.   To build and load custom Firmware: Import the hello_world_cm7 project into MCUXpresso IDE. In the Managed Linker Script settings, enable "Link application to RAM".   Adjust memory settings to match the DFU project requirements, ensuring ITCM is the first RAM region.   Build the project and generate a binary file.   Use the modified dfu-util tool to append CRC32 to the binary and download it to the board. Verify that the custom Firmware executes correctly. CRC Added:   New Firmware loaded successfully:   For Chinese version and demo, please check this link:  https://www.nxpic.org.cn/module/forum/forum.php?mod=viewthread&tid=803149&fromuid=3253523
View full article
There are two main methods for importing a project from GUI Guider to MCUXpresso: Linking the whole GUI Guider project into MCUXpresso. Copying and replacing the GUI on a pre-built LVGL project on MCUXpresso (like the "lvgl_guider" SDK example code). Although the first method is quite convenient, there are times when a user might have a GUI already on an established project. In this case, the second method might be very useful. However, when trying to add lottie widgets to a GUI of an already established project (like the "lvgl_guider" SDK example code), extra steps are required, as this widget uses a proprietary library from Samsung which requires extra steps to add and enable. This document describes the steps needed to add rlottie widgets to a project that is already established in MCUXpresso. GUI Guider 1.8.0, MCUXpresso v11.10.0 and SDK 2.16.000 were used for this document, although the process should be the same for future versions.   Once the Lottie widget has been added to the GUI on GUI Guider, you will want to follow the common steps to import this GUI into the MCUXPresso project. Replace the "custom" and "generated" folders on the MCUXpresso project with the GUI Guider folders: <GUI Guider Project Installation>\custom. <GUI Guider Project Installation>\generated.   TIP: You can open the default location of the MCUXpresso project on the file explorer by selecting the project, opening the "Show In" window by pressing Alt + Shift + W, and selecting "System Explorer":   TIP: You can open the default location of the GUI Guider project on the file explorer by clicking on the green folder icon on the top menu bar:   Copy the "lib" folder from: <GUI Guider Project Installation>\lib into the MCUXpresso project.   Copy the "rlottie" folder from: <GUI Guider Project Installation>\sdk\core\rlottie into the MCUXpresso project.   That’s it for file management. Now, in MCUXpresso: Include the "lib" and "rlottie" folders as source folders by adding their path under: Project properties > C/C++ General > Paths and Symbols > Source Location.   Include the rlottie folder as include path by adding its path under the following two compilers' include paths: Project properties > C/C++ Build > Settings > MCU C++ Compiler > Includes > Include Paths.   Project properties > C/C++ Build > Settings > MCU C Compiler > Includes > Include Paths.   As mentioned on the LVGL documentation for "Rlottie player", we need to add the "-rlottie" flag to the linker, but also link the rlottie library (librlottie.a) to the project. This is done by setting the following on Project Properties > C/C++ Build > Settings > MCU C++ Linker > Libraries:   Finally, enable the macro definition: #define LV_USE_RLOTTIE 1 under the "lv_conf.h" file on "source" to tell LVGL that we are using the rlottie library.   With these steps, the rlottie application was imported, along with its headers and libraries, and this rlottie feature was enabled by linking them to the build configuration. Because of this, the application compiles without any errors. Great! Note: There's a possibility that the following error shows up when compiling: If this is the case, simply change the following macro in "source" > "lv_conf.h" from '0' to '1' to enable user data in the lv_font_t variable type:   However, when executing the application, the screen goes black. Turns out, as soon as the application tries to execute the first rlottie instruction from the ".a" archived library, it is unable to execute anything, which causes the application to halt and get stuck on a black screen. This happens as soon as the application calls line 113 of the "lv_rlottie.c" file to construct the rlottie widget: (This file is under <project folder>\lvgl\lvgl\src\extra\libs\rlottie)   But there was no issue when building the application, so what gives? Well, the Rlottie library is quite memory heavy, so we also need to provide it with memory according to its requirements. We can do this by increasing the heap and stack size from their "default" state to something like 0x800000 for the stack and 0x1000 for the heap. These values are what GUI Guider provides to its projects when using Rlottie widgets.   With this, the MCUXpresso project will now have the rlottie libraries enabled, and also have enough memory to successfully debug/run the project on the i.MX RT board.       Happy "Lottie-ing"!   Edwin.
View full article
1. Abstract This article aims to implement the simultaneous input of 4 groups of 48Khz 32bit 2ch audio data on the RT685 platform, and then assemble the received data into a 48Khz 32bit 8ch audio and output it through I2S. This solution is also done at the request of customers, because there are always harmonic problems when customers make it. After analyzing the customer's situation, it is found that the customer has two main problems: (1) Harmonic problem: After receiving 4 channels of 8 bytes, it is directly copied to the sending buffer. This will cause timing problems. It does not take into account the problem of buffering data in the audio data storage pool. The time required to receive enough audio data is at least greater than the time required for copying and sending. Therefore, the problem is reflected in the problem that the customer found harmonic problems when testing the output audio waveform. (2) Audio synchronization problem: After the customer received 4 channels of audio data, he tested the receiving buffer and found that the 4 channels of data were out of sync. Therefore, in order to help customers, I helped customers make this application demo directly, and made a matching test audio source to send a set of 48Khz sampling rate 32bit dual-channel, fixed increment audio data in a loop, such as 0X00-0XFF in a loop. The following is the block diagram of this application platform:   Figure 1 System Block Diagram In the above figure, a MIMXRT685-EVK implements the function of outputting 48Khz, 32bit*2ch, and sends data in a loop: 0X00, 0X01….0XFF. Another MIMXRT685-EVK is the focus of this article, which implements 4 groups of I2S to receive data at a sampling rate of 48khz and 32bit*2ch, and then assembles the received data into audio data with a sampling rate of 48Khz and 32bit*8ch and sends it out. In the above figure, in order to reduce the connection of external lines, for BCLK and WS signals, only one group is directly connected to I2S3, and the other I2S2, I2S4, and I2S5 share the I2S3 signal internally. Then, for DATA data, a line is made externally with 4 heads, and connected to the data pins of each group of audio interfaces respectively. The following is a detailed description of this solution. 2. Hardware platform establish The pinouts of the two boards are given below. Because the platform uses many pins, specific allocation is required. 2.1 Audio source board A MIMXRT685-EVK is used as an audio source, and the pinouts for sending 48Khz 32bit*2ch are as follows:   Figure 2 Pinout of audio source board 2.2 Audio transceiver board Another MIMXRT685-EVK is used as an audio transceiver board to receive 4-channel audio synchronization data sent by the audio source and assemble it into a 48Khz 32bit*8ch waveform for transmission.   Figure 3 Pin assignment of audio transceiver board 2.3 Dual-board hardware connection The source and target connections of the two boards are as follows:   Figure 4 Two board pin connection   Figure 5 two board connection After the hardware is ready, the software solution and code are provided. 3. Software solution and software implementation In the process of writing the code, we tried many solutions, such as: (1) When receiving, directly assemble it into the TDM format buffer to be sent, and then send it. However, since it is assembled into TDM, one I2S needs to receive 8 byte per frame, and then do the offset to receive the next one. If the reception is carried out according to the 8-byte DMA, the callback of the 4 groups of I2S will enter frequently, resulting in a large CPU load, so this solution is abandoned. (2) The 4 groups of I2S are connected to each other, and the 10ms buffer is connected, and then the DMA method is used to copy from memory to memory. However, since the DMA of RT685 is relatively weak, it can only achieve a maximum offset of 32bit 4word=16byte, that is, a 16-byte offset. However, in fact, a group of audio data is 32bit*2, and 4 groups are 32bit*8=32byte offset, so DMA cannot meet the requirements. Therefore, the DMA memory-to-memory copy solution is abandoned and memcpy is used instead. (3) Use the I2S_RxTransferReceiveDMA function to perform DMA reception. However, in fact, when one group is called, it starts receiving directly, and waits until the next group of I2S interfaces calls I2S_RxTransferReceiveDMA. This has caused an asynchronous situation. Even if the I2S enable is turned off in I2S_RxTransferReceiveDMA, the 4th group of I2S is enabled after the several groups of I2S of I2S_RxTransferReceiveDMA are called. This method can only achieve the synchronization of the reception of the first group of data, because later, it is necessary to go to the callback to re-trigger the reception of the second frame of data. Therefore, the callback of the 4 groups of I2S calls I2S_RxTransferReceiveDMA, which will inevitably cause new synchronization problems. Therefore, this method is abandoned and it is considered to use two groups of DMA descriptors to do ping-pong. In this way, the reception will continue in a loop without the intervention of CPU code. 3.1 Solution Implementation Several solutions have been described above. Finally, we choose to use 4 audio channels to receive audio data and cache 10ms audio data buffer. The conversion from receiving buffer to sending buffer adopts memcpy method, and test whether this copy time can meet the actual needs, to ensure that the buffer pool of receiving buffer is greater than this copy time, which is enough to prepare the sending buffer. The solution for receiving data transfer is as follows:   Figure 6 Data buffer transfer The above is 4 groups of I2S receiving their own 10ms data respectively. The buffer is actually prepared for 20ms. A single DMA receives a frame for 10ms, and the other 10ms is used for pingpong buffer. The sending buffer is used to copy the received 4 groups of I2S buffers into a 32bit*8ch array in TDM format, and then two groups of ping-pong buffers are also made. In fact, it is to cache 10ms data. The buffer prepares two groups of 10ms. When the first 10ms frame is received, the second buffer is used to receive it. At the same time, the data of the first buffer is copied to the first buffer of the sending buffer, and the first buffer is used for sending. After the sending is completed, it is transferred to the second buffer to receive and send. In this way, as long as the time is controlled well, there will be no data error problem. The data volume of 10ms is 3840Byte, because the receiving frequency is 48Khz, that is, there are 48000 frames in 1s, and each frame is 32bit*2=8Byte, then 10ms=>4800*8Byte=38400Byte. 3.2 Software code implementation The software code implementation part is mainly divided into 4 I2S receiving signal sharing, I2S DMA pingpong configuration, data transfer, sending I2S and other parts. The details are given below 3.2.1 4-way I2S receiving From the above, we can know that the 4 I2S receiving signal is not completely connected with wires, but adopts the method of sharing BCLK and WS signals and receiving DATA separately. I2S2, I2S4, I2S5 share the BCLK of I2S3, and the WS code is as follows: /* Set shared signal set 0: SCK, WS from Flexcomm1 */ I2S_BRIDGE_SetShareSignalSrc(kI2S_BRIDGE_ShareSet0, kI2S_BRIDGE_SignalSCK, kI2S_BRIDGE_Flexcomm3); I2S_BRIDGE_SetShareSignalSrc(kI2S_BRIDGE_ShareSet0, kI2S_BRIDGE_SignalWS, kI2S_BRIDGE_Flexcomm3); /* Set flexcomm3 SCK, WS from shared signal set 0 */ I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm2, kI2S_BRIDGE_SignalSCK, kI2S_BRIDGE_ShareSet0); I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm2, kI2S_BRIDGE_SignalWS, kI2S_BRIDGE_ShareSet0); I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm4, kI2S_BRIDGE_SignalSCK, kI2S_BRIDGE_ShareSet0); I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm4, kI2S_BRIDGE_SignalWS, kI2S_BRIDGE_ShareSet0); I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm5, kI2S_BRIDGE_SignalSCK, kI2S_BRIDGE_ShareSet0); I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm5, kI2S_BRIDGE_SignalWS, kI2S_BRIDGE_ShareSet0); 3.2.2 I2S DMA pingpong configuration In order to achieve 4-channel audio synchronization and receive 10ms audio buffer, two I2S DMA descriptors are used to implement the ping-pong function to collect data to two ping-pong buffers in turn. The code is as follows: #define I2S_BUFFER_SIZE 3840 //10ms SDK_ALIGN(static dma_descriptor_t I2S2_s_rxDmaDescriptors[2U], FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE); SDK_ALIGN(static dma_descriptor_t I2S3_s_rxDmaDescriptors[2U], FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE); SDK_ALIGN(static dma_descriptor_t I2S4_s_rxDmaDescriptors[2U], FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE); SDK_ALIGN(static dma_descriptor_t I2S5_s_rxDmaDescriptors[2U], FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE); SDK_ALIGN(static uint8_t I2S2_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S3_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S4_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S5_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); static i2s_transfer_t I2S2_s_RxTransfer[2] = {{ .data = I2S2_s_Buffer[0], .dataSize = I2S_BUFFER_SIZE, }, { .data = I2S2_s_Buffer[1], .dataSize = I2S_BUFFER_SIZE, }}; static i2s_transfer_t I2S3_s_RxTransfer[2] = {{ .data = I2S3_s_Buffer[0], .dataSize = I2S_BUFFER_SIZE, }, { .data = I2S3_s_Buffer[1], .dataSize = I2S_BUFFER_SIZE, }}; static i2s_transfer_t I2S4_s_RxTransfer[2] = {{ .data = I2S4_s_Buffer[0], .dataSize = I2S_BUFFER_SIZE, }, { .data = I2S4_s_Buffer[1], .dataSize = I2S_BUFFER_SIZE, }}; static i2s_transfer_t I2S5_s_RxTransfer[2] = {{ .data = I2S5_s_Buffer[0], .dataSize = I2S_BUFFER_SIZE, }, { .data = I2S5_s_Buffer[1], .dataSize = I2S_BUFFER_SIZE, }}; I2S_RxGetDefaultConfig(&I2S2_s_RxConfig); I2S2_s_RxConfig.divider = DEMO_I2S_CLOCK_DIVIDER; I2S2_s_RxConfig.masterSlave = DEMO_I2S_TX_MODE;//DEMO_I2S_RX_MODE I2S_RxInit(DEMO_I2S2_RX, &I2S2_s_RxConfig); I2S_RxGetDefaultConfig(&I2S3_s_RxConfig); I2S3_s_RxConfig.divider = DEMO_I2S_CLOCK_DIVIDER; I2S3_s_RxConfig.masterSlave = DEMO_I2S_TX_MODE;//DEMO_I2S_RX_MODE I2S_RxInit(DEMO_I2S3_RX, &I2S3_s_RxConfig); I2S_RxGetDefaultConfig(&I2S4_s_RxConfig); I2S4_s_RxConfig.divider = DEMO_I2S_CLOCK_DIVIDER; I2S4_s_RxConfig.masterSlave = DEMO_I2S_TX_MODE;//DEMO_I2S_RX_MODE I2S_RxInit(DEMO_I2S4_RX, &I2S4_s_RxConfig); I2S_RxGetDefaultConfig(&I2S5_s_RxConfig); I2S5_s_RxConfig.divider = DEMO_I2S_CLOCK_DIVIDER; I2S5_s_RxConfig.masterSlave = DEMO_I2S_TX_MODE;//DEMO_I2S_RX_MODE I2S_RxInit(DEMO_I2S5_RX, &I2S5_s_RxConfig); DMA_Init(DEMO_DMA); DMA_EnableChannel(DEMO_DMA, DEMO_I2S2_RX_CHANNEL); DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S2_RX_CHANNEL, kDMA_ChannelPriority1); DMA_CreateHandle(&I2S2_s_DmaRxHandle, DEMO_DMA, DEMO_I2S2_RX_CHANNEL); I2S_RxTransferCreateHandleDMA(DEMO_I2S2_RX, &I2S2_s_RxHandle, &I2S2_s_DmaRxHandle, I2S2_RxCallback, (void *)&I2S2_s_RxTransfer); DMA_EnableChannel(DEMO_DMA, DEMO_I2S3_RX_CHANNEL); DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S3_RX_CHANNEL, kDMA_ChannelPriority1); DMA_CreateHandle(&I2S3_s_DmaRxHandle, DEMO_DMA, DEMO_I2S3_RX_CHANNEL); I2S_RxTransferCreateHandleDMA(DEMO_I2S3_RX, &I2S3_s_RxHandle, &I2S3_s_DmaRxHandle, I2S3_RxCallback, (void *)&I2S3_s_RxTransfer); DMA_EnableChannel(DEMO_DMA, DEMO_I2S4_RX_CHANNEL); DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S4_RX_CHANNEL, kDMA_ChannelPriority1); DMA_CreateHandle(&I2S4_s_DmaRxHandle, DEMO_DMA, DEMO_I2S4_RX_CHANNEL); I2S_RxTransferCreateHandleDMA(DEMO_I2S4_RX, &I2S4_s_RxHandle, &I2S4_s_DmaRxHandle, I2S4_RxCallback, (void *)&I2S4_s_RxTransfer); DMA_EnableChannel(DEMO_DMA, DEMO_I2S5_RX_CHANNEL); DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S5_RX_CHANNEL, kDMA_ChannelPriority2); DMA_CreateHandle(&I2S5_s_DmaRxHandle, DEMO_DMA, DEMO_I2S5_RX_CHANNEL); I2S_RxTransferCreateHandleDMA(DEMO_I2S5_RX, &I2S5_s_RxHandle, &I2S5_s_DmaRxHandle, I2S5_RxCallback, (void *)&I2S5_s_RxTransfer); I2S_TransferInstallLoopDMADescriptorMemory(&I2S2_s_RxHandle, I2S2_s_rxDmaDescriptors, 2U); I2S_TransferInstallLoopDMADescriptorMemory(&I2S3_s_RxHandle, I2S3_s_rxDmaDescriptors, 2U); I2S_TransferInstallLoopDMADescriptorMemory(&I2S4_s_RxHandle, I2S4_s_rxDmaDescriptors, 2U); I2S_TransferInstallLoopDMADescriptorMemory(&I2S5_s_RxHandle, I2S5_s_rxDmaDescriptors, 2U); if (I2S_TransferReceiveLoopDMA(DEMO_I2S2_RX, &I2S2_s_RxHandle, &I2S2_s_RxTransfer[0], 2U) != kStatus_Success) { assert(false); } if (I2S_TransferReceiveLoopDMA(DEMO_I2S3_RX, &I2S3_s_RxHandle, &I2S3_s_RxTransfer[0], 2U) != kStatus_Success) { assert(false); } if (I2S_TransferReceiveLoopDMA(DEMO_I2S4_RX, &I2S4_s_RxHandle, &I2S4_s_RxTransfer[0], 2U) != kStatus_Success) { assert(false); } if (I2S_TransferReceiveLoopDMA(DEMO_I2S5_RX, &I2S5_s_RxHandle, &I2S5_s_RxTransfer[0], 2U) != kStatus_Success) { assert(false); } I2S_Enable(DEMO_I2S2_RX); I2S_Enable(DEMO_I2S3_RX); I2S_Enable(DEMO_I2S4_RX); I2S_Enable(DEMO_I2S5_RX); Here, the code has been modified, mainly the I2S_TransferLoopDMA function in fsl_i2s_dma.c, which is blocked: I2S_Enable(base); In order to realize the function of 4-channel synchronous reception. 3.2.3 Audio data received and transferred Because when receiving, each audio interface takes turns to receive its own 2ch data, but when sending, it is necessary to send 4-channel received audio dual-channel data, that is, 32bit*8ch data, so after receiving ping, the ping data needs to be transferred to the sending ping buffer. The code for transfer is as follows: #define I2S_BUFFER_SIZE 3840 //10ms SDK_ALIGN(static uint8_t I2S2_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S3_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S4_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S5_s_Buffer[2][I2S_BUFFER_SIZE], sizeof(uint32_t)); SDK_ALIGN(static uint8_t I2S1_s_Buffer[2][I2S_BUFFER_SIZE*4], sizeof(uint32_t)); if( s_pingpong == 1) { for(ch = 0;ch < 480; ch++) //480=I2S_BUFFER_SIZE(3840)/8 { memcpy(&I2S1_s_Buffer[0][0 + (32*ch)], &I2S2_s_Buffer[0][8*ch], 8); memcpy(&I2S1_s_Buffer[0][8 + (32*ch)], &I2S3_s_Buffer[0][8*ch], 8); memcpy(&I2S1_s_Buffer[0][16 + (32*ch)], &I2S4_s_Buffer[0][8*ch], 8); memcpy(&I2S1_s_Buffer[0][24 + (32*ch)], &I2S5_s_Buffer[0][8*ch], 8); } } else { for(ch = 0;ch < 480; ch++) { memcpy(&I2S1_s_Buffer[1][0 + (32*ch)], &I2S2_s_Buffer[1][8*ch], 8); memcpy(&I2S1_s_Buffer[1][8 + (32*ch)], &I2S3_s_Buffer[1][8*ch], 8); memcpy(&I2S1_s_Buffer[1][16 + (32*ch)], &I2S4_s_Buffer[1][8*ch], 8); memcpy(&I2S1_s_Buffer[1][24 + (32*ch)], &I2S5_s_Buffer[1][8*ch], 8); } } 3.2.4 Send TDM audio code The sending code also uses the I2S DMA method, but because there is no need to send multiple channels at the same time, only a single channel, there is no need to consider the synchronization problem, and no DMA descriptor is used. After the sending buffer is ready, the I2S_TxTransferSendDMA method is used. The code is as follows: I2S_TxGetDefaultConfig(&I2S1_s_TxConfig); I2S1_s_TxConfig.divider = DEMO_I2S1_CLOCK_DIVIDER; I2S1_s_TxConfig.masterSlave = kI2S_MasterSlaveNormalMaster; I2S1_s_TxConfig.wsPol = true; I2S1_s_TxConfig.mode = kI2S_ModeDspWsLong;//kI2S_ModeDspWsShort; I2S1_s_TxConfig.dataLength = 32U; I2S1_s_TxConfig.frameLength = 32 * 8U; I2S1_s_TxConfig.position = DEMO_TDM_DATA_START_POSITION; I2S1_s_TxConfig.pack48 = true; I2S_TxInit(DEMO_I2S1_TX, &I2S1_s_TxConfig); I2S_EnableSecondaryChannel(DEMO_I2S1_TX, kI2S_SecondaryChannel1, false, 64 + DEMO_TDM_DATA_START_POSITION); I2S_EnableSecondaryChannel(DEMO_I2S1_TX, kI2S_SecondaryChannel2, false, 128 + DEMO_TDM_DATA_START_POSITION); I2S_EnableSecondaryChannel(DEMO_I2S1_TX, kI2S_SecondaryChannel3, false, 192 + DEMO_TDM_DATA_START_POSITION); DMA_EnableChannel(DEMO_DMA, DEMO_I2S1_TX_CHANNEL); DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S1_TX_CHANNEL, kDMA_ChannelPriority3); DMA_CreateHandle(&I2S1_s_DmaTxHandle, DEMO_DMA, DEMO_I2S1_TX_CHANNEL); I2S_TxTransferCreateHandleDMA(DEMO_I2S1_TX, &I2S1_s_TxHandle, &I2S1_s_DmaTxHandle, I2S1_TxCallback, (void *)&I2S1_s_TxTransfer); if( s_pingpong == 1) { I2S1_s_TxTransfer.data = I2S1_s_Buffer[0]; I2S1_s_TxTransfer.dataSize = I2S_BUFFER_SIZE*4; I2S_TxTransferSendDMA(DEMO_I2S1_TX, &I2S1_s_TxHandle, I2S1_s_TxTransfer); } else { I2S1_s_TxTransfer.data = I2S1_s_Buffer[1]; I2S1_s_TxTransfer.dataSize = I2S_BUFFER_SIZE*4; I2S_TxTransferSendDMA(DEMO_I2S1_TX, &I2S1_s_TxHandle, I2S1_s_TxTransfer); } 3.2.5 Send and receive I2S callback processing For the receiving I2S2, 3, 4, 5, there are 4 channels in total. Each time 10ms of data is received, a callback will be entered. In the callback, you only need to record the flag. When all 4 flags are recorded, it means that the 4 channels of the same 10ms data have been received, and the data can be copied to the sending buffer. Of course, in order to test whether the callback entry frequency is once every 10ms, this article makes a GPIO callback for testing. The following is the code for recording I2S callback static void I2S2_RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData) { s_allRXTriggerred |= 0x01; } static void I2S3_RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData) { s_allRXTriggerred |= 0x02; } static void I2S4_RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData) { s_allRXTriggerred |= 0x04; } static void I2S5_RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData) { /* Enqueue the same original buffer all over again */ s_allRXTriggerred |= 0x08; GPIO_PortToggle(GPIO, 1, 1<<0); if( s_pingpong == 0) { s_pingpong = 1; } else { s_pingpong = 0; } } static void I2S1_TxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData) { GPIO_PortToggle(GPIO, 1, 1<<8); //__NOP(); } So far, all functions of a MIMXRT685-EVK for 4 I2S reception and 1 I2S TDM transmission have been completed. 3.2.6 Audio source code The audio source is made on another MIMXRT685-EVK to send 48Khz, 32bit*2ch audio data, and the data is sent in a loop from 0X00 to 0XFF. The code is as follows: int main(void) { BOARD_InitBootPins(); BOARD_InitBootClocks(); BOARD_InitDebugConsole(); BOARD_I3C_ReleaseBus(); BOARD_InitI3CPins(); CLOCK_EnableClock(kCLOCK_InputMux); /* attach main clock to I3C (500MHz / 20 = 25MHz). */ CLOCK_AttachClk(kMAIN_CLK_to_I3C_CLK); CLOCK_SetClkDiv(kCLOCK_DivI3cClk, 20); /* attach AUDIO PLL clock to FLEXCOMM1 (I2S1) */ CLOCK_AttachClk(kAUDIO_PLL_to_FLEXCOMM1); /* attach AUDIO PLL clock to FLEXCOMM3 (I2S3) */ CLOCK_AttachClk(kAUDIO_PLL_to_FLEXCOMM3); /* attach AUDIO PLL clock to MCLK */ CLOCK_AttachClk(kAUDIO_PLL_to_MCLK_CLK); CLOCK_SetClkDiv(kCLOCK_DivMclkClk, 1); SYSCTL1->MCLKPINDIR = SYSCTL1_MCLKPINDIR_MCLKPINDIR_MASK; wm8904Config.i2cConfig.codecI2CSourceClock = CLOCK_GetI3cClkFreq(); wm8904Config.mclk_HZ = CLOCK_GetMclkClkFreq(); /* Set shared signal set 0: SCK, WS from Flexcomm1 */ I2S_BRIDGE_SetShareSignalSrc(kI2S_BRIDGE_ShareSet0, kI2S_BRIDGE_SignalSCK, kI2S_BRIDGE_Flexcomm1); I2S_BRIDGE_SetShareSignalSrc(kI2S_BRIDGE_ShareSet0, kI2S_BRIDGE_SignalWS, kI2S_BRIDGE_Flexcomm1); /* Set flexcomm3 SCK, WS from shared signal set 0 */ I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm3, kI2S_BRIDGE_SignalSCK, kI2S_BRIDGE_ShareSet0); I2S_BRIDGE_SetFlexcommSignalShareSet(kI2S_BRIDGE_Flexcomm3, kI2S_BRIDGE_SignalWS, kI2S_BRIDGE_ShareSet0); #if 1 PRINTF("Configure codec\r\n"); /* protocol: i2s * sampleRate: 48K * bitwidth:16 */ if (CODEC_Init(&codecHandle, &boardCodecConfig) != kStatus_Success) { PRINTF("codec_Init failed!\r\n"); assert(false); } /* Initial volume kept low for hearing safety. * Adjust it to your needs, 0-100, 0 for mute, 100 for maximum volume. */ if (CODEC_SetVolume(&codecHandle, kCODEC_PlayChannelHeadphoneLeft | kCODEC_PlayChannelHeadphoneRight, DEMO_CODEC_VOLUME) != kStatus_Success) { assert(false); } PRINTF("Configure I2S\r\n"); #endif /* * masterSlave = kI2S_MasterSlaveNormalMaster; * mode = kI2S_ModeI2sClassic; * rightLow = false; * leftJust = false; * pdmData = false; * sckPol = false; * wsPol = false; * divider = 1; * oneChannel = false; * dataLength = 16; * frameLength = 32; * position = 0; * watermark = 4; * txEmptyZero = true; * pack48 = false; */ I2S_TxGetDefaultConfig(&s_TxConfig); s_TxConfig.divider = DEMO_I2S_CLOCK_DIVIDER; s_TxConfig.masterSlave = DEMO_I2S_TX_MODE; I2S_TxInit(DEMO_I2S_TX, &s_TxConfig); DMA_Init(DEMO_DMA); DMA_EnableChannel(DEMO_DMA, DEMO_I2S_TX_CHANNEL); DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S_TX_CHANNEL, kDMA_ChannelPriority3); DMA_CreateHandle(&s_DmaTxHandle, DEMO_DMA, DEMO_I2S_TX_CHANNEL); StartSoundPlayback(); while (1) { } } static void StartSoundPlayback(void) { PRINTF("Setup looping playback of sine wave\r\n"); s_TxTransfer.data = &g_Music[0]; s_TxTransfer.dataSize = sizeof(g_Music); I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX, &s_TxHandle, &s_DmaTxHandle, TxCallback, (void *)&s_TxTransfer); /* need to queue two transmit buffers so when the first one * finishes transfer, the other immediatelly starts */ I2S_TxTransferSendDMA(DEMO_I2S_TX, &s_TxHandle, s_TxTransfer); I2S_TxTransferSendDMA(DEMO_I2S_TX, &s_TxHandle, s_TxTransfer); } static void TxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData) { /* Enqueue the same original buffer all over again */ i2s_transfer_t *transfer = (i2s_transfer_t *)userData; I2S_TxTransferSendDMA(base, handle, *transfer); } Audio data buffer:   Figure 7 Audio source sends buffer The corresponding test results are given:   Figure 8 Audio source sending data test It can be seen that the data sent by the audio source is cyclical and can be sent in an increasing loop. 4. Test results There are several points to verify about the test results: (1) 4-channel audio receives pingpong buffer, whether a single buffer is 10ms, that is, a 10ms audio data pool. (2) How long is the data memory copy time, whether it will exceed the length of the receiving audio data pool. (3) Whether the received 4-channel data is synchronized, whether the assembled send buffer data is the 32bit*8ch data assembled from the corresponding 4-channel 2ch data. (4) Whether the sent audio waveform is the correct 32bit*8ch TDM data. The following are the verification test results for these points. 4.1 4 I2S audio 10ms data pool This verification is very simple. Define a pin GPIO, initialize the output to 0, and then reverse it in the received callback interrupt. This article chooses to reverse it in the I2S5 callback. The test results are as follows:   Figure 9 ch1 10ms duration Channel 1 is the exact 10ms because of the callback reversal received. Here is a general picture of the test:   Figure 10 Time test overview Ch1: I2S5 callback entry frequency Ch2: memory copy time Ch3: Send callback entry frequency It can be seen that the frequency of sending and receiving is 10ms, because the sending frequency is also 48Khz, but because it is 8ch, the data volume is 4 times that of receiving, and all the data of the 4 receiving channels need to be stuffed in. 4.2 Time consumption of receiving and copying to sending buffer For data copying, that is, assembling the data received from 4 I2S channels into 4 buffers into the sending buffer, this time test is on the second channel of the oscilloscope, and the results are as follows:   Figure 11 copy data time It can be seen that the copying time is less than 500us, which is much shorter than the 10ms of the audio receiving data pool. Therefore, you can use memcpy casually without worrying about the copying time being too long. This also makes up for the regret that I wanted to use DMA for memory to memory copying before, but it could not be realized due to DMA performance issues. 4.3 Verification of the synchronization of the data received on the 4 I2S In order to verify the synchronization, this article closes the 4 I2S receiving channel after receiving 100times 10ms, and prints out the corresponding 4 I2S audio receiving buffer. The results of the 4 I2S buffer are as follows:   Figure12 I2S2 receive buffer   Figure 13 I2S3 receive buffer   Figure 14 I2S4 receive buffer   Figure 15 I2S5 receive buffer It can be seen that the receive buffer data of the 4 I2S are completely synchronized, and all start from 0XB8.   4.4 Send buffer corresponding to 4-channel audio TDM The send buffer is printed after 100 receptions, and then memcpy is performed to the send buffer, and the printout of the send buffer data is as follows:   Figure 16 I2S1 transfer buffer It can be seen that the buffer also starts from 0XB8, and the 4 groups of received data are copied to the send buffer and assembled into 32bit*8ch data. It can be seen that the TDM send buffer is also correct.   4.5 Sending 48Khz 32bit 8ch audio data waveform   Figure 17 Transmitting and receiving audio waveforms The upper group is the waveform of the audio source, and the lower group is the waveform of TDM transmission. Due to the limitation of the analysis software of the logic analyzer, it can only analyze 2ch 64bit data at most, so only part of the data can be seen here, but from the waveform, it can be seen that the waveform of sending TDM can achieve 32bit*8ch, and every 8byte data in a frame is the same, which also explains the synchronization of 4-channel audio reception. In the above figure, the data of ch2 is actually 00, 01, 02, 03, 04, 05, 06, 07, 4 groups of the same data in one frame, and the waveform can also be seen that there are 4 groups of the same data, and 4 groups of 2ch are enough to form 32bit 8ch TDM. Finally, here is another TDM waveform tested on the oscilloscope:   Figure 18 Sending TDM waveform It can be seen that BCLK=12.28Mhz is consistent with the expected 48khz*32bit*8=12.288Mhz. The WS signal is also measured to be 48Khz, which meets the set 48Khz sampling rate. DATA is also transmitting with data changes, and it can be seen that the waveform pattern within a frame is repeated by about 4 groups, which also shows that the 4 groups of received data are synchronized. So far, the function of RT600 4-channel 48KHZ 32bit*2ch input and assembling into 48Khz 32bit*8ch output has been realized!  
View full article
How to create RT AVB switch&endpoint platform 1. Abstract In the previous article, it talked about how to use a single-point RT1170 as a talker and a single-point RT1170 as a listener, and connect the two boards directly to implement AVB endpoint testing. However, in actual use, many applications are multipoint to multipoint, but AVB switch is required. Therefore, based on the previous article, this article adds another listener endpoint and AVB switch to implement an AVB platform with one talker and two listeners. Fig 1 The AVB switch can be a third-party AVB switch product. Of course, you can also consider using NXP's upcoming new product RT1180. This chip has AVB/TSN switch function, and our RT1180 supporting stack has also been released. 2. Platform creation This article will use two AVB switches to do AVB testing: one uses the NXP official MIMXRT1180-EVK as an AVB switch, and the other uses the third-party product MOTU's AVB switch. The endpoints use three NXP MIMXRT1170-EVK boards, one for talker configuration and the other two for listener configuration. For the configuration of RT1170 as endpoint, that is, talker and listener, you can refer to the previous article: RT1170 AVB fresh tasting Here you can directly start quickly, take the avb_app.bin prepared in the stack and burn it directly to MIMXRT1170-EVK for talker and listener configuration. Of course, if there are some customized functions that modify the source code, you can also refer to the above article to recompile, generate the avb_app.bin file and then burn it. 2.1 Software and hardware Hardware:       MOTU AVB SWITCH(switch)       MIMXRT1180-EVK*1(switch)       MIMXRT1170-EVK*3(1: talker, 2: listener), hardware need to be modified, refer to the previous document Software: RT1170 AVB/TSN stack: genavb_tsn-mcuxpresso-SDK_2_13_0-5_6_1: https://mcuxpresso.nxp.com/download/52643189c4d74a7b26b8e096ab28df0e RT1180 AVB/TSN stack: genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0 : https://mcuxpresso.nxp.com/download/c584c33a8d4f55c29b5505b9be8f537a   2.2 Configure RT1170 AVB endpoints Directly burn the files in avbstack: genavb_tsn-mcuxpresso-SDK_2_13_0-5_6_1\binaries\genavb-avb_audio_app-evaluation-freertos_rt1176-5_6_1.tar\genavb-avb_audio_app-evaluation-freertos_rt1176-5_6_1\release\avb_app.bin to the three MIMXRT1170-EVK development boards and enter the serial download mode to program: Fig 2 The three boards are burned with the same code. After burning, let the board enter the internal boot mode and configure the talker and listener through the serial port. After the code is burned successfully, the onboard serial port will keep sending log information. You only need to enter INSERT on the keyboard to enter the shell command line state. 2.2.1 1MIMXRT1170-EVK do the talker configuration cd .. ls mkdir avb_app write avb_app/mclock_role 0 mkdir avdecc write avdecc/btb_mode 0 mkdir fgptp write fgptp/gmCapable 1 mkdir port0 write port0/hw_addr 00:22:33:44:55:66 2.2.2 2 MIMXRT1170-EVK do the listener configuration cd .. ls mkdir avb_app write avb_app/mclock_role 1 mkdir avdecc write avdecc/btb_mode 1 write avdecc/talker_id 0x00049f4455660000 2.3 AVB Switch configuration     The following are two SWITCH configuration connections: 2.3.1 MOTU AVB Switch Use MOTU AVB switch as the AVB switch connection block diagram: Fig 3   The physical board connections are as follows: Fig 4 For the dedicated AVB switch, no specific configuration is required, because you can think of it as a switch with AVB function, which can realize the forwarding function of AVB data. You only need to connect the 1G network port of a talker and the 1G network ports of two listeners to the network port of MOTU AVB SWITCH. Then as long as the functions of the talker and the listener are normal, the entire audio transmission can be normal. The talker is responsible for collecting the audio data information of the microphone and then forwarding it to the two listeners for playback. Of course, the two listeners need to be connected to the speakers respectively. 2.3.2 RT1180 AVB switch For the configuration of RT1180 AVB switch, there are two methods: quick start and self-compilation. If there is no change in the source code, you can directly use the bin file that comes with the stack. Here you need to pay attention to select the correct bin file. RT1180 has two cores: CM33 and CM7 cores. The CM33 image supports the TSN/AVB bridge function, that is, the switch, and the CM7 image supports the TSN endpoint function.    MIMXRT1180-EVK contains multi-network ports, the situation is: Fig 5 Fig 6 Therefore, when using the AVB switch network port, you need to pay attention to using ENET0, 1, 2, and 3 ports. The connection diagram of using MIMXRT1180-EVK as the AVB switch network port is as follows: Fig 7 The actual connection diagram is as follows: Fig 8 To implement the RT1180 code, you need to download the RT1180 M33 TSN bridge code to the MIMXRT1180-EVK board. If the source code of the AVB/TSN stack does not need to be modified, you can use the ready-made bin file for testing: genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0\binaries\genavb-tsn_app-evaluation-freertos_rt1189_cm33-6_0_0\release\tsn_app.bin There are many ways to burn, you can use tools or command line methods. The tool can be MCUBootutility or the official SEC tool. Here we choose to use the MCUBootutility tool, download link: https://github.com/JayHeng/NXP-MCUBootUtility/releases/tag/v6.2.0 If you use the SEC tool to download, you can refer to the stack documentation: genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0\doc\ NXP_GenAVB_TSN_MCUXpresso_User_s_Guide_6_0_rev0.pdf, chapter 11 Flash Image booting. When use the MCUBootutility tool, it needs to do the modification: \NXP-MCUBootUtility-6.2.0\src\targets\MIMXRT1189 \MIMXRT1189\bltargetconfig.py Modify: #flexspiNorMemBase0 = 0x38000000 # CM33 Secure #flexspiNorMemBase0Ns = 0x28000000 # CM33 Non-Secure To: flexspiNorMemBase0 = 0x28000000 # CM33 Non-Secure flexspiNorMemBase0Ns = 0x38000000 # CM33 Secure Fig 9 Burn the tsn_app.bin to the RT1180 address 0x2800b000。 Let the MIMXRT1180-EVK board enter serial download mode,SW5:1-OFF,2-OFF,3-OFF,4-ON. Then, find another usb cable to connect J33 to do the code flash downloading. After the code is programmed, need to enter the internal boot mode for QSPI: SW5:1-OFF,2-ON,3-OFF,4-OFF. This completes the burning of the app with AVB switch function. This code does not need to enter the shell to configure the filesystem like RT1170. For the RT1180 bridge code, after burning, the switch function will be built-in after restarting. Of course, if you need to recompile your own project, you can directly refer to the stack documentation: NXP_GenAVB_TSN_MCUXpresso_User_s_Guide_6_0_rev0.pdf. If you use Linux system to compile, the method is the same as RT1170, three steps:      (1) Patch the AVB stack for the RT1180 SDK     (2)add two soft links to the RT1180 AVB stack, one for the board SDK and the other for the AVB SDK source code. The structure is as follows:   Fig 10    (3) At last, build ./ build_release.sh \genavb_tsn-mcuxpresso-SDK_2_15_0-6_0_0\genavb-apps-freertos-6_0_0.tar\genavb-apps-freertos-6_0_0\boards\evkmimxrt1180\demo_apps\avb_tsn\tsn_app\cm33\armgcc\ build_release.sh Then, it will generate the according tsn_app.bin file. 3. AVB network data packet analysis I have always wanted to check the AVB network data packets, so I thought of the following method to do it. I also found a general network switch that can package some of the network ports to specific network ports. This method is used here just to check the basic packets. In principle, the general switch does not have the AVB physical layer function, so it should have some impact on the synchronization function. However, due to the limitation of the equipment, this article only has a basic understanding of the AVB data packet structure. Prepare a switch with port mirror function: NETGERA plus switch ProSAFE GS105E. Then configure the switch to mirror the data of ports 2 and 3 to port 1: Fig 11 Then the entire AVB system connection diagram is as follows: Fig 12 The physical connection diagram is as follows: Fig 13 Open the entire system platform and let the system function run, that is, the talker endpoint has sound input and the amplifiers of the two listener endpoints have output. Open the wireshark software on the PC and capture the packets. The captured situation is as follows: Fig 14 As you can see, there are many AVTP packets, and there are two destination addresses. To analyze AVTP packets, you must first know what the standard AVTP packets are like. The standard packets have the following structure: Fig 15 Next, open the wireshark software, configure the network port to be captured, and compare the captured data packets: Fig 16 As you can see, the whole packet is basically captured, but the details, such as VLAN tag and IEC 61883 header, are not present. This is probably caused by the physical layer of ordinary switches cannot support AVB. However, the audio data above can still be seen, and it is indeed dual-channel, but the data is only transmitted through one channel. Therefore, for the RT1170 listener, although a dual-channel speaker is connected, the two speakers correspond to the left and right channels, but when listening, only one speaker channel has sound, and the other has no sound. This is consistent with the captured data packet. The source of this is that the stack code uses one channel for microphone acquisition, and although the audio is configured with two channels, there is actually only one channel with data. So far, the architecture and test of the AVB switch&endpoint platform have been realized. The test effect can be viewed in the video.    
View full article
RT1170 camera CSI Y8 format modification 1.Abstract RT1170's CSI can support YUV format. The so-called YUV is divided into three components: Y represents luminance, that is, grayscale value; UV represents chrominance, which describes chroma and saturation. Similar to RGB, YUV is also a color encoding method, which can separate luminance information Y from chroma information UV. If you want to display black and white, you can have no UV information, only Y information, that is, Y800=Y8, and you can also display the complete image. For RT1170 YUV, the official SDK provides demo based on the YUV444 format, but in actual use, some customers need the function of the Y8 format, so how should they configure it based on the existing YUV SDK? From the reference manual of RT1170, you can see the following information: Fig 1 This description can be understood as requiring the Y8 mode, as long as the configuration: CSI_CR20[BINARY_EN]=0 CSI_CR20[BIG_END]=1 However, in reality, with this configuration, the original YUV code cannot display the camera data. So how should the camera's Y8 configuration be done to display black and white images on the LCD? This article will give a detailed explanation. 2. RT1170 CSI Camera Y8 format configuration and testing 2.1 Hardware and software situation Board:MIMXRT1170-EVK REV C4 LCD:  RK055AHD091 Camera:OV5640 Code:SDK_2_15_000_MIMXRT1170-EVK\boards\evkmimxrt1170\driver_examples\csi\mipi_yuv\cm7 IDE: MCUXPresso IDE v11.9.0 2.2 Y8 formation configuration   In fact, for CSI_CR20 configuration, you also need to enable the Histogram function, which is the following register bits: Fig 2 Here, based on the current SDK demo evkmimxrt1170_csi_mipi_yuv_cm7 demo, modify it to the Y8 format, list the modification points, mainly modify the file:csi_mipi_yuv.c (1) static void DEMO_InitPxp(void) Modify: PXP_SetCsc1Mode(DEMO_PXP, kPXP_Csc1YCbCr2RGB); To: PXP_SetCsc1Mode(DEMO_PXP, kPXP_Csc1YUV2RGB); If this item is not modified, LCD will just display the Green color. (2)static void DEMO_InitCamera(void) Before BOARD_InitMipiCsi(); Add the this code: CSI->CR20 |= CSI_CR20_QRCODE_EN_MASK | CSI_CR20_HISTOGRAM_EN_MASK; Here, didn’t configure CSI_CR20[BINARY_EN]=0, as after reseting, this bit is default to 0. If in the practical usage, this bit is modified to 1, then here, need to modify BINARY_EN to 0, it means the format is Y8, not Y1. The reason that can’t display the correct Y8 previously, is caused by the bit HISTOGRAM_EN is not set. (3) static void DEMO_CSI_MIPI_YUV(void) Modify structure psBufferConfig as follows: pxp_ps_buffer_config_t psBufferConfig = { .pixelFormat = kPXP_PsPixelFormatY8, //kPXP_PsPixelFormatYUV1P444, /* Note: This is 32-bit per pixel */ .swapByte = false, .bufferAddrU = 0U, .bufferAddrV = 0U, .pitchBytes = DEMO_CAMERA_WIDTH,//DEMO_CAMERA_WIDTH * DEMO_CAMERA_BUFFER_BPP,// }; Mainly 2 points: .pixelFormat = kPXP_PsPixelFormatY8, .pitchBytes  = DEMO_CAMERA_WIDTH, If you only change the pixel format to Y8, but pitchBytes is not changed to the camera width, the resulting LCD display will be a small strip on the top, instead of the entire LCD screen showing the camera's Y8 format black and white image. So far, all Y8-related modification projects have been completed. Finally, it should be noted that the default SDK LCD display is not the one selected in this article: RK055AHD091. So you need to modify the DEMO_PANEL macro in display_support.h to the following: #define DEMO_PANEL DEMO_PANEL_RK055AHD091 Then, build the project, and download it to the board MIMXRT1170-EVK. 2.3 Test result after modification Below we use the same color picture to test the YUV and Y8 display effects in front of the camera. here are the pictures:  the camera format of the picture on the left is YUV444, and the picture on the right is in Y8 format. You can see that the left one is in color, and the right one is in black and white. The black and white Y8 camera data acquisition and LCD display have been successfully completed.
View full article
1.    Abstract When using the RT600 SDK USB composite example, the customer found that the default HS interval was 125us, and the code was: mimxrt685audevk_dev_composite_hid_audio_unified_bm. However, in actual applications, the 125us packet interval for data transmission will cause a large interrupt load on the CPU, so the customer hopes to change the interval to a larger during, such as 1ms. After changing interval to 1ms, it is found that the data packet can be sent to the RT chip, but there is indeed a problem with the playback on the RT side, speaker no voice. Changing it to 500us in synchronous mode is possible work, but at 500us, the customer's CPU load still reaches more than 80%, which is not convenient for subsequent application code expansion, so it is still hoped to achieve a 1ms method. This article will give the transmission of different intervals of UAC and provide a solution for 1ms intervals. 2.    Test situation    Board:MIMXRT685-AUD-EVK    SDK:SDK_2_15_000_MIMXRT685-AUD-EVK    USB Analyzer:Lecroy USB-TOS2-A01-X 2.1 Platform connection situation First, given the connection between MIMXRT685-AUD-EVK, USB analyzer and audio source. This article mainly tests the RT685 UAC speaker function, that is, USB transmits audio source data to RT685, and plays the audio source through a player (which can be headphones), or speaker. The J7 USB port of EVK is connected to the A port of Lecroy, and the B port of Lecroy is connected to the audio source, which can be a PC or a mobile phone. If using a PC, the speaker needs to be selected as USB AUDIO+HID DEMO, because the name of the board after UAC enumeration is USB AUDIO+HID DEMO. The other end of Lecroy is connected to the PC for transmitting USB bus data. The specific connection diagram is as follows:      Fig 1 EVK USB analyzer connection 2.2 Different audio interval data package situation This chapter gives the modifications under different intervals, as well as the results of USB analyzer packet capture. The clock system of the audio synchronous/isochronous audio endpoint can be synchronized through SOF. The USB 1.0 sampling rate must be locked to the 1ms SOF beat. The USB2.0 high-speed HS endpoint can be locked to the 125us SOF beat. If you want to change the interval, it is usually a multiple of 125us, that is, it can be 250us, 500us, 1ms, etc. The modification method is usually to directly modify the USB stack's usb_audio_config.h:   #define HS_ISO_OUT_ENDP_INTERVAL (0x01) The relationship between HS_ISO_OUT_ENDP_INTERVAL and the real during time is: 125us*2^( HS_ISO_OUT_ENDP_INTERVAL -1) So: HS_ISO_OUT_ENDP_INTERVAL=1 : 125us HS_ISO_OUT_ENDP_INTERVAL=2 : 250us HS_ISO_OUT_ENDP_INTERVAL=3 : 500us HS_ISO_OUT_ENDP_INTERVAL=4 : 1000us The following are the four situations mentioned above respectively through USB analyzer packet capture test. 2.2.1 125us interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x01) Fig 2 125us interval It can be seen that when the interval is configured to 125us, the SOF beat interval in front of each OUT packet is 125us, and the length of the OUT data packet is 24Byte. The data in this 24Byte packet is the actual audio data. In this case, the playback is normal   2.2.2 250us interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x02) The packet capture configured as 250us is shown in the figure below. It can be seen that the SOF beat interval in front of the two OUT packets is 250us, and the length of the OUT data packet is 48 Byte. In other words, as the interval increases, the length of the data packet also increases proportionally, that is, the transmission time is longer, but a packet contains more data packets. At this time, the RT side also needs to provide more USB receiving buffers to receive data. Fig 3 250us interval This situation, the speaker play normally, have the audio sound. 2.2.3 500us interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x03) Fig 4 500us interval SYNC mode At this time, you can see that the data packet has become 96 Bytes, and the data content is also normal audio data, but the playback has problems and no sound can be heard. However, if you configure: #define USB_DEVICE_AUDIO_USE_SYNC_MODE (0U) It is not the SYNC mode, it can hear the audio sound, the packet situation is: Fig 5 500us interval no SYNC mode However, the asynchronous mode also has problems. After a long period of operation, it may become out of sync and the playback may become stuck. Therefore, it is still necessary to use the 1ms method in the synchronous mode. 2.2.4 1ms interval packet situation #define HS_ISO_OUT_ENDP_INTERVAL (0x04) Fig 6 interval 1ms It can be seen that the data packet length has become 192 bytes, and the SOF beat interval in front of the OUT packet has indeed become 1ms. The audio data packet also looks like normal audio data, so the audio data here is successfully transmitted from USB to RT. However, the playback is abnormal and no sound can be heard. 3. 1ms interval solution Now let's debug the project to find the problem. Because the USB analyzer can show that the audio data of the USB bus is actually transmitted, we must first check whether the USB receives the data packet in the kUSB_DeviceAudioEventStreamRecvResponse event in USB_DeviceAudioCompositeCallback of audio_unified.c, whether the data packet length is correct, and whether the data content looks like audio data. The following is the debug result: Fig 7 data packet received situation So from this point of view, the USB interface data packet is received, and the data looks like real audio data. If it is abnormal data, it is either all 0 or irregular data that changes, but the test result cannot be played. So now to check the I2S playback function, composite.h file, TxCallback function: Fig 8 I2S play data buffer We can see, the real data transfer to the I2S data buffer are always 0, and the reality should be g_composite.audioUnified.startPlayFlag none zero, and also use: s_TxTransfer.dataSize = g_composite.audioUnified.audioPlayTransferSize; s_TxTransfer.data=audioPlayDataBuff + g_composite.audioUnified.tdReadNumberPlay; But, after testing, audioPlayDataBuff data are also 0. So the issue should be the USB received side, receive the data, but when save the data to the audioPlayDataBuff have issues, go back to audio_unified.c Fig 9 USB_DeviceAudioCompositeCallback Fig10 USB_AudioSpeakerPutBuffer After testing, in the Fig 9, audioUnified.tdWriteNumberPlay never larger than g_deviceAudioComposite->audioUnified.audioPlayTransferSize * AUDIO_CLASS_2_0_HS_LOW_LATENCY_TRANSFER_COUNT=192*6   #define AUDIO_CLASS_2_0_HS_LOW_LATENCY_TRANSFER_COUNT \ (0x06U) /* 6 means 6 mico frames (6*125us), make sure the latency is smaller than 1ms for sync mode */ The low latency here is a delay buffer. Therefore, the USB cache data must at least be able to hold the delayed data packet. Unit 1 represents an interval frame. Now it is changed to an interval of 1ms, 6 delay units, which means that the delay here is 6ms. Here, the receiving buffer size needs to be larger, which is related to the following definition: Fig 11 USB device callback g_composite.audioUnified.audioPlayBufferSize = AUDIO_PLAY_BUFFER_SIZE_ONE_FRAME * AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT; #define AUDIO_PLAY_BUFFER_SIZE_ONE_FRAME AUDIO_OUT_TRANSFER_LENGTH_ONE_FRAME #define AUDIO_OUT_FORMAT_CHANNELS (0x02U) #define AUDIO_OUT_FORMAT_SIZE (0x02) #define AUDIO_OUT_SAMPLING_RATE_KHZ (48) /* transfer length during 1 ms */ #define AUDIO_OUT_TRANSFER_LENGTH_ONE_FRAME \ (AUDIO_OUT_SAMPLING_RATE_KHZ * AUDIO_OUT_FORMAT_CHANNELS * AUDIO_OUT_FORMAT_SIZE) #define AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT \ (2U) /* 2 units size buffer (1 unit means the size to play during 1ms) */ Here, try to set larger AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT, let it should be at least can put 6ms data, as 1 unit is 1ms play data size, and it needs to have more than 6ms, so, set to 12 unit, the modified definition: #define AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT 12 Build the project and download it, now capture the USB bus data again: Fig 12 1ms interval data packet play successfully   This data waveform is the data that can be played normally. Below is the video of the MIMXRT685-AUD-EVK test. The attachment provides two demos of RT685 and RT1170, both modified to 1ms interval, and the modification method of RT1170 is exactly the same 4.    Summarize Whether it is RT600, RT1170, or more precisely the UAC code of the entire RT series, if you need to modify the interval, the main focus is on the following macros in usb_audio_config.h, the following is for 1ms interval: #define HS_ISO_OUT_ENDP_INTERVAL (0x04)//(0x01)//(0X04) #define AUDIO_CLASS_2_0_HS_LOW_LATENCY_TRANSFER_COUNT \ (0x06U) /* 6 means 16 mico frames (6*125us), make sure the latency is smaller than 1ms for ehci high speed */ #define AUDIO_SPEAKER_DATA_WHOLE_BUFFER_COUNT \ (12U)//(2U) /* 2 units size buffer (1 unit means the size to play during 1ms) */ Test video:
View full article
RT1170 CMSIS DAP+IDE debug based on ECC enabled 1.    Abstract This article aims to solve the problem that after enabling the ECC function, RT1170 cannot be debugged using CMSIS DAP in the three major IDEs (MCUXpresso, IAR, MDK). ECC is enabled by burning the relevant fuses and enabling ROM preloading, which means that the ROM will help initialize the RAM. However, in actual use, it is found that different debuggers have different appearances on the IDE. For example, Segger JLINK can directly implement debugging, but when CMSIS DAP is combined with the three major IDEs, there will be a problem that the code cannot be debugged after being downloaded to RAM. Here, taking MCUXpresso IDE as an example, after burning the ECC-related fuses on the MIMXRT1170-EVK board, if the project is burned to RAM, it can be debugged directly. However, if the project is burned to flash, there will be problems with flashloader: Fig 1    So is this problem caused by the mismatch of flashloader, or does it require additional operations? This article will give a specific solution! 2. RT1170 ECC basic enable and solution 2.1 RT1170 ECC basic    ECC stands for Error Correcting Code, which can detect and correct memory errors. So what ECC does RT1170 have? They are: MECC64, XECC, FlexRAM ECC. MECC64 MECC64 supports 1-bit error correction and 2-bit error detection,to 2bit, it cannot correct errors, just detect the error. MECC64 is mainly protects OCRAM1 and 2 in the chip. MECC1 protects OCRAM1, and MECC2 protects OCRAM2. OCRAM1 ECC and OCRAM2 ECC are used to store ECC check values. If ECC64 is not enabled, it can be used as a normal OCRAM. An 8-bit ECC check value (8 bits) is calculated for every 64 bits of data. The ECC algorithm is Hsiao Hamming. XECC XECC is External ECC controller, which is used to provide ECC function for external storage space. XECC supports 1-bit error correction and 2-bit error detection. External memory includes XECC_FLEXSPI1, XECC_FLEXSPI2, and XECC+SEMC. XECC can calculate a 4-bit ECC check value for every 4 bits. The XECC check value is immediately following the original value. For example, for a 32-bit data, a 4-bit ECC check value is generated for every 4 bits. 32-bit original data needs to generate 32-bit ECC check data, which requires a total of 64 bits of space. Algorithm: Hsiao Hamming algorithm FlexRAM ECC FlexRAM ECC is used to protect the ITCM, DTCM and OCRAM of FlexRAM. It supports 1-bit error correction and 2-bit error detection. A 7/8-bit ECC check value is calculated for every 4 bytes of DTCM or 8 bytes of ITCM/OCRAM, and the ECC check value is placed in the ECC RAM. 2.2 RT1170 ECC enable    The method used in this article to enable ECC is to directly enable the relevant fuse bit. - MECC_ENABLE (0x840[2]) = 1 - XECC_ENABLE (0x840[3]) = 1 - ROM preloading (0x950[0]) = 1 - FLEXRAMECC_ENABLE (0x840[15]) = 1 For more software configuration information, please refer to the official application note AN13204: https://www.nxp.com/docs/en/application-note/AN13204.pdf The following is the situation after burning the relevant fuses. Burning the fuses can enter the serial download mode and use MCUbootutility to burn: Fig 2 2.3 ECC debug issue solution methods After testing in many ways, such as initializing RAM in the script, because of the characteristics of ECC, RAM needs to be flashed once, but it is found that the general code speed of flashing RAM directly is too slow, resulting in download timeout problems, and then it is changed to use DMA to move data to RAM to ensure that RAM is flashed once, but the result is still not good, so flashing RAM is not the fundamental way to solve the debug problem. Finally, by chance, ECC is turned off in the connection script first, especially FlexRAM ECC, and it is found that the burning algorithm can be called to perform external flash operations at this time. In this way, the code can be successfully downloaded, and then reset to let the ROM turn on the ECC function by itself. The reason why the RAM project can work is that the process of downloading RAM is a process of flashing RAM, so the RAM code can work directly. For the debugger burning and simulation of Flash code, it is still necessary to turn off the ECC module first, mainly the FlexRAM ECC module. Of course, for the sake of insurance, we can directly turn off all MECC and FlexRAM ECC register enable bits, let the flashloader work first, and directly control the register address: 0x40014100=0;0x40018100=0;0x40028108=0; 0x40014100 PIPE_ECC_EN[ECC_EN], control MECC1 0x40018100 PIPE_ECC_EN[ECC_EN], control MECC2 Fig 3 0x40028108 FLEXRAM_CTRL ECC_EN, control FlexRAM ECC Fig 4 According to the actual test situation, disabling FlexRAM ECC is effective. The problem should be that the area where the flashloader used is stored is the DTCM of FlexRAM. Check the area of ​​the Flashloader of the burning algorithm as follows: Fig 5 3  Three major IDEs script and testing Here, share the three IDE(MCUXPresso, IAR, MDK)+CMSIS DAP+ECC related modified script file. 3.1 MCUXpresso IDE Script path in the IDE: C:\nxp\MCUXpressoIDE_11.9.0_2144\ide\LinkServer\binaries\Scripts Prepare one script file T1170_connect_M7_wake_M4_ecc.scp,copy it to the above path, and the content is: 1 REM ====================================== 2 REM Copyright 2020-2024 NXP 3 REM All rights reserved. 4 REM SPDX-License-Identifier: BSD-3-Clause 5 REM ====================================== 100 REM ======================================================================= 110 REM RT1170_connect_M7_wake_M4.scp 150 REM ======================================================================= 160 PRINT "RT1170 Connect M7 and Wake M4 Script" 170 REM ======================================================================= 180 REM Uncomment ProbeList for standalone script use (outside the stub) 190 REM ======================================================================= 200 REM ProbeList 210 p% = ProbeFirstFound 220 REM ProbeOpenByIndex p% 230 WireSwdConnect p% 240 SelectProbeCore p% 0 250 CMInitApDp this 252 REM ======================================================================= 254 REM Disable ECC 256 GOSUB 1500 260 REM ======================================================================= 270 REM The M4 AP is not visible while the core is held in reset 280 REM Prepare a spin code in RAM and wake up / reset the M4 to it 290 REM This serves two purposes: 300 REM - enables the M4 AP, required for debug visibility 310 REM - prevents M4 code from interfering with flash programming on M7 320 REM ======================================================================= 330 REM Prepare spin code 340 GOSUB 1000 350 REM ======================================================================= 360 PRINT "Setting M4 clock" 370 REM Set m4_clk_root to OSC_RC_400M / 2: CLOCK_ROOT1 = mux(2), div(1) 380 Poke32 this 0x40CC0080 0x201 390 PRINT "Resetting M4 core" 400 REM Save current reset SRMR and prevent M4 SW reset affecting the system 410 s% = Peek32 this 0x40C04004 420 Poke32 this 0x40C04004 0x00000C00 430 Poke32 this 0x40C04284 0x1 440 Poke32 this 0x40C04004 s% 450 REM ======================================================================= 460 REM Release M4 if needed 500 s% = Peek32 this 0x40c04000 510 IF s% & 1 == 1 THEN GOTO 560 520 PRINT "Releasing M4" 530 s% = s% | 1 540 Poke32 this 0x40c04000 s% 550 REM ======================================================================= 560 PRINT "View cores on the DAP AP" 570 WireSwdConnect p% 580 CoreList p% 590 SelectProbeCore p% 0 600 REM ======================================================================= 610 REM Potentially FlexRAM might need to be set to ensure TCMs are available 620 REM Uncomment next line if needed 630 REM GOSUB 800 640 REM ======================================================================= 650 REM Finished - 0 to select the M7, 1 to select M4 660 d% = 0 670 REM ======================================================================= 680 REM Setup VTOR in preparation for VECTRESET 690 GOSUB 1300 700 REM ======================================================================= 710 END 800 REM ====================== SUB: Configure FlexRAM ======================== 810 PRINT "Configuring FlexRAM for 256KB I-TCM, 256KB D-TCM, 0KB OCRAM" 820 REM FlexRAM TCM_CTRL - force RAM clocking ON and set fast mode = b100 830 Poke32 this 0x40028000 0x4 840 REM IOMUXC_GPR17/18 FlexRAM 32KB banks allocation - I(b11), D(b10), OC(b01) 850 Poke32 this 0x400E4044 0x0000AAFF 860 Poke32 this 0x400E4048 0x0000AAFF 870 REM IOMUXC_GPR16 Enable FLEXRAM_BANK_CFG in GPR16/17 880 s% = Peek32 this 0x400E4040 890 s% = s% | 4 900 Poke32 this 0x400E4040 s% 910 RETURN 1000 REM ==================== SUB: Set up M4 spin code ======================== 1010 REM Setup some spin code into an area of D-TCM (0x2021FF00) 1020 REM Condensed vector table format taking up 2 words of memory: 1030 REM - x00: SP (dummy), two back-to-back branch-to-self opcodes (b 0) 1040 REM - x04: PC - points to address x00 (+1 Thumb) 1050 PRINT "Setting M4 spin code" 1060 Poke32 this 0x2021FF00 0xE7FEE7FE 1070 Poke32 this 0x2021FF04 0x2021FF01 1080 REM Set top/bottom 16 bits of RAM address into CM4 VTOR iomuxc_lpsr_GPR0/1 1090 Poke32 this 0x40C0C000 0xFF00 1100 Poke32 this 0x40C0C004 0x2021 1110 RETURN 1300 REM ==================== SUB: Setup CM7 VTOR ============================= 1310 REM Upon VECTRESET, VTOR is loaded with the value from this register. 1320 REM If the address is invalid, a hard fault occurs after VECTRESET. 1330 REM These registers are set in preparation for a pre-flash driver VECTRESET 1340 REM requested by the stub. 1350 REM BootROM VTOR 1360 s% = 0x210000 1370 REM Set addr >> 7 into CM7 VTOR iomuxc_lpsr_GPR26 (RevB) or 0x400e404c (Rev A) 1380 v% = Peek32 this 0x40C84800 1390 IF v% & 0x00FFFFF0 == 0x1170A0 Then GOTO 1420 1400 Poke32 this 0x40C0C068 s% >> 7 1410 GOTO 1430 1420 Poke32 this 0x400E404C s% >> 7 1430 RETURN 1440 REM ======================================================================= 1500 REM ====================== SUB: Disable M7 TCM ECC and OCRAM ECC ========== 1510 REM FlexRAM_CTRL - disable TCM ECC and OCRAM ECC 1520 Poke32 this 0x40028108 0x00000000 1530 REM MECC1/2 PIPE_ECC_EN - disable ECC 1540 Poke32 this 0x40014100 0x00000000 1550 Poke32 this 0x40018100 0x00000000 1560 RETURN  MCUXpresso debug configuration,in the “connect script” item, select the above prepared .scp file: Fig 6 The result after Debug is: Fig 7 We can see, the code downloading and debugging all works now. 3.2 IAR IDE ECC script and testing IAR project script path: \MIMXRT1170-EVK-hello_world_demo_cm7\hello_world_demo_cm7\evkmimxrt1170 Prepare the file: evkmimxrt1170_connect_cm7_disableECC.mac The content is:   /* * Copyright 2019-2021 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ __var rev; initSysPll2() { __var t; // ANADIG_PLL_PLL_528_CTRL t = __readMemory32(0x40C84240, "Memory"); if (t & 0x800000) { // SysPll2 has been initialized t &= ~0x40000000; __writeMemory32(t, 0x40C84240, "Memory"); return; } t = __readMemory32(0x40C84270, "Memory"); t |= 0x80808080; __writeMemory32(t, 0x40C84270, "Memory"); t = __readMemory32(0x40C84240, "Memory"); t &= ~(0x802000); t |= 0x40000000; __writeMemory32(t, 0x40C84240, "Memory"); // ANADIG_PLL_PLL_528_MFN __writeMemory32(0, 0x40C84280, "Memory"); // ANADIG_PLL_PLL_528_MFI __writeMemory32(22, 0x40C84290, "Memory"); // ANADIG_PLL_PLL_528_MFD __writeMemory32(0x0FFFFFFF, 0x40C842A0, "Memory"); // ANADIG_PLL_PLL_528_CTRL __writeMemory32(0x40000008, 0x40C84240, "Memory"); __delay(30); // ANADIG_PLL_PLL_528_CTRL t = __readMemory32(0x40C84240, "Memory"); t |= 0x800000 | 0x800; __writeMemory32(t, 0x40C84240, "Memory"); __delay(250); t = __readMemory32(0x40C84240, "Memory"); t &= ~0x800; __writeMemory32(t, 0x40C84240, "Memory"); do { t = __readMemory32(0x40C84240, "Memory"); } while ((t & 0x20000000) == 0); t |= 0x2000; __writeMemory32(t, 0x40C84240, "Memory"); t &= ~0x40000000; __writeMemory32(t, 0x40C84240, "Memory"); } initSysPll2Pfd1() { __var t, stable; t = __readMemory32(0x40C84270, "Memory"); if (((t & 0x8000) != 0) || (((t & 0x3F00) >> 😎 != 16)) { stable = t & 0x4000; t |= 0x8000; __writeMemory32(t, 0x40C84270, "Memory"); t = __readMemory32(0x40C84270, "Memory"); t &= ~0x3F00; t |= 16 << 8; __writeMemory32(t, 0x40C84270, "Memory"); t = __readMemory32(0x40C84250, "Memory"); t ^= 0x4; __writeMemory32(t, 0x40C84250, "Memory"); t = __readMemory32(0x40C84270, "Memory"); t &= ~0x8000; __writeMemory32(t, 0x40C84270, "Memory"); do { t = __readMemory32(0x40C84270, "Memory") & 0x4000; } while (t == stable); } else { t &= ~0x8000; __writeMemory32(t, 0x40C84270, "Memory"); } } SDRAM_WaitIpCmdDone() { __var reg; do { reg = __readMemory32(0x400D403C, "Memory"); __delay(10); }while((reg & 0x3) == 0); __writeMemory32(0x00000003, 0x400D403C, "Memory"); // clear IPCMDERR and IPCMDDONE bits } setSemcClock() { initSysPll2(); initSysPll2Pfd1(); // Set SEMC root clock to use sys pll2 pfd1 divided by 3: 198Mhz __writeMemory32(0x602, 0x40cc0200, "Memory"); } initSDRAM() { // Config IOMUX __writeMemory32(0x00000000, 0x400E8010, "Memory"); __writeMemory32(0x00000000, 0x400E8014, "Memory"); __writeMemory32(0x00000000, 0x400E8018, "Memory"); __writeMemory32(0x00000000, 0x400E801C, "Memory"); __writeMemory32(0x00000000, 0x400E8020, "Memory"); __writeMemory32(0x00000000, 0x400E8024, "Memory"); __writeMemory32(0x00000000, 0x400E8028, "Memory"); __writeMemory32(0x00000000, 0x400E802C, "Memory"); __writeMemory32(0x00000000, 0x400E8030, "Memory"); __writeMemory32(0x00000000, 0x400E8034, "Memory"); __writeMemory32(0x00000000, 0x400E8038, "Memory"); __writeMemory32(0x00000000, 0x400E803C, "Memory"); __writeMemory32(0x00000000, 0x400E8040, "Memory"); __writeMemory32(0x00000000, 0x400E8044, "Memory"); __writeMemory32(0x00000000, 0x400E8048, "Memory"); __writeMemory32(0x00000000, 0x400E804C, "Memory"); __writeMemory32(0x00000000, 0x400E8050, "Memory"); __writeMemory32(0x00000000, 0x400E8054, "Memory"); __writeMemory32(0x00000000, 0x400E8058, "Memory"); __writeMemory32(0x00000000, 0x400E805C, "Memory"); __writeMemory32(0x00000000, 0x400E8060, "Memory"); __writeMemory32(0x00000000, 0x400E8064, "Memory"); __writeMemory32(0x00000000, 0x400E8068, "Memory"); __writeMemory32(0x00000000, 0x400E806C, "Memory"); __writeMemory32(0x00000000, 0x400E8070, "Memory"); __writeMemory32(0x00000000, 0x400E8074, "Memory"); __writeMemory32(0x00000000, 0x400E8078, "Memory"); __writeMemory32(0x00000000, 0x400E807C, "Memory"); __writeMemory32(0x00000000, 0x400E8080, "Memory"); __writeMemory32(0x00000000, 0x400E8084, "Memory"); __writeMemory32(0x00000000, 0x400E8088, "Memory"); __writeMemory32(0x00000000, 0x400E808C, "Memory"); __writeMemory32(0x00000000, 0x400E8090, "Memory"); __writeMemory32(0x00000000, 0x400E8094, "Memory"); __writeMemory32(0x00000000, 0x400E8098, "Memory"); __writeMemory32(0x00000000, 0x400E809C, "Memory"); __writeMemory32(0x00000000, 0x400E80A0, "Memory"); __writeMemory32(0x00000000, 0x400E80A4, "Memory"); __writeMemory32(0x00000000, 0x400E80A8, "Memory"); __writeMemory32(0x00000010, 0x400E80AC, "Memory"); // EMC_39, DQS PIN, enable SION __writeMemory32(0x00000000, 0x400E80B8, "Memory"); __writeMemory32(0x00000000, 0x400E80BC, "Memory"); __writeMemory32(0x00000000, 0x400E80C0, "Memory"); __writeMemory32(0x00000000, 0x400E80C4, "Memory"); __writeMemory32(0x00000000, 0x400E80C8, "Memory"); __writeMemory32(0x00000000, 0x400E80CC, "Memory"); __writeMemory32(0x00000000, 0x400E80D0, "Memory"); __writeMemory32(0x00000000, 0x400E80D4, "Memory"); __writeMemory32(0x00000000, 0x400E80D8, "Memory"); __writeMemory32(0x00000000, 0x400E80DC, "Memory"); __writeMemory32(0x00000000, 0x400E80E0, "Memory"); __writeMemory32(0x00000000, 0x400E80E4, "Memory"); __writeMemory32(0x00000000, 0x400E80E8, "Memory"); __writeMemory32(0x00000000, 0x400E80EC, "Memory"); __writeMemory32(0x00000000, 0x400E80F0, "Memory"); __writeMemory32(0x00000000, 0x400E80F4, "Memory"); __writeMemory32(0x00000000, 0x400E80F8, "Memory"); __writeMemory32(0x00000000, 0x400E80FC, "Memory"); // PAD ctrl // PDRV = 1b (normal); PULL = 10b (PD) __writeMemory32(0x00000008, 0x400E8254, "Memory"); __writeMemory32(0x00000008, 0x400E8258, "Memory"); __writeMemory32(0x00000008, 0x400E825C, "Memory"); __writeMemory32(0x00000008, 0x400E8260, "Memory"); __writeMemory32(0x00000008, 0x400E8264, "Memory"); __writeMemory32(0x00000008, 0x400E8268, "Memory"); __writeMemory32(0x00000008, 0x400E826C, "Memory"); __writeMemory32(0x00000008, 0x400E8270, "Memory"); __writeMemory32(0x00000008, 0x400E8274, "Memory"); __writeMemory32(0x00000008, 0x400E8278, "Memory"); __writeMemory32(0x00000008, 0x400E827C, "Memory"); __writeMemory32(0x00000008, 0x400E8280, "Memory"); __writeMemory32(0x00000008, 0x400E8284, "Memory"); __writeMemory32(0x00000008, 0x400E8288, "Memory"); __writeMemory32(0x00000008, 0x400E828C, "Memory"); __writeMemory32(0x00000008, 0x400E8290, "Memory"); __writeMemory32(0x00000008, 0x400E8294, "Memory"); __writeMemory32(0x00000008, 0x400E8298, "Memory"); __writeMemory32(0x00000008, 0x400E829C, "Memory"); __writeMemory32(0x00000008, 0x400E82A0, "Memory"); __writeMemory32(0x00000008, 0x400E82A4, "Memory"); __writeMemory32(0x00000008, 0x400E82A8, "Memory"); __writeMemory32(0x00000008, 0x400E82AC, "Memory"); __writeMemory32(0x00000008, 0x400E82B0, "Memory"); __writeMemory32(0x00000008, 0x400E82B4, "Memory"); __writeMemory32(0x00000008, 0x400E82B8, "Memory"); __writeMemory32(0x00000008, 0x400E82BC, "Memory"); __writeMemory32(0x00000008, 0x400E82C0, "Memory"); __writeMemory32(0x00000008, 0x400E82C4, "Memory"); __writeMemory32(0x00000008, 0x400E82C8, "Memory"); __writeMemory32(0x00000008, 0x400E82CC, "Memory"); __writeMemory32(0x00000008, 0x400E82D0, "Memory"); __writeMemory32(0x00000008, 0x400E82D4, "Memory"); __writeMemory32(0x00000008, 0x400E82D8, "Memory"); __writeMemory32(0x00000008, 0x400E82DC, "Memory"); __writeMemory32(0x00000008, 0x400E82E0, "Memory"); __writeMemory32(0x00000008, 0x400E82E4, "Memory"); __writeMemory32(0x00000008, 0x400E82E8, "Memory"); __writeMemory32(0x00000008, 0x400E82EC, "Memory"); __writeMemory32(0x00000008, 0x400E82F0, "Memory"); __writeMemory32(0x00000008, 0x400E82FC, "Memory"); __writeMemory32(0x00000008, 0x400E8300, "Memory"); __writeMemory32(0x00000008, 0x400E8304, "Memory"); __writeMemory32(0x00000008, 0x400E8308, "Memory"); __writeMemory32(0x00000008, 0x400E830C, "Memory"); __writeMemory32(0x00000008, 0x400E8310, "Memory"); __writeMemory32(0x00000008, 0x400E8314, "Memory"); __writeMemory32(0x00000008, 0x400E8318, "Memory"); __writeMemory32(0x00000008, 0x400E831C, "Memory"); __writeMemory32(0x00000008, 0x400E8320, "Memory"); __writeMemory32(0x00000008, 0x400E8324, "Memory"); __writeMemory32(0x00000008, 0x400E8328, "Memory"); __writeMemory32(0x00000008, 0x400E832C, "Memory"); __writeMemory32(0x00000008, 0x400E8330, "Memory"); __writeMemory32(0x00000008, 0x400E8334, "Memory"); __writeMemory32(0x00000008, 0x400E8338, "Memory"); __writeMemory32(0x00000008, 0x400E833C, "Memory"); __writeMemory32(0x00000008, 0x400E8340, "Memory"); // Config SDR Controller Registers/ __writeMemory32(0x10000004, 0x400d4000, "Memory"); // MCR __writeMemory32(0x00000081, 0x400d4008, "Memory"); // BMCR0 __writeMemory32(0x00000081, 0x400d400C, "Memory"); // BMCR1 __writeMemory32(0x8000001D, 0x400d4010, "Memory"); // BR0, 64MB __writeMemory32(0x00000F32, 0x400d4040, "Memory"); // SDRAMCR0, 32bit __writeMemory32(0x00772A22, 0x400d4044, "Memory"); // SDRAMCR1 __writeMemory32(0x00010A0D, 0x400d4048, "Memory"); // SDRAMCR2 __writeMemory32(0x21210408, 0x400d404C, "Memory"); // SDRAMCR3 __writeMemory32(0x80000000, 0x400d4090, "Memory"); // IPCR0 __writeMemory32(0x00000002, 0x400d4094, "Memory"); // IPCR1 __writeMemory32(0x00000000, 0x400d4098, "Memory"); // IPCR2 __writeMemory32(0xA55A000F, 0x400d409C, "Memory"); // IPCMD, SD_CC_IPREA SDRAM_WaitIpCmdDone(); __writeMemory32(0xA55A000C, 0x400d409C, "Memory"); // SD_CC_IAF SDRAM_WaitIpCmdDone(); __writeMemory32(0xA55A000C, 0x400d409C, "Memory"); // SD_CC_IAF SDRAM_WaitIpCmdDone(); __writeMemory32(0x00000033, 0x400d40A0, "Memory"); // IPTXDAT __writeMemory32(0xA55A000A, 0x400d409C, "Memory"); // SD_CC_IMS SDRAM_WaitIpCmdDone(); __writeMemory32(0x00000017, 0x400d4150, "Memory"); // DCCR __writeMemory32(0x21210409, 0x400d404C, "Memory"); // enable sdram self refresh after initialization done. __message "SDRAM init done"; } restoreFlexRAM() { __var base; __var value; base = 0x400E4000; value = __readMemory32(base + 0x44, "Memory"); value &= ~(0xFFFF); value |= 0xFFAA; __writeMemory32(value, base + 0x44, "Memory"); value = __readMemory32(base + 0x48, "Memory"); value &= ~(0xFFFF); value |= 0xFFAA; __writeMemory32(value, base + 0x48, "Memory"); value = __readMemory32(base + 0x40, "Memory"); value &= ~(0xFF << 8); value |= 0x7 | (0xaa << 8); __writeMemory32(value, base + 0x40, "Memory"); __message "FlexRAM configuration is restored"; } clearECC() { __writeMemory32(0x00000000, 0x40014100, "Memory"); __writeMemory32(0x00000000, 0x40018100, "Memory"); __writeMemory32(0x00000000, 0x40028108, "Memory"); } execUserPreload() { restoreFlexRAM(); setSemcClock(); initSDRAM(); clearECC(); }   Mainly add the above red code, which is used to clear the MECC and FlexRAM ECC enable bit. Add the modified mac script: Fig 8 Debug result is: Fig 9 We can see, in the IAR also can do the code downloading and debugging, the script also works for the ECC enabled board. 3.3 MDK IDE ECC script and test result   Open the project path: \MIMXRT1170-EVK-hello_world_demo_cm7\hello_world_demo_cm7\evkmimxrt1170 Prepare the file:evkmimxrt1170_flexspi_nor_sdram.ini, the content is:   /* * Copyright 2019-2021 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ FUNC void restoreFlexRAM(void) { unsigned int value; value = _RDWORD(0x400E4044); value &= ~(0xFFFF); value |= 0xFFAA; _WDWORD(0x400E4044, value); value = _RDWORD(0x400E4048); value &= ~(0xFFFF); value |= 0xFFAA; _WDWORD(0x400E4048, value); value = _RDWORD(0x400E4040); value &= ~(0xFF << 8); value |= 0x7 | (0xAA << 8); _WDWORD(0x400E4040, value); } FUNC void SDRAM_WaitIpCmdDone(void) { unsigned long reg; do { reg = _RDWORD(0x400D403C); }while((reg & 0x3) == 0); _WDWORD(0x400D403C,0x00000003); // clear IPCMDERR and IPCMDDONE bits } FUNC void EnableOSC400M(void) { unsigned int reg; // CTRL1: power down reg = _RDWORD(0x40C84050); reg &= ~0x1; _WDWORD(0x40C84050,reg); // CTRL2: enable clock reg = _RDWORD(0x40C84060); reg |= 0x1; _WDWORD(0x40C84060,reg); } FUNC void EnableOSC24M(void) { unsigned int reg; reg = _RDWORD(0x40C84020); if(0 == (reg & 0x10)) { reg = 0x14; // OSC_EN and LP_EN _WDWORD(0x40C84020,reg); reg = _RDWORD(0x40C84020); while (0 == (reg & 0x80000000)); } } FUNC void EnablePllLdo(void) { unsigned int reg; // CTRL_AI_CTRL _WDWORD(0x40C84820,0x00000000); // CTRL_AI_WDATA _WDWORD(0x40C84830,0x00000105); // PMU_LDO_PLL reg = _RDWORD(0x40C84500); reg |= 0x10000; _WDWORD(0x40C84500,reg); _Sleep_(100); // PMU_POWER_DETECT_CTRL _WDWORD(0x40C84580,0x00000100); _Sleep_(1); // PMU_REF_CTRL _WDWORD(0x40C84570,0x00000010); } FUNC void InitSysPll2Pfd1(void) { unsigned int reg; unsigned int stable; // ANADIG_PLL_PLL_528_PFD reg = _RDWORD(0x40C84270); if (((reg & 0x8000) != 0) || (((reg & 0x3F00) >> 😎 != 16)) { stable = reg & 0x4000; reg |= 0x8000; _WDWORD(0x40C84270,reg); reg = _RDWORD(0x40C84270); reg &= ~0x3F00; reg |= 16 << 8; _WDWORD(0x40C84270,reg); reg = _RDWORD(0x40C84250); reg ^= 0x4; _WDWORD(0x40C84250,reg); reg = _RDWORD(0x40C84270); reg &= ~0x8000; _WDWORD(0x40C84270,reg); do { reg = _RDWORD(0x40C84270) & 0x4000; } while (reg == stable); } else { //syspll2 pfd1 has been initialized already reg &= ~0x8000; _WDWORD(0x40C84270,reg); } } FUNC void InitSysPll2(void) { unsigned int reg; // ANADIG_PLL_PLL_528_CTRL reg = _RDWORD(0x40C84240); if (reg & 0x800000) { // SysPll2 has been initialized reg &= ~0x40000000; _WDWORD(0x40C84240, reg); return; } reg = _RDWORD(0x40C84270); reg |= 0x80808080; _WDWORD(0x40C84270, reg); reg = _RDWORD(0x40C84240); reg &= ~(0x802000); reg |= 0x40000000; _WDWORD(0x40C84240, reg); // ANADIG_PLL_PLL_528_MFN _WDWORD(0x40C84280, 0); // ANADIG_PLL_PLL_528_MFI _WDWORD(0x40C84290, 22); // ANADIG_PLL_PLL_528_MFD _WDWORD(0x40C842A0, 0x0FFFFFFF); // ANADIG_PLL_PLL_528_CTRL _WDWORD(0x40C84240, 0x8 | 0x40000000); _Sleep_(30); // ANADIG_PLL_PLL_528_CTRL reg = _RDWORD(0x40C84240); reg |= 0x800000 | 0x800; _WDWORD(0x40C84240, reg); _Sleep_(250); reg = _RDWORD(0x40C84240); reg &= ~0x800; _WDWORD(0x40C84240, reg); do { reg = _RDWORD(0x40C84240); } while ((reg & 0x20000000) == 0); reg |= 0x2000; _WDWORD(0x40C84240, reg); reg &= ~0x40000000; _WDWORD(0x40C84240, reg); } FUNC void SetSemcClock(void) { //EnableOSC400M(); EnablePllLdo(); InitSysPll2(); InitSysPll2Pfd1(); // Set SEMC root clock // Use sys pll2 pfd1 divided by 3: 198Mhz _WDWORD(0x40CC0200,0x00000602); } FUNC void _clock_init(void) { SetSemcClock(); } FUNC void _sdr_Init(void) { // Config IOMUX _WDWORD(0x400E8010, 0x00000000); _WDWORD(0x400E8014, 0x00000000); _WDWORD(0x400E8018, 0x00000000); _WDWORD(0x400E801C, 0x00000000); _WDWORD(0x400E8020, 0x00000000); _WDWORD(0x400E8024, 0x00000000); _WDWORD(0x400E8028, 0x00000000); _WDWORD(0x400E802C, 0x00000000); _WDWORD(0x400E8030, 0x00000000); _WDWORD(0x400E8034, 0x00000000); _WDWORD(0x400E8038, 0x00000000); _WDWORD(0x400E803C, 0x00000000); _WDWORD(0x400E8040, 0x00000000); _WDWORD(0x400E8044, 0x00000000); _WDWORD(0x400E8048, 0x00000000); _WDWORD(0x400E804C, 0x00000000); _WDWORD(0x400E8050, 0x00000000); _WDWORD(0x400E8054, 0x00000000); _WDWORD(0x400E8058, 0x00000000); _WDWORD(0x400E805C, 0x00000000); _WDWORD(0x400E8060, 0x00000000); _WDWORD(0x400E8064, 0x00000000); _WDWORD(0x400E8068, 0x00000000); _WDWORD(0x400E806C, 0x00000000); _WDWORD(0x400E8070, 0x00000000); _WDWORD(0x400E8074, 0x00000000); _WDWORD(0x400E8078, 0x00000000); _WDWORD(0x400E807C, 0x00000000); _WDWORD(0x400E8080, 0x00000000); _WDWORD(0x400E8084, 0x00000000); _WDWORD(0x400E8088, 0x00000000); _WDWORD(0x400E808C, 0x00000000); _WDWORD(0x400E8090, 0x00000000); _WDWORD(0x400E8094, 0x00000000); _WDWORD(0x400E8098, 0x00000000); _WDWORD(0x400E809C, 0x00000000); _WDWORD(0x400E80A0, 0x00000000); _WDWORD(0x400E80A4, 0x00000000); _WDWORD(0x400E80A8, 0x00000000); _WDWORD(0x400E80AC, 0x00000010); // EMC_39, DQS PIN, enable SION _WDWORD(0x400E80B8, 0x00000000); _WDWORD(0x400E80BC, 0x00000000); _WDWORD(0x400E80C0, 0x00000000); _WDWORD(0x400E80C4, 0x00000000); _WDWORD(0x400E80C8, 0x00000000); _WDWORD(0x400E80CC, 0x00000000); _WDWORD(0x400E80D0, 0x00000000); _WDWORD(0x400E80D4, 0x00000000); _WDWORD(0x400E80D8, 0x00000000); _WDWORD(0x400E80DC, 0x00000000); _WDWORD(0x400E80E0, 0x00000000); _WDWORD(0x400E80E4, 0x00000000); _WDWORD(0x400E80E8, 0x00000000); _WDWORD(0x400E80EC, 0x00000000); _WDWORD(0x400E80F0, 0x00000000); _WDWORD(0x400E80F4, 0x00000000); _WDWORD(0x400E80F8, 0x00000000); _WDWORD(0x400E80FC, 0x00000000); // PAD ctrl // PDRV = 1b (normal); PULL = 10b (PD) _WDWORD(0x400E8254, 0x00000008); _WDWORD(0x400E8258, 0x00000008); _WDWORD(0x400E825C, 0x00000008); _WDWORD(0x400E8260, 0x00000008); _WDWORD(0x400E8264, 0x00000008); _WDWORD(0x400E8268, 0x00000008); _WDWORD(0x400E826C, 0x00000008); _WDWORD(0x400E8270, 0x00000008); _WDWORD(0x400E8274, 0x00000008); _WDWORD(0x400E8278, 0x00000008); _WDWORD(0x400E827C, 0x00000008); _WDWORD(0x400E8280, 0x00000008); _WDWORD(0x400E8284, 0x00000008); _WDWORD(0x400E8288, 0x00000008); _WDWORD(0x400E828C, 0x00000008); _WDWORD(0x400E8290, 0x00000008); _WDWORD(0x400E8294, 0x00000008); _WDWORD(0x400E8298, 0x00000008); _WDWORD(0x400E829C, 0x00000008); _WDWORD(0x400E82A0, 0x00000008); _WDWORD(0x400E82A4, 0x00000008); _WDWORD(0x400E82A8, 0x00000008); _WDWORD(0x400E82AC, 0x00000008); _WDWORD(0x400E82B0, 0x00000008); _WDWORD(0x400E82B4, 0x00000008); _WDWORD(0x400E82B8, 0x00000008); _WDWORD(0x400E82BC, 0x00000008); _WDWORD(0x400E82C0, 0x00000008); _WDWORD(0x400E82C4, 0x00000008); _WDWORD(0x400E82C8, 0x00000008); _WDWORD(0x400E82CC, 0x00000008); _WDWORD(0x400E82D0, 0x00000008); _WDWORD(0x400E82D4, 0x00000008); _WDWORD(0x400E82D8, 0x00000008); _WDWORD(0x400E82DC, 0x00000008); _WDWORD(0x400E82E0, 0x00000008); _WDWORD(0x400E82E4, 0x00000008); _WDWORD(0x400E82E8, 0x00000008); _WDWORD(0x400E82EC, 0x00000008); _WDWORD(0x400E82F0, 0x00000008); _WDWORD(0x400E82FC, 0x00000008); _WDWORD(0x400E8300, 0x00000008); _WDWORD(0x400E8304, 0x00000008); _WDWORD(0x400E8308, 0x00000008); _WDWORD(0x400E830C, 0x00000008); _WDWORD(0x400E8310, 0x00000008); _WDWORD(0x400E8314, 0x00000008); _WDWORD(0x400E8318, 0x00000008); _WDWORD(0x400E831C, 0x00000008); _WDWORD(0x400E8320, 0x00000008); _WDWORD(0x400E8324, 0x00000008); _WDWORD(0x400E8328, 0x00000008); _WDWORD(0x400E832C, 0x00000008); _WDWORD(0x400E8330, 0x00000008); _WDWORD(0x400E8334, 0x00000008); _WDWORD(0x400E8338, 0x00000008); _WDWORD(0x400E833C, 0x00000008); _WDWORD(0x400E8340, 0x00000008); // Config SDR Controller Registers/ _WDWORD(0x400d4000,0x10000004); // MCR _WDWORD(0x400d4008,0x00000081); // BMCR0 _WDWORD(0x400d400C,0x00000081); // BMCR1 _WDWORD(0x400d4010,0x8000001D); // BR0, 64MB _WDWORD(0x400d4040,0x00000F32); // SDRAMCR0, 32bit _WDWORD(0x400d4044,0x00772A22); // SDRAMCR1 _WDWORD(0x400d4048,0x00010A0D); // SDRAMCR2 _WDWORD(0x400d404C,0x21210408); // SDRAMCR3 _WDWORD(0x400d4090,0x80000000); // IPCR0 _WDWORD(0x400d4094,0x00000002); // IPCR1 _WDWORD(0x400d4098,0x00000000); // IPCR2 _WDWORD(0x400d409C,0xA55A000F); // IPCMD, SD_CC_IPREA SDRAM_WaitIpCmdDone(); _WDWORD(0x400d409C,0xA55A000C); // SD_CC_IAF SDRAM_WaitIpCmdDone(); _WDWORD(0x400d409C,0xA55A000C); // SD_CC_IAF SDRAM_WaitIpCmdDone(); _WDWORD(0x400d40A0,0x00000033); // IPTXDAT _WDWORD(0x400d409C,0xA55A000A); // SD_CC_IMS SDRAM_WaitIpCmdDone(); _WDWORD(0x400d4150,0x00000017); // DCCR _WDWORD(0x400d404C,0x21210409 ); // enable sdram self refresh again after initialization done. } FUNC void Setup (void) { SP = _RDWORD(0x30002000); // Setup Stack Pointer PC = _RDWORD(0x30002004); // Setup Program Counter _WDWORD(0xE000ED08, 0x30002000); // Setup Vector Table Offset Register } FUNC void DisableECC(){ _WDWORD(0x40014100, 0x00000000); _WDWORD(0x40018100, 0x00000000); _WDWORD(0x40028108, 0x00000000); } FUNC void OnResetExec (void) { // executes upon software RESET _clock_init(); _sdr_Init(); DisableECC(); Setup(); // Setup for Running } restoreFlexRAM(); _clock_init(); _sdr_Init(); DisableECC(); LOAD %L INCREMENTAL // Download Setup(); // Setup for Running // g, main  In the project, add the prepared script file: Fig 10 Debug result is: Fig 11 We can see, in MDK, debugging can also be successful using CMSIS DAP. Information sharing: For MCUXPresso IDE, subsequent scripts will automatically add ECC support, the new version is MCUXpresso11.10.0. Scripts for other IDEs need to be added by themselves.  
View full article
How to load MDK RAM app to the RT1170 external flash 1. Abstract This guide is requested by our end customer, he wants to realize the MDK project RAM code download to the MIMXRT1170 external QSPI flash. So, based on the NXP RT1170 SDK, and the MIMXRT1170-EVK board, generate the MDK project, reallocate the app image, generate the image, and use the tool to download the code to the external flash. 2. App image prepare 2.1 Generate one SDK MDK standalone project Open the SDK_2_15_000_MIMXRT1170-EVK webpage: https://mcuxpresso.nxp.com/en/builder?hw=MIMXRT1170-EVK Download the SDK, and generate one MDK standalone project: Fig 1 Fig 2 After downloading, we will get the MIMXRT1170-EVK-iled_blinky_cm7.zip, which is the MDK project. 2.2 MDK project image reallocation As the RAM image is the none-xip image, normally to the IDE, can’t download to the flash directly, as the debug is in the RAM. If want to download to the flash, we can generate the app image, then use the tool to program it to the external flash. Normally, none-xip image, the IVT offset is 0X400, so we need to reallocate the image start address, here, we can use 0X2000 as the app entry address. Fig 3 ITCM default size is 256K=0X4000, so modify the linker file-> scf file like this: Fig 4 Now, to generate the hex and bin image file, which is used for the tool downloading. Fig 5 To build the bin file command: $K\ARM\ARMCC\bin\fromelf.exe --bin --output=debug\@L.bin !L Fig 6   Building, you can find the file in the folder: MIMXRT1170-EVK-iled_blinky_cm7\iled_blinky_cm7\debug Fig 7 2.3 MDK project debug after reallocation After the image reallocation, some customer may still need the MDK RAM project can do the debugging, here, also need to modify the debug .ini file. The Setup also need to change the SP, PC and Vector table offset register address. Fig 8 Then build and debug the code, we can find it can enter the ram image debug mode: Fig 9 3. App image download We can use the MCUBootUtility Tool to download the code: https://github.com/JayHeng/NXP-MCUBootUtility/releases/tag/v6.1.0 the related user manual is: https://github.com/JayHeng/NXP-MCUBootUtility Download the tool. MIMXRT1170-EVK enter the serial download mode by changing SW1: 1-OFF,2-OFF,3-OFF,4-ON Power off and power on the board again, find another USB cable to connect the J20 USB1 interface. Then, use the MCUBootutility to connect the board: Fig 10 After connection, select the MDK project generated .hex file: Fig 11 Press the All-in-One-Action button, to download the code, this is the downloaded result: Fig 12 Press the “Reset device” button to exit the tool. Then MIMXRT1170-EVK board change SW1: 1-OFF, 2-OFF, 3-ON,4-OFF Press the EVK on board reset button, SW4, you will find the LED is blinking, it means the MDK RAM project already download to the external QSPI flash, and boot OK.
View full article
1 Introduction RT1060 MCUXpresso SDK provide the ota_bootloader project, download link: https://mcuxpresso.nxp.com/en/welcome ota_bootloader path: SDK_2_10_0_EVK-MIMXRT1060\boards\evkmimxrt1060\bootloader_examples\ota_bootloader  this ota_bootloader can let the customer realize the on board app update, the RT1060 OTA bootloader mainly have the two functions: ISP method update APP Provide the API to the customer, can realize the different APP swap and rollback function ISP method is from the flashloader, the flashloader put the code in the internal RAM, when use it, normally RT chip need to enter the serial download mode, and use the sdphost download the flashloader to the internal RAM, then run it to realize the ISP function. But OTA_bootloader will put the code in the flash directly, RT chip can run it in the internal boot mode directly, each time after chip reset, the code will run the ota_bootloader at first, this time, customer can use the blhost to communicate the RT chip with UART/USB HID directly to download the APP code. So, to the ISP function in the OTA_bootloader, customer also can use it as the ISP secondary bootloader to update the APP directly. Then reset after the 5 seconds timeout, if the APP area is valid, code will jump to the APP and run APP. To the ota_bootloader swap and rollback function, customer can use the provided API in the APP directly to realize the APP update, run the different APP, swap and rollback to the old APP.    This document will give the details about how to use the ota_bootloader ISP function to update the APP, how to modify the customer application to match the ota_bootloader, how to resolve the customer APP issues which need to use the SDRAM with the ota_bootloader, how to use the ota_bootloader API to realize the swap and rollback function, and the related APP prepare. 2 OTA bootloader ISP usage Some customers need the ISP secondary bootloader function, as the flashloader need to put in the internal RAM, so customer can use the ota_bootloader ISP function realize the secondary bootloader requirement, to this method, customer mainly need to note two points: 1) APP need to add the ota_header to meet the ota_bootloader demand. 2) APP use the SDRAM, ota_bootloader need to add DCD memory  2.1 application modification OTA_bootloader located in the flash from 0x60000000, APP locate from 0x60040000. We can find this from the ota_bootloader bootloader_config.h file: #define BL_APP_VECTOR_TABLE_ADDRESS (0x60040000u) From 0x60040000, the first 0X400 should put the ota_header, then the real APP code is put from 0x60040400. Now, take the SDK evkmimxrt1060_iled_blinky project as an example, to modify it to match the ota_bootloader. Application code need to add these files: ota_bootloader_hdr.c, ota_bootloader_board.h, ota_bootloader_supp.c, ota_bootloader_supp.h These files can be found from the evkmimxrt1060_lwip_httpssrv_ota project, put the above files in the led_blinky source folder. 2.1.1 memory modification    APP memory start address modify from 0x60000000 to 0x60040000, which is the ota_bootloader defined address。 Fig 1 2.1.2 ota_hdr related files add   In the led_blinky project source folder, add the mentioned ota_bootloader related files. Fig 2 2.1.3 linker file modification In the evkmimxrt1060_iled_blinky_debug.ld, add boot_hdr, length is 0X400. We can use the MCUXpresso IDE linkscripts folder, add the .ldt files to modify the linker file. Here, in the project, add one linkscripts folder, add the boot_hdr_MIMXRT1060.ldt file, build . Fig 3 After the build, we can find the ld file already add the ota_header in the first 0x400 range. Fig 4 The above is the generated evkmimxrt1060_iled_blinky_ota_0x60040000.bin file, we can find already contains the boot_hdr. From offset 0x400, will put the real APP code. 2.2 ISP test related command Test board is MIMXRT1060-EVK, download the evkmimxrt1060_ota_bootloader project at first, can use the mcuxpresso IDE download it directly, then press the reset button, put the blhost and evkmimxrt1060_iled_blinky_ota_0x60040000.bin in the same folder, use the following command:   blhost.exe -t 50000 -u 0x15a2,0x0073 -j -- get-property 1 0 blhost.exe -t 2048000 -u 0x15a2,0x0073 -j -- flash-erase-region 0x60040000 0x6000 9 blhost.exe -t 5242000 -u 0x15a2,0x0073 -j -- write-memory 0x60040000 evkmimxrt1060_iled_blinky_ota_0x60040000.bin blhost.exe -t 5242000 -u 0x15a2,0x0073 -j -- read-memory 0x60040000 0x6000 flexspiNorCfg.dat 9 Fig 5 After the download is finished, press the reset, wait 5 seconds, it will jump to the app, we can find the MIMXRT1060-EVK on board led is blinking. This method realize the flash ISP bootloader downloading and the app working. The above command is using the USB HID to download, if need to use the UART, also can use command like this: blhost.exe -t 50000 -p COM45,19200 -j -- get-property 1 0 blhost.exe -t 50000 -p COM45,19200 -j -- flash-erase-region 0x60040000 0x6000 9 blhost.exe -t 50000 -p COM45,19200 -j -- write-memory 0x60040000 evkmimxrt1060_iled_blinky_ota_0x60040000.bin blhost.exe -t 50000 -p COM45,19200 -j -- read-memory 0x60040000 0x6000 flexspiNorCfg.dat 9 To the UART communication, as the ota_bootloader auto baud detect issues, it’s better to use the baudrate not larger than 19200bps, or in the ota_bootloader code, define the fixed baudrate, eg, 115200. 2.3 Bootloader DCD consideration Some customer use the ota_bootloader to associate with their own application, which is used the external SDRAM, and find although the downloading works, but after boot, the app didn’t run successfully. In the APP code, it add the DCD configuration, but as the ota_bootloader already put in the front of the QSPI, app will use the ota_hdr, which will delete the dcd part, so, to this situation, customer can add the dcd in the ota_bootloader. The SDK ota_bootloader didn’t add the dcd part in default. Now, modify the ota_bootloader at first, then prepare the sdram app and test it. 2.3.1 Ota_bootloader add dcd 2.3.1.1 add dcd files   ota_bootloader add dcd.c dcd.h, these two files can be found from the SDK hello_world project. Copy these two files to the ota_bootloader board folder. From the dcd.c code, we can see, dcd_data[] array is put in the “.boot_hdr.dcd_data” area, and it need to define the preprocessor: XIP_BOOT_HEADER_DCD_ENABLE=1 2.3.1.2 add dcd linker code   Modify the mcuxpresso IDE ld files, and add the dcd range.    .ivt : AT(ivt_begin) { . = 0x0000 ; KEEP(* (.boot_hdr.ivt)) /* ivt section */ . = 0x0020 ; KEEP(* (.boot_hdr.boot_data)) /* boot section */ . = 0x0030 ; KEEP(*(.boot_hdr.dcd_data)) __boot_hdr_end__ = ABSOLUTE(.) ; . = 0x1000 ; } > m_ivt Fig 6 0x60001000 : IVT 0x6000100c : DCD entry point 0x60001020 : boot data 0x60001030 : DCD detail data 2.3.1.3 add IVT dcd entry point   ota_bootloader->MIMXRT1062 folder->hardware_init_MIMXRT1062.c,modify the image_vector_table, fill the DCD address to the dcdc_data array address as the dcd entry point. Fig 7 Until now, we finish the ota_bootloader dcd add, build the project, generate the image, we can find, DCD already be added to the ota_bootloader image. Fig 8 DCD entry point in the IVT and the DCD data is correct, we can burn this modified ota_bootloader to the MIMXRT1060-EVK board. 2.3.2 SDRAM app prepare Still based on the evkmimxrt1060_iled_blinky project, just put some function in the SDRAM. From memory, we can find, the SRAM is in the RAM4: Fig 9 In the led_blinky.c, add this header: #include <cr_section_macros.h> Then, put the systick delay code to the RAM4 which is the SDRAM area. __RAMFUNC(RAM4) void SysTick_DelayTicks(uint32_t n) { g_systickCounter = n; while (g_systickCounter != 0U) { } } Now, we already finish the simple SDRAM app, we also can test it, and put the breakpoint in the SysTick_DelayTicks, we can find the address is also the SDRAM related address, generate the evkmimxrt1060_iled_blinky1_SDRAM_0x60040000.bin. If use the old ota_bootloader to download this .bin, we can find the led is not blinking. 2.3.3 Test result Command refer to chapter 2.2 ISP test related command, download the evkmimxrt1060_iled_blinky1_SDRAM_0x60040000.bin with the new modified ota_bootloader project, we can find, after reset, the led will blink. So the SDRAM app works with the modified ota_bootloader. 3 OTA bootloader swap and rollback OTA bootloader can realize the swap and rollback function, this part will test the ota_bootloader swap and rollback, prepare two apps and download to the different partition area, then use the UART input char to select the swap or rollback function, to check the which app is running. 3.1  memory map ota bootloader memory information. Fig 10 The above map is based on the external 8Mbyte QSPI flash. OTA bootloader: RT SDK ota bootloader code Boot meta 0: contains 3 partition start address, size etc. information, ISP peripheral information. Boot meta 1: contains 3 partition start address, size etc. information, ISP peripheral information. Swap meta 0: bootloader will use meta data to do the swap operation Swap meta 1: bootloader will use meta data to do the swap operation Partition 1: APP1 location Partition 2: APP2 location Scratch part: APP1 backup location, start point is before 0x60441000, which is enough to put APP1 and multiple sector size, eg, APP1 is 0X5410, sector size is 0x1000, APP1 need 6 sectors, so the scratch start address is 0x60441000-0x6000=0x43b000. User data: user used data area 3.2 swap and rollback basic Fig11  The APP1 and APP2 put in the partition1 and partition2 need to contains the ota_header which meet the bootloader demand, bootloader will check the APP CRC, if it passed, then will boot the app, otherwise, it will enter the ISP mode. Partition2 image need to has the valid header, otherwise, swap will be failed.   Swap function will erase partition2 scratch area, then put the partition1 code to the scratch, erase the partition 1 position, and write the partition 2 image to the partition1 position.   Rollback function will run the previous APP1, erase the partition 2 position, copy the parititon 1 image back to the partition 2, erase partition 1 position, copy partition 2 scratch image back to partition 1. 3.2.1 boot meta boot_meta 0: 0x0x6003c000 size: 0x20c boot_meta 1: 0x0x6003d000 size: 0x20c   OTA bootloader can read boot meta from the two different address, when the two address meta are valid(tag is 'B', 'L', 'M', 'T'), bootloader will choose the bigger version meta. If both meta is not valid, bootloader will copy default boot meta data to the boot meta 0 address.   Boot meta contains 3 partition start address, size information. SDK demo can call bootloader API to find the partition information, then do the image program.   Boot meta also contains the ISP peripheral information, the timeout(5s) information, the structure is: //!@brief Partition information table definitions typedef struct { uint32_t start; //!< Start address of the partition uint32_t size; //!< Size of the partition uint32_t image_state; //!< Active/ReadyForTest/UnderTest uint32_t attribute; //!< Partition Attribute - Defined for futher use uint32_t reserved[12]; //!< Reserved for future use } partition_t; //!@brief Bootloader meta data structure typedef struct { struct { uint32_t wdTimeout; uint32_t periphDetectTimeout; uint32_t enabledPeripherals; uint32_t reserved[12]; } features; partition_t partition[kPartition_Max];//16*4*7 bytes uint32_t meta_version; uint32_t patition_entries; uint32_t reserved0; uint32_t tag; } bootloader_meta_t;   3.2.2 swap meta Swap meta 0 : 0x6003e000, size 0x50 Swap meta 1 : 0x6003f000, size 0x50    OTA bootloader will read the swap meta from 2 different place, if it is not valid, bootloader will set the default data to swap meta 0(0x6003e000). If both image are valid, bootloader will choose the bigger version meta.    Bootloader will refer to the meta data to do the swap operation, sometimes, after reset, meta data will be modified automatically. It mainly relay on the swap_type: kSwapType_ReadyForTest :After reset, do swap operation. Modify meta swap_type to kSwapType_Test. Reset, as the meta data is kSwapType_Test, bootloader can do the rollback. kSwapType_Test : After reset, do rollback after operation, swap_type change to kSwapType_None.  kSwapType_Rollback bootloader will write kSwapType_Test to the meta, after reset, bootloader will refer to kSwapType_Test to do the operation.  kSwapType_Permanent After reset, modify meta data to kSwapType_Permanent, then the APP will boot with partition 1. Swap structure: //!@brief Swap progress definitions typedef struct { uint32_t swap_offset; //!< Current swap offset uint32_t scratch_size; //!< The scratch area size during current swapping uint32_t swap_status; // 1 : A -> B scratch, 2 : B -> A uint32_t remaining_size; //!< Remaining size to be swapped } swap_progress_t; typedef struct { uint32_t size; uint32_t active_flag; } image_info_t; //!@brief Swap meta information, used for the swapping operation typedef struct { image_info_t image_info[2]; //!< Image info table #if !defined(BL_FEATURE_HARDWARE_SWAP_SUPPORTED) || (BL_FEATURE_HARDWARE_SWAP_SUPPORTED == 0) swap_progress_t swap_progress; //!< Swap progress #endif uint32_t swap_type; //!< Swap type uint32_t copy_status; //!< Copy status uint32_t confirm_info; //!< Confirm Information uint32_t meta_version; //!< Meta version uint32_t reserved[7]; //!< Reserved for future use uint32_t tag; } swap_meta_t; 3.3 Common used API Bootloader provide the API for the customer to use it, the common used API are: 3.3.1 update_image_state   update swap meta data, before update, it will check the partition1 image valid or not, if image is not valid, swap meta data will not be updated, and return failure. 3.3.2 get_update_partition_info   get partition information, then define the image program address. 3.3.3 get_image_state   get the current boot image status. None/permanent/UnderTest 3.4  Swap rollback APP prepare Prepare two APPs: APP1 and APP2 bin file, and use the USB HID download to the partition1 and paritition 2. After reset, run APP1 in default, then use the COM input to select the swap and rollback function. Code is: int main(void) { char ch; status_t status; /* Board pin init */ BOARD_InitPins(); BOARD_InitBootClocks(); /* Update the core clock */ SystemCoreClockUpdate(); BOARD_InitDebugConsole(); PRINTF("\r\n------------------hello world + led blinky demo 2.------------------\r\n"); PRINTF("\r\nOTA bootloader test...\r\n" "1 - ReadyForTest\r\n" "3 - kSwapType_Permanent\r\n" "4 - kSwapType_Rollback\r\n" "5 - show image state\r\n" "6 - led blinking for 5times\r\n" "r - NVIC reset\r\n"); // show swap state in swap meta get_image_swap_state(); /* Set systick reload value to generate 1ms interrupt */ if (SysTick_Config(SystemCoreClock / 1000U)) { while (1) { } } while(1) { ch = GETCHAR(); switch(ch) { case '1': status = bl_update_image_state(kSwapType_ReadyForTest); PRINTF("update_image_state to kSwapType_ReadyForTest status: %i\n", status); if (status != 0) PRINTF("update_image_state(kSwapType_ReadyForTest): failed\n"); else NVIC_SystemReset(); break; case '3': status = bl_update_image_state(kSwapType_Permanent); PRINTF("update_image_state to kSwapType_Permanent status: %i\n", status); if (status != 0) PRINTF("update_image_state(kSwapType_Permanent): failed\n"); else NVIC_SystemReset(); break; case '4': status = bl_update_image_state(4); // PRINTF("update_image_state to kSwapType_Rollback status: %i\n", status); if (status != 0) PRINTF("update_image_state(kSwapType_Rollback): failed\n"); else NVIC_SystemReset(); break; case '5': // show swap state in swap meta get_image_swap_state(); break; case '6': Led_blink10times(); break; case 'r': NVIC_SystemReset(); break; } } } When download the APP, need to use the correct ota_header in the first 0x400 area, otherwise, swap will failed. const boot_image_header_t ota_header = { .tag = IMG_HDR_TAG, .load_addr = ((uint32_t)&ota_header) + BL_IMG_HEADER_SIZE, .image_type = IMG_TYPE_XIP, .image_size = 0, .algorithm = IMG_CHK_ALG_CRC32, .header_size = BL_IMG_HEADER_SIZE, .image_version = 0, .checksum = {0xFFFFFFFF}, }; This is a correct sample: Fig 12 Image size and checksum need to use the real APP image information, in the attached file, provide one image_header_padding.exe, it can input the none ota header image, then it will output the whole image which add the ota_header contains the image size and the image crc data in the first 0x400 range. 3.5 Test steps and result Prepare the none header APP1 evkmimxrt1060_APP1_0X60040400.bin, APP2 image evkmimxrt1060_APP2_0X60240400.bin, and put blhost.exe, image_header_padding.exe in the same folder. APP1 and APP2 just the printf version is different, one is version1, another is version2. Printf result: hello world + led blinky demo 1 hello world + led blinky demo 2 Attached OTAtest folder is used for test, but need to download the blhost.exe from the below link, and copy the blhost.exe to the OTAtest folder. https://www.nxp.com/webapp/sps/download/license.jsp?colCode=blhost_2.6.6&appType=file1&DOWNLOAD_ID=null Then run the following bat command: image_header_padding.exe evkmimxrt1060_APP1_0X60040400.bin 0x60040400 sleep 20 blhost.exe -t 50000 -u 0x15a2,0x0073 -j -- get-property 1 0 sleep 20 blhost.exe -u -t 1000000 -- flash-erase-region 0x6003c000 0x4000 sleep 50 blhost -u -t 5000 -- flash-erase-region 0x60040000 0x10000 sleep 50 blhost -u -t 5000 -- write-memory 0x60040000 boot_img_crc32.bin sleep 100 image_header_padding.exe evkmimxrt1060_APP2_0X60240400.bin 0x60040400 sleep 20 blhost -u -t 5000 -- flash-erase-region 0x60240000 0x10000 sleep 50 blhost -u -t 5000 -- flash-erase-region 0x6043b000 0x10000 sleep 50 blhost -u -t 5000 -- write-memory 0x60240000 boot_img_crc32.bin sleep 100 pause The function is to generate the APP1 with the correct ota_header, erase parititon 1,program APP1 to partition 1, generate the APP2 with the correct ota_header, erase partition 2 and scratch area, program APP2 to partition 2, Run the .bat file should in 5 seconds after reset, then use the ISP to connect it: blhost.exe -t 50000 -u 0x15a2,0x0073 -j -- get-property 1 0 ota_bootloader can use the ota_bootloader project download directly to the 0X60000000 area. After downloading, reset the chip, and wait for 5 seconds, the APP will run. This is the test result: Fig 13 From the test result, we can find, in the first time boot, APP1 running, image state: none Input 1, do the swap, will find the APP2 running, image state: undertest Input 3, select permanent, reset will find, still APP2 running, image state: permanent Input 4, choose rollback, reset will find APP1 running, image states: none Until now, finish the swap and rollback function. Input 6, will find the APP contains the SDRAM led blinky is working.          
View full article
The RT1170 supports the ability to trigger dual ADC’s in SyncMode or AsyncMode via the ADC External Trigger Control (ADC_ETC): In SyncMode, ADC1 and ADC2 are controlled by the same trigger source. In AsyncMode, ADC1 and ADC2 are controlled by separate trigger source. In AsyncMode (TRIGa_CTRL[SYNC_MODE]==0), the ADC conversion clock frequency maximum is 88 MHz, but in SyncMode (TRIGa_CTRL[SYNC_MODE]==1), the ADC conversion clock frequency must be constraint to a lower frequency due to switching noise inherent to its design architecture.  Reducing the conversion clock frequency reduces the switching noise that is observed. NXP is currently conducting further characterization in order to specify the maximum frequency of conversion in SyncMode across process, voltage, and temperature.  However, on typical samples at room temperature 60 MHz is the maximum frequency.
View full article
RT1170 SBL ISP download app for remap function 1. Abstract Previously wrote a post about using the official SBL ISP method to download the APP: RT1170 SBL ISP download SDRAM APP Recently, a customer also needs to use RT1170 SBL ISP to download code, but the code app that needs to be downloaded is based on MCUXpresso IDE, which generates a bin file and can be programmed to different locations in the flash, and then use remap to run the corresponding app. Regarding remap, we can know from the SBL documentation that RT1170 can directly support it: Fig 1 Usually, if combined with SFW for SD card, cloud and other app updates, the remap function can be directly supported. Because SFW currently only supports two IDEs: IAR and MDK, and does not support MCUXPresso IDE, it is not particularly convenient for customers to use MCUXPresso to develop apps. Moreover, customers do not need to use an SD card or network cloud to update the code. SBL's ISP Updates are enough. So how to use SBL to implement the remap function of two MCUXpresso apps? For MCUXpresso App, you can use one project, so you only need to modify the content to identify different apps and burn them to different flash addresses. The specific implementation methods and steps are given below. The spatial structure of SBL, APP1, and APP2 codes is as follows: Fig 2 2. SBL operation and modification 2.1 SBL configuration and downloading Refer to the doc chapter 2.1: RT1170 SBL ISP download SDRAM APP Generate the related sbl_iar project. 2.2 SBL add remap code Opern sbl project file: sbl-master\boot\sbl_boot.c int sbl_boot_main(void) code modified like this: int sbl_boot_main(void) { char ch = 0; struct image_header br_hdr1 = { .ih_hdr_size = 0x2000 }; struct boot_rsp rsp = { .br_hdr = &br_hdr1, .br_flash_dev_id = 1, .br_image_off = 0x80000 }; int rc = 0; #ifdef CONFIG_BOOT_SIGNATURE #if defined(SOC_IMXRTYYYY_SERIES) || defined(SOC_LPC55S69_SERIES) CRYPTO_InitHardware(); #endif #endif sbl_flash_init(); #ifdef TEST_FUNCTION enable_image(Permanent_mode); #endif BOOT_LOG_INF("Bootloader Version %s", BOOTLOADER_VERSION); os_heap_init(); BOOT_LOG_INF("remap or not:Y/N\r\n\r\n"); ch = GETCHAR(); BOOT_LOG_INF("input=%c,\r\n\r\n",ch); if((ch == 'Y') || (ch == 'y')) { BOOT_LOG_INF("With remap!\r\n\r\n"); SBL_EnableRemap(BOOT_FLASH_ACT_APP, BOOT_FLASH_ACT_APP+FLASH_AREA_IMAGE_1_SIZE, FLASH_AREA_IMAGE_1_SIZE); } else if((ch == 'N') || ((ch == 'n') )) { BOOT_LOG_INF("Without remap!\r\n\r\n"); SBL_DisableRemap(); } else { BOOT_LOG_INF("Without remap!\r\n\r\n"); } #ifdef SINGLE_IMAGE rc = boot_single_go(&rsp); #else #ifdef SOC_REMAP_ENABLE rc = boot_remap_go(&rsp); #else rc = boot_go(&rsp); #endif #endif /* SINGLE_IMAGE*/ if (rc != 0) { while (1) { BOOT_LOG_ERR("Unable to find bootable image"); SDK_DelayAtLeastUs(3000000, BOARD_BOOTCLOCKRUN_CORE_CLOCK); } } BOOT_LOG_INF("Bootloader chainload address offset: 0x%x", rsp.br_image_off); BOOT_LOG_INF("Reset_Handler address offset: 0x%x", rsp.br_image_off + rsp.br_hdr->ih_hdr_size); BOOT_LOG_INF("Jumping to the image\r\n\r\n"); do_boot(&rsp); BOOT_LOG_ERR("Never should get here"); for (;;); } After modification, build the IAR SBL project, then use the debugger download the sbl to the MIMXRT1170-EVK board. 3. APP prepare Refer to doc chapter 2.2: RT1170 SBL ISP download SDRAM APP In order to know the detail app, we can use the hello_world project, and modify the code like the following: int main(void) { char ch; /* Init board hardware. */ BOARD_ConfigMPU(); BOARD_InitPins(); BOARD_BootClockRUN(); BOARD_InitDebugConsole(); PRINTF("hello world1->real addr is 0X30100000\r\n"); //app1 // PRINTF("hello world2->real addr is 0X30200000\r\n");//app2 while (1) { ch = GETCHAR(); PUTCHAR(ch); } } Use app1 printf code, to generate the hello_world1.bin,then add the secure header which match to the SBL, generate the hello_app1.bin. Use app2 printf code, to generate the hello_world2.bin,then add the secure header which match to the SBL, generate the hello_app2.bin. Now, give the details how to generate the related secure app: Open sbl-master\target\evkmimxrt1170\env.bat: Change the path to: cd ..\..\component\secure\mcuboot\scripts copy the mcuxpresso project generated bin file:hello_world1.bin and hello_world2.bin to: sbl-master\component\secure\mcuboot\scripts Use the following commander: python imgtool.py sign --key xxxx_priv.pem --align 4 --version "1.1" --header-size 0x400 --pad-header --slot-size 0x100000 --max-sectors 32 hello_world1.bin hello_app1.bin python imgtool.py sign --key xxxx_priv.pem --align 4 --version "1.1" --header-size 0x400 --pad-header --slot-size 0x100000 --max-sectors 32 hello_world2.bin hello_app2.bin to generate the hello_app1.bin, hello_app2.bin. Fig 3 3. Test result Use the MCUbootutility SBL OTA run mode, after board reset, in 5 seconds to connect the board, then burn: hello_app1.bin to 0X30100000 hello_app2.bin to 0X30200000 Fig 4 Fig 5 After downloading, exit MCUBootutility. Reset the board, in the console wait the log appear, then input ‘Y’ or ‘N’ to select which app boots: ‘Y’: remap, APP2 boot ‘N’: without remap, APP1 boot Test result is:   Fig 6 From the test result, we can see the remap function already works OK.  
View full article
This demo code shows how to synchronize the PWM signals with another internal timer or an off-chip source. It allows you to achieve slower PWM frequencies than those that can be achieved with internal clocks as well as that multiple modules and multiple chips can be synchronized to each other. The idea is the following: The QTMR generates a PWM signal (external clock signal) which is routed through the XBAR to clocking the eFlexPWM, and at the same, the external clock signal is routed to an IO PAD in the first MCU. In a second MCU, an IO PAD is routed through the XBAR to clocking the eFlexPWM (see Figure 1).   Figure 1     The demo code (only MCU1 part so far) can me tested using the SDK for EVKB-IMXRT1050 v2.14.0. Probe the PWM signals using an oscilloscope: - At J24-1  GPIO_AD_B0_03  XBAR1_INOUT17 (QTMR PWM signal) - At J24-6  GPIO_SD_B0_00  FLEXPWM1_PWM0_A - At J24-3  GPIO_SD_B0_01  FLEXPWM1_PWM0_B Please remember weld resistors 0Ω at R280 R281.
View full article
RT1050 Boundary Scan test based on lauterbach 1. Abstract Boundary Scan is a method of testing interconnections on circuit boards or internal sub-blocks of circuits. You can also debug and observe the pin status of the integrated circuit, measure the voltage or analyze the sub-modules inside the integrated circuit, and test based on the JTAG interface. NXP officials have provided two good application notes: AN13507 (LPC) and AN12919 (RT). Based on the reference application note test method, this article provides the boundary scan test results for NXP MIMXRT1050-EVK revA1. It can use Lauterbach to connect the chip and perform boundary scan to control the external pins. A script file is also provided. It can realize one-click connection to boundary scan and achieve level control of external pins. 2. RT1050 test details 2.1 Hardware platform Lauterbach:LA3050 MIMXRT1050-EVK rev A1 hardware modification point are as follows: (1)Modify fuse bit 0X460[19], which is DAP_SJC_SWD_SEL, from 0-SWD to 1-JTAG. To modify Fuse, you can enter serial download mode and use MCUbootUtility to connect and modify it. Fig 1 (2)DNP R38 ,R323,R309,R152,R303   (3)  JTAG_MODE connect to 3.3V= on board TP11 connect to J24_8 (4)R35 connect 100K resistor (5)ONOFF pin pull up external 100K resistor to 3.3V,board modification point is SW2 pin3 or pin4 connect 100K resistor and pull up to J24_8.    (6) disconnect J32,J33 which will disconnect the on board debugger, because this test need to use the external Lauterbach.    (7) Use the external Lauterbach connect to JTAG interface J21, the connection picture is:     Fig 2 2.2 Software operation Download Lauderbach's supporting software and install it. After installation, open the TRACE32 ICD Arm USB. If the Lauderbach device is connected, the interface will open successfully. Fig 3 At this time, you can enter the relevant commands in the yellow box in the picture above. Here you need to prepare the .bsdl file of the chip, which is usually placed on the chip introduction page of nxp.com. For example, the link to the bsdl file of RT1050 is https://www.nxp.com/downloads/en/bsdl/RT1050.bsdl You can copy the RT1050.bsdl file to the Lauderbach installation path: C:\T32 Next, enter the following command in the window to open the boundary scan window SYStem.Mode Down BSDL.RESet BSDL.ParkState Select-DR-Scan BSDL.state Here, it will open the window: Fig 4 Click FILE item, input the downloaded RT1050.bsdl , then in the window input the commander: BSDL.SOFTRESET     Fig 5 Click check->BYPASSall,IDCODEall,SAMPLEall, make sure the 3 methods can be passed. It is found here that the following problems are encountered when clicking IDCODEall:   Fig 6 It prompts that the IDCODE read is 188c301d, but the expected IDCODE is 088c301d. So what is the correct IDCODE? You can view RT1050RM:   Fig 7 It can be seen that the currently read 188c301d is in line with RM and is correct. Therefore, the version of bsdl downloaded from the official website needs to be modified. Open the RT1050.bsdl file:   Fig 8 Modify line 408 version from 0000 to 0001,Fig 8 is the modified result. Save, run the above commands again, we can see the current BYPASSall,IDCODEall,SAMPLEall connection result is:   Fig 9 Fig 10 Fig 11 To test the output control situation you need to do: BSDLSET 1.: instructions->EXTEXT, DR mode->Set Write, Filter data-> uncheck intern BSDL.state->Run: check SetAndRun, TwoStepDR,  Click RUN button. BSDLSET 1. Window, you can control the pin output status, eg, control GPIO_AD_B1_06 which is J22_2, control the output level: 1 high, 0 low.   Fig 12 2.3 Automation control command script As can be seen from Section 2.2, single-step operation requires manual typing of commands. In actual testing, the efficiency is very low, so scripting language can be used to directly implement automated command control. Below, we take RT1050 as an example to control the level of the onboard GPIO_AD_B1_06 and J22_2 pins, and use a multimeter to test the high and low levels. In this way, when the TRACE32 software is opened, you only need to open the script directly, enter the debug mode, run it to the end with one click, and view the board Just turn on the light and control the status. Script language, suffix .cmm, step: File->New Script, enter the following script command: ;system setup SYStem.Mode Down SYStem.CPU CortexM7 SYSTEM.CONFIG.DEBUGPORTTYPE JTAG SYStem.JtagClock 1MHz ;BSDL Settings BSDL.RESet BSDL.ParkState Select-DR-Scan BSDL.state ;configure boundary scan chain BSDL.FILE RT1050.bsdl ;Check boundary scan chain BSDL.SOFTRESET BSDL.BYPASSall BSDL.IDCODEall BSDL.SAMPLEall ;Perform Sample test BSDL.RUN BSDL.SetAndRun ON BSDL.TwoStepDR ON BSDL.SET 1. BSDL.SET 1. IR EXTEST BSDL.RUN BSDL.SET 1. PORT GPIO_AD_B1_06 0 BSDL.SET 1. PORT GPIO_AD_B1_06 1 BSDL.SET 1. PORT GPIO_AD_B1_06 0 WAIT 6.s BSDL.SET 1. PORT GPIO_AD_B1_06 1 WAIT 6.s BSDL.SET 1. PORT GPIO_AD_B1_06 0 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 1 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 0 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 1 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 0 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 1 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 0 WAIT 2.s BSDL.SET 1. PORT GPIO_AD_B1_06 1 WAIT 2.s Function: Pull the GPIO_AD_B1_06 pin high and low 6 times, with no delay, delay 5s, delay 2s. After the script is written, save it and debug it.   Fig 13 This is the video for the testing: It can be seen that automatic control of the onboard GPIO_AD_B1_06 and J22_2 pins can be achieved, and there is no disconnection issue when the test delay is greater than 5S, indicating that the BSDL automatic test has been completed so far. If you encounter problems, be sure to pay attention to whether the hardware modification points of the board have been completely modified.   At last, thanks so much for my colleague @leilei_du  and @albert_li 's endless help!    
View full article
RT1170 SBL ISP download SDRAM APP 1. Abstract NXP officially launched SBL and SFW for RT bootloader, which can well meet the requirements for secondary bootloader in regular use. Such as ISP, OTA, encryption and other functions. For specific SBL/SFW situations, you can view the application notes: https://www.nxp.com/docs/en/user-guide/MCUOTASBLSFWUG.pdf This article is mainly based on SBL and uses the ISP method to download user apps. Recently I encountered a case about RT1170 using the SBL ISP function to download APP. After configuring SBL, there is no problem in downloading simple SDK codes such as led_blinky and helloword. However, if you download the SDK GUI demo, such as vglite_examples\vector_freertos code, we find that the boot fails . The same applies to operations such as app offset, and the code size does not exceed 1MByte. However, SDK GUI demo uses SDRAM, so we speculated that it is related to SBL's SDRAM enablement. This article will explain how to use SBL ISP to download an app with SDRAM and make it boots OK. 2. Operation steps 2.1 SBL configuration and programming Firstly, Download SBL source code and unzip it: https://github.com/nxp-mcuxpresso/sbl Download the ARM GCC and install it, here is the gcc-arm-none-eabi-9-2019-q4-major-win32.exe link: https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-win32-sha2.exe?revision=ba95cefa-1880-4932-94d4-ebf30ad3f619&rev=ba95cefa1880493294d4ebf30ad3f619&hash=B2513193FEEED9E850C62399EFF9DA04C0F0A809 The install path is: C:\Program Files (x86)\GNU Tools Arm Embedded\9 2019-q4-major\bin Open \sbl-master\target\evkmimxrt1170\ sblprofile.py, modify EXEC_PATH to the new installed ARM_GCC path: EXEC_PATH   = r'C:\Program Files (x86)\GNU Tools Arm Embedded\9 2019-q4-major\bin' This is the SBL configuration steps: (1). Open \sbl-master\target\evkmimxrt1170\ env.bat input: scons –menuconfig Fig 1 (2). Configure Single image OTA    MCU SBL core->[*]Enable single image function Fig 2 (3) Configure enable SDRAM Fig 3 Fig 4 Fig 5 After configuration, save the .config file, save and exit it. Fig 6 (4) Generate the sbl iar project In the window, input:  scons --ide=iar Then use IAR IDE to open \sbl-master\target\evkmimxrt1170\iar\sbl.eww You can see, the SDRAM DCD has been added now: Fig 7 (5) Configure the secure information Input the following command in the commander window: cd ..\..\component\secure\mcuboot\scripts Switch the commander path, then use the following command to generate the pub key and private key: python imgtool.py keygen -k xxxx_priv.pem -t rsa-2048-sign python imgtool.py getpub -k xxxx_priv.pem -o xxxx_pub.pem -t sign Fig 8 Open the file in path:sbl-master\component\secure\mcuboot\scripts\ xxxx_pub.c, copy the pub key information, and replay it to the file in path: \sbl-master\component\secure\mcuboot\sign-rsa2048-pub.c Then, it will update the SBL pub key information, now open the IAR project: \sbl-master\target\evkmimxrt1170\iar \sbl.eww Build the project, and use the debugger to download the SBL code to the MIMXRT1170-EVK board, I use the EVK on board debugger CMSIS DAP to download the sbl code.   2.2 APP configuration This document app is using the MCUXpresso IDE to import the SDK project: evkmimxrt1170_vector_freertos_cm7 Configure the flash start location to offset address:0X30100400 Fig 9 Delete the FCB and DCD header like this: Fig 10 Build the project, and generate the bin file:evkmimxrt1170_vector_freertos_cm7.bin, copy it to the SBL folder: sbl-master\component\secure\mcuboot\scripts Still in the commander window which you open the env.bat after you change the path previously: python imgtool.py sign --key xxxx_priv.pem --align 4 --version "1.1" --header-size 0x400 --pad-header --slot-size 0x100000 --max-sectors 32 evkmimxrt1170_vector_freertos_cm7.bin app2.bin This will help the app to add the header which matches the SBL requirement, and generate the app2.bin, which is the used app downloading file. 3. Test Result After the above configuration, it already downloads the SBL to the MIMXRT1170-EVK, and prepares the used app which contains the SDRAM, now use the MCUBootutility tool to download the app2.bin. Fig 11 Note, the Tools->Run Mode, should be SBL OTA mode. Find another USB cable to connect the EVK SDP J20 to the PC, after the EVK board reset, within the 5 seconds, connect the board by connection the MCUBootutility button “connect to SBL ISP”, then in the Fig 11, step 4, add the prepared app2.bin, step 3, input the address to: 0X30100000, then use step 5 to download the app. After app is downloaded, reset and exit the connection. Reset the board, wait 5 seconds, you will find the LCD can display the figure, it means the GUI code is working, and the printf log is: Fig 12 The board displays the result like this: Fig 13 At this point, the app with SDRAM has been successfully run in combination with SBL, indicating that the configuration of SBL with SDRAM is successful.        
View full article
RT1170 Boundary Scan test based on lauterbach   1. Abstract Boundary Scan is a method of testing interconnections on circuit boards or internal sub-blocks of circuits. You can also debug and observe the pin status of the integrated circuit, measure the voltage or analyze the sub-modules inside the integrated circuit, and test based on the JTAG interface. NXP officials have provided two good application notes: AN13507 (LPC) and AN12919 (RT). Based on the reference application note test method, this article provides the boundary scan test results for NXP MIMXRT1170-EVK revC1. It can use Lauterbach to connect the chip and perform boundary scan to control the external pins. A script file is also provided. It can realize one-click connection to boundary scan and achieve level control of external pins. 2. RT1170 test details   2.1 Hardware platform Lauterbach:LA3050 MIMXRT1170-EVK rev C1: The hardware modification point is to remove the onboard resistors R187, R208, R195 and R78. The purpose is that J6 prohibits external circuits from interfering with JTAG related pins. Disconnect J5, J6, J7, J8, that is, disconnect the onboard debugger, and use an external Lauterbach connection to J1. The connection situation is as follows: Fig 1 RT1170 directly supports both SWD and JTAG by default, so unlike RT10XX which needs to modify the fuse to convert from SWD to JTAG, RT1170 can directly use the JTAG interface.   2.2 Software operation Download Lauderbach's supporting software and install it. After installation, open the TRACE32 ICD Arm USB. If the Lauderbach device is connected, the interface will open successfully. Fig 2 At this time, you can enter the relevant commands in the yellow box in the picture above. Here you need to prepare the .bsdl file of the chip, which is usually placed on the chip introduction page of nxp.com. For example, the link to the bsdl file of RT1170 is: https://www.nxp.com/downloads/en/bsdl/i.MXRT1170_BDSL.bsdl You can copy the i.MXRT1170_BSDL.bsdl file to the Lauderbach installation path: C:\T32 Next, enter the following command in the window to open the boundary scan window and the i.MXRT1170_BSDL.bsdl file: SYStem.Mode Down BSDL.RESet BSDL.ParkState Select-DR-Scan BSDL.state Here, it will open the window: Fig 3 Click FILE item, input the downloaded i.MXRT1170_BSDL.bsdl, then in the window.,input the commander: BSDL.SOFTRESET Fig 4 Click check->BYPASSall,IDCODEall,SAMPLEall, make sure the 3 methods can be passed. Fig 5 Fig 6 Fig 7 To test the output control situation, it need to do the following operation: BSDLSET 1.: instructions->EXTEXT, DR mode->Set Write, Fileter data->uncheck intern BSDL.state->Run: check SetAndRun, TwoStepDR, Click RUN. BSDLSET 1. Can control the related pins, eg, GPIO_AD_26 is on the on board D34 LED. 1 ON,0 OFF. Fig 8   2.3 Automation control command script As can be seen from Section 2.2, single-step operation requires manual typing of commands. In actual testing, the efficiency is very low, so scripting language can be used to directly implement automated command control. Below, taking RT1170 as an example, we provide a script to control the on-board D34 light on and off. In this way, when the TRACE32 software is opened, you only need to open the script directly, enter the debug mode, run it to the end with one click, and check the on-board light control status. Script language file, the suffix is .cmm, step: File->New Script, enter the following script command: ;system setup SYStem.Mode Down SYStem.CPU CortexM7 SYSTEM.CONFIG.DEBUGPORTTYPE JTAG SYStem.JtagClock 1MHz ;BSDL Settings BSDL.RESet BSDL.ParkState Select-DR-Scan BSDL.state ;configure boundary scan chain BSDL.FILE i.MXRT1170_BDSL.bsdl ;Check boundary scan chain BSDL.SOFTRESET BSDL.BYPASSall BSDL.IDCODEall BSDL.SAMPLEall ;Perform Sample test BSDL.RUN BSDL.SetAndRun ON BSDL.TwoStepDR ON BSDL.SET 1. BSDL.SET 1. IR EXTEST BSDL.SET 1. PORT GPIO_AD_26 0 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 1 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 0 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 1 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 0 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 1 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 0 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 1 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 0 WAIT 1.s BSDL.SET 1. PORT GPIO_AD_26 1 WAIT 1.s Function, the led will be blinking 5 times, duration is 1s. Save the script, then debug it. Fig 9 This is the video for the testing:   It can be seen that the onboard light D34 can automatically flash, indicating that the BSDL automatic test has been completed so far.          
View full article
RT1050 FlexIO OV7670 with TFT LCDdisplay 1 Abstract Regarding the RT10XX flexIO collecting OV7670 camera data and displaying it on TFT LCD, in fact, the NXP official website has a very good application note AN12686, but the test is based on RT1010 and not EVK. It may be difficult for actual customers to test directly. When the author was supporting customers, I encountered customers who wanted to implement flexIO on RT1050 EVK to collect parallel port OV7670 data and display it on TFT LCD, which is the LCD with SPI interface, so this article gives the specific test results of the finished product, RT1050 flexIO and There are some differences between RT1010 flexIO. RT1010 flexIO has 8 shifters, but RT1050 only has 4 shifters, so some code modifications need to be made and transplanted to RT1050. Since it is going to run on MIMXRT1050-EVKB, you also need to consider the flexIO pins that can be used, modify the EVKB, and manually weld the relevant pins to configure the corresponding camera signals and LCD display signals. This article mainly comes from problems encountered by customers during testing, so it provides specific hardware connections, software code sharing, test finished product results, etc. 2. Software and hardware prepare Since AN12686 has given the principle in great detail, this article aims to give the differences and the specific conditions of working on RT1050-EVKB. 2.1 Hardware configuration The platform is based on MIMXRT1050-EVKB revA1, OV7670 module, 2.4-inch TFT LCD LCD SPI serial touch TFT color screen ILI9321, with a resolution of 240*320.     For the OV7670 module status and pin status, please check the article:    RT1050 CSI OV7670 camera eLCD display The camera module pins are as follows: Fig 1    TFT LCD picture: Fig 2 Pin No Signal Description 1 GND Power ground 2 VCC Power 3.3V 3 CLK SPI clock 4 MOSI SPI data 5 RES LCD reset 6 DC LCD data/commander select pin 7 BLK Backlight control switch, backlight is turned on by default, low level turns off the backlight 8 MISO Touch data reading 9 CS1 Display selection pin 10 CS2 Touch selection pin 11 PEN Touch interrupt signal For LCD, this article only uses the display part and does not use the rough mold part. Considering the pin layout of MIMXRT1050-EVKB, the application note flexIO1 is not used here, but FlexIO2 is selected. The actual RT1050-EVKB and OV7670 module and LCD connection pins are given below. The connection between the LCD signal pin and the MCU MIMXRT1050-EVKB RevA1 signal pin is as follows: LCD signal and pin MIMXRT1050-EVKB revA1 signal and pin GND P1 GND J24_7 3.3V VCC P2 3.3V J24_8 CLK P3 GPIO_AD_B1_15(SPI3_CLK) R98 MOSI P4 GPIO_AD_B1_14(SPI3_MOSI) R99 RES P5 GPIO_AD_B0_02(GPIO1_IO02) J24_2 DC P6 GPIO_AD_B1_10(GPIO01_IO26) J23_1 CS1 P9 GPIO_AD_B1_12(GPIO01_IO28) R100   OV7670 signal pin and MCU MIMXRT1050-EVKB RevA1 signal pin connection situation: 0V7670 signal and pin MIMXRT1050-EVKB revA1 signal and pin OV7670_D0 P3 GPIO_B0_05(FLEXIO2_D05) SW5_1 OV7670_D1 P4 GPIO_B0_06(FLEXIO2_D06) SW5.2 OV7670_D2 P5 GPIO_B0_07(FLEXIO2_D07) SW5_3 OV7670_D3 P6 GPIO_B0_08(FLEXIO2_D08) SW5_4 OV7670_D4 P7 GPIO_B0_09(FLEXIO2_D09) SW6_1 OV7670_D5 P8 GPIO_B0_10(FLEXIO2_D10) SW7_1 OV7670_D6 P9 GPIO_B0_11(FLEXIO2_D11) SW6_2 OV7670_D7 P10 GPIO_B0_12(FLEXIO2_D12) SW6_3 XCLK P11 GPIO_B0_13(FLEXIO2_D13) SW7_2 PCLK P12 GPIO_B0_14(FLEXIO2_D14) SW6_4 HREF(HS) P13 GPIO_B0_15(FLEXIO2_D15) R258/R324 VSYNC P14 GPIO_AD_B0_03(GPIO01_03) J24_1 I2C_SDA P15 GPIO_AD_B1_01(I2C1_SDA) J23_5 I2C_SCL P16 GPIO_AD_B1_00(I2C1_SCLK) J23_6 PWDN P1 GPIO_AD_B1_02(GPIO1_IO18) J22_7 RESET P2 GPIO_AD_B1_03(GPIO1_IO19) J22_8 3.3V P18 3.3V J22_7 GND P17 GND J22_8 In order to reduce the impact of the signal, MIMXRT1050-EVKB removes R323, R316, R309, and D6 on the board. The physical connection situation is as follows: Fig 3 2.2 Software configuration Since the flexIO of RT1050 is different from the 8 shifters of RT1010, the DMA configuration needs to be modified. The difference code of flexio_ov7670 is as follows:   static FLEXIO_CAMERA_Type s_FlexioCameraDevice = { .flexioBase = BOARD_CAMERA_FLEXIO_INST, .datPinStartIdx = BOARD_CAMERA_FLEXIO_DATA_PIN_START_INDEX, .pclkPinIdx = BOARD_CAMERA_FLEXIO_PCLK_PIN_INDEX, .hrefPinIdx = BOARD_CAMERA_FLEXIO_HREF_PIN_INDEX, .shifterStartIdx = 0U, .shifterCount = 4, .timerIdx = 0U, }; static void configDMA(void) { uint32_t soff, smod = 0u, size=0u; while(1u << size < DMA_TRSF_SIZE) /* size = log2(DMA_TRSF_SIZE) */ { size++; } if(DMA_TRSF_SIZE == DMA_MINOR_LOOP_SIZE) { soff = 0u; } else { soff = DMA_TRSF_SIZE; while(1u << smod < DMA_MINOR_LOOP_SIZE) /* smod = log2(DMA_MINOR_LOOP_SIZE) */ { smod++; } } /* Configure DMA TCD */ DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].SADDR = FLEXIO_CAMERA_GetRxBufferAddress(&s_FlexioCameraDevice); DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].SOFF = soff; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].ATTR = DMA_ATTR_SMOD(smod) | DMA_ATTR_SSIZE(size) | DMA_ATTR_DMOD(0u) | DMA_ATTR_DSIZE(size); DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].NBYTES_MLNO = 16; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].SLAST = 0u; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].DADDR = (uint32_t)(*pFlexioCameraFrameBuffer); DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].DOFF = 8; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].CITER_ELINKNO = DMA_MAJOR_LOOP_SIZE; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].DLAST_SGA = -OV7670_FRAME_BYTES; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].CSR = 0u; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].CSR |= DMA_CSR_DREQ_MASK; DMA0->TCD[FLEXIO_CAMERA_DMA_CHN].BITER_ELINKNO = DMA_MAJOR_LOOP_SIZE; /* Configure DMA MUX Source */ DMAMUX->CHCFG[FLEXIO_CAMERA_DMA_CHN] = DMAMUX->CHCFG[FLEXIO_CAMERA_DMA_CHN] & (~DMAMUX_CHCFG_SOURCE_MASK) | DMAMUX_CHCFG_SOURCE(FLEXIO_CAMERA_DMA_MUX_SRC); /* Enable DMA channel. */ DMAMUX->CHCFG[FLEXIO_CAMERA_DMA_CHN] |= DMAMUX_CHCFG_ENBL_MASK; } The code structure adopts: the camera uses flexIO mode to collect DMA transfer. After collecting one frame, DMA stores the data into the buffer, and then displays one frame of data uniformly on the LCD. Since there are many configuration codes for flexIO OV7670 and LCD SPI, we will not explain them one by one here. Please check the attached code source code for details. There is a header file of horsepic.h in the code. This file is a 320*240 RGB565 picture of a horse. It is used to test the LCD display separately. Usually after connecting the LCD, you need to test the LCD display separately. You can use a fixed picture to get the display. , here is the method of converting the picture into a C array: First adjust the picture to the LCD resolution size, and then convert it through the LVGL online conversion tool, select CF_RGB565A8, but the RGB565 generated by this format will have 1 more byte each, you can do it yourself After deletion, it can be called by code: https://lvgl.io/tools/imageconverter Display horse picture code: convert8to16(); ILI9341_FillPic(0, 0, OV7670_FRAME_WIDTH-1u, OV7670_FRAME_HEIGHT-1u, (uint16_t *)(horse16)); Display result: Fig 4 3 Test result and summarize    About RT1050-EVKB, use flexIO to collect OV7670 data and display the situation through TFT LCD. Please check the video for the specific code situation. Check the attached source code. You can see from the video results that the flexIO OV7670 camera data can be successfully displayed and the code can successfully run the function.
View full article