资讯动态

AT91SAM7SE实例包全解析:IAR下从启动文件到外设调试

发布时间:2026/10/16 17:49:29 来源:尧图企业网站定制
简介面向使用IAR Embedded Workbench调试AT91SAM7SE系列微控制器的开发者这份实例程序包将官方AT91LIB中的经典示例集中整理帮助快速掌握ARM7TDMI-S内核芯片的驱动写法。资源覆盖内部/外部Flash、DataFlash、NAND/NOR Flash、TWI EEPROM、USART硬件流控、PWM、ISO7816、SSC-I2S、GPIO及系统定时器等基础外设并包含USB DeviceHID键盘、Mass Storage、CDC串口、CCID、Audio Speaker等复杂工程全部可直接导入IAR进行编译、断点调试。包内共75个文件以40个zip工程为主要代码载体辅以24个HTML说明页、PDF文档与若干图片/动图便于对照文档逐模块理解压缩包整体仅14.62MB便于下载、备份与离线查阅。目前已有430人学习无论是刚接触ARM7的初学者还是需要快速移植外设驱动的进阶开发者都能从这份资料中找到中断处理、接口初始化、USB协议栈实现等完整参考大幅缩短项目开发周期。1. IAR下的AT91SAM7SE实例包先亮灯还是先看启动文件网上下到的AT91SAM7SE实例程序包十有八九是早期原厂评估板的代码工程里没有坑坑全在工程选项和启动文件配合上。这套包把UART、TWI、SPI、ADC、SDRAM、定时器这些外设的最少可运行代码集中放在IAR工程里核心价值不是跑通演示而是给一块你没有数据手册也能抄作业的模板。适合两类人一类是刚转ARM7的嵌入式新人想借真实工程理解启动和时钟另一类是手上正好有SAM7SE板子需要快速验证某个外设能不能用在量产设计上的工程师。下面按我实际拆包、改板、烧录的顺序讲每一步都能对应到包里的文件。2. 拆包先看四类文件启动汇编、链接脚本与外设库的关系拿到包先不要急着开工程先看目录结构。这个包不像现在的SDK那么规整里面既有IAR工程文件也有早期AT91外设库。如果你打开工程后第一件事是点编译大概率会看到一堆跟启动顺序有关的警告甚至直接编译不过。原因是启动文件、内存布局和外设库三者之间有隐含的顺序约定谁先谁后必须按原包来。2.1 目录里分得清这四类东西后面就顺了我拆这类包的习惯是先按扩展名把文件分类。包里最常见的是.ewp、.icf、.s、.c、.h这几类对应关系大概是这样的文件类型后缀解决什么问题IAR工程文件.ewp记录编译选项、调试器、下载算法链接脚本.icf决定代码放在Flash还是RAM地址对不对启动代码.s / .c复位向量、数据段拷贝、low_level_init外设头文件.h寄存器定义老库尤其依赖头文件版本如果包里有settings目录那是IAR保存的调试器界面配置可以不管。真正要改的是.icf里定义的Flash和RAM地址以及.s启动文件里复制的向量表位置。这些文件在包里通常是成对出现的一个工程对应一份.icf不要跨工程乱用。经验是先把.icf拷出来看一眼首尾再打开low_level_init.c最后才看main.c。很多人直接看main结果外设初始化函数跳来跳去最后连时钟是多少都不知道。2.2 启动文件复位后的第一次分支不是mainARM7TDMI复位后第一条指令从地址0取出来但这里有个关键点在IAR的老工程里复位向量直接跳到cstartup再由cstartup做段拷贝、调low_level_init最后才进入__iar_program_start。整个路径一般是这样/* 简化版复位序列实际以包内 cstartup.s 为准 */ Reset_Handler: ldr r0, __sdata_load ; 数据段加载地址 ldr r1, __sdata ; 数据段运行地址 ldr r2, __edata ; 数据段结束地址 copy_loop: cmp r1, r2 bcs copy_done ldr r3, [r0], #4 str r3, [r1], #4 b copy_loop copy_done: ldr r0, low_level_init blx r0 ldr r0, __iar_program_start bx r0这段代码的逻辑很直接先把初始化数据从Flash搬到RAM然后调一次low_level_init最后才进入IAR的C运行环境。low_level_init是这份实例包里最值得读的函数它负责三件事关闭看门狗、配置主时钟和PLL、配置EBI外设总线。如果你改动了启动文件比如把low_level_init删掉了症状往往是程序能连上调试器但运行后外设全部不工作因为时钟没起来就成了死循环。参数上要注意__sdata_load、__sdata、__edata这些符号是链接脚本提供的不是启动文件自己定义的改了.icf里段名会直接编译错。另外一个隐蔽点早期IAR的ARM7工程默认是ARM模式不是Thumb模式。你看到的汇编如果全是16位短指令那可能是被换过内核模式的错误工程。AT91SAM7SE的IAR工程里启动文件应当用32位ARM指令编写。2.3 .icf链接脚本为什么Flash在0x00100000RAM在0x00200000AT91SAM7SE的内部Flash和SRAM映射地址跟Cortex-M完全不一样。Cortex-M的Flash通常从0x08000000开始而AT91SAM7SE的内部Flash从0x00100000开始SRAM从0x00200000开始。如果你沿用其它芯片的.icf最典型的结果是烧录时提示地址越界或者烧进去根本跑不起来。包内的.icf一般会写成这样define symbol __ICFEDIT_region_ROM_start__ 0x00100000; define symbol __ICFEDIT_region_ROM_end__ 0x0017FFFF; define symbol __ICFEDIT_region_RAM_start__ 0x00200000; define symbol __ICFEDIT_region_RAM_end__ 0x0020FFFF;这里针对的是SAM7SE512512KB Flash、64KB SRAM。如果你换用SAM7SE256ROM结束地址要改成0x0013FFFFRAM结束地址保持0x0020FFFF。我见过有人从SE512的工程拷到SE256上忘了改ROM范围编译链接没报错一运行就异常。因为多出来的代码段地址超出了实际Flash容量下载算法写了半天也没写进去。如果包里还用了外部SDRAM.icf里会额外有一段define region SDRAM_region [from 0x20000000 to 0x203FFFFF];之类的定义。SDRAM的基地址由EBI的片选决定不同板子可能挂在CS0或CS1上地址不同。拿到包先确认板子的SDRAM接在哪个片选再跟.icf对照否则main里访问的指针地址就是错的。3. 时钟、GPIO与串口外设最小系统在实例里怎么落地这个包的外设示例其实不难难点在初始化顺序。AT91SAM7SE的外设时钟默认是关闭的你要用哪个外设就得在PMC里打开对应的外设时钟。跟Cortex-M把外设时钟集中在一个RCC里不同AT91的PMC用PMC_PCER寄存器按外设ID使能。3.1 主时钟与PLL先看晶振频率再算倍频分频实例包里的时钟代码基本分成四步等待主振荡器稳定、配置PLL、等待PLL锁定、切换主时钟源。这里最容易踩坑的是没等就切或者PLL参数算错导致MCK不是你要的值。/* 以外部18.432MHz晶振为例目标PLL输出约96MHzMCKPOSTDIV后的值 */ /* 1. 配置主振荡器启动时间 */ AT91C_BASE_PMC-PMC_MOR AT91C_CKGR_OSCOUNT | (96 8) | (1 0); /* 2. 等待主振荡器就绪 */ while(!(AT91C_BASE_PMC-PMC_SR AT91C_PMC_MOSCS)); /* 3. 配置PLLDIV16, MUL82(实际乘83), 1.152MHz*83 ≈ 95.6MHz */ AT91C_BASE_PMC-PMC_PLLR (AT91C_CKGR_OUT_HIGH) | (16 8) | (82 0) | (AT91C_CKGR_USBDIV_1); /* 4. 等待PLL锁定 */ while(!(AT91C_BASE_PMC-PMC_SR AT91C_PMC_LOCK));这段代码里DIV16是把18.432MHz先分到1.152MHzMUL82表示倍频到约95.6MHz。为什么选这个组合因为USB和外设常常要求48MHz上下95.6MHz再经过MCK分频就能得到约47.8MHz接近48MHz。实际工程要以板子上的晶振为准如果是12MHz晶振DIV和MUL都要重算。计算方式记住一条PLL输出频率 主时钟频率 / DIV × (MUL 1)。不要直接写MUL83因为MUL字段的意义是倍数减一。这个混淆在初学AT91的人里特别常见算出来频率差一倍串口和定时器全部偏。3.2 GPIO点灯PER、OER、SODR三个寄存器一次说清GPIO在AT91SAM7SE上并不是直接写ODR而是先要让引脚归属于PIO控制器再配置方向和输出值。早期AT91库里有PIO_PER、PIO_OER这样的宏名字看着玄学其实逻辑非常清楚。/* 打开PIOA的外设时钟 */ AT91C_BASE_PMC-PMC_PCER (1 AT91C_ID_PIOA); /* 让PA0归属PIO控制而不是外设功能 */ AT91C_BASE_PIOA-PIO_PER (1 0); /* 配置PA0为输出方向 */ AT91C_BASE_PIOA-PIO_OER (1 0); /* 输出高电平亮灯 */ AT91C_BASE_PIOA-PIO_SODR (1 0); /* 输出低电平灭灯 */ AT91C_BASE_PIOA-PIO_CODR (1 0);PIO_PER里的PER是 Pin Enable Register让引脚从外设功能切回PIO功能。PIO_OER是 Output Enable Register决定方向。PIO_SODR和PIO_CODR是置位和清零寄存器写1有效写0不影响。这种设计的好处是读改写不会破坏其它引脚状态。如果你看到代码里写的是PIO_ODR那可能有半个字节是输入方向检查一下是不是抄错了寄存器名。实例包里的LED例程一般还会带按键按键那一路要配PIO_ODR输出禁用和PIO_IER中断使能。如果只点灯不看中断IAR调试下直接用SODR/CODR就够了。3.3 串口从哪里开始USART初始化与printf移植AT91SAM7SE的UART其实叫USART功能比普通UART多但裸机用法差不多。波特率计算公式是CD MCK / (16 × BaudRate)CD写进US_BRGR。这个CD必须是整数如果除不尽就取整波特率会有误差误差超过2%就会乱码。/* 假设MCK已经是48MHz目标波特率115200 */ unsigned int cd 48000000 / (16 * 115200); /* cd 26 */ /* 配置USART模式正常模式无校验8位数据 */ AT91C_BASE_US0-US_MR AT91C_US_CHMODE_NORMAL | AT91C_US_PAR_NONE; /* 装载波特率时钟分频 */ AT91C_BASE_US0-US_BRGR cd; /* 同时使能接收和发送 */ AT91C_BASE_US0-US_CR AT91C_US_RXEN | AT91C_US_TXEN;注意先写US_MR再写US_BRGR因为某些库实现的US_MR写入会连带影响波特率分频。实例包里通常还有一个UartPutc之类的函数直接操作US_THR发送寄存器。要移植printf的话IAR的做法是重写putchar或者__write把字符逐个喂给UartPutc。我一般直接在main.c里加一个int putchar(int ch) { while(!(AT91C_BASE_US0-US_CSR AT91C_US_TXRDY)); AT91C_BASE_US0-US_THR ch; return ch; }US_CSR里的TXRDY表示发送数据寄存器为空但没有TXEMPTY这类标志表示移位寄存器已经腾空。如果你要在发送完最后一个字符后立刻切GPIO光看TXRDY不够还要等TXEMPTY否则最后一个字节可能没发完就被引脚切换淹没了。这个细节在串口做AT指令交互时尤其重要。4. 用IAR下载和调试Flash烧录、Remap与运行模式的选择实例包是给你在板子上跑的不是给你看着玩的。IAR环境里最常被忽略的是工程选项里的调试器配置和下载算法。很多人程序编译通过一按下载就报错问题不在代码在IDE配置。4.1 工程选项按这个顺序过一遍拿到工程先打开 Options不要直接编译。我一般按四个位置检查第一General Options 里的 Device 要选对芯片型号比如 AT91SAM7SE512。如果选成SE256Flash大小和调试器识别的ID都对不上。第二Debugger 里选实际的调试器类型这个包写的年代早默认可能是并口调试器你现在用USB调试器就必须改。第三Debugger 的 Download 页要勾选使用Flash下载算法否则程序会被加载进RAM而不是烧进Flash。第四如果板子有外部SDRAM并且调试时要在SDRAM里跑数据还要在Linker段的.icf里把SDRAM区域加进去。这些设置不复杂但顺序反了会很困惑。比如你先点了编译再去看设置编译器已经把优化选项和后端用默认值处理了跟工程原始配置不一致一些老的汇编代码会被重新编译出不同行为。4.2 Remap与启动模式为什么烧进Flash后第一次复位最重要AT91SAM7SE有个跟ARM7核不完全一样的地方复位后地址0的访问会被芯片内部逻辑映射到内部Flash但如果你在low_level_init里做了重映射地址0会改挂到RAM或外部存储上。IAR的调试器在连接时会重置芯片并设置PC如果它把PC设到0而0这时指向RAMRAM又是空白就会看到跑飞。实例包的处理方式是在启动早期完成Remap让中断向量表之后也能在RAM中快速响应。这个Remap动作通常在low_level_init的EBI配置之后用一个小函数写控制寄存器。你在调试时如果看到PC突然跳到0x00000004或0x00000008同时Disassembly窗口里是一堆0xFF大概率就是Remap完成后调试器没有重新加载符号。我的建议是第一次调试用IAR的复位后停在main入口不要让它复位后直接全速运行。等确认Remap和时钟正常后再改成复位后全速运行。别小看这一步能省你一个下午的定位时间。4.3 Flash烧录失败时先看这几处烧录失败有很多种常见的几种表现是擦除时校验失败、写入后校验不一致、下载到一半JTAG断开。AT91SAM7SE是ARM7核Flash操作需要执行特定的时序IAR通过下载算法程序来完成不像Cortex-M那样调试器直接写Flash。如果下载算法选错最常见的报错是找不到Flash起始地址。这时候打开工程的.board文件或者调试器配置文件看Flash起始地址是不是0x00100000。有些老的IAR版本默认Flash地址是0跟AT91SAM7SE的实际地址不一致导致擦除第一步就失败。还有一个容易忽略的点板子上的BMS引脚或者TST引脚状态会影响启动模式。如果芯片被拉进了测试模式调试器可以连接但无法正常擦写Flash。遇到这种情况先查硬件上TST引脚是不是被拉高了而不是反复重试下载。4.4 RAM调试与Flash调试的区别实例包里可能有专门的RAM版本工程这类工程把.icf的ROM区域改到SRAM地址程序通过调试器加载到RAM里跑。RAM调试的好处是擦写快、不磨损Flash坏处是一掉电程序就没。Flash调试则是程序固化到Flash里复位后自动运行。我在抓启动类问题时喜欢用RAM调试因为可以反复改low_level_init而不必每次等Flash擦写。但注意RAM调试时向量表和栈地址必须在RAM范围内如果栈底被放到Flash地址函数调用一深就崩。检查.icf里STACK和HEAP段的位置保证它们在RAM区域的结尾位置。5. 避坑记录五次烧录现场的现象、原因与处理这里整理五条我实际遇到过的坑每条都是“现象、原因、解决”三段式的写法。前三条是初学者高频问题后两条是换芯片、升级IDE后才会碰到的。5.1 一烧进去就跑飞反复复位现象程序下载成功一运行就停在HardFault或者不停复位printf完全没有输出。把断点设在main.c第一行也停不住好像代码根本没执行。原因这个包的老工程里low_level_init被放在了数据段拷贝之后调用但如果你用新版本IAR打开启动文件的段符号名对不上导致low_level_init的地址是个无效值。另一种可能是看门狗没有先关PLL锁定耗时超过了看门狗超时时间复位循环就产生了。解决在启动汇编调用low_level_init之前先关看门狗。具体做法是在low_level_init.c的第一行写AT91C_BASE_WDTC-WDTC_WDMR (AT91C_WDTC_WDDIS | AT91C_WDTC_WDKEY_VALUE);然后在调试器里把断点放在low_level_init第一行单步进去如果能正常停在第一行就说明启动汇编没问题。之后再逐步打开PLL锁定等待每步都用单步跨过看是否还会复位。5.2 串口全是乱码现象程序能正常运行LED按预期闪但串口打印出来的内容像ASCII错位一样换电脑也这样。波特率怎么改都只对一半比如115200下乱码9600下勉强能看。原因MCK实际频率跟预期差很多。程序里按48MHz算波特率但PLL配置算错或者主振荡器没等到稳定MCK实际只有几MHz。另一个常见原因是USART被配置成过采样模式US_MR里的OVER位为1波特率公式变成了MCK / (8 × Baud)而代码还用16分频去算CD结果差一倍。解决先不要裸眼看乱码用一个逻辑分析仪或者示波器去量TX脚空闲电平和起始位宽度。以115200为例1 bit宽度约8.68微秒如果量出来是17微秒就是过采样设置错误。然后把波特率计算里加上对OVER的判断unsigned int cd; if (over) { cd mck / (8 * baud); } else { cd mck / (16 * baud); } AT91C_BASE_US0-US_BRGR cd;5.3 调试器连接不上现象点调试器连接提示无法识别设备或者JTAG链异常但芯片供电正常晶振也在起振。原因AT91SAM7SE的JTAG引脚跟PIO复用如果程序里把TDI/TMS对应的引脚配置成了GPIO输出调试器就没法再控制内核。还有一种情况是复位引脚被电容拉低时间太长调试器在复位期间握手失败。解决按住板子上的复位键再点连接连接过程中不要松开。如果这样能连上说明是调试器复位时序问题在IAR里把复位方式改成软件复位。要是仍连不上检查TDI/TDO/TCK/TMS这四根线的上拉电阻尤其TMS必须有上拉否则JTAG状态机走不到正确状态。5.4 SDRAM例程一跑就死现象程序在内部Flash里正常运行一旦进入SDRAM读写例程要么卡死要么读出来的数据是0xFF。原因SDRAM的初始化需要先给控制器配置时序再发预充电和刷新命令。实例包里如果针对的是某块特定板子SDRAM时序参数匹配的是该板子的颗粒。你换了板子颗粒的CL、刷新周期都不一定相同沿用原参数就会出问题。解决把SDRAM初始化函数拆成两个阶段先初始化和刷新再执行读写测试。读写测试用最简单的模式写一遍unsigned int *sram (unsigned int *)0x20000000; int i; for(i 0; i 1024; i) { sram[i] 0x5A5A0001 i; } for(i 0; i 1024; i) { if(sram[i] ! (0x5A5A0001 i)) { while(1); /* 停在错误位置方便看地址 */ } }如果在第二个循环里卡住说明刷新周期不够或者列地址宽度不对。查板子SDRAM颗粒的数据手册把行地址和列地址宽度配置进EBI的EBI_CFGR。刷新间隔参数在内存控制器寄存器里别用SDRAM例程的默认值要按颗粒的刷新周期要求算。5.5 把工程从旧IAR升级到新IAR后编译报错现象工程原来在IAR老版本里编译正常换到新版本后满屏错误主要集中在寄存器头文件和启动汇编部分说什么符号未定义或者语法错误。原因早期AT91外设库的写法很老部分头文件用了非标准的位域定义和重名宏。新编译器对类型检查更严格一些隐式转换直接报错。另一个大坑是.icf文件格式有版本差异老版本里的place in语法可能不被新版本完整支持。解决不要一个个改头文件先升级外设库版本或者把包里的头文件换成新IAR兼容的版本。如果还想保留老库编译选项里把语言标准从C99放宽到C89很多位域警告会消失。.icf就老老实实对照新版本语法逐行改重点看place in和initialize by copy这两类语句。6. 验证顺序与进阶玩法点灯、回环、SDRAM一次跑全等你把前面几章都过完整套包能在IAR里编译、烧录、稳定运行接下来不是急着往项目里塞功能而是先做一轮完整的验证。我自己的习惯是固定一个顺序按“最小系统、数据通路、存储、外设”逐步扩展。第一步点灯。把GPIO例程编译烧进去确认复位后LED按预期亮灭。这一步验证的是启动文件、时钟、PIO外设时钟是否正常。如果灯不亮不要往下走回去查low_level_init和.icf。第二步串口回环。把USART的TX接到RX上程序里发一字节收一字节再比对是否一致。这一步同时验证USART发送、接收和波特率误差。通过后我会再测一轮大数据量发送比如从0到255循环发10遍确认没有丢字节。回环测试不要只用单字节单字节在部分寄存器缓冲下可能掩盖时序问题。第三步定时器与中断。把定时器例程的分频参数改成1ms触发一次在中断里翻转LED。验证AIC中断控制器配置是否正确。AT91SAM7SE的中断要逐级打开外设使能、AIC使能、全局中断三处少一处中断都不会触发。第四步SDRAM读写。如果板子有SDRAM用前面那个递增数据模式来回读多跑几轮。我一般会跑1000轮反正速度很快。这一步过了外部存储就可以放心在项目里用。第五步看门狗测试。把看门狗打开注释掉喂狗代码确认系统在设定时间内复位。这个测试千万别在量产前跳过很多早期板子就是运行时偶尔复位查了半天最后发现是狗没喂好。有一次我帮一个同事调板子程序白天跑得好好的凌晨就会死一次。当时查了三天怀疑是电源波动最后发现是SDRAM刷新定时器配置在低温下偏差变大导致一个BLANK周期丢失。从那以后我每次拿到新板子都会强制自己按点灯、回环、存储测试的顺序完整跑一遍不是走个流程是真的在里面花时间观察每次复位的规律。这套AT91SAM7SE实例包让我少走了不少弯路希望也能帮你把IAR下的第一块板子顺利点亮少翻几次车。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑