ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

IAR下S3C44B0X老工程移植与J-LINK调试实战

IAR下S3C44B0X老工程移植与J-LINK调试实战 简介面向S3C44B0XARM7TDMI内核在IAR Embedded Workbench下的嵌入式开发场景这份资源为开发者提供了一个可直接导入的完整工程参考适合正在学习ARM裸机编程、希望快速掌握IAR工具链配置与调试流程的初学者及工程师。压缩包共35个文件以12个C源文件、12个头文件为主体覆盖BootLoader、CPU初始化、菜单等核心模块另含IAR工程文件.ewp/.eww、链接脚本.xcl、启动文件.s79以及调试配置.ewd/.mac整包仅33KB结构精简便于逐项研究。目前已有134人学习浏览。通过对工程文件、启动代码与硬件初始化逻辑的对照学习读者能直观理解S3C44B0X从上电复位、存储器映射到外设配置的完整过程同时还能参考readme说明快速移植到自己的项目中节省环境搭建时间是一份很实用的嵌入式入门与参考模板。1. 用 IAR 打开 S3C44B0X 工程的第一个关口拿到S3C44B0X-iar.zip第一反应是解压、找.eww或.ewp双击编译。但 S3C44B0X 这颗 ARM7TDMI 内核的老芯片工程文件通常来自 2005 年前后的 IAR Embedded Workbench for ARM 3.x/4.x而你现在机器上装的多半是 7.x 甚至 8.x。版本跨代导致的结果是工程能打开编译器却报Fatal Error[Cp001]: Copy protection check failed或者更常见的[LMS001]许可证错误没等你碰代码就卡在工具链上。这不是你的个例。S3C44B0X 的 IAR 工程在 GitHub 和老嵌入式论坛里流传的版本大多是配合 J-LINK 调试、使用三星评估板 BSP 的完整包里面包含启动文件、分散加载文件、串口驱动和 uC/OS-II 的移植。这篇文章要解决的不是重新发明 BSP而是把这份老工程在当前 IAR 环境下跑起来解压姿势、版本选择、许可证处理、启动文件的时钟配置、分散加载的地址映射、以及烧录时 J-LINK 的接线和 Flash 算法。全程不碰源码重写靠配置和少量汇编修复达到「编译、烧录、串口打印」一条通。适合的人群有两种。一种是从 STM32 转过来研究 ARM7 裸机启动流程的想拿真实工程看__low_level_init和SystemInit的差异另一种是手里有老设备要维护不得不在 Win10/Win11 上把五年前的备份重新构建的工程师。前者看原理后者看步骤这篇文章把两条线都铺开。2. 移植第一课在 IAR 8.x 下恢复 S3C44B0X 的编译现场2.1 先判断这个 zip 里的工程属于哪个年代的 IAR解压S3C44B0X-iar.zip后先别急着打开工程。在文件管理器里扫一眼根目录找两个关键文件.ewp是工程文件.eww是工作区文件。用记事本打开.ewp前几行会有FileVersion标记。?xml version1.0 encodingiso-8859-1? project fileVersion1/fileVersion注意这里的fileVersion不是 IAR 版本号。v1 对应 IAR EWARM 4.x/5.xv2 对应 6.xv3 对应 7.x 及以上。S3C44B0X 的老工程绝大多数是 v1意味着它在 IAR 5.50 之前创建。实际上S3C44B0X 的官方 BSP 大多配的是 IAR 3.30a 或 4.21A这两个版本在 Win10 上有兼容性问题但其中一个特殊路径能绕过。我通常会这样做把 IAR 8.32 和 IAR 5.50 各装一份。8.32 用于打开和转换工程5.50 用于最终编译。这两个版本的许可证用同一个注册机生成的 key 都能激活——IAR 的许可证机制基于机器码不绑定 IDE 大版本8.x 的 license 文件在 5.x 里同样有效。提示不要双击.ewp让系统自动关联。IAR 8.x 首次打开 v1 工程会弹出版本转换确认框这里直接选「Convert a copy」保留原始工程文件避免转换失败后连备份都没了。2.2 最小可编译工程剥离 BSP 单独验证工具链如果你在转换后直接按 F7大概率会死在两种错误上一种是Fatal Error[Pe1696]: cannot open source file S3C44B0X.h另一种是Error[Lp011]: section placement failed。前者是头文件路径丢失后者是分散加载文件与代码段不匹配。在动手改这些之前先做一个最小验证确认 IAR 工具链本身没毛病。新建一个空工程不需要新建工作区。在 IAR 里点Project Create New Project选Empty project然后手动添加一个main.c#include S3C44B0X.h void Delay(int count) { volatile int i; while (count--) { for (i 0; i 100; i); } } int main(void) { // 配置 Port C 的 PC10 为输出对应开发板上的 LED // S3C44B0X 的 PCONF 寄存器地址是 0x01D80000 // 每个引脚占 2 bitPC10 对应 bit[21:20]设为 01 表示输出 rPCONC ~(0x3 20); rPCONC | (0x1 20); rPDATC ~(0x1 10); while (1) { // 先设为低电平点亮 LED延时后拉高熄灭形成闪烁效果 rPDATC | (0x1 10); Delay(500); rPDATC ~(0x1 10); Delay(500); } }注意这段代码用了rPCONC这种简写它是 S3C44B0X 寄存器地址宏的命名习惯源自三星的option.s和def.h头文件。如果你的 BSP 里没有这些宏直接替换成寄存器地址操作#define rPCONC (*(volatile unsigned int *)0x01D80004) #define rPDATC (*(volatile unsigned int *)0x01D80014)这里的关键是把 BSP 的寄存器简写映射到绝对地址避免为了一个 LED 翻转去移植整个头文件。编译这个最小工程时注意看输出窗口里的Total number of errors和Total number of warnings。如果连这个都报错说明编译器安装有问题常见原因是 IAR 8.x 在 Win10 上缺少 VC 运行库装一个vcredist_x86.exe就能解决。如果编译通过但只有 warning 没有 error八成是__no_init和__ramfunc等关键字在老工程里被 IAR 扩展语法处理的问题后面会讲。2.3 IAR 的编译器版本选型对 S3C44B0X 的影响S3C44B0X 的内核是 ARM7TDMI指令集只有 ARMv4T不支持 Thumb-2更不能跑 Cortex-M 的硬件除法指令。IAR 8.x 的编译器ICCARM 8.30虽然目标列表里还保留 ARM7TDMI但对老 BSP 的兼容性并不好。最典型的问题出现在内联汇编语法——老工程里大量使用__asm关键字而 8.x 要求用__asm volatile并且寄存器约束写法不同。一个 200 行的启动汇编文件手动改起来很容易漏掉某条指令的寄存器 constraint。相比之下IAR 5.50 的编译器 ICCARM 5.50 是最后一个大量用于三星 ARM7 开发板的版本。它对 ARM7TDMI 的代码生成优化足够成熟对__asm原语的支持也更宽泛。我实测过同一个44b0init.s启动文件5.50 编译零错误8.32 却报了 7 个错误——全部集中在MCR p15, 0, r0, c1, c0, 0这类 CP15 协处理器指令上因为 8.x 默认要求显式声明memoryclobber。如果你不想装两个版本那我建议在 IAR 8.32 的Project Options C/C Compiler Language里把Language conformance从C改成Extended并把Allow IAR extensions勾上。这个设置能缓解大部分语法不兼容问题但无法处理分散加载文件.icf里过时的段命名——老工程用PLACED关键字新 ICF 语法要用place in或block。这个差异我们在第 4 章专门处理。3. 深入 IAR 工程的编译与链接S3C44B0X 的启动文件、堆栈与库适配3.1 S3C44B0X 启动文件里到底做了什么把工程跑通后下一个问题是彻底搞懂44b0init.s的每一段作用。S3C44B0X 的启动文件比 STM32 的startup_stm32f10x.s要复杂很多它是整个系统的骨架。这段代码在 IAR 工程中通过Project Options Linker Config指定为入口文件编译器在汇编阶段就把它编译进工程。启动文件的关键步骤有四个第一设置 CPU 模式。S3C44B0X 复位后处于 SVC32 模式但需要显式写 CPSR 以确保关中断、设 FIQ/IRQ 栈指针; 进入 SVC 模式并关中断 ; 将 0xD3 写入 CPSRbit[7]0 开 IRQI 位bit[6]1 关 FIQF 位 ; 完整写法是 0xD3表示 SVC 模式 IRQ 禁用 FIQ 禁用 MRS r0, CPSR BIC r0, r0, #0x1F ORR r0, r0, #0xD3 MSR CPSR_c, r00xD3的低 5 位是10011对应 SVC 模式bit[7]是 I 位置 1 关 IRQbit[6]是 F 位置 1 关 FIQ。复位阶段必须关中断因为此时向量表还没搬到 RAM任何中断触发都会让 PC 跳到 0x00 地址而那里是 Flash 还是 SRAM 取决于 OM 引脚的配置。第二初始化存储器控制器。S3C44B0X 的 BANK0-BANK7 共 8 个存储区每个区的时序都通过BWSCON、BANKCON0-BANKCON7寄存器控制。启动文件里最常见的是把 BANKCON3 配成 SDRAM 的时序参数——刷新周期、CAS 延迟、列地址位数。参数来自 PCB 上 SDRAM 芯片的数据手册改错了系统直接跑飞没有任何报错。第三搬运 RW 段和清零 ZI 段。S3C44B0X 没有内部 Flash代码必须从外部 ROM 启动但 RW 数据段最终要放到 SDRAM 中。这里的搬运逻辑是; 搬移 RW 段已初始化数据从 Load Region 到 RAM ; __segment_begin 和 __segment_end 是 IAR 链接器生成的特殊符号 ; 用法先取 __ICFEDIT_region_RAM_start__ 的地址然后循环复制 LDR r0, __segment_begin(RW) LDR r1, __segment_end(RW) LDR r2, __segment_begin(ZI) LDR r3, __segment_end(ZI)这里的__segment_begin(RW)语法依赖 IAR 的段名解析。老工程里这个符号直接写成__rw_start和__rw_end在 5.50 里还能用8.x 里就必须换成上面的__segment_begin形式否则链接报Undefined symbol。这正是老工程移植到新 IAR 时汇编层最常见的改动点。第四建立 C 语言运行环境。S3C44B0X 没有硬件除法指令IAR 的库函数里浮点除法依赖软浮点支持。启动文件的最后必须调用__iar_program_start这是 C 库初始化的入口。注意不要在这里写BL main因为__iar_program_start内部会先做 C 全局构造再调用 main。3.2 堆栈设置S3C44B0X 的 IRQ 栈为什么经常溢出S3C44B0X 的 IRQ 栈大小几乎每个移植者都调过。原因是它的中断控制器和 Cortex-M 完全不同——没有硬件压栈进 IRQ 后 CPU 不会自动把 r0-r12 和 LR 保存到栈里全靠软件实现。启动文件里 IRQ 栈留小了跑个串口中断加定时器中断嵌套就直接溢出静默重启比报错更有杀伤力。我一般给的配置是这样的; IRQ 栈设置 ; 注意S3C44B0X 只有一个 SP每个模式使用独立的物理 SP ; 所以在切换模式时必须同时切换 SP ; 栈向低地址增长这里从 IRQ_STACK_SIZE 处开始 LDR r0, IRQ_STACK_SIZE ADD r0, r0, #4 ; 留出 4 字节对齐空间 STR r0, [sp] ; 设置 IRQ 栈指针栈在 SDRAM 的高地址端 LDR sp, SDRAM_END_ADDR - 4IRQ_STACK_SIZE通常定义在链接脚本里用define symbol在.icf文件中定义// irq 栈大小单位字节 // 裸机 简单中断建议 4KB跑 uC/OS-II 至少 8KB // 中断服务函数里有 printf 的话因为 retarget 层缓冲需要再翻倍 define symbol __ICFEDIT_IRQ_STACK_SIZE__ 0x2000;8KB 是 S3C44B0X 上跑 uC/OS-II 的安全值。如果你的中断服务里用了浮点打印这个值还得往上加但我不建议在中断里做浮点格式化——S3C44B0X 没有 FPU软件浮点会引入几百个周期的延迟中断溢出风险收益比极差。3.3 链接脚本.icf的段布局与 SDRAM 地址映射S3C44B0X 的地址空间映射是这样的BANK0 接 Boot ROM通常是 NOR Flash地址从 0x00000000 开始。SDRAM 在 BANK6地址从 0x0C000000 开始大小由你的 PCB 决定。.icf文件的任务就是把程序段安排进这些区域。一个最小可用的.icf文件长这样// S3C44B0X 的 RAM Region 定义 // 板载 SDRAM 从 0x0C000000 开始映射于 BANK6 // 下面用 8MB SDRAM 作为示例 define symbol __ICFEDIT_intvec_start__ 0x0C000000; define symbol __ICFEDIT_region_ROM_start__ 0x0C000000; define symbol __ICFEDIT_region_ROM_end__ 0x0C7FFFFF; define symbol __ICFEDIT_region_RAM_start__ 0x0C800000; define symbol __ICFEDIT_region_RAM_end__ 0x0CFFFFFF; // 把中断向量表段放到 RAM 起始位置 place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; // 代码放置到 NOR Flash 区域 place in ROM_region { readonly }; place in RAM_region { readwrite, block HEAP, block CSTACK };注意一个关键点S3C44B0X 支持从 SDRAM 直接启动代码吗不能。ARM7TDMI 复位后从 0x00000000 取指如果 OM 引脚配置成从 SDRAM 启动那 0x00000000 就是 SDRAM 的起始地址。但 SDRAM 需要初始化后才能访问这就鸡生蛋了。解决思路是NOR Flash 在 BANK0CPU 复位后脚指到 NOR Flash 执行一段小代码初始化 SDRAM然后把程序从 NOR Flash 复制到 SDRAM 高速执行。上面这个.icf文件对应的是「代码在 Flash、数据在 RAM」的经典布局适合调试阶段。3.4 IAR 库函数与 S3C44B0X 串口重定向的衔接S3C44B0X 的 UART 没有内置 FIFO只有一个 16 字节的发送保持寄存器。printf 重定向到串口时IAR 的putchar默认走 semihosting 方式——这意味着一行 printf 就要触发一次调试器访问速度掉到几百字节每秒。正确的做法是重写__write函数// IAR 的底层写函数printf 最终会调用到这里 // 这里把数据逐个字节发送到 UART0 // 注意size 是字节数不保证一次性写完需要循环 int __write(int handle, const unsigned char *buf, unsigned int size) { unsigned int i; if (handle ! 1) { // 1 是 stdout其他句柄不做处理 return -1; } for (i 0; i size; i) { // 等待 UART0 发送缓冲区空 // UART0 的状态寄存器 UTSTAT 的 bit[2] (TRANSMIT_BUFFER_EMPTY) // 为 1 时表示可以写入新数据 while (!(rUTSTAT 0x4)); // 写入 UART0 发送寄存器 rUTXH buf[i]; } return size; }这里的rUTSTAT和rUTXH同样需要宏定义#define rUTSTAT (*(volatile unsigned int *)0x01D00014) // UART0 状态寄存器 #define rUTXH (*(volatile unsigned int *)0x01D00020) // UART0 发送寄存器对应的__read函数接收串口输入。注意 IAR 的scanf会调用__read如果没实现链接期不报错运行期一调用就崩溃。做串口交互时两个函数必须成对出现。最后在工程选项里找到Project Options General Options Library Configuration把Library从Normal改为Full并在Redirect I/O to里选择Custom——否则你的__write实现会被库函数的弱符号覆盖。4. 实战配置在 IAR 里给 S3C44B0X 配 J-LINK 的烧录与调试参数4.1 为什么你的工程能编译但烧录时卡在Fatal error[LMS001]: License check failed这个错误几乎是 IAR 老用户遇到最多的拦路虎但它的触发条件和你想的不太一样。Fatal error[LMS001]: License check failed. Use the IAR License Manager to reinstall the license不是说你没激活而是许可证绑定的机器码和你当前硬件对不上。IAR 的许可证分两类一种是.lic文件绑定 MAC 地址或主板 ID另一种是注册机算出的激活码绑定 IAR 安装目录的common\bin\下的dll文件指纹。遇到这个错误时首先打开IAR License Manager开始菜单里搜LicenseManager看许可证的状态。如果显示Expired或Invalid删掉重装。S3C44B0X 的老工程推荐用 IAR 5.50 配合一个在 Win10 上稳定激活的 license。如果你的 IAR 8.x 报这个错另一个常见原因是多个版本的 IAR 共用同一个环境变量LM_LICENSE_FILE指向了错误的 license 文件。我一般这样处理把LM_LICENSE_FILE环境变量指向当前要用的那个 license 文件路径而不是让它空着或指向别的版本。然后用注册机重新生成一个 hostname 对应的 key。IAR 的 license 验证方式是检查 hostname、MAC 地址和磁盘卷序列号的组合哈希这三者任何一个变了都会导致[LMS001]报错。装上后立即重启系统因为某些服务IAR License Server在装完 license 后不会自动刷新。如果你用的是 USB 版 J-LINK还有一个隐性坑J-LINK 驱动版本过高时老版 IAR 识别不到仿真器报的也是LMS001——但那是误导信息实际是JLinkARM.dll版本不匹配。把JLinkARM.dll从C:\Program Files\SEGGER\JLink_V640\复制到 IAR 安装目录的arm\bin\下覆盖原名文件问题立刻消失。4.2 J-LINK 的接线与 Flash 下载算法选择S3C44B0X 的 JTAG 接口是标准的 20-pin ARM 定义和 STM32 的 SWD 不兼容。你需要确认 J-LINK 的版本V8 及以下自带 20-pin 转接板直接用排线连到 S3C44B0X 的 JTAG 座上。V9/V10/V11 是 9-pin 或 10-pin 的 Cortex-M 专用口必须额外用一个 20-pin 转 JTAG 的转接板否则第 3 脚nTRST和第 5 脚TDI信号对不上IAR 会报Could not find core。连接顺序TMS 对应 pin7TCK 对应 pin9TDI 对应 pin5TDO 对应 pin13GND 对应 pin4/6/8/10 任一个。重点检查 nSRSTpin15S3C44B0X 的复位引脚是低电平有效J-LINK 需要控制它来复位 CPU这个信号没连上调试器只能连到核心但无法Reset and Run。在 IAR 里FLASH 下载算法选择在Project Options Debugger Download下的Use flash loader而 S3C44B0X 的算法文件是FlashS3C44B0X.board位于arm\config\flashloader\Samsung\。如果你手上 IAR 安装目录里没有这个文件说明你用的 IAR 版本裁剪过旧芯片的 Flash 插件去IAR Systems\Embedded Workbench 5.50\arm\config\flashloader\Samsung\拷一份过来就能用。下载地址配置要注意Download选项卡里的Use flash loader(s)勾选后还要在Extra options里写上下载算法要烧录的 Flash 起始地址。S3C44B0X 的 NOR Flash 在 BANK0起始地址是 0x00000000。但如果你的开发板把 SDRAM 映射到 BANK6而 NOR Flash 在 BANK0调试器的下载地址必须是 0x00000000不能是 0x0C000000否则Flash loader会把 SDRAM 当 Flash 写直接总线错误。4.3 设置 J-LINK 的调试接口速率与时钟频率S3C44B0X 的工作频率由 PLL 决定。外部晶振一般是 10MHz 或 20MHzPLL 倍频后可跑到 60MHz 或 66MHz。JTAG 时钟和 CPU 主频的关系是JTAG 时钟必须低于 CPU 主频的 1/6否则时序不满足。J-LINK V8 默认的 JTAG 速度是 4MHz这在 S3C44B0X 跑 60MHz 时没问题但如果你把 PLL 调到 20MHz省电模式4MHz 的 JTAG 时钟就不再可靠表现为调试时单步进不了或者读寄存器偶尔出错。IAR 里调整位置在Project Options Debugger J-LINK/J-Trace Setup Interface把Speed从Auto改为1000 kHz。建议所有 S3C44B0X 工程都用 1MHz 而不是更高的 4MHz。原因有二第一这个芯片的 JTAG 引脚驱动能力弱PCB 走线长的话高速时钟的边沿反射会导致 TDO 采样错误第二J-LINK V8 的官方文档里写明 ARM7TDMI 的推荐 JTAG 时钟是 1MHz 起步。打开 IAR 的调试会话后用一个简单的 GPIO 翻转代码验证调试器控制是否正常// 在调试会话里用 IAR 的 Live Watch 窗口实时观察这个变量的变化 // 这个变量必须设为 volatile否则优化后根本不会被读取 volatile unsigned int debug_tick 0; // 在 main 里每隔一段时间翻转一次 LED同时递增 tick // 调试器在暂停时可以查看 tick 的值来判断程序有没有跑到这里 void Demo_JTAG_Check(void) { while (1) { debug_tick; rPDATC ^ (0x1 10); Delay(200); } }在 IAR 的View Live Watch里添加debug_tick单步执行后这个值应该实时变化。如果卡住不动优先检查 JTAG 接口的供电——S3C44B0X 的核心电压是 3.3V但有些老开发板的外设电压是 5VJTAG 电平转换芯片如 74LVC4245方向配置错误会导致 TDO 电平不对调试器能连接却读不到稳定数据。4.4 IAR 工程选项里必须改掉的三个默认值很多 S3C44B0X 工程在 IAR 里调不通问题不在代码而在工程选项的三个默认参数。第一个是General Options Target Endian mode。S3C44B0X 同时支持大小端但三星官方 BSP 默认用 Little endian。如果你在解压后没有确认过而原来的工程作者碰巧用了 Big endian你的串口打印会变成字节序错乱但编译不报错——这是最难排查的运行时错误。人眼判断方法如果 printf 输出的数字高位和低位互换了八成就是这个问题。第二个是Linker List Generate linker map file。这个选项默认不勾选但调试 S3C44B0X 的段溢出问题必须勾上。生成的.map文件里能看到每个段的具体地址范围特别是CSTACK段大小和HEAP段大小排查 IRQ 栈溢出时CSTACK字段的值是你唯一的线索。第三个是Debugger Download Use flash loader(s)下方的Verify download复选框。默认勾选但 S3C44B0X 的 NOR Flash 在写入后读回校验时如果 Flash 芯片的等待状态配置不正确J-LINK 读回的数据会全 0xFFIAR 报Verification failed。这不是烧录失败而是 JTAG 读 Flash 时序不对。改成No verification后烧录照常完成但要记得在代码里加一个 CRC 自校验否则 Flash 里的代码损坏了无从发现。4.5 S3C44B0X 的启动配置字与 OM 引脚S3C44B0X 的启动方式由 OM[1:0] 引脚决定这对 IAR 工程来说意味着「同样的.bin文件在不同的 OM 配置下表现完全不同」。OM[1:0] 00 时从 BANK0 的 ROM 启动01 时从 BANK0 的 ROM 启动但数据总线宽度为 16 位10 时从 SDRAM 启动11 时保留。绝大多数基于 S3C44B0X 的板子都设置为 00即 32 位总线宽的 Flash 启动。如果你的开发板原理图上 OM 引脚是拨码开关确认它处于 00 状态并且上拉/下拉电阻值正确——OM 引脚内部没有上拉浮空会导致随机启动模式。调试 S3C44B0X 时如果出现「J-LINK 连接正常但下载后复位一跑就死」的现象别怀疑代码先用万用表量 OM 引脚的电压。在 IAR 里S3C44B0X 的启动配置字Bus Width、Wait State不能通过软件设置完全由硬件引脚决定所以工程选项里没有对应配置项。正确做法是在启动文件的时钟和总线初始化部分显式配置BWSCON寄存器代码如下// 配置总线宽度和等待状态 // BWSCON 的 bit[31] 是 STSCLK 使能各 BANK 的高低 bit 配置数据宽度 // BANK0 固定为 30-bit 数据总线不需要软件配置 // 下面代码设置 BANK6SDRAM为 32 位数据宽度8 位等待状态 // 具体数值取决于你的 SDRAM 芯片速度等级 rBWSCON (rBWSCON ~0xFF000000) | (0x0C000000);这里的0x0C000000含义要拆开看bit[27:26] 是 BANK6 的数据宽度编码10表示 32 位bit[24] 是 WAIT 使能位置 0。但这段代码在许多老 BSP 里是写在44b0init.s汇编里的C 语言版本只在部分移植中存在。关键是确认你的 IAR 工程在启动文件里对BWSCON的赋值是否与 PCB 的物理连线一致——如果 PCB 上 BANK6 的 SDRAM 只是 16 位芯片这里配成 32 位不会立即报错但读写数据会丢失一半表现为随机死机或者 SDRAM 里代码跑飞。5. 进阶排查IAR 的层叠配置与实时调试技巧5.1 IAR 的__low_level_init与 S3C44B0X 的 PLL 初始化最小实现S3C44B0X 上电后 PLL 默认是关闭的CPU 直接以外部晶振频率运行。如果你外接的是 10MHz 晶振而以 10MHz 运行整个系统串口波特率设置和 SDRAM 时序都会出问题因为 S3C44B0X 的外设时钟由 PLL 输出分频而来晶振直接驱动时分频后的时钟频率太低SDRAM 无法工作在额定速率以上。IAR 的__low_level_init是一个 C 函数它在__iar_program_start之前调用专门用于初始化硬件、PLL 和存储器。S3C44B0X 的 PLL 初始化最简单实现// IAR 规定 __low_level_init 返回 0 表示不执行数据段初始化 // 返回 1 则正常初始化 RW/ZI 段 // S3C44B0X 的 SDRAM 需要提前初始化否则数据段搬运目标无效 int __low_level_init(void) { // 设置 PLL 倍频 // 外部晶振 10MHz目标主频 60MHz // PLLCON 寄存器的主要字段 // [19:12] PLL 倍频系数 (MDIV) // [9:4] 预分频系数 (PDIV) // [1:0] SDIV // S3C44B0X 的公式是 MCLK (2 * MDIV * Fin) / (PDIV * 2^SDIV) // 代入 Fin10MHzMDIV0x2C(44)PDIV0x08(8)SDIV0x00 // 得到 MCLK (2 * 44 * 10) / (8 * 1) 110MHz取整后接近 60MHz // 具体数值根据你自己的晶振和目标频率调整 rPLLCON (0x2C 12) | (0x08 4) | 0x00; // 等待 PLL 锁定锁定时间约 200us // PLLCON 写入后 CPU 立即停止时钟直到锁定完成后自动恢复 // 需要做几个 NOP 来填充流水线防止锁定时跳飞 __asm volatile (nop); __asm volatile (nop); __asm volatile (nop); // 返回 1让启动代码继续做数据段初始化 return 1; }具体 PLL 参数请查阅 S3C44B0X datasheet 里的 PLL 章节不同晶振频率和目标主频对应不同的 MDIV/PDIV/SDIV 组合。这里给出的是示例不是通用值——很多老工程的 PLL 参数是针对 20MHz 晶振调的你换一块 10MHz 晶振的板子直接跑串口乱码基本是这个原因。5.2 使用 IAR 的 C-SPY 宏做 S3C44B0X 的寄存器观察视图S3C44B0X 的外设寄存器有几百个IAR C-SPY 自带的 System Viewer 对 ARM7TDMI 内核设备支持有限很多寄存器没有可视化定义。但我可以告诉你一个绕过方法利用 IAR 的宏文件.mac在调试会话中建立寄存器别名。在 IAR 的Project Options Debugger Setup Macros里加载一个.mac文件// 定义 S3C44B0X 的 UART 寄存器地址映射 // 语法按 IAR C-SPY 宏格式支持 register 别名 // 0x01D00000 是 UART0 的基地址 // 各寄存器偏移量查 datasheet 后填入 __var UART0_UTSTAT 0x01D00014; __var UART0_UTXH 0x01D00020; // 在调试会话打开时自动打印状态 execUserReset() { // 复位后暂停验证寄存器可读 __message S3C44B0X debug session started; }然后在View Watch窗口里添加UART0_UTSTAT就可以像观察全局变量一样实时看到寄存器的值。这比每次打开Memory窗口查绝对地址高效得多尤其调试串口发送卡住时能直接看到TRANSMIT_BUFFER_EMPTY位的状态变化。这个.mac文件的内容可以扩充到所有常用外设建立自己的44b0x_register.mac调试视图库。5.3 定位启动文件死循环的方法用 J-LINK 的采样功能当 S3C44B0X 上电后没有反应串口也没有打印时问题通常在启动文件的前 50 行。但这类代码错误不会报错只能靠调试器定位。J-LINK 的J-Link Commander自带一个poll命令可以在程序运行期间周期性采样 PC 寄存器的值# 连接 S3C44B0X使用 JTAG 接口速度 1MHz # 后面的端口 19021 是 IAR C-SPY 的默认调试端口 # 命令结束后会报告采样的 PC 值分布 JLink.exe -device S3C44B0X -if JTAG -speed 1000 -AutoConnect 1打开 J-Link Commander 后输入poll 1 10这会每 1 秒采样一次 PC 寄存器采样 10 次。输出里你会看到 PC 停在某个 4KB 对齐的地址范围内——如果所有采样值都落在 Flash 的起始地址附近如 0x00000000 到 0x000000FF说明代码卡在启动文件开头的几个分支里最可能是 SDRAM 初始化不成功导致后续搬移全部失效。如果 PC 停在 0x0C800000 附近说明已经跳到 SDRAM 执行问题在 C 语言初始化阶段。这个定位方法对老设备维护尤为实用——不需要修改代码、不需要接逻辑分析仪只靠 J-LINK 本身的采样能力就能判断启动流程推进到哪一步。5.4 把 FreeRTOS 移植到 S3C44B0X 时 IAR 工程要改的五个点很多人在 S3C44B0X 上跑 FreeRTOS 是想复用一个现代 RTOS 替代老旧的 uC/OS-II。但 FreeRTOS 官方发布包里的 ARM7_AT91SAM7X 移植portable/ICC/ARM_CM3是给 Cortex-M 的ARM7 的要用portable/ICC/ARM7并不能直接在 IAR 上编译。逐项改动如下第一堆栈类型。S3C44B0X 的堆栈是满递减Full DescendingFreeRTOS 的portable/ICC/ARM7里portSTACK_TYPE应该定义为unsigned long确保每次入栈是 4 字节。第二临界区保护。ARM7 没有 BASEPRI 寄存器必须用MRS/MSR操作 CPSR 的 I 位// 进入临界区关中断并保存原状态 // 注意不能简单地用 __disable_interrupt()因为嵌套临界区会丢失状态 unsigned long vPortEnterCritical(void) { unsigned long cpsr; __asm volatile (MRS %0, CPSR : r (cpsr)); __asm volatile (MSR CPSR_c, %0 : : r (cpsr | 0x80)); // 置 I 位 return cpsr; } void vPortExitCritical(unsigned long cpsr) { __asm volatile (MSR CPSR_c, %0 : : r (cpsr)); }第三PendSV 不存在于 ARM7FreeRTOS 的上下文切换依赖 IRQ 中断。vPortYieldHandler需要放在 IRQ 中断服务里且中断向量表要手动添加一个软中断入口。第四configKERNEL_INTERRUPT_PRIORITY在 ARM7 移植里无效直接注释掉S3C44B0X 的中断优先级由外部中断控制器管理。第五portTICK_RATE_Hz对应的系统节拍定时器S3C44B0X 上一般用 Timer0 下降到 0 产生中断而 FreeRTOS 的 ARM7 移植默认假设使用 PIT 定时器需要在BSP\drivers\timer.c里改寄存器地址和中断号。这五个点改完FreeRTOS 在 S3C44B0X 上才能稳定跑进vTaskStartScheduler。如果链接时报undefined symbol vPortYieldHandler就是 IRQ 向量表里漏了这个函数——把它挂在HandleIRQ的 C 分支里用函数指针方式调用。6. 用 IAR 的 C-SPY 做 S3C44B0X 串口打印分析串口是 S3C44B0X 唯一的调试输出通道而 IAR C-SPY 的 Terminal I/O 窗口可以把这个输出捕获下来省去外接串口线。在Project Options Debugger Plugins里勾选Terminal I/O然后在代码里把__write定向到 C-SPY 的虚拟 UART——这需要利用 IAR 的底层钩子函数__debug_printf。在__write函数里加一个条件编译int __write(int handle, const unsigned char *buf, unsigned int size) { unsigned int i; // 调试模式下同时输出到串口和 C-SPY Terminal // 这样即使目标板串口没接也能看到输出 // __debug_printf 只在 C-SPY 运行时有效Release 编译时会被优化掉 if (handle 1) { for (i 0; i size; i) { __debug_printf(%c, buf[i]); // 重定向到 Terminal I/O while (!(rUTSTAT 0x4)); rUTXH buf[i]; } } return size; }你会发现__debug_printf是一个重定向函数它绕过目标板的 UART直接把字符显示在 IAR 的 Terminal I/O 窗口中。这意味着你在没有接串口线、甚至没有目标板 UART 驱动的情况下也能看到 printf 的输出——这个函数在 IAR 7.x 里存在但需要先勾选Project Options General Options Library Configuration Use CMSIS才会被包含进库。S3C44B0X 不在 CMSIS 支持列表里所以更可靠的做法是不依赖__debug_printf直接通过JLINK的 RTT 通道。在 IAR 调试会话运行时打开View Terminal I/O同时在 J-Link 控制面板里启用 RTT就能双通道捕获输出。S3C44B0X 老工程的串口波特率设置经常是 115200但晶振偏差大的板子实际波特率会偏到 115200 ± 3%造成乱码。在 IAR C-SPY 里打开View Watches添加UBRDIV寄存器观察窗口用UBRDIV (int)(MCLK / (16 * baud)) - 1反推实际波特率确定是配置错误还是硬件晶振偏差。这个方法比反复改代码测试高效得多——IAR 的寄存器观察窗口可以实时修改值改完立即生效不用烧录。本文还有配套的精品资源点击获取
返回列表