
简介本资源是一套基于AS3933芯片的125kHz低频发射驱动代码面向嵌入式开发工程师及RFID/门禁系统开发者解决MCU通过SPI协议精准控制低频发射模块的核心问题。资源包共45个文件包含6个C源文件含main.c、piezo.c等核心驱动逻辑、9个头文件如platform.h、switch.h等硬件抽象层定义、20个HTML文档提供寄存器说明与配置指南以及Makefile.mk等构建脚本整体压缩后仅96KB轻量易集成。已有704人学习下载适用于动物追踪、无源RFID唤醒、近场身份识别等典型125kHz应用场景。读者可直接获取完整SPI初始化流程、AS3933寄存器映射表、发射启停控制函数及针对PIC24FJ64GB002平台的硬件适配代码尤其包含USB通信协同逻辑与H桥驱动接口具备即用性与可移植性。 搞嵌入式这几年低频通信这块我最常用的方案就是AS3933配MCU自己搭发射端。说它是“AS3933低频发射代码”其实有点误导——AS3933本身是一颗3通道低频唤醒接收芯片真正把125kHz频率发射出去的是MCU控制的LC谐振电路。整个链路的正确理解是MCU用定时器PWM产生125kHz载波再通过OOK调制叠加数据经LC天线谐振发射对面的AS3933收到匹配的唤醒码后把WKUP引脚拉高通知主控开机干活。这套组合在做PKE无钥匙进入、资产追踪、电子耳标、低功耗传感器唤醒这类近场场景里非常常见。如果你是第一次接触125kHz方案或者正在为“为什么AS3933收不到我发的信号”挠头这篇文章把发射端代码、AS3933驱动初始化、天线参数计算和联调坑位一次讲透。1. 项目概况这套125kHz低频唤醒系统能做什么1.1 AS3933的角色定位AS3933是ams公司现在是ams OSRAM一颗经典的3通道低频唤醒接收芯片工作频率覆盖15kHz到150kHz最常用的点就是125kHz。它内部有3路独立接收通道可以接三个正交绕制的天线比如X/Y/Z三轴这样无论发射端朝哪个方向都能保证至少一路通道收到信号。芯片平时处于极低功耗的监听状态典型工作电流只有2.7µA左右一旦收到的数据符合预设的Manchester编码和相关码correlation pattern芯片就会把WKUP引脚拉高输出唤醒中断给后端MCU。这个“平时几乎不耗电、有匹配信号才干活”的特性决定了它天生适合电池供电的低功耗场景。我最早接触它是在一个资产追踪标签项目里标签平时整个系统休眠只有AS3933在监听外部低频激活器一广播标签才从睡梦里被叫醒。1.2 整个系统的数据流从数据流来看这套链路长这样MCU_A发射端→ 125kHz LC天线 → 空间磁场近场耦合 → AS3933接收端→ 唤醒中断 → MCU_B被唤醒的主控注意125kHz属于长波频段它的通信方式更接近“磁耦合”跟433MHz、2.4GHz的远场射频不一样。长波信号绕射能力强穿透水、土壤、动物组织的能力也突出所以在动物耳标、井下定位、水下唤醒这些场景里125kHz几乎是不二之选。缺点也明显信道容量低数据率上不去传不了大数据包只适合传短的控制命令和ID码。1.3 这套代码适合谁如果你正在做以下事情这篇文章正好能当参考PKE无钥匙进入系统的低频唤醒链路资产追踪标签的低频激活器传感器网络的低功耗唤醒节点动物耳标读取与写入低成本验证125kHz收发链路无论你用的是STM32、AVR还是国产MCU核心思路是一致的定时器产生125kHz载波GPIO控制开关实现OOK调制LC天线谐振辐射。下面我把每一步的“为什么”也讲清楚这样你换芯片也能自己推导。2. 发射端核心设计MCU怎么把125kHz发出去2.1 为什么发射端要自己搭电路市面上专门做125kHz发射的芯片也有但很多项目更愿意让MCU直接产生载波。原因很实在125kHz频率不高MCU定时器/PWM完全应付得来近场发射功率不需要大几米内足够自己搭LC电路成本最低几个电阻电容电感就搞定很多PKE钥匙里的发射电路本质就是一颗低端MCU加一个LC谐振回路。这里要注意一个概念125kHz天线并不是“无线电天线”那种长度匹配的辐射体而是一个LC谐振回路。天线线圈就是电感并联一个电容组成谐振电路当载波频率等于回路谐振频率时线圈里的电流最大产生的交变磁场最强接收端才能捡到信号。这个道理跟荡秋千一样你推的节奏跟秋千自然频率一致秋千才能越荡越高。2.2 用定时器PWM精确输出125kHz载波频率精度直接决定接收灵敏度。AS3933接收端的频率选择是有带宽的如果发射频率偏离125kHz超过1kHz以上灵敏度会明显下降距离一下就缩水。所以第一步是把载波频率算准。以STM32F103为例系统时钟72MHz用定时器输出PWM定时器输出频率 72MHz ÷ (PSC1) ÷ (ARR1)要得到125kHz需要(PSC1) × (ARR1) 72000000 ÷ 125000 576取 PSC71ARR7正好576输出就是125kHz占空比50%。void LF_Carrier_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); TIM_TimeBaseStructure.TIM_Prescaler 71; TIM_TimeBaseStructure.TIM_Period 7; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 4; // 占空比50% TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM3, TIM_OCInitStructure); }如果用的是AVR比如ATmega328P主频16MHz定时器1工作在CTC模式输出比较值设为6316MHz ÷ 2 ÷ 64 125kHz道理一样。核心就一句话把你系统时钟除成一个对125kHz的整数分频比然后配置到定时器上。我实测下来只要分频计算正确PWM波形频率误差基本在几十赫兹以内完全满足AS3933的解调需求。2.3 LC天线谐振算准电容是重点125kHz发射天线的本质是一个LC并联谐振回路。谐振频率公式f 1 ÷ (2π√(LC))其中L是天线线圈电感量C是并联谐振电容。项目里常见的电感量在470µH到6.8mH之间我习惯先把L定下来然后反推CC 1 ÷ (4π²f²L)把f125kHz代进去我可以直接给一张“抄作业”表线圈电感L理论谐振电容C实际标称组合470µH3.45nF3.3nF 150pF1mH1.62nF1.5nF 120pF2.2mH738pF680pF 56pF3.3mH492pF470pF 22pF6.8mH239pF220pF 18pF注意表格里是理论值实际电路还有线圈分布电容、PCB走线寄生电容甚至人手靠近天线都会改变谐振点。所以我强烈建议天线两端并联一个5-30pF的可调微调电容上电后用示波器看天线两端电压手动调到幅度最大的位置。这一步很关键我见过太多人代码没问题就是天线失谐导致距离不到10厘米。3. AS3933驱动与初始化细节3.1 SPI接口时序AS3933的配置接口是SPI片选CS低有效一次通信固定16位第1位是读写标志0写1读接着7位是寄存器地址最后8位是数据。写寄存器时CS拉低依次移出这16位然后拉高CS结束事务。我实测用SPI Mode 0CPOL0CPHA0空闲低电平、上升沿采样就能正常工作单片机标准SPI外设直接接。如果你是用GPIO模拟SPI注意MSB先行时钟频率控制在1MHz以内。AS3933不是高速器件配置接口慢一点完全没有问题稳定第一。void AS3933_WriteReg(uint8_t reg, uint8_t data) { uint16_t frame (0 15) | ((reg 0x7F) 8) | data; CS_LOW(); for (int i 15; i 0; i--) { SCLK_LOW(); MOSI_HIGH((frame i) 1); SCLK_HIGH(); } CS_HIGH(); }有个习惯建议你养成写完寄存器之后读一遍回读校验。AS3933支持读寄存器调试初期花几十行代码写个回读函数能帮你排除大量“配置写了但没写进去”的灵异问题。3.2 初始化流程AS3933上电后不能直接用需要初始化配置。不同批次芯片寄存器位域定义略有差异我这里按通用流程来具体bit含义以你手头的数据手册为准等待芯片上电稳定延时2ms以上配置内部时钟相关寄存器让芯片内部工作时钟稳定选择接收频率对应125kHz频段配置数据率和Manchester解码使能写入前导码长度和相关码pattern设置唤醒阈值和监听模式void AS3933_Init(void) { Delay_ms(5); // 以下为参考示例具体值以你的芯片手册为准 AS3933_WriteReg(0x00, 0x2D); AS3933_WriteReg(0x01, 0x50); AS3933_WriteReg(0x02, 0x2B); AS3933_WriteReg(0x03, 0x00); AS3933_WriteReg(0x04, 0x00); AS3933_WriteReg(0x05, 0x4A); AS3933_WriteReg(0x06, 0x04); AS3933_WriteReg(0x07, 0x34); }初始化顺序里最容易犯的错是“没等上电稳定就写寄存器”。AS3933上电初期内部电压还没建立这时候写寄存器大概率失败而且失败表现很隐蔽——回读不出来或者读到全0。加一个可靠的上电延时或者干脆在硬件上用RC延时电路控制复位脚能省很多调试点。3.3 Manchester编码为什么必须用它AS3933内部的解码器只认Manchester编码。Manchester编码的规则是每个数据位用两个半位表示并且位中间必然有一次电平跳变。逻辑0前半段低、后半段高也就是低-高跳变逻辑1前半段高、后半段低也就是高-低跳变接收端就是靠检测这个“中间跳变”恢复时钟和数据。好处是数据和时钟合在一起接收端不需要独立的同步时钟抗干扰能力也强。坏处是数据率低因为每个bit要占两个半位的时间。假设数据率选4kbps一个Manchester位周期就是250µs。载波125kHz一个载波周期8µs250µs里大约31个载波周期。发射端代码里需要准确控制半位时间125µs。实现方式可以用定时器中断也可以在主循环里用延时函数但延时方案受中断影响大如果系统里有其他中断建议用硬件定时器。4. 发射代码与调制实现4.1 OOK调制最简单的调制方式125kHz近场发射最常用OOKOn-Off Keying开关键控。要发“1”就让载波工作要发“0”就把载波关掉。本质就是一个开关电路MCU实现非常直接用一个GPIO控制载波PWM的输出使能。关键点在“开关的时刻必须和Manchester边界对齐”。如果开关沿抖动接收端Manchester解码器就会误判。所以发送一个字节时先用定时器排好半位时间然后在半位整数倍处翻转或开关载波。我见过有人用裸延时实现结果发送前脚一次、后脚一次接收端时好时坏最后查出来是延时函数被中断打断时序全乱了。4.2 发送代码核心逻辑核心发送函数其实很短// 发射一个Manchester位 void LF_SendManchesterBit(uint8_t bit) { if (bit) { // 逻辑1高-低 Carrier_On(); // 前半周期输出125kHz DelayHalfBit(); Carrier_Off(); // 后半周期关闭 DelayHalfBit(); } else { // 逻辑0低-高 Carrier_Off(); DelayHalfBit(); Carrier_On(); DelayHalfBit(); } }Carrier_On和Carrier_Off的实现取决于你的驱动电路。最简单的方式是用一个GPIO控制PWM输出使能比如STM32的TIM_Cmd或GPIO控制一个MOS管的栅极。如果在电路上使用“载波脚调制脚”双GPIO方案那么载波脚一直输出125kHz PWM调制脚控制功率开关两路信号的时序也要对齐防止出现毛刺。4.3 报文格式设计为了提高唤醒成功率发射端报文一般包含四段载波预热段先连续输出一段载波让接收端AGC和增益稳定前导码固定图样的Manchester序列用于接收端同步相关码pattern和接收端配置一致的编码这是触发唤醒的关键有效负载最后是ID或命令数据接收端主控唤醒后再通过其他通信方式获取典型数据结构Preamble16bit或32bit交替0/1Pattern16bit相关码比如0b0101011001101000Data8-32bit这里有个特别容易踩的坑发射端的一组pattern必须和AS3933寄存器里配置的相关码完全一致否则永远唤醒不了。比如芯片里写的是“高-低-高-低-低-高……”的顺序你发射的时候按“低-高-低-高-高-低……”发位序反了芯片就不认。调试时先用逻辑分析仪抓发射端波形再对着数据手册手动推一遍比自己盲猜快得多。4.4 功率驱动电路选择MCU引脚直接带LC回路电流一般只有几十毫安驱动能力不够。实际项目里我常用两种电路。第一种低功耗短距离MCU GPIO经100Ω电阻接到天线一端天线另一端接地。适合距离50厘米以内的场景优点是电路极简缺点是天线的Q值不高辐射效率一般。第二种中距离两个GPIO反相输出分别接到LC天线两端等效半桥推挽两端电压摆幅翻倍距离能做到1-2米。或者用一颗AO3400这类小MOS管做低边开关驱动功率能再往上提。注意LC回路在开关瞬间会有较高的电压尖峰MCU引脚或MOS管栅极需要加钳位保护不然烧引脚是常有的事。5. 联调中的常见问题与排查心法5.1 频率偏了怎么办最常见的现象是“接收端完全没反应”。先用示波器测发射端PWM输出确认频率是不是真的125kHz。我有一次把ARR配置算错了实际输出是127kHzAS3933正好落在频率选择性边缘距离只有几厘米折腾了一个下午才发现是频率偏了。排查建议示波器测载波频率有没有误差用频率计或逻辑分析仪辅助验证如果是外部晶振精度不高考虑用微调或者改用有源晶振。5.2 天线失谐怎么调天线失谐的典型表现是“发射端波形看着有距离就是上不去”。用示波器探头测天线两端电压125kHz谐振时电压幅度应该明显高于失谐时。具体操作从最小电容开始每次加一点并联电容观察天线电压幅度最大点那就是谐振点。把微调电容固定在这个位置整机再封装。这里有个细节示波器探头本身有寄生电容测天线高压点时会引入十几pF的负载可能把谐振点拉偏。测量时尽量用10X探头并且在同一位置对比不要换来换去。5.3 AS3933从不唤醒或乱唤醒从不唤醒先查pattern是否一致再查Manchester极性。Manchester编码有两个流派IEEE标准和传统定义低-高和高-低代表的逻辑正好反过来。AS3933内部有极性配置位你发射端按一种方式编码接收端却按另一种配置解码结果就是永远对不上。乱唤醒多半是阈值设置过低或者环境里的125kHz底噪大。AS3933有唤醒阈值寄存器适当调高阈值能滤掉不少干扰。另外检查天线选频特性接收端天线带宽太宽也容易引入噪声配合接收端LC谐振电容把带宽收窄一些。5.4 功耗优化心得发射端的功耗大头在载波发射。125kHz发射回路本身电流不大但如果用推挽电路持续发射电池还是扛不住。建议发射端只在广播唤醒码的时候开载波发完立刻关。接收端AS3933监听电流才2.7µA左右后端MCU进入深度睡眠这套系统整体平均功耗能做到很低。如果要在接收端做“多级唤醒”可以让第一级用128bit超长pattern提高灵敏度第二级再加一个更长的匹配码确认误唤醒率能压得非常低。这个做法比较高级但实际项目里很有用尤其是在工业现场有大量电磁噪声的环境里。根据我个人联调的经验125kHz这套方案最折磨人的从来不是代码逻辑而是频率和天线这两件“看不见摸不着”的事。代码写好了先别急着调距离拿示波器把载波频率、Manchester波形、天线谐振点这三个基础项全部确认一遍再谈距离和灵敏度。最后再分享一个小技巧给发射端加一个可调电位器或拨码开关来切换pattern调试时切换不同编码组合能帮你在接收端快速确认哪一段没匹配上省下大量来回改固件的时间。本文还有配套的精品资源点击获取