简介:基于STM8的LT8920无线模块测试程序,面向嵌入式无线通信开发者和STM8初学者,旨在解决LT8920芯片与STM8单片机的快速联调与数据收发问题。资源共62个文件,压缩包仅825KB,主要包含c/h源码、IAR工程文件(ewp/ewd/eww)、十六进制固件(hex)及编译输出文件(out/o等),可直接导入STM8开发环境查看、编译与烧录。目前已有1054人学习下载,源码中涵盖了LT8920驱动、SPI通信、数据打包解包以及发送接收示例,便于对照理解2.4G无线链路的工作流程。通过修改工程可实现自定义帧格式、调整发射功率与接收灵敏度,适应不同距离和场景需求;配套的工程配置和驱动代码也可作为二次开发的基础模板,为后续项目提供参考。 我接触LT8920这块2.4GHz无线芯片,是因为一个无线鼠标项目。原本方案用的是nRF24L01,但采购拿到LT8920的样品,价格更低,仓库里也有现成模块,让我先写个测试程序看看能不能替代。于是就有了这套基于STM8的LT8920无线模块测试程序,折腾了大概两个晚上,自己搭了一套可复用的收发测试代码。
LT8920是一颗2.4GHz GFSK收发芯片,SPI接口控制,常用于无线鼠标、无线音频、遥控器这类短距离无线产品。和nRF24L01那种“寄存器预定义好、基本照抄就行”的芯片不同,LT8920的寄存器配置灵活,发射频率、数据速率、发射功率、PLL参数这些都要自己计算和写入,所以测试程序的“桥梁”作用特别明显。STM8则是小家电、低成本无线产品里很常见的8位MCU,开发环境成熟,外设够用,用它来驱动LT8920既适合快速验证模块,也方便后面直接移植到正式固件。
这篇内容写给谁看?给那些和我一样,手头刚拿到LT8920模块,或者已经焊好板子但不知道寄存器该怎么配、硬件该怎么连的开发者。我会把硬件接线、开发环境、初始化流程、收发测试代码和测试中踩过的坑全部写出来,内容偏向实操,看完照着做,基本能跑通一轮最简单的收发。
1. 测试程序的定位:不是量产固件,是“芯片能不能用”的判官
写LT8920测试程序之前,先得想清楚一个问题:这个测试程序到底要回答什么?很多人拿到模块就直接去翻数据手册,想把每个寄存器都弄明白,结果陷进去出不来。我自己的经验是,测试阶段最重要的是建立几个明确的“验收指标”,程序只要能回答这几个问题,就算完成使命。
1.1 测试程序要回答的四个问题
第一个问题:模块通电后能不能正常响应SPI命令?这里面包含芯片本身的供电、地线连接、SPI引脚是否焊好等。第二个问题:寄存器读写是否正确?如果写进去的值和读出来的值不一致,后面的配置全都不作数。第三个问题:芯片能不能进入发射/接收状态,GIO引脚能不能给出正确的状态变化。第四个问题:两块模块之间能不能正常收发数据,误码率在可接受范围内。
这四个问题层层递进,从“芯片活着”到“芯片能用”,每一层都可能卡住。我看过不少同事拿模块不先做寄存器回读,上来就配置发射,结果信号那边怎么都收不到,最后排查半天发现是写寄存器时字节顺序反了。所以这套测试程序的第一个模块,就是SPI读写回环测试,把全部关键寄存器读出来,和写进去的值比对,再把结果通过串口打印出来。
1.2 为什么用STM8而不是直接拿上位机USB转SPI
可能有人会问,测试LT8920用USB转SPI工具接上位机写脚本不也挺方便?确实可以,但有几个问题:一是很多上位机工具模拟的SPI时序和MCU实际跑出来的时序有差异,LT8920这类芯片对时序还是有要求的,特别是片选和SCK的配合;二是最后产品固件肯定要跑在MCU上,直接用STM8来测,后面代码可以无缝迁移,不用二次开发。
用STM8还有一个好处:它的GPIO中断和定时器资源足够处理LT8920的GIO状态检测,也能方便地加软件延时。测试程序里我用了一个普通的按键触发发射,接收端用串口打印收到的数据,整个调试过程非常直观。STM8的串口、SPI、GPIO这些外设的库函数现在也比较成熟,写测试程序不用自己从头造轮子。我在工程里直接用了寄存器操作加少量库函数,这样代码量可控,核心逻辑清晰。
2. 硬件连线:STM8与LT8920的SPI接线
说完了测试程序的定位,接下来讲硬件。LT8920模块一般会做成一个小的PCB模块,引脚包括VCC、GND、CS、SCK、SI、SO、GIO1、GIO2之类的。拿到模块后,先看清楚丝印,找手册确认每个引脚的功能,再决定怎么接到STM8上。
2.1 引脚对应与最小电路
我用的主控是STM8S103F3P6,这个芯片算是STM8家族里的入门型号,Flash有8KB,RAM有1KB,对于跑一个无线模块测试程序来说绰绰有余。LT8920模块与STM8的接线如下表所示:
| LT8920模块引脚 | 连接到STM8 | 说明 |
|---|---|---|
| VCC | 3.3V | 注意模块供电电压范围,通常3.3V |
| GND | GND | 共地 |
| CS(片选) | PC3 | 低电平有效,控制SPI通信开始/结束 |
| SCK(时钟) | PC5 | SPI时钟 |
| SI(主出从入) | PC6 | SPI数据输入到LT8920 |
| SO(主入从出) | PC7 | LT8920数据输出到STM8 |
| GIO1 | PB4 | 状态引脚,检测收发状态 |
| GIO2 | PB5 | 可选,用于RX/TX切换等 |
如果直接用STM8的硬件SPI,也有办法,但我更推荐测试程序里用软件模拟SPI。原因很简单:软件SPI引脚选择自由,代码逻辑可以直接看到每一位的电平变化,一旦通信不正常,比较容易定位是时钟极性、相位问题还是引脚接错问题。硬件SPI虽然速度更快,但在测试阶段反而是个“黑盒”,出了问题不好查。
2.2 供电与去耦:一个容易被忽略的坑
LT8920在发射状态下电流会瞬间变化,如果供电走线太细或者去耦电容不足,电源电压会产生比较大的纹波,严重时会导致芯片PLL失锁、发射距离忽远忽近。我第一次测试时,直接用杜邦线从STM8开发板的3.3V引脚取电,结果接收端偶尔能收到数据,但距离稍微拉远一点就掉包,后来加了一颗10uF钽电容和一颗100nF陶瓷电容靠近模块引脚放置,问题明显改善。
另外一个经验是,如果LT8920模块上有独立的射频VCC引脚,最好和数字VCC分开走线,并且在模块底部铺完整地平面。测试阶段虽然不用追求射频性能极致,但至少要把去耦和地线做好,否则后面所有测试结论都可能被供电问题污染。
3. STM8开发环境:从安装到点灯
STM8的开发环境选择,网上说法比较多。有人用IAR for STM8,有人用STVD搭配Cosmic编译器,还有人用PlatformIO。我这次用的是STVD+Cosmic的组合,因为它在STM8的开发中免费且足够用,但有一个限制:Cosmic免费版对代码大小有32KB的限制,对测试程序和一般产品固件来说完全够用,除了偶尔遇到寄存器结构体对齐问题,其他都还好。
3.1 安装流程与环境变量
第一步安装STVD,第二步安装Cosmic编译器,第三步安装ST-LINK驱动。这三步的顺序其实无所谓,但要注意:STVD和Cosmic是两套独立软件,STVD是IDE,Cosmic是编译器工具链,两者装完后要在STVD的Project->Settings里把Cosmic工具链路径配置正确,否则编译时会报找不到编译器。
ST-LINK驱动方面,Windows 10以上系统通常能自动识别,但建议去ST官网装最新的ST-LINK驱动,这样连接SWIM调试时更稳定。开发板烧录接口用的SWIM,只需要SWIM、RST、GND三根线,配合ST-LINK就能下载和在线调试,这一点比51单片机用串口下载要方便不少。
3.2 新建工程与第一个测试代码
STVD新建工程的向导逻辑比较老派,第一步选择芯片型号时要注意选对具体型号,比如STM8S103F3P6要选STM8S103F3P。如果选错型号,Link时会出现地址空间不匹配的报错。创建工程后,main.c里默认什么都没有,可以从最简单的GPIO翻转开始验证环境。
#include "stm8s.h" void delay(unsigned long cnt) { while(cnt--); } void main(void) { GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST); while(1) { GPIO_WriteReverse(GPIOD, GPIO_PIN_0); delay(100000L); } }这个点灯程序烧进去之后,如果LED能闪烁,说明编译、烧录、硬件都没问题,接下来就可以写SPI通信代码了。这里多说一句,STM8的库函数版本有好几个,我用的是STM8S标准外设库,网上也能找到老的库版本,建议直接用官网最新版,函数命名和兼容性都更好。
4. LT8920初始化:寄存器配置流程拆解
LT8920的初始化是整个测试程序的核心。这颗芯片几乎所有射频参数都通过SPI寄存器配置,而且一大特点是寄存器长度多为48位(6字节),这和很多SPI芯片一个寄存器就8位/16位完全不同。刚开始做这个项目时,我不小心把寄存器按16位来算,结果写到哪里都是错的,后来仔细看手册才发现问题。
4.1 理解LT8920的SPI读写协议
LT8920的SPI通信可以理解为:一个命令字节(包含读/写标志和寄存器地址)+ N个数据字节。命令字节的最高位用于区分读写,通常1表示写,0表示读。具体到代码上,我封装了下面两个函数:
#define LT8920_CS_LOW() GPIO_WriteLow(GPIOC, GPIO_PIN_3) #define LT8920_CS_HIGH() GPIO_WriteHigh(GPIOC, GPIO_PIN_3) static void SPI_GPIO_Init(void) { GPIO_Init(GPIOC, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_FAST); GPIO_Init(GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST); GPIO_Init(GPIOC, GPIO_PIN_6, GPIO_MODE_OUT_PP_LOW_FAST); GPIO_Init(GPIOC, GPIO_PIN_7, GPIO_MODE_IN_PU_NO_IT); } static unsigned char SPI_ReadWriteByte(unsigned char byte) { unsigned char i; for (i = 0; i < 8; i++) { if (byte & 0x80) GPIO_WriteHigh(GPIOC, GPIO_PIN_6); else GPIO_WriteLow(GPIOC, GPIO_PIN_6); GPIO_WriteLow(GPIOC, GPIO_PIN_5); GPIO_WriteHigh(GPIOC, GPIO_PIN_5); byte <<= 1; if (GPIO_ReadInputPin(GPIOC, GPIO_PIN_7)) byte |= 0x01; } return byte; }上面的代码里,我在SCK上升沿之前更新SI,在SCK上升沿之后读取SO,这样对应SPI Mode 0(CPOL=0, CPHA=1)的常见习惯。如果你的模块手册指定其他模式,只要调整这几行的顺序即可。
写寄存器函数很直接:
void LT8920_WriteReg(unsigned char reg, unsigned char *dat, unsigned char len) { unsigned char i; LT8920_CS_LOW(); SPI_ReadWriteByte(0x80 | reg); for (i = 0; i < len; i++) { SPI_ReadWriteByte(dat[i]); } LT8920_CS_HIGH(); }读寄存器函数类似,只是命令字节不带0x80,然后用SPI_ReadWriteByte(0xFF)来得到SO上返回的数据。
4.2 初始化序列:频率、数据速率、发射功率、校准
LT8920的寄存器配置虽然灵活,但初始化基本有一个固定套路:先配置频率字,再配置数据速率、发射功率等参数,然后启动校准,最后进入待机或收发状态。频率字一般通过公式计算载波频率对应的寄存器值,不同寄存器位的含义要按手册查表,这一块没法偷懒,必须认真对照。
我测试时用的是2.405GHz频点,数据速率配置为1Mbps。发射功率方面,测试程序里先用了中等功率,验证链路通断后再调大。注意每次修改发射功率后,最好回读一下寄存器,确认写入是有效的。
校准这一步特别容易漏。有几次我把寄存器配置写完后直接进入发射模式,结果GIO引脚完全没有反应,后来在数据手册里查到,芯片上电后需要依次触发VREG校准、XTAL校准和PLL校准,没有一个状态寄存器专门汇报校准完成,所以要在每个校准命令后面加固定延时,给芯片留出足够的处理时间。
5. 收发测试流程:从按键触发到串口打印
初始化写好后,收发流程就相对简单了。整个测试程序的核心逻辑是:发射端按下按键,把一串固定数据通过LT8920发出去;接收端一旦检测到前导码和数据包,就把收到的内容通过串口打印出来。
5.1 发射端的软件流程
发射端的流程如下:首先调用LT8920_Init()完成上电初始化,然后进入一个大循环,检测按键是否按下。按键按下后,把要发送的数据填充到发送缓冲区,然后调用LT8920_TX_Packet()进入发射状态。发送完成后,LT8920自动回到待机状态,等待下一次触发。
实际代码里,我预留了一个测试帧格式,包括前导码固定字段、长度字段、数据字段和简单的8位校验。这样接收端在判断“收到数据”时,可以先用校验做一次过滤,避免环境噪声导致的误判。
// 发送一帧数据的简化流程 unsigned char tx_buf[16] = "LT8920 TEST"; LT8920_SetFrequency(2405); // 设置载波频率为2405MHz LT8920_SetDataRate(1); // 1Mbps LT8920_SetTxPower(LT8920_PWR_0dBm); LT8920_Init(); while (1) { if (KEY_IS_PRESSED()) { LT8920_TX_Packet(tx_buf, 12); delay_ms(100); } }这里要提醒一下:发射结束后不能立刻连续发下一帧,最好加一点延时,给接收端留出处理时间。测试程序里我用了100ms间隔,实际产品可以根据空中帧长和ACK超时来压缩。
5.2 接收端的软件流程
接收端的流程和发射端类似,初始化之后一直保持在RX模式。LT8920的GIO1引脚在检测到有效前导码时会发生变化,所以接收端可以配置GIO1为输入,配合查询或中断方式判断是否有数据到达。测试程序里我简单用查询方式,每隔一小段时间读取一次GIO1,如果发现状态变化,再去读寄存器或FIFO区域获得实际数据。
数据拿到后,通过STM8的UART串口打印到电脑终端。串口波特率我配了115200,测试时用串口助手接收,每一帧前面加上“RX:”前缀,收到一帧打一行,非常直观。
接收端有一个需要注意的点:如果长时间收不到数据,GIO1可能会一直处于某种电平,这时候最好能判断“是否超时”——如果一段时间内没有新的数据包,就重新初始化一次接收状态。测试环境里射频干扰不大,但工业现场就不好说了,加上超时重收机制会让程序更耐用。
5.3 实测结果与简单的信号判断
我在办公室实测,两块模块距离大概5米,中间隔了一堵石膏墙。发射端用2.405GHz频点,1Mbps数据速率,中等发射功率,接收端几乎每次都能在按键后100ms以内收到数据,串口打印连续且稳定。然后把距离拉到15米左右,隔了一堵混凝土墙,开始出现丢包,这基本符合LT8920在这种低成本模块加PCB天线条件下的典型表现。
测试程序除了收发数据,还能做一个简单的信号质量判断:接收端统计收到的帧数和CRC错误数,通过串口打出来。这样在调天线匹配或调整发射功率时,不用靠耳朵听或者看信号灯,直接看误码率数字变化,比什么都直观。
6. 踩坑记录:模块测试阶段最容易忽略的三件事
这部分我单独拿出来讲,因为整个测试过程中遇到的三个问题,几乎每一个都会让新手折腾半天,而这些问题在数据手册里写得并不明显。
6.1 坑一:片选时序和SPI模式的搭配
LT8920的CS引脚在SPI通信开始时拉低,结束时拉高,这个大家都懂。但容易忽略的是:CS信号要和SCK的第一拍保持足够的时间关系。我用软件SPI时,最开始在CS拉低后立刻发送命令字节,结果偶尔出现第一次通信失败,后来发现是因为CS刚拉低,芯片内部还没准备好,需要加一点延时再开始SCK。解决办法很简单,在CS拉低后加几个空操作,或者放入一个多余的SPI时钟周期。这个延时大约几微秒就够了,不会影响整体速度。
6.2 坑二:GIO引脚的配置和电气特性
LT8920的GIO引脚功能需要通过寄存器配置,不是上电默认就是理想的状态。我一开始以为GIO1上电后就是数据接收指示输出,直接拿它做中断触发,结果发现上电后它一直不变,查了手册才知道要先把对应寄存器位设成正确的工作模式。另外,GIO引脚的输出类型也需要和主控匹配,有些模块上拉了上拉电阻,有些没有,和STM8的输入模式搭配不对时,会导致电平判断不稳。
6.3 坑三:频率字配置对实际频率的影响
LT8920的频率字不是简单的十进制频率值,而是载波频率和参考频率通过特定公式算出来的寄存器值。如果参考频率算错,实际发出的频率会偏离目标频点,接收端灵敏度会大幅下降,甚至完全收不到。测试时我专门用一个频谱仪看过,配置目标是2405MHz,实际发出来却偏了大概2MHz,就是参考频率参数填错了。对于没有频谱仪的开发者,最简单的办法是让发射端和接收端使用完全相同的频率配置代码,只要两边认的“频道”一致,即使绝对频率有所偏差,通信仍然能通。
结尾
这个测试程序做完之后,LT8920最终没有用在那款无线鼠标里,主要是产品经理觉得换芯片的测试周期太长了。但这次调试过程让我把LT8920的寄存器体系、SPI时序、频率计算这些关键点都摸了一遍。后来另一款无线遥控器项目直接用上了这套代码,从拿到模块到完成整机联调,比第一次省了差不多一半时间。
最后再分享一个小技巧:如果你手头没有频谱仪,又想快速确认LT8920是不是真的在发射,可以找一台带FM解调或者频谱扫描的软件定义接收机,拉远一点看频段能量变化。或者更简单一点,用另一块正常的LT8920模组做接收端,配合串口打印,在近距离下先验证链路通断,再慢慢拉大距离看衰减特性。这套“先通断、后距离、再误码”的测试方法,我认为是最省时间的。
这个项目后续还可以扩展:加上数据包重传、ACK机制,或者用STM8的定时器做一个自动的心跳包测试,检查长时间连续运行下模块的性能稳定性。测试程序说白了就是“把不确定变成确定”的过程,把LT8920每个寄存器都调明白,后面产品固件怎么写都不慌。
本文还有配套的精品资源,点击获取