逐飞 RT1064 库 GCC (VSCode) 移植踩坑
2021-05-13
Summary: 该文介绍了在 MacOS 下,使用 GCC 工具链在 VSCode 中利用 Cortex Debug 插件编译调试逐飞 RT1064 库的踩坑过程。(Linux 下类似,但是具体步骤本文并没有写)
本文迁移自老博客,原始链接为 https://lookas2001.com/%e9%80%90%e9%a3%9e-rt1064-%e5%ba%93-gcc-vscode-%e7%a7%bb%e6%a4%8d%e8%b8%a9%e5%9d%91/
本文所记录的是我整个过程的踩坑经历,关于项目和其使用说明请参考项目主页:hilookas/SeekFree_RT1064_Library_GCC_Porting | GitHub
关于逐飞 RT1064 RT-Thread 库的移植项目请参考其项目主页:hilookas/SeekFree_RT1064_RTThread_Library_GCC_Porting | GitHub
从山外 K60 库开始,我开始了我的漫漫智能车之路。山外库只支持 IAR,但是可惜 IAR 并不支持 MacOS/Linux,逐飞 RT1064 库增加了对 Keil 的支持,但是 Keil 仍然只支持 Windows。我不甘心与在 Mac 上与嵌入式无缘,同时好在开源工具链 GCC 支持(几乎)所有平台,故有了这篇折腾文章。
单片机相关
现在的微控制单元(单片机)一般会有两个文档,一个是 Data Sheet ,负责介绍芯片电气相关的参数,另一个是 Reference Manual ,负责介绍硬件上一些功能如何使用,寄存器布局等。
初次之外厂商提供一些 Application Note,用于介绍特定的功能是如何使用,这个文档一般比较口语化,而且部分还提供中文翻译,推荐要实现一个功能的时候可以优先参考。
NXP 提供 SDK ,SDK 提供了许多样例供参考,和一些芯片的基础库,将寄存器封装起来,避免用户自己直接操作寄存器(太头疼)。
CMSIS 是一个 ARM 的标准,用于标准化来自不同厂商的 ARM 芯片差异。将芯片的 ARM 内核相关的寄存器封装起来。
CMSIS 还提供 CMSIS Pack,其中包含了不同厂商自己外设的驱动程序。
NXP 的 SDK 包含了 NXP 的 CMSIS Pack (SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064
里的内容其实就是 NXP 的 CMSIS Pack 的)
ref:
- i.MX RT1064跨界处理器_ Arm Cortex-M7_ 恩智浦_NXP 半导体
- i.MX RT1064 | Crossover MCU with ARM Cortex-M7 up to 600 MHz | NXP Semiconductors
IMXRT1064RM.pdf
from NXP- NXP i.MX RT1064-EVK开发板入门 - i.MX应用处理器 - 一板网电子技术论坛 供参考 RT1064 官方开发板
- 从 i.MX RT1060 到 i.MX RT1064 的迁移指南
- Select Board | MCUXpresso SDK Builder NXP SDK 下载。 RT1064 的 Device 和 Board 均可,推荐 Board,但是我下的是 Device ,选择最少功能即可,平台为 Windows(其他平台无法生成 IAR 和 Keil 文件),工具为全部
Getting Started with MCUXpresso SDK for EVK-MIMXRT1064.pdf
from NXP SDKSDK_2.9.1_MIMXRT1064xxxxA/docs/
- CMSIS – Arm Developer
- CMSIS | CMSIS Search – Arm Developer
- 痞子衡嵌入式:ARM Cortex-M文件那些事(1)- 源文件(.c/.h/.s) - 痞子衡 - 博客园
- BSP与HAL关系(转) - lemaden - 博客园
- 嵌入式中的BSP---BSP到底是什么?_shangtang1的博客-CSDN博客
- rt1064 rt1052恩智浦智能车MIMXRT1064单片机最小系统核心板 逐飞-淘宝网
- SeekFree/逐飞科技RT1064开源库: 逐飞科技针对参加各类竞赛以及使用RT1064进行产品开发,制作的恩智浦RT1064高性能MCU开源库。
- #i.MX RT1064智能车应用
- i.MX RT1064智能车应用入门指导--逐飞科技
- “逐飞科技RT1064开源库”之GPIO详解 - 「MCU加油站」 - 恩智浦技术社区
核心板启动基本流程
RT1064 芯片有内嵌的 BootROM (直接刻在芯片上的那种),其会根据启动引脚和内部保险丝的情况决定从哪个位置启动。同时 RT1064 芯片内部有一颗使用 FlexSPI2 的 Flash (即 RT1064 的 4 意义所在),没有特殊配置,芯片会从这个 Flash 启动。
BootROM 会从 Flash 的 IVT(Image Vector Table) 读取 BootROM 配置信息(如 PC 指针的值,和配置 SEMC SDRAM 的“脚本”)
ref:
- 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(1)- Boot简介 - 痞子衡 - 博客园 i.MXRT_Boot_1050MemMap.PNG (1163×653) 很好的一个博主,推荐关注
- 【网友经验分享】自己整理了一份i.MX6启动流程 (amobbs.com 阿莫电子论坛)
i.mx6 linux启动流程分析.pdf
介绍了一下 i.MX6 带操作系统启动流程。其与 RT1064 类似,区别在于 RT1064 没有 MMU ,无法运行操作系统。 - NXP的I.MX6U系列SoC启动流程 - Cqlismy - 博客园
- Arm芯片上电启动流程剖解_Yannick Jiang 的专栏-CSDN博客_arm上电启动流程
- I.MX6启动流程_metersun的专栏-CSDN博客 官方文档翻译
- RT1050 FLEXSPI 控制flash简述_lwplwp2的博客-CSDN博客
- Synchronous dynamic random-access memory - Wikipedia
芯片的寄存器
寄存器有两种,一种是处理器寄存器,ARM 核心使用,一种是外设寄存器,用于控制芯片中的外设的(如 UART)。后者被映射到处理器的内存空间中,可以被寻址,具体地址参见芯片手册。
ref:
- 学C语言的时候老师说寄存器没有地址,但是ARM芯片手册中,每个寄存器又都有一个地址?寄存器到底有没有地址? - 知乎
- 寄存器映射与直接操作寄存器_王建国的专栏-CSDN博客_寄存器地址映射
- 寄存器映射与重映射_吃蛋糕的小学生的博客-CSDN博客
- STM32寄存器映射 - 知乎
统一文件编码为 UTF-8
逐飞的库文件编码是乱的,强迫症的我不能对此坐视不管。库里的绝大多数文件是使用 GB2312 编码,但是有两个文件是同时使用了 UTF-8 和 GB2312 编码,更加神奇的是,还有三个文件中参加了几个中文符号!这导致 iconv 的转换编码进程被卡住。这主要涉及到以下几个文件:
UTF-8 和 GB2312 编码混在一起:
Libraries/seekfree_libraries/common/SEEKFREE_PRINTF.c
Libraries/seekfree_libraries/common/SEEKFREE_PRINTF.h
混有中文标点符号:
Libraries/nxp_libraries/middleware/usb/host/class/usb_host_cdc.c
Libraries/nxp_libraries/drives/fsl_semc.h
Libraries/nxp_libraries/utilities/debug_console/fsl_debug_console_conf.h
本来就是用 UTF-8 编码的:
./Project/RT1064智能车推荐引脚分配.txt
./Project/CODE/本文件夹作用.txt
为了加快修改所有源代码文件编码为 UTF-8 的进程,我简单的写了一个脚本,供各位参考:
#!/bin/bash
# Convert GB2312 to UTF-8 (MacOS) and CRLF to LF
# from https://docs.moodle.org/310/en/Converting_files_to_UTF-8
# from https://gist.github.com/jappy/2012320
# change * to *.c to specify only code files
# for x; do
# 涉及到 *.c *.h *.s *.S *.txt
find . -type f -name "*" | while read x; do
iconv -f GB2312 -t UTF-8 < "$x" | tr -d '\015' > "$x.tmp"
mv "$x.tmp" "$x"
done
改了 deceive typo
Libraries/nxp_libraries/deceive
目录名错了,是 device
IAR 和 Keil 的配置也做了相应的调整
确定需要编译的文件
逐飞库里并不是所有的文件都需要编译的,有些文件加入编译后会导致编译不通过(如引用的头文件不存在),甚至导致运行时错误(错误覆盖了某些函数),具体可以参见 files.cmake
(根据 IAR program/RT106X.ewp
配置文件进行了注释)
但是,相对于 IAR 的配置,有些文件是仍旧需要的,否则会导致编译不过(引用的头文件不存在)。以下头文件是需要的:
Libraries/nxp_libraries/xip/
Libraries/nxp_libraries/CMSIS/Include/
Libraries/nxp_libraries/CMSIS/Driver/Include/
ref:
移植链接文件
链接文件负责将代码合适的摆放在芯片所需要的位置上(如下文提到的 IVT 必须放到指定位置上,芯片才能正常启动)
MIMXRT1064xxxxx_seekfree.ld
文件修改自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064/gcc/MIMXRT1064xxxxx_flexspi_nor.ld
逐飞核心板所需要的内存布局可以参考 IAR 的链接文件 Project/IAR/icf/MIMXRT1064xxxxx_flexspi_nor.icf
和逐飞的 Libraries/doc/read me.txt
说明文档。
ref:
- Output Section Attributes (LD)
- Using LD, the GNU linker - Command Language
- 链接脚本的作用及格式 | feng 言 feng 语
- 简单的ld链接脚本学习 - 简书
- 链接、装载与库 --- 动态链接 | Technology Blog
- 彻底理解链接器:四,重定位 - SegmentFault 思否
- 彻底理解链接器:六,大型项目是如何被构建出来的
- 痞子衡嵌入式:ARM Cortex-M文件那些事(2)- 链接文件(.icf) - 痞子衡 - 博客园
- IAR一些破事儿_MTzhou的专栏-CSDN博客
关于链接的加载地址(LMA)与运行地址(VMA)
一段代码的加载地址并不一定与运行地址相同,比如,在程序运行前,先从 ROM 里加载代码到 RAM 中,再在 RAM 中执行代码,速度会比直接从 ROM 中执行快很多。GCC Linker 默认情况下,加载地址为运行地址,但是其也支持自行使用 AT 执行指定加载地址。
ref:
加载地址设置这里有坑,设置了 AT 之后的代码好像并不会自动与前面的代码错开放置,需要自行计算排好。
Linker output.map 是一个很好的调试工具
ref:
- linker scripts - GCC - Sections has wrong LMA - Stack Overflow
- gcc - GNU linker ARM - why my sections overlap? - Stack Overflow
- c - Linker script error: section overlap - Stack Overflow
- c - RISC-V linker throwing sections LMA overlap error despite LMA's belonging to different memory regions - Stack Overflow
- Section overlap in ld. | AVR Freaks
移植启动文件
主要涉及到芯片上电启动后 Reset_Handler
和,在其中运行的 SystemInit
。
需要配置 DTCM 和 ITCM 的内存分配,以及复制全局变量等到对应的位置。
Libraries/nxp_libraries/startup/GCC-ARM/startup_MIMXRT1064.s
文件修改自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064/gcc/startup_MIMXRT1064.S
同时还修改了 Libraries/nxp_libraries/device/system_MIMXRT1064.c
和 Libraries/seekfree_libraries/common/common.h
(代码放置位置的快捷宏)
ref:
- 使用 i.MX RT FlexRAM ITCM DTCM 的大小配置是在 Reset_Handler 中进行
- i.MX RT1062笔记(1) ----Flex RAM 配置_weixin_30772105的博客-CSDN博客
- IMXRT1052/1064 如何将代码存放在ITCM中_不咸不要钱-CSDN博客
- 关于DMA,TCM(ITCM和DTCM)和Cache的理解!_木牛的博客-CSDN博客
- FreeRTOS之Cortex-M中断管理 - Stephen1120 - 博客园
- ARM assembly: bad instruction "mov32" - Stack Overflow gcc asm 并不支持 mov32 这个伪指令
- gcc - How to get value of variable defined in ld linker script from C - Stack Overflow
- ARM汇编语言入门 - 知乎 寄存器相关
- ARM汇编寄存器简介_曹太强的专栏-CSDN博客 总览
- ARM指令和Thumb指令的区别_itismine的专栏-CSDN博客_thumb指令
- ARM汇编入门
- ARM寄存器结构小记 | 上善若水
- GNU ARM 汇编指令 - 摩斯电码 - 博客园
- ARM汇编和Gnu汇编的转换 - 电子工程世界(EEWORLD)
- ARM汇编基础教程——ARM汇编简介 ARM汇编基础教程 ——ARM汇编简介 - 知乎
- GNU ARM汇编快速入门_bytxl的专栏-CSDN博客
- ARM嵌入式开发中的GCC内联汇编简介_ce123的技术博客-CSDN博客
- 汇编跳转指令B、BL、BX、BLX 和 BXJ的区别_bytxl的专栏-CSDN博客
- ARM指令B BL BLX BX区别_从此寂寞的自留地-CSDN博客_arm bl
- Assembler User Guide: LDR (immediate offset)
- 004.ARM指令之LDR - 简书
- PC指针和SP指针_Andy的博客-CSDN博客_pc指针和sp指针
- MSP430学习篇——IAR常用汇编伪指令-半导体新闻-摩尔芯球
- IAR ARM中的汇编语言学习笔记_suyong_yq的专栏-CSDN博客
- Cortex-M3的编程模式 - 知乎
- RT1060 BEE 加密(用户自定义Key) MCUBootUtility 烧录实例讲解 - 何其乐 - 博客园
- NXP-MCUBootUtility/README-zh.md at master · JayHeng/NXP-MCUBootUtility
- XIP原理知识 - 乐讯K800 K800+ 交流
解决 GCC 并不帮助我初始化全局变量的问题
IAR 提供了 __iar_program_start
函数,拷贝在 icf 链接文件中定义的需要拷贝的数据。GCC 提供的相同地位的函数 _start
好像不帮我拷贝那些 LMA 与 VMA 不同的块。
解决方案:在 startup_MIMXRT1064
手动把变量初始化了。
ref:
- gcc - What is the use of _start() in C? - Stack Overflow
- A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux
- Linux x86 Program Start Up
- IAR IDE学习之---关于工程设置中“program entry”作用的猜想_孤独的思考-CSDN博客
- IAR中Overlay和manual initialization的使用技巧_Quard_D的博客-CSDN博客
- IAR 下将函数指定到RAM中_leumber的专栏-CSDN博客
移植 CMake 文件
NXP SDK 提供的样例是是使用 CMake 编译的,逐飞的核心板由于其外置 RAM ,需要手动开启一些编译 Flag,具体可以参见 flags.cmake
和 Libraries/doc/read me.txt
。
CMakeLists.txt
,*.cmake
和 *.sh
修改自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/boards/evkmimxrt1064/demo_apps/led_blinky/armgcc
下相关文件。
ref:
- CMake Tutorial — CMake 3.19.0-rc3 Documentation
- CMake 入门实战 | HaHack
- 1. 一个简单的开始 · Cmake 基础教程
- 【使用CMake组织C++工程】2:CMake 常用命令和变量
- Get started with CMake Tools on Linux
- c++ - Automatically add all files in a folder to a target using CMake? - Stack Overflow
- cmake - Can one add further source files to an executable once defined? - Stack Overflow
- target_include_directories — CMake 3.20.1 Documentation
- c++ - Cmake Cannot specify include directories when use target target_include_directories - Stack Overflow
关于 GDB 直接使用不好使的问题
在使用 Cortex Debug 之前,我有尝试直接使用 GDB (通过 JLinkGDBServer)一段时间,但是 GDB 直接使用好像不是特别好使,continue
monitor halt
很烦,并且使用 GDB 的自带寄存器显示功能 info registers
的显示好像是错的。Cortex Debug 用完后,感觉还不错,就没有再继续研究 GDB 了。
ref:
- GDB 单步调试 - 为生存而奔跑 - C++博客 Snapshot: GDB 单步调试 - 为生存而奔跑 - C++博客
- J-Link GDB Server - SEGGER Wiki
- 用gdb调试Cortex-M系列芯片 | Simple
- GDB and OpenOCD (OpenOCD User’s Guide)
- Registers (Debugging with GDB)
- scripting - What are the best ways to automate a GDB debugging session? - Stack Overflow
- assembly - Insight debugger shows wrong values in registers - Stack Overflow
- c - gdb prints wrong values when modifying arguments - Stack Overflow
- Solved: Re: RT1064+Jlink debug issue - NXP Community ERROR: Cannot read register 0 (R0) while CPU is running 这个错误仍然会偶发性的发生(包括使用 Keil 的时候)
- ERROR: Can not read register 15 (R15) while CPU is... - NXP Community
解决 JLink 调试器启动芯片后外置 RAM 访问出错的问题
现象是使用 Cortex Debug 插件启动芯片后,程序会第一次死在 Libraries/nxp_libraries/device/system_MIMXRT1064.c
的复制向量表(到外置 RAM)过程中,进入 HardFault 中断,使用 JMem 查看后发现对应位置(0x80000000 外置 RAM)没有数据(显示为 -
)。在 Reset_Handler 中设置断点的情况下,芯片自动重启后会恢复正常(进入到 main 函数)。
芯片自带的 BootROM 负责根据内置 Flash 上的 IVT 区域的 DCD(Device Configuration Data) 配置信息配置 SEMC 与外置 RAM 的通信。猜测 JLink 启动芯片的时候并不会运行芯片自带的 BootROM ,故需要 JLinkScript 在每次启动芯片的时候,手动配置一下外置 RAM 相关的数据。
使用从 NXP SDK 中提取到的 evkmimxrt1064_sdram_init.jlinkscript (来自 SDK_2.9.1_MIMXRT1064xxxxA/boards/evkmimxrt1064/sdmmc_examples/sdcard_interrupt/evkmimxrt1064_sdram_init.jlinkscript
),附加到 Cortex Debug JLink 启动选项(文件 launch.json
)后,可以正常调试运行了。
ref:
- Does debug init SDRAM for xip app on MIMXRT1052? - NXP Community SDK 中有需要的 JLinkScript
- Re: LPC54608 - debug application from SDRAM using ... - NXP Community 该帖子和上一帖子有供参考的 JLinkScript
- Debugging RT1064 via JLink - NXP Community
- Solved: Jlink无法连接RT1064 - NXP Community
- Solved: could not connect to target(Jlink, MCUXpresso) - NXP Community
- 如何用八进制SPI闪存和SD卡启用启动 关于 IVT 中的 DCD
- arm的软断点和硬断点_xiaoyaofeidao的博客-CSDN博客_arm硬件断点
- NVIC_SystemReset使用及系统复位 恩智浦工程师推荐使用 NVIC_SystemReset 防止进行软重启,虽然不是我的解决方案,但是供参考
- Debugging a HardFault on Cortex-M | IAR Systems
关于 CMSIS-DAP
CMSIS-DAP 可以用 pyOCD 与其通讯。在 MacOS 下,运行 pyOCD 与 CMSIS-DAP 通讯是“免驱”的。OpenOCD 好像也可以与它通讯,但是没有继续尝试...另外根据 Cortex Debug 的文档,Homebrew 的 OpenOCD 版本比较老,推荐使用编译方式安装或者直接从官网下载
ref:
- DAPLink
- Board IDs · pyocd/pyOCD Wiki
- DAPLink/CMSIS-DAP & Debug Adapter Protocol · Issue #560 · ARMmbed/DAPLink
- CMSIS DAP - Handbook | Mbed
- Firmware for CoreSight Debug Access Port
- CMSIS-DAP下载器Keil 5无法识别 - DAP下载器 - 野火电子论坛 no debug unit device found
- no debug unit found error in mbed NXP LPC1768,KEIL, - Question | Mbed
- Getting OpenOCD « Open On-Chip Debugger
- Open On-Chip Debugger: OpenOCD User’s Guide
- About (OpenOCD User’s Guide)
- OpenOCD - Open On-Chip Debugger - Browse /openocd/0.11.0 at SourceForge.net
- 跟我一起学OpenOCD(一) - 知乎
- CMSIS-DAP和openOCD那些事 - 知乎
- Build Your Own Low Cost CMSIS-DAP Debug Unit – ravikiranb.com
- Jlink ob制作教程 三线Jlink ob神器!!!_sunyiming537的专栏-CSDN博客_jlink ob
- vllogic/openocd_cmsis-dap_v2: 支持CMSIS-DAP v2接口协议,支持ARM、RISCV、ESP32等目标芯片,详见Wiki及release
DAPLink使用说明 V1.2.pdf
from 逐飞DAP 下载器使用说明.zip
解决 Cortex Debug 调试界面,外设寄存器无法正常显示的问题
复制 RT1064 的 SVD 文件即可
MIMXRT1064.xml
即 SVD 文件来自 NXP SDK 的 SDK_2.9.1_MIMXRT1064xxxxA/devices/MIMXRT1064/MIMXRT1064.xml
,也是 NXP CMSIS-Pack 的 NXP.MIMXRT1064_DFP.13.0.0.pack/MIMXRT1064.xml
ref:
解决没有初始化无线的时候,无线中断处理函数内无法正常打断点的问题
rt,经过测试发现,如果没有调用初始化函数,Cortex Debug 反汇编根本无法找到中断处理函数,只有在初始化后才能找到。故认为是如果没有调用初始化函数,编译器自动优化掉了处理函数。
在使用断点功能的时候需要注意并不是所有位置打了断点都能生效的。
ref:
JLink 和核心板之间的连接
DIO CLK GND 是必须连接的,VCC 可以连接(这四根引脚即为核心板调试引脚最靠近 Type-C 口的那四个),RST 实测不连也可以 Reset (该功能不依赖该引脚)
如果有转换板的话,板子上其实有一个四线的口,用那个口即可
- jlink的SWD与JTAG下载模式的对应接线方法 - Darren_pty - 博客园
- 史上最全面的JTAG和SWD接口的定义/STM32/STM8工程师的福音/JTAG转SWD接口仿真/告别杂乱的仿真线/终于讲清楚了JTAG/SWD - 华为云
- J-link使用SWD下载的连线方式 - blue^boy - 博客园 1403799-20190707133842818-707679784.png (497×303)
- 浅论各种调试接口(SWD、JTAG、Jlink、Ulink、STlink)的区别_不积跬步,无以至千里!-CSDN博客_swd接口
- RST Pin for SWD interface is necessary? - Nordic Q&A - Nordic DevZone - Nordic DevZone
使用串口调试
核心板上调试口的串口(标注 TX RX 字样),默认是要连到 UART1_TX_B12 UART1_RX_B13 两个脚上,但是实际上空了两个 0R 电阻位作为跳线。如果需要使用调试口上的串口,需要手动用一坨锡或者电阻连上。或者在主板上引出这两个口
Tips:修改 Project/USER/inc/RT106X_config.h
可以配置默认串口。
Tips:使用 screen /dev/tty.xxx 115200
可以连接到串口
ref:
Cortex Debug 参考
- Home · Marus/cortex-debug Wiki
- J-Link Visual Studio Code - SEGGER Wiki
- DAPLink+VSCode调试nrf52_moderate | Half Coder
- Cortex-debug 调试器使用介绍_Null-CSDN博客
- Developing M4 Applications Using Visual Studio Code Cortex Debug 实例
- 使用VSCode开发Cortex-M系列芯片 | ZZSHUB Cortex Debug 笔记
- VSCode ARM Cortex-M debugging – 東 Higaski
- vscode + cortex-debug,stm32 开发更有趣了 - 知乎