目录
- STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解)
- STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解)
- STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明
- STC8H开发(四): FwLib_STC8 封装库的介绍和使用注意事项
- STC8H开发(五): SPI驱动nRF24L01无线模块
- STC8H开发(六): SPI驱动ADXL345三轴加速度检测模块
- STC8H开发(七): I2C驱动MPU6050三轴加速度+三轴角速度检测模块
- STC8H开发(八): NRF24L01无线传输音频(对讲机原型)
- STC8H开发(九): STC8H8K64U模拟USB HID外设
- STC8H开发(十): SPI驱动Nokia5110 LCD(PCD8544)
- STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计
- STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储
- STC8H开发(十三): I2C驱动DS3231高精度实时时钟芯片
- STC8H开发(十四): I2C驱动RX8025T高精度实时时钟芯片
- STC8H开发(十五): GPIO驱动Ci24R1无线模块
- STC8H开发(十六): GPIO驱动XL2400无线模块
XL2400 简介
小众的2.4G射频收发芯片, 和 Ci24R1, XN297L 一样, 都属于 nRF24L01 派生的 SOP8 版本. 在寄存器和操作上类似于nRF24L01, 但是寄存器中存在大量多字节的设置, 没有中断, 完全靠轮询工作, 这是这个型号的特点.
在兼容性上, 和XN297L管脚布局一致但是寄存器不一样, 比XN297L的外围电路元件更少, 只需要一个16MHz晶振, 两个电容就能工作. 和Ci24R1比管脚和寄存器都不一样.
具体的参数可以查看官网上的产品介绍 和手册 XL2400规格书V2.0a.pdf, XL240X应用说明v2.1a.pdf, 市场上还有型号为 WL2400 的芯片, 看手册应该是同一个芯片.
XL2400 管脚和典型电路
管脚定义
PIN | Name | I/O | 说明 |
---|---|---|---|
1 | CSN | DI | SPI 片选信号 |
2 | SCK | DI | SPI 时钟信号 |
3 | DATA/IRQ | IO | SPI 数据输入/输出/中断信号 |
4 | VDD | Power | 电源(+2.1 ~ +3.6V,DC) |
5 | XC1 | AI | 晶振输入 |
6 | XC2 | AO | 晶振输出 |
8 | VSS | GND | 地 |
7 | ANT | RF | 天线接口 |
可以和 Ci24R1 对比一下, 仅仅是管脚位置不同
电路
电路非常简单, C3可以省略, C7可以用1pF至3pF.
没有现成的模块, 在立创打的板子, 成品图, 兼容XN297, 因此多预留了一些焊盘
STC8H 驱动 XL2400
驱动说明
从测试的过程看, 基于GPIO模拟SPI驱动比较稳妥, 如果用硬件SPI, 收发的通信成功率太低, 主要遇到的问题是SPI读取时, 有30%概率会读到全为0xFF的内容, 猜测是XL2400的驱动能力较弱, 无法拉低读周期的电平?
STC8H对三线SPI半双工通信没有说明, 还需要进一步尝试. 因此以下仅说明基于GPIO模拟SPI驱动的方式.
接线
示例代码中, 使用了与硬件SPI一样的Pin, 实际上换成其他Pin也一样, 因为都是通过GPIO模拟驱动.
Pin connection: P35 => CSN P34 => DATA P32 => SCK VDD1 => 3.3V XC1,XC2 => 16MHz OSC GND => GND
示例代码
代码下载地址
- GitHub https://github.com/IOsetting/FwLib_STC8/tree/master/demo/gpio/xl2400
- Gitee https://gitee.com/iosetting/fw-lib_-stc8/tree/master/demo/gpio/xl2400
在SPI目录下也有硬件SPI驱动方式的代码, 通信效果较差, 有兴趣的可以试一下. 如果能改进为硬件SPI收发就更好.
基础宏定义
切换收发模式, 通过main.c中的XL2400_MODE设置
// 0:TX, 1:RX #define XL2400_MODE 1
宏定义和Ci24R1是一样的, 只是XL2400的CE操作更复杂一点, 需要读写两个字节所以没放到宏定义里
#define XL2400_CSN P35 #define XL2400_SCK P32 #define XL2400_MOSI P34 #define XL2400_PLOAD_WIDTH 32 // Payload width #define XL2400_DATA_OUT() GPIO_P3_SetMode(GPIO_Pin_4, GPIO_Mode_Output_PP) #define XL2400_DATA_IN() GPIO_P3_SetMode(GPIO_Pin_4, GPIO_Mode_Input_HIP) #define XL2400_DATA_LOW() XL2400_MOSI = 0 #define XL2400_DATA_HIGH() XL2400_MOSI = 1 #define XL2400_DATA_READ() XL2400_MOSI #define XL2400_CLK_LOW() XL2400_SCK = 0 #define XL2400_CLK_HIGH() XL2400_SCK = 1 #define XL2400_NSS_LOW() XL2400_CSN = 0 #define XL2400_NSS_HIGH() XL2400_CSN = 1
SPI基础通信, 寄存器读写和多字节读写
SPI基本读写和 Ci24R1 完全一致, 可以参考 Ci24R1 的对应部分. 从官方的代码样例移植时, 并没有使用官方提供的操作方式, 因为相对比之下, 现在这种写法更稳妥. XL2400 没有单字节命令, 只有普通的双字节命令读写, 其它的多字节读写也和 Ci24R1 是一样的.
XL2400的CE操作
void XL2400_CE_Low(void) { XL2400_ReadToBuf(XL2400_CMD_R_REGISTER | XL2400_REG_CFG_TOP, cbuf, 2); *(cbuf + 1) &= 0xBF; XL2400_WriteFromBuf(XL2400_CMD_W_REGISTER | XL2400_REG_CFG_TOP, cbuf, 2); } void XL2400_CE_High(void) { XL2400_ReadToBuf(XL2400_CMD_R_REGISTER | XL2400_REG_CFG_TOP, cbuf, 2); *(cbuf + 1) |= 0x40; XL2400_WriteFromBuf(XL2400_CMD_W_REGISTER | XL2400_REG_CFG_TOP, cbuf, 2); }
XL2400 的初始化
XL2400的初始化, 有一部分和nRF24L01一致, 另一部分是特有的
void XL2400_Init(void) { // Analog config XL2400_ReadToBuf(XL2400_CMD_R_REGISTER | XL2400_REG_ANALOG_CFG0, xbuf, 13); *(xbuf + 4) &= ~0x04; *(xbuf + 12) |= 0x40; XL2400_WriteFromBuf(XL2400_CMD_W_REGISTER | XL2400_REG_ANALOG_CFG0, xbuf, 13); // Switch to software CE control, wake up RF XL2400_WakeUp(); // 开启所有 Pipe 的 Auto ACK XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_EN_AA, 0x3F); // 启用所有 Pipe XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_EN_RXADDR, 0x3F); // 地址宽度 5 bytes XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_SETUP_AW, 0xAF); // 重试次数和间隔 XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_SETUP_RETR, 0x33); // 无线速率 1Mbps XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_RF_SETUP, 0x22); // 接收通道0和通道1的接收字节数 *(cbuf + 0) = XL2400_PLOAD_WIDTH; *(cbuf + 1) = XL2400_PLOAD_WIDTH; XL2400_WriteFromBuf(XL2400_CMD_W_REGISTER | XL2400_REG_RX_PW_PX, cbuf, 2); // 关闭动态接收大小 XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_DYNPD, 0x00); // Other features //bit7&6=00 return status when send register address //bit5=0 long data pack off //bit4=1 FEC off //bit3=1 FEATURE on //bit2=0 Dynamic length off //bit1=0 ACK without payload //bit0=0 W_TX_PAYLOAD_NOACK off XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_FEATURE, 0x18); // 开启 RSSI *(cbuf + 0) = 0x10; *(cbuf + 1) = 0x00; XL2400_WriteFromBuf(XL2400_CMD_W_REGISTER | XL2400_REG_RSSI, cbuf, 2); }
XL2400 发送
发送沿用了官方例子, 在写入发送内容, 拉高CE后, 轮询状态等待发送结果. 如果是MAX_RT或TX_DS_FLAG 则返回结果.
uint8_t XL2400_Tx(uint8_t *ucPayload, uint8_t length) { uint8_t y = 100, status = 0; XL2400_ClearStatus(); XL2400_WriteFromBuf(XL2400_CMD_W_TX_PAYLOAD, ucPayload, length); XL2400_CE_High(); // Retry until timeout while (y--) { SYS_DelayUs(100); status = XL2400_ReadStatus(); // If TX successful or retry timeout, exit if ((status & (MAX_RT_FLAG | TX_DS_FLAG)) != 0) { break; } } XL2400_CE_Low(); return status; }
XL2400 接收
也沿用了官方例子, 轮询等待待接收结果状态, 并读出接收到的字节
uint8_t XL2400_Rx(void) { uint8_t i, status, rxplWidth; status = XL2400_ReadStatus(); if (status & RX_DR_FLAG) { XL2400_CE_Low(); rxplWidth = XL2400_ReadReg(XL2400_CMD_R_RX_PL_WID); XL2400_ReadToBuf(XL2400_CMD_R_RX_PAYLOAD, xbuf, rxplWidth); XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_STATUS, status); // UART1_TxChar('>'); // for (i = 0; i < rxplWidth; i++) // { // UART1_TxHex(*(xbuf + i)); // } } return status; }
每次在调用 之前, 需要设置一下RX状态, 否则不会接收
void XL2400_SetRxMode(void) { XL2400_CE_Low(); XL2400_ClearStatus(); XL2400_WriteReg(XL2400_CMD_W_REGISTER | XL2400_REG_CFG_TOP, 0x7F); // XL2400_RxCalibrate(); XL2400_CE_High(); SYS_Delay(1); }
XL2400通信速率
时间有限没有充分测试, 仅测试了1Mbps速率开启ACK情况下的通信情况. 接收不设间隔, 发送间隔为2 - 3 毫秒时达到最高速率, 大约每1.7秒发送256组, 每组32个字节, 速率为4.8K 字节每秒, 这样看速度只有同等设置下nRF24L01的1/5, 可能和软件模拟的SPI有关.