1. 项目缘起与核心目标最近在捣鼓一些需要低成本、高性能MCU的小玩意儿手头正好有几片雅特力Artery的AT32F421F8P7。这芯片在圈子里热度不低主打一个“小身材大能量”——TSSOP20的封装却塞进了ARM Cortex-M4内核主频能跑到150MHz还带12位ADC和DAC价格也相当有竞争力。对于很多需要紧凑设计、又有一定算力需求的场景比如智能传感器、小型电机控制或者一些消费电子的小模块它是个非常值得考虑的选项。但芯片好不好用光看数据手册是远远不够的必须得上手“盘”它。所以这篇笔记就记录下我从零开始对AT32F421F8P7进行初步测试的完整过程包括开发环境搭建、程序下载调试、核心外设验证以及踩过的一些坑目标是给同样想尝试这款芯片的朋友提供一个清晰、可复现的参考路径。2. 开发环境搭建与工具链配置工欲善其事必先利其器。对于AT32F421系列雅特力官方提供了比较完善的生态支持我们可以基于熟悉的ARM开发工具链来开展工作。2.1 硬件准备清单测试的第一步是准备好硬件。除了AT32F421F8P7核心板或自制的最小系统板你还需要以下几样东西调试下载器这是连接电脑和单片机的桥梁。官方推荐使用AT-LINK它对自家芯片的支持最完美功能也最全。当然市面上常见的ST-LINK/V2或兼容的DAP-Link也基本支持通过SWD接口可以进行下载和调试。我手头有一个ST-LINK这次测试就先用它。连接线需要杜邦线母对母来连接调试器和目标板。SWD接口最少需要四根线SWDIO数据、SWCLK时钟、GND地和3.3V电源。如果你的目标板可以独立供电比如通过USB转TTL的5V供电那么可以不接调试器的3.3V但GND必须共地这是通信的基础。目标板确保你的AT32F421F8P7最小系统板工作正常。最基本的要求是电源3.3V稳定、复位电路正常、启动模式引脚BOOT0正确配置为从主Flash启动通常下拉到GND、外部高速晶振如果用到起振正常。注意在连接调试器之前最好先用万用表测量一下目标板的3.3V和GND之间是否短路避免上电烧毁调试器或芯片。这是硬件工程师的基本修养能避免很多悲剧。2.2 软件环境搭建软件方面我们主要需要集成开发环境IDE、设备支持包和编程工具。IDE选择与安装我选择的是Keil MDK-ARM也就是大家常说的Keil5。因为它生态成熟插件丰富对ARM Cortex-M系列的支持度极高。你可以从Arm官网下载并安装MDK的社区版有代码大小限制或购买正式授权。安装过程比较简单一路Next即可。安装AT32设备支持包Keil本身不认识AT32的芯片需要安装对应的设备家族包Device Family Pack, DFP。前往雅特力官方网站的“下载中心”找到AT32F421系列对应的AT32F421_DFP包下载后双击安装。安装成功后在Keil中新建项目时就能在设备选择列表里看到“Artery”目录下的AT32F421F8P7了。调试器驱动确认如果你使用ST-LINK需要确保电脑上安装了ST-LINK的USB驱动。通常当你第一次插入ST-LINK到电脑时Windows会自动尝试安装也可以从ST官网下载独立的驱动安装程序。安装后可以在设备管理器中看到“STMicroelectronics STLink dongle”之类的设备且没有感叹号。可选AT32 Workbench雅特力还提供了一个名为AT32 Workbench的图形化配置工具类似于STM32的CubeMX。它可以用来图形化配置引脚、时钟、外设并生成初始化代码框架。对于快速搭建项目原型非常有用。你可以从官网下载并安装它在复杂项目初期用Workbench生成基础代码能节省大量时间。3. 第一个工程点亮LED与SWD连接实战理论准备就绪现在开始实战。我们从最经典的“点灯”程序开始这不仅能验证编译下载流程也能确认芯片是否“活着”。3.1 创建Keil工程与基础配置打开Keil点击“Project - New uVision Project”选择一个空文件夹存放工程并命名为AT32F421_Test。在弹出的设备选择窗口中依次选择“Artery” - “AT32F421 Series” - “AT32F421F8P7”点击OK。接下来会弹出“Manage Run-Time Environment”窗口这里我们可以添加需要的软件组件对于第一个简单工程我们暂时什么都不选直接点击OK关闭。工程创建好后我们需要进行一些关键配置。点击工具栏的“魔术棒”按钮Options for Target。Target标签确认芯片型号和晶振频率我们暂时使用内部RC振荡器HSI所以这里填8M即可后续时钟配置会细化。Output标签勾选“Create HEX File”方便我们后续用其他工具烧录。C/C标签在“Define”框中根据芯片型号添加全局宏定义例如AT32F421F8P7。在“Include Paths”里添加固件库的头文件路径通常指向你下载的AT32标准外设库AT32F421_Firmware_Library下的include和libraries目录。Debug标签这是重中之重。在“Use”下拉框中选择你的调试器比如“ST-Link Debugger”。然后点击右侧的“Settings”。在“Debug”子标签中确认“Port”选择为SW。在“Flash Download”子标签中我们需要添加针对AT32F421的Flash编程算法。点击“Add”在弹出的窗口中如果你安装了AT32的DFP包这里应该能看到“AT32F421xx Flash”之类的选项添加它。如果没有你需要手动找到DFP安装目录下的.FLM文件添加进去。这一步确保了Keil知道如何擦写这块芯片的Flash。3.2 编写简易点灯程序假设我们的硬件连接是LED阴极接地阳极通过一个限流电阻连接到芯片的PA1引脚即GPIOA的第1个引脚。首先在工程中新建一个main.c文件。代码的核心思路是初始化系统时钟先使用默认的HSI、初始化GPIOA的Pin1为推挽输出模式然后在主循环中翻转该引脚的电平并加上延时。#include at32f421.h #include at32f421_clock.h // 简单的延时函数基于循环不精确仅用于测试 void delay_ms(uint32_t ms) { for(uint32_t i 0; i ms; i) { for(uint32_t j 0; j 8000; j) { __NOP(); } } } int main(void) { // 1. 初始化系统时钟默认使用HSI 8MHz后续可配置为更高频率 system_clock_config(); // 2. 使能GPIOA的外设时钟 crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); // 3. 配置PA1为推挽输出模式 gpio_init_type gpio_init_struct; gpio_default_para_init(gpio_init_struct); gpio_init_struct.gpio_pins GPIO_PINS_1; gpio_init_struct.gpio_out_type GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_pull GPIO_PULL_NONE; gpio_init_struct.gpio_mode GPIO_MODE_OUTPUT; gpio_init_struct.gpio_drive_strength GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOA, gpio_init_struct); while(1) { // 4. 点亮LED假设高电平点亮 gpio_bits_set(GPIOA, GPIO_PINS_1); delay_ms(500); // 5. 熄灭LED gpio_bits_reset(GPIOA, GPIO_PINS_1); delay_ms(500); } }3.3 编译、下载与调试点击Keil的“Build”按钮或F7编译工程。如果一切配置正确你会在“Build Output”窗口看到“0 Error(s), 0 Warning(s)”。接下来是关键一步连接硬件。用杜邦线将ST-LINK与目标板连接好ST-LINKSWDIO- 目标板PA13(SWDIO)ST-LINKSWCLK- 目标板PA14(SWCLK)ST-LINKGND- 目标板GNDST-LINK3.3V- 目标板3.3V(如果目标板独立供电此线可不接但GND必须接)给目标板上电。点击Keil的“Load”按钮或F8下载程序。如果成功你会看到“Flash Load finished at 00:00:00”之类的提示。此时复位一下目标板你应该能看到LED开始闪烁。实操心得第一次下载时最容易出现“No ULINK/ST-LINK found”或“Flash Download failed”错误。排查顺序如下1. 检查USB线是否插好设备管理器里调试器驱动是否正常。2. 检查SWDIO和SWCLK两根线是否接反、虚焊。3. 检查目标板供电是否正常测量3.3V电压。4. 检查Keil的Debug配置中Port是否选为SW。5. 检查Flash Download配置中是否添加了正确的编程算法。6. 尝试降低SWD时钟频率在Debug Settings的“SW Device”扫描设置里。7. 检查芯片的复位引脚是否被意外拉低或者BOOT0引脚电平是否正确。4. 核心外设初步测试与性能摸底点亮LED只是第一步证明了最小系统和下载通路是好的。接下来我们需要测试几个核心外设来感受一下这颗M4内核芯片的实力。4.1 系统时钟配置与性能测试AT32F421F8P7最高可以跑到150MHz这比同价位很多M0/M3芯片要强不少。我们可以通过配置锁相环PLL来提升系统时钟。在system_clock_config()函数中通常位于at32f421_clock.c我们可以修改时钟树配置。例如将HSI8MHz通过PLL倍频到120MHz。void system_clock_config(void) { // ... 省略其他初始化代码 // 配置PLL将HSI 8MHz倍频到120MHzPLL 8MHz / 2 * 30 120MHz pll_config.pll_mult PLL_MULT_30; pll_config.pll_source CRM_PLL_SOURCE_HSI; pll_config.pll_div CRM_PLL_DIV_2; crm_pll_config(pll_config); // 选择PLL作为系统时钟源 crm_sysclk_switch(CRM_SCLK_PLL); // ... 等待时钟切换成功等后续代码 }修改后重新编译下载你会发现LED闪烁的速度明显变快了因为我们的延时循环是基于指令周期的。为了更精确地测试性能可以写一个简单的代码来测试GPIO翻转速度。在一个空循环里不断翻转某个引脚用示波器测量方波频率可以直观反映内核执行效率。在120MHz下如果使用寄存器直接操作GPIOA-scr pin; GPIOA-clr pin;翻转频率可以达到几十MHz远超普通M0内核。4.2 12位ADC采样测试AT32F421F8P7内置一个12位逐次逼近型ADC多达10个外部通道。我们来测试一下ADC的采样功能。假设我们想测量开发板上某个电位器的电压连接到PA0即ADC的通道0。首先需要初始化ADC和对应的GPIO引脚PA0配置为模拟输入模式。然后配置ADC的采样时间、分辨率、对齐方式等。这里采用单次转换模式进行测试。void adc_init_single(void) { // 使能ADC和GPIOA时钟 crm_periph_clock_enable(CRM_ADC_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); // 配置PA0为模拟输入 gpio_init_type gpio_init_struct; gpio_default_para_init(gpio_init_struct); gpio_init_struct.gpio_pins GPIO_PINS_0; gpio_init_struct.gpio_mode GPIO_MODE_ANALOG; gpio_init(GPIOA, gpio_init_struct); // ADC基础配置 adc_base_config_type adc_base_struct; adc_base_default_para_init(adc_base_struct); adc_base_struct.sequence_mode FALSE; // 非扫描模式 adc_base_struct.repeat_mode FALSE; // 非重复模式 adc_base_struct.data_align ADC_RIGHT_ALIGNMENT; // 数据右对齐 adc_base_struct.ordinary_channel_length 1; // 一个常规通道 adc_base_config(ADC1, adc_base_struct); // 配置采样时间 adc_ordinary_channel_set(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_239_5); // 使能ADC adc_enable(ADC1, TRUE); // 等待ADC就绪 while(!adc_flag_get(ADC1, ADC_RDY_FLAG)); // 开始校准 adc_calibration_init(ADC1); while(adc_calibration_init_status_get(ADC1)); adc_calibration_start(ADC1); while(adc_calibration_status_get(ADC1)); } uint16_t adc_read_value(void) { adc_ordinary_software_trigger_enable(ADC1, TRUE); // 软件触发转换 while(!adc_flag_get(ADC1, ADC_CCE_FLAG)); // 等待转换结束 return adc_ordinary_conversion_data_get(ADC1); }在主循环中调用adc_read_value()并打印通过串口或转换为电压值电压 采样值 / 4095 * 3.3V就能看到ADC的读数随着电位器旋转而变化。通过测试可以发现其线性度和稳定性符合12位ADC的预期。4.3 定时器中断与PWM输出测试定时器是单片机的“心脏”。AT32F421拥有多个高级、通用和基本定时器。我们测试通用定时器TIM2的更新中断和PWM输出功能。定时器中断配置TIM2每1ms产生一次更新中断在中断服务程序里对一个计数器加一可以用来实现更精确的延时或系统心跳。void tim2_interrupt_init(void) { // 使能TIM2时钟 crm_periph_clock_enable(CRM_TIM2_PERIPH_CLOCK, TRUE); // 时基配置假设系统时钟120MHz预分频器设为12000-1则计数器时钟为10kHz // 自动重载值设为10-1则产生1ms (10kHz / 10 1kHz) 中断 timer_init(TIM2, 11999, 9); // 参数为预分频值和重载值 timer_interrupt_enable(TIM2, TIMER_OVF_INT, TRUE); // 使能溢出中断 nvic_irq_enable(TIM2_IRQn, 0, 0); // 使能NVIC中断 timer_enable(TIM2, TRUE); // 启动定时器 } // 在stm32f4xx_it.c或单独的中断文件中实现中断服务函数 void TIM2_IRQHandler(void) { if(timer_flag_get(TIM2, TIMER_OVF_FLAG) ! RESET) { timer_flag_clear(TIM2, TIMER_OVF_FLAG); // 用户代码例如递增一个ms计数器 g_sys_tick_ms; } }PWM输出配置TIM2的通道1对应PA0注意与ADC测试冲突需换引脚或分时复用输出一个1kHz占空比50%的方波。void tim2_pwm_init(void) { // 初始化GPIO以PA5为例TIM2_CH1的替代输出引脚需查数据手册确认 gpio_init_struct.gpio_pins GPIO_PINS_5; gpio_init_struct.gpio_mode GPIO_MODE_OUTPUT; gpio_init_struct.gpio_out_type GPIO_OUTPUT_PUSH_PULL; gpio_init_struct.gpio_pull GPIO_PULL_NONE; gpio_init_struct.gpio_drive_strength GPIO_DRIVE_STRENGTH_STRONGER; gpio_init(GPIOA, gpio_init_struct); gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_1); // 复用功能AF1 // 定时器时基配置产生1kHz PWM120MHz / (1200 * 100) 1kHz timer_base_init(TIM2, 1199, 99); // 预分频1199重载值99 // 配置通道1为PWM模式1 timer_channel_output_config_type oc_config; timer_channel_output_default_para_init(oc_config); oc_config.output_state TRUE; oc_config.oc_mode TIMER_OC_MODE_PWM1; oc_config.oc_polarity TIMER_OC_POLARITY_HIGH; timer_channel_output_config(TIM2, TIMER_SELECT_CHANNEL_1, oc_config); // 设置占空比比较值 (重载值1) * 占空比 100 * 50% 50 timer_channel_value_set(TIM2, TIMER_SELECT_CHANNEL_1, 50); timer_primary_output_config(TIM2, TRUE); timer_enable(TIM2, TRUE); }用示波器探头测量PA5引脚应该能看到一个标准的1kHz方波。通过修改比较值可以动态调整占空比这是控制电机速度、LED亮度等应用的基础。5. 深度测试DAC输出与模拟性能验证AT32F421F8P7的一个亮点是集成了12位DAC这在同级别小封装MCU中并不多见。我们可以用它来生成简单的波形测试其模拟性能。5.1 DAC初始化与直流输出DAC有两个通道我们测试通道1对应PA4。首先需要将PA4配置为模拟模式DAC输出引脚固定无需配置为复用功能。然后初始化DAC并使能输出缓冲Buffer以获得更好的驱动能力。void dac_init(void) { // 使能DAC和GPIOA时钟 crm_periph_clock_enable(CRM_DAC_PERIPH_CLOCK, TRUE); crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE); // 配置PA4为模拟模式DAC_OUT1 gpio_init_struct.gpio_pins GPIO_PINS_4; gpio_init_struct.gpio_mode GPIO_MODE_ANALOG; gpio_init(GPIOA, gpio_init_struct); // DAC通道1配置 dac_init_type dac_init_struct; dac_default_para_init(dac_init_struct); dac_init_struct.dac_trigger DAC_TRIGGER_SOFTWARE; // 软件触发 dac_init_struct.dac_wave_generation DAC_WAVE_GENERATION_NONE; // 无波形生成 dac_init_struct.dac_output_buffer DAC_OUTPUT_BUFFER_ENABLE; // 使能输出缓冲 dac_init(DAC_CHANNEL_1, dac_init_struct); // 使能DAC通道1 dac_enable(DAC_CHANNEL_1, TRUE); } void dac_set_voltage(uint16_t value) { // 设置12位数据右对齐 dac_software_trigger_enable(DAC_CHANNEL_1, TRUE); dac_data_set(DAC_CHANNEL_1, DAC_ALIGN_12BIT_R, value); }在主循环中可以调用dac_set_voltage(2048)来设置输出大约1.65V半量程的电压。用万用表测量PA4引脚电压应该非常稳定。改变value从0到4095输出电压应在0V到~3.3V之间线性变化。5.2 生成三角波与性能观察要测试DAC的动态性能我们可以用定时器触发DAC配合DMA来生成波形这样可以解放CPU。这里我们用更简单的方法在定时器中断里手动更新DAC数据寄存器生成一个三角波。首先开启一个定时器中断如TIM3周期为100us。在中断服务函数中递增一个变量当达到峰值后递减形成三角波索引然后将索引值赋给DAC。volatile uint16_t dac_triangular_index 0; volatile int8_t dac_direction 1; // 1为增-1为减 void TIM3_IRQHandler(void) { if(timer_flag_get(TIM3, TIMER_OVF_FLAG)) { timer_flag_clear(TIM3, TIMER_OVF_FLAG); // 更新三角波索引 dac_triangular_index dac_direction; if(dac_triangular_index 4095) dac_direction -1; if(dac_triangular_index 0) dac_direction 1; // 更新DAC输出 dac_data_set(DAC_CHANNEL_1, DAC_ALIGN_12BIT_R, dac_triangular_index); } }用示波器观察PA4引脚应该能看到一个频率约为1 / (2 * 4096 * 100us) ≈ 1.22Hz的三角波。虽然频率很低但波形应该很平滑阶梯感不明显这说明DAC的建立时间和线性度不错。如果想提高输出频率可以缩短定时器中断周期但会受到中断处理速度和DAC更新速度的限制。对于更高频率或更复杂的波形必须使用DMA。注意事项DAC的输出阻抗和驱动能力有限。直接驱动低阻抗负载会导致电压跌落和失真。在实际应用中如果驱动后级电路通常需要在DAC输出后接一个电压跟随器运算放大器进行缓冲和阻抗匹配。6. 开发调试中的常见问题与排查实录在实际测试过程中难免会遇到各种问题。我把遇到的和可能遇到的典型问题整理如下供大家参考。6.1 连接与下载问题排查表问题现象可能原因排查步骤与解决方案Keil无法识别调试器No ULINK/ST-LINK found1. USB驱动未安装或异常。2. 调试器硬件故障。3. USB线或接口接触不良。1. 检查设备管理器尝试重新安装驱动。2. 换一个USB口或电脑试试。3. 换一根质量好的USB线。能识别调试器但连接目标失败Connection refused1. SWD接口线接错或虚焊。2. 目标板未供电或供电不足。3. 芯片处于休眠、待机模式或复位状态。4. SWD引脚被复用为普通IO且初始化后未释放。1. 用万用表通断档检查SWDIO、SWCLK、GND连接。2. 测量目标板3.3V电源是否稳定。3. 尝试按住复位键再点击连接或检查复位电路。4. 如果是自己写的程序导致尝试擦除芯片使用“Erase Full Chip”功能。Flash下载失败Flash Download failed1. Flash编程算法未添加或错误。2. 芯片写保护未解除。3. 电源波动或干扰。4. 芯片型号选错。1. 在Debug-Flash Download设置中确认添加了正确的.FLM文件。2. 使用编程工具如AT-LINK Utility进行全片擦除解除保护。3. 检查电源在VDD和GND之间并联一个10uF和0.1uF的电容。4. 确认Keil工程中选择的设备型号是AT32F421F8P7。程序下载成功但运行不正常如LED不闪1. 系统时钟配置错误导致程序实际运行速度极慢或极快。2. 中断向量表地址错误多见于有Bootloader的情况。3. GPIO引脚配置错误模式、上下拉。4. 硬件连接错误如LED接反。1. 检查system_clock_config()函数确认时钟源和倍频系数。2. 检查Keil的Target选项里IROM1的起始地址是否为0x08000000。3. 使用调试器单步调试查看GPIO相关寄存器配置是否与预期一致。4. 用万用表测量引脚电平是否随程序变化。6.2 外设使用中的“坑”GPIO复用功能映射AT32的引脚复用功能AF映射关系可能与STM32不完全相同。例如TIM2_CH1在AT32F421上可能映射到PA0或PA5需要仔细查阅数据手册Datasheet和引脚定义说明而不是想当然。使用gpio_pin_mux_config()函数时第二个参数GPIO_PINS_SOURCEx和第三个参数GPIO_MUX_x必须严格对照手册填写。时钟使能顺序在使用任何外设GPIO、USART、ADC、TIM等前必须先使能其对应的外设时钟通过crm_periph_clock_enable函数。这是一个常见的疏忽点会导致外设无法工作但程序编译下载都正常。ADC采样时间与输入阻抗ADC的采样时间需要根据信号源的内阻来设置。如果信号源阻抗较大如10kΩ需要增加采样时间如ADC_SAMPLETIME_239_5让采样电容有足够时间充电到稳定值否则采样结果会偏低且不稳定。中断优先级与嵌套如果使用了多个中断需要合理配置NVIC的中断优先级通过nvic_irq_enable函数的后两个参数。不合理的优先级可能导致高优先级中断打断低优先级中断处理或中断响应不及时。对于实时性要求不高的系统可以全部设为默认优先级0。DAC输出缓冲的影响使能DAC输出缓冲Buffer可以提高带载能力但会引入一定的偏移误差和非线性。在需要极高精度的场合可以尝试禁用缓冲DAC_OUTPUT_BUFFER_DISABLE但此时输出阻抗很高必须外接运放。6.3 性能与功耗优化初探初步测试下来AT32F421F8P7的性能令人满意。为了进一步挖掘潜力或优化电池供电项目可以关注以下几点灵活时钟配置不是所有任务都需要150MHz全速运行。可以使用CRM时钟恢复模块的动态时钟切换功能在空闲时切换到低速时钟如HSI/8甚至进入睡眠、停机模式大幅降低功耗。使用DMA对于ADC连续采样、DAC波形生成、USART大数据量收发等场景一定要启用DMA。这可以将CPU从繁琐的数据搬运中解放出来同时减少因中断频繁进入/退出带来的开销和功耗实现更高的数据吞吐率。代码优化对于关键循环或算法可以检查反汇编代码看看是否有优化空间。例如使用__IOvolatile关键字正确定义寄存器映射避免编译器产生不必要的内存访问指令。对于数学运算可以尝试使用CMSIS-DSP库中的优化函数。经过这一轮从环境搭建到核心外设的初步测试AT32F421F8P7给我的印象是一款性价比极高、性能越级的入门级M4芯片。它的开发流程与STM32高度相似降低了迁移成本。丰富的模拟外设ADC、DAC和通信接口USART、I2C、SPI使其能覆盖广泛的应用场景。当然它的内存和Flash相对较小这款是64KB Flash16KB SRAM在开发复杂应用时需要精打细算。总的来说对于需要一定处理能力又受限于成本和尺寸的项目AT32F421F8P7是一个非常有力的竞争者。后续如果有机会我会再深入测试它的低功耗模式、通信接口以及更复杂的应用例程。