资讯详情

资讯详情

LT8920无线模块STM8驱动测试:SPI通信与收发程序实战

简介这份资源是面向嵌入式开发者的LT8920 2.4G无线模块测试程序基于STM8微控制器实现适合想入门2.4GHz无线通信或需要快速验证LT8920收发功能的开发者。压缩包共62个文件约825KB以c与h源码、o与cout编译产物、pbi与ewp等IAR工程配置为主另含hex烧录文件、bat脚本及工程浏览文件覆盖驱动、应用与工程配置各环节。资源中提供了LT8920初始化、SPI通信、时钟配置、中断处理与延时等模块代码并附带完整STM8工程可直接在IDE中打开编译运行便于读者理解芯片工作模式、数据收发流程与参数配置方法。目前已有1056人学习下载可作为2.4G无线通信开发与STM8编程实践的参考起点。1. LT8920 与 STM8 搭在一起为什么值得花一个下午跑通LT8920 是一颗 Sub-1GHz 频段的射频收发芯片常见于 433MHz 的短距无线场景SPI 接口、寄存器配置驱动、带包处理成本低到可以忽略不计。STM8 是 ST 家的 8 位 MCU资源紧、外设朴素但胜在便宜、稳定、工具链成熟。把这两颗芯片凑一块是很多低成本无线节点、遥控器、传感器采集端的典型组合。问题在于LT8920 的公开资料零散寄存器手册写得像黑匣子STM8 又没有 Arduino 那种现成库很多人卡在“SPI 能读出版本号但就是收不到包”这一步。这篇笔记就围绕 LT8920 无线模块测试程序在 STM8 上的落地把初始化、收发、参数、排错一条线讲清楚适合手里已经有一对模块、想先跑通点对点通信再谈组网的嵌入式开发者。2. 先理清 LT8920 的寄存器模型与 STM8 的 SPI 约束2.1 LT8920 的寄存器分页与命令字格式LT8920 的寄存器空间不是平铺的它用“页”来扩展地址。每次读写前要先写一个命令字节命令字节里包含页选择、读写方向和地址。常见做法是命令字节高 3 位选页次 1 位表示读或写低 4 位是页内地址。如果你把它当成普通 SPI 从机直接发地址就读写大概率读回全 0 或全 1。我一般会先封装两个最底层的函数一个写寄存器一个读寄存器。写的时候先发命令字节再发数据字节读的时候先发命令字节再发一个 dummy 字节把数据移进来。这里有个细节LT8920 的 SPI 是模式 0CPOL0、CPHA0数据在时钟上升沿采样。STM8 的 SPI 配置里要对应设成 CPOL0、CPHA0否则读出来的值会整体移位。// LT8920 底层读写基于 STM8 SPI // cmd 格式bit7~5 页号bit4 读写位(1 读 0 写)bit3~0 页内地址 #define LT_CMD_WRITE(page, addr) ((uint8_t)(((page) 5) | (0x00) | ((addr) 0x0F))) #define LT_CMD_READ(page, addr) ((uint8_t)(((page) 5) | (0x10) | ((addr) 0x0F))) uint8_t lt_read_reg(uint8_t page, uint8_t addr) { uint8_t val; LT_CS_LOW(); // 片选拉低 spi_write_byte(LT_CMD_READ(page, addr)); val spi_read_byte(); // 发 dummy 读回数据 LT_CS_HIGH(); // 片选拉高 return val; } void lt_write_reg(uint8_t page, uint8_t addr, uint8_t val) { LT_CS_LOW(); spi_write_byte(LT_CMD_WRITE(page, addr)); spi_write_byte(val); LT_CS_HIGH(); }逻辑说明片选必须手动控制因为 LT8920 要求命令字节和数据字节在同一个片选周期内完成。参数说明page 一般取 0 或 1addr 是 0x00 到 0x0F。如果你读回的值始终是 0xFF先查片选时序再查 SPI 模式最后才怀疑芯片。2.2 STM8 SPI 的时钟与片选配置STM8 的 SPI 时钟来自主时钟分频8 位机跑 16MHz 时SPI 时钟建议先降到 1MHz 以内等通信稳定再往上提。LT8920 的 SPI 最高能到 10MHz但 STM8 的 GPIO 翻转速度和走线质量往往才是瓶颈。我一般会在初始化时把 SPI 分频设成 1/8也就是 2MHz 左右先保证读写寄存器不出错。片选脚用普通 GPIO 推挽输出不要用硬件 NSS因为 LT8920 的时序要求片选在命令和数据之间保持低电平硬件 NSS 在多字节传输时容易提前拉高。配置代码如下void stm8_spi_init(void) { // 片选脚推挽输出默认拉高 LT_CS_DDR | LT_CS_PIN; LT_CS_CR1 | LT_CS_PIN; LT_CS_CR2 ~LT_CS_PIN; LT_CS_HIGH(); // SPI 使能主模式CPOL0 CPHA0分频 1/8 SPI_CR1 SPI_CR1_SPE | SPI_CR1_MSTR | SPI_CR1_BR_1; SPI_CR2 SPI_CR2_SSM | SPI_CR2_SSI; // 软件从机管理内部 SSI 拉高 SPI_CR1 | SPI_CR1_SPE; } uint8_t spi_write_byte(uint8_t data) { while (!(SPI_SR SPI_SR_TXE)); // 等发送缓冲空 SPI_DR data; while (!(SPI_SR SPI_SR_RXNE)); // 等接收完成 return SPI_DR; }逻辑说明SPI_CR2 里打开 SSM 和 SSI是为了在不用硬件 NSS 的情况下避免模式错误。参数说明SPI_CR1_BR_1 对应分频 1/8具体值查 STM8 参考手册的 BR 位定义。如果你发现读回的数据偶尔错一位先把分频调大再检查杜邦线长度。2.3 最小测试程序读版本号与写回读在写收发逻辑之前先做一个最小验证读 LT8920 的版本寄存器再往一个测试寄存器写值读回。版本寄存器通常在页 0 的某个固定地址不同批次可能不同但只要能读出一个非 0 非 0xFF 的稳定值就说明 SPI 通了。void lt_basic_test(void) { uint8_t ver lt_read_reg(0, 0x00); // 读版本或状态寄存器 printf(LT8920 ver/status: 0x%02X\r\n, ver); lt_write_reg(0, 0x0F, 0xA5); // 往测试寄存器写 uint8_t back lt_read_reg(0, 0x0F); printf(write 0xA5 read: 0x%02X\r\n, back); }逻辑说明如果 back 等于 0xA5SPI 读写链路基本没问题。参数说明测试寄存器地址选一个手册里标注为可读写的不要选只读的状态位。这一步过了再往下做射频配置。3. 把 LT8920 配成能收发包的状态初始化序列与参数3.1 上电后的必写寄存器清单LT8920 上电后不能直接收发要先写一串配置数据速率、频点、发射功率、包格式、同步字、CRC 使能。这些寄存器分布在页 0 和页 1顺序不能乱因为有些配置依赖前面的时钟设置。我一般按这个顺序来先写页 0 的时钟和模式寄存器把芯片从休眠唤醒再写页 1 的射频参数包括频点和速率最后回到页 0 写包控制寄存器和使能收发。下面是一个可抄的初始化骨架具体地址以你手里的手册为准但结构是通用的。void lt8920_init(void) { // 1. 唤醒并设置时钟 lt_write_reg(0, 0x01, 0x80); // 软复位或唤醒 delay_ms(10); lt_write_reg(0, 0x02, 0x3F); // 时钟分频等 // 2. 射频参数频点、速率 lt_write_reg(1, 0x00, 0x6C); // 频点相关 lt_write_reg(1, 0x01, 0x28); // 数据速率相关 lt_write_reg(1, 0x02, 0x0A); // 频偏 // 3. 包控制同步字、CRC、包长 lt_write_reg(0, 0x03, 0xAA); // 同步字高字节 lt_write_reg(0, 0x04, 0x55); // 同步字低字节 lt_write_reg(0, 0x05, 0x01); // 使能 CRC // 4. 使能收发 lt_write_reg(0, 0x06, 0x01); // 进入待机 lt_write_reg(0, 0x07, 0x01); // 使能接收 }逻辑说明每一步之间加短延时尤其是唤醒之后。参数说明频点寄存器决定你工作在 433.92MHz 还是其他频点速率寄存器决定空中速率两者要和对端一致。如果你写完之后读回发现某些位没写进去检查是不是写到了只读寄存器。3.2 频点与速率怎么算、怎么对齐LT8920 的频点不是直接写 MHz而是写分频比。常见做法是先确定参考晶振频率再根据手册公式算目标频点对应的寄存器值。如果你不想算也可以直接用模块厂给的配置值但要知道它对应的大致频点。速率同理寄存器值决定符号率。收发双方必须用同一组频点和速率否则就是互相听不见。我踩过的坑是两块模块买了不同批次厂家的默认配置不一样一块 433.92MHz一块 434.5MHz结果近距离能收到但丢包严重远了直接没反应。后来统一用同一份配置表问题消失。参数典型值说明频点433.92MHz常见 ISM 频点寄存器值按手册算速率10kbps低速更稳适合 STM8 处理频偏20kHz与速率匹配太小收不到太大占带宽同步字0xAA55收发一致越长越抗干扰CRC使能16 位 CRC减少误包3.3 发送与接收的最小闭环配置好之后先做单向发送一块模块发固定包另一块收并打印。发送时把数据写进 TX FIFO然后触发发送接收时轮询状态寄存器看到 RX FIFO 非空就读出来。void lt_send_packet(uint8_t *buf, uint8_t len) { lt_write_reg(0, 0x08, len); // 写包长 for (uint8_t i 0; i len; i) { lt_write_reg(0, 0x09, buf[i]); // 写 TX FIFO } lt_write_reg(0, 0x07, 0x02); // 触发发送 while (!(lt_read_reg(0, 0x00) 0x01)); // 等发送完成 } uint8_t lt_recv_packet(uint8_t *buf) { uint8_t status lt_read_reg(0, 0x00); if (status 0x02) { // RX FIFO 非空 uint8_t len lt_read_reg(0, 0x0A); for (uint8_t i 0; i len; i) { buf[i] lt_read_reg(0, 0x0B); } return len; } return 0; }逻辑说明发送完成标志和接收非空标志在状态寄存器里具体位定义查手册。参数说明包长不要超过 FIFO 深度LT8920 的 FIFO 一般是 32 字节或 64 字节。如果你发完收不到先查状态寄存器的错误标志再看同步字和 CRC 配置。4. 调试 LT8920 时最容易翻车的几个地方4.1 现象SPI 读回全是 0xFF 或 0x00原因片选时序不对或者 SPI 模式设错。LT8920 要求片选在命令和数据之间保持低电平如果你用硬件 NSS它可能在两个字节之间拉高导致命令被截断。另外 CPOL/CPHA 设错也会让数据整体移位。解决改用普通 GPIO 控制片选手动拉低拉高确认 SPI 模式为 0用示波器看时钟和数据线的相位关系。如果没有示波器先把 SPI 时钟降到 100kHz 以下再试。4.2 现象能读版本号但收不到包原因频点、速率、同步字、CRC 配置不一致。两块模块只要有一项不同就收不到。另外如果发射功率设得太低近距离可能勉强收到但丢包严重。解决把两块模块的配置寄存器逐项读出来对比确保完全一致。发射功率先设到最大等通了再降。同步字先用默认值不要自己乱改。4.3 现象发送完成标志一直不置位原因TX FIFO 写溢出或者发送触发命令写错。LT8920 的 FIFO 深度有限如果你一次写超过深度的数据后面的会覆盖前面的状态机可能卡住。解决包长控制在 FIFO 深度以内发送前先读状态寄存器确认 FIFO 有空位触发发送的命令字节要写对页和地址。4.4 现象接收数据偶尔错位或 CRC 报错原因空中干扰或者速率太高导致 STM8 处理不过来。STM8 是 8 位机如果空中速率设到 100kbps 以上中断或轮询可能来不及读 FIFO导致溢出。解决降低空中速率到 10kbps 或更低接收用中断方式而不是纯轮询在 FIFO 快满时及时读走数据。如果环境干扰大换频点或加屏蔽。4.5 现象两块模块单独测试都正常放一起就互相干扰原因频点太近或者发射功率太大导致接收端饱和。Sub-1GHz 频段虽然穿透好但近距离大功率会让接收机前端过载。解决把发射功率降到最低一档再试两块模块之间保持一米以上距离如果必须近距离加衰减器或把天线拧松。5. 把测试程序变成可复用的收发框架5.1 用状态机替代裸轮询裸轮询在简单测试里够用但一旦要同时处理按键、串口和射频就会丢包。我一般会把 LT8920 的收发做成状态机空闲、发送中、接收中、错误处理四个状态主循环里轮询状态机射频中断只置标志。typedef enum { LT_IDLE, LT_TX, LT_RX, LT_ERROR } lt_state_t; lt_state_t lt_state LT_IDLE; void lt_poll(void) { switch (lt_state) { case LT_IDLE: if (tx_req) { lt_send_packet(tx_buf, tx_len); lt_state LT_TX; } else { lt_write_reg(0, 0x07, 0x01); lt_state LT_RX; } break; case LT_TX: if (lt_read_reg(0, 0x00) 0x01) { lt_state LT_IDLE; } break; case LT_RX: if (lt_read_reg(0, 0x00) 0x02) { uint8_t len lt_recv_packet(rx_buf); if (len) { rx_ready 1; } lt_state LT_IDLE; } break; default: lt8920_init(); lt_state LT_IDLE; break; } }逻辑说明状态机把发送和接收分开避免同时操作射频。参数说明tx_req 和 rx_ready 是应用层标志由主循环或其他中断置位。如果你发现状态卡在 LT_TX检查发送完成标志是否被正确清除。5.2 加一层包协议长度、序号、校验裸收发只能传固定长度实际用的时候需要包协议。我一般会在 LT8920 的载荷里再加一层第一个字节是长度第二个字节是序号最后两个字节是校验和。这样即使射频层 CRC 漏检应用层还能再挡一次。void build_packet(uint8_t *out, uint8_t *data, uint8_t len, uint8_t seq) { out[0] len 4; // 总长度 out[1] seq; // 序号 for (uint8_t i 0; i len; i) { out[2 i] data[i]; } uint16_t sum 0; for (uint8_t i 0; i len 2; i) { sum out[i]; } out[len 2] sum 8; out[len 3] sum 0xFF; }逻辑说明序号用于去重和丢包检测校验和用于应用层验证。参数说明len 是有效数据长度总长度不要超过射频层 FIFO 深度。如果你发现校验和经常错先查射频层 CRC 是否使能再查空中干扰。5.3 用串口打印做黑匣子STM8 没有调试器的时候串口打印就是唯一的黑匣子。我一般会在关键路径上加打印初始化每一步、发送前后、接收中断、错误状态。但打印本身会拖慢时序所以正式跑的时候要把打印关掉或只保留错误打印。#define LT_DEBUG 1 #if LT_DEBUG #define LT_LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define LT_LOG(fmt, ...) #endif逻辑说明用宏控制调试开关避免正式版里残留打印。参数说明printf 要重定向到串口STM8 上一般用 UART1 或 UART2。如果你发现加了打印就丢包说明打印太重改成只打印错误码。5.4 验证方法用固定包做误包率统计跑通之后不要只看“能收到”要统计误包率。我一般会让发送端每 100ms 发一个带序号的包接收端统计收到多少、丢了多少、校验错多少。跑 1000 个包如果误包率低于 1%基本可用如果高于 5%就要查频点、速率和天线。指标合格线说明误包率1%近距离无遮挡丢包序号连续序号跳变说明丢包校验错0有校验错说明干扰或速率高接收信号强度稳定如果芯片支持 RSSI看波动这套测试程序我前后改过几版最大的教训是不要一上来就调射频参数先把 SPI 读写和片选时序用示波器确认死。很多“收不到”的问题根源都在 SPI 模式或片选上跟射频配置没关系。另一个习惯是每改一个寄存器就打印出来对比不要一次改一堆否则出了问题不知道是哪个参数导致的。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →