目录

Ci24R1 简介

Ci24R1是Si24R1的SOP8封装简化版, 厂商为南京中科微, 他们还有一个比较常见的型号是Si24R1, Si24R1就是应用极广的nRF24L1的克隆版. Ci24R1的通信协议和Si24R1, nRF24L01是兼容的, 另外支持蓝牙BLE4.2标准.

具体到参数上, 与nRF24L01类似, 都是2.4GHz频段的无线通信芯片, 官网的介绍: 低成本高性能2.4GHz 无线收发芯片(支持蓝牙版). 专为低功耗无线场合设计,集成嵌入式ARQ基带协议引擎的无线收发器芯片. 工作频率范围为2400MHz-2525MHz,共有126个1MHz带宽的信道, 支持2Mbps,1Mbps,250Kbps三种数据速率, 支持发射BLE4.2标准的数据包,可以方便的向手机传输数据.

主要特性

  • 频段: 2.4GHz ISM
  • 调制方式: GFSK/FSK
  • 数据速率: 2Mbps/1Mbps/250Kbps
  • 关断功耗: 1uA
  • 待机功耗: 15uA
  • 快速启动时间: ≤ 130uS
  • 内部集成高PSRR LDO
  • 宽电源电压范围: 1.9-3.6V
  • 宽数字I/O电压范围:1.9-5.25V
  • 低成本晶振: 16MHz±60ppm
  • 接收灵敏度: -83dBm @2MHz
  • 最高发射功率: 7dBm
  • 接收电流(2Mbps): 15mA
  • 发射电流(2Mbps): 12mA(0dBm)
  • 支持三线SPI接口
  • 内部集成智能ARQ基带协议引擎
  • 收发数据硬件中断输出
  • 支持1bit RSSI 输出
  • 极少外围器件,降低系统应用成本
  • 封装: SOP8, DFN8(220.8mm)

对标的芯片

Ci24R1对标的是2.4G SOP8芯片, 主要是面向廉价的有无线通信需求的产品, 这类芯片主要有 XN297, XN297L, XL2400/WL2400, 都是三线SPI通信, 只需要一个晶振和一两个电容, 外围电路极少. Ci24R1的优势是同时支持 2.4GHz 和 BLE4.2.

这几个型号芯片的管脚布局各有不同, 并且驱动方式也不太一样.

Ci24R1 管脚和典型电路

管脚布局

SOP8封装(左) 和 DFN8封装(右)

管脚定义

PINNameI/O说明
1CSNDISPI 片选信号
2SCKDISPI 时钟信号
3DATA/IRQIOSPI 数据输入/输出/中断信号
4XC1AI晶振输入
5XC2AO晶振输出
6VDDPower电源(+2.1 ~ +3.6V,DC)
7ANTRF天线接口
8VSSGND

电路

STC8H 驱动 Ci24R1

驱动说明

厂商提供的测试代码, 都是基于GPIO模拟SPI驱动, 开始以为可以用硬件SPI驱动, 后来在STC8H上测试, 发现不可行, 主要存在两个问题

  1. Ci24R1仅仅提供了一个DATA口, 对应SPI的MOSI, 但是还复用IRQ, 所以使用硬件SPI的话, 需要随时切换MOSI pin的工作状态
  2. STC8H的硬件SPI驱动时, 会有一半概率无法正确读取, 得到的全是0xFF
  3. STC8H即使用GPIO模拟驱动SPI, 也必须将IO模式设置为推挽, 使用准双向时读写正常, 但是发送会失败, 尚不清楚原因

接线

示例代码中, 使用了与硬件SPI一样的Pin, 实际上换成其他Pin也一样, 因为都是通过GPIO模拟驱动.

P35(SS, Ignored) => CSN
P34(MOSI) => DATA
P32(SPCLK) => SCK
VDD1 => 3.3V
XC1,XC2 => 16MHz OSC
GND => GND

示例代码

代码下载地址

基础宏定义

切换收发模式, 通过main.c中的

// 0:TX, 1:RX
#define CI24R1_MODE 1

因为涉及到对MOSI Pin的模式切换, 涉及到对CE电平的操作(寄存器写), 这部分都用宏定义保证性能

#define CI24R1_CSN  P35
#define CI24R1_MOSI P34
#define CI24R1_SCK P32 #define CI24R1_DATA_OUT() GPIO_P3_SetMode(GPIO_Pin_4, GPIO_Mode_Output_PP)
#define CI24R1_DATA_IN() GPIO_P3_SetMode(GPIO_Pin_4, GPIO_Mode_Input_HIP)
#define CI24R1_DATA_LOW() CI24R1_MOSI = 0
#define CI24R1_DATA_HIGH() CI24R1_MOSI = 1
#define CI24R1_DATA_READ() CI24R1_MOSI #define CI24R1_CLK_LOW() CI24R1_SCK = 0
#define CI24R1_CLK_HIGH() CI24R1_SCK = 1 #define CI24R1_NSS_LOW() CI24R1_CSN = 0
#define CI24R1_NSS_HIGH() CI24R1_CSN = 1 #define CI24R1_CE_LOW() CI24R1_WriteReg(CI24R1_CMD_CE_OFF, CI24R1_CMD_NOP)
#define CI24R1_CE_HIGH() CI24R1_WriteReg(CI24R1_CMD_CE_ON, CI24R1_CMD_NOP)

模拟SPI基础通信

void CI24R1_WriteByte(uint8_t value)
{
uint8_t i = 0;
CI24R1_CLK_LOW();
CI24R1_DATA_OUT();
for (i = 0; i < 8; i++)
{
CI24R1_CLK_LOW();
if (value & 0x80)
{
CI24R1_DATA_HIGH();
}
else
{
CI24R1_DATA_LOW();
}
CI24R1_CLK_HIGH();
value = value << 1;
}
CI24R1_CLK_LOW();
} uint8_t CI24R1_ReadByte(void)
{
uint8_t i = 0, RxData; CI24R1_DATA_IN();
CI24R1_CLK_LOW();
for (i = 0; i < 8; i++)
{
RxData = RxData << 1;
CI24R1_CLK_HIGH();
if (CI24R1_DATA_READ())
{
RxData |= 0x01;
}
else
{
RxData &= 0xfe;
}
CI24R1_CLK_LOW();
}
CI24R1_CLK_LOW();
return RxData;
}

Ci24R1 单字节命令, 寄存器读写

对nRF24L01熟悉的都知道其寄存器读写的方式, 其实是两个字节的通信, Ci24R1比较特殊的地方在于有一个单字节的写命令, 用于切换DATA Pin的模式

void CI24R1_WriteReg(uint8_t reg,uint8_t value)
{
CI24R1_NSS_LOW();
CI24R1_WriteByte(reg);
CI24R1_WriteByte(value);
CI24R1_NSS_HIGH();
} uint8_t CI24R1_ReadReg(uint8_t reg)
{
uint8_t reg_val;
CI24R1_NSS_LOW();
CI24R1_WriteByte(reg);
reg_val = CI24R1_ReadByte();
CI24R1_NSS_HIGH();
return reg_val;
} void CI24R1_WriteCmd(uint8_t cmd)
{
CI24R1_NSS_LOW();
CI24R1_WriteByte(cmd);
CI24R1_NSS_HIGH();
}

Ci24R1 的多字节读写命令

void CI24R1_WriteFromBuf(uint8_t reg, const uint8_t *pBuf, uint8_t len)
{
uint8_t ctr;
CI24R1_NSS_LOW();
CI24R1_WriteByte(reg);
for (ctr = 0; ctr < len; ctr++)
{
CI24R1_WriteByte(*pBuf++);
}
CI24R1_NSS_HIGH();
} void CI24R1_ReadToBuf(uint8_t reg, uint8_t *pBuf, uint8_t len)
{
uint8_t ctr;
CI24R1_NSS_LOW();
CI24R1_WriteByte(reg);
for (ctr = 0; ctr < len; ctr++)
{
pBuf[ctr] = CI24R1_ReadByte();
}
CI24R1_NSS_HIGH(); }

Ci24R1 的初始化

初始化有几个需要注意的点

  1. CONFIG的最后一个bit标识是TX还是RX
  2. 地址宽度没有特殊情况都用5 bytes
  3. payload是否变宽, 如果是, 则不需要定义每个pipe的payload宽度, 如果否, 则必须定义对应pipe的payload宽度(CI24R1_REG_RX_PW_Px), 否则不会有接收
  4. 是否变宽还会影响到 CI24R1_REG_FEATURE 中的一位
  5. 如果开启ACK, TX地址和RX P0地址一定是一样的, 两个模块之间通信可以使用完全一样的TX和RX P0

开始测试时, 可以使用低码率(250Kbps)加大功率(11dB), 另外模块可以靠的近一点, 例如五六公分, 避免非程序的问题导致调试失败

void CI24R1_Init(void)
{
CI24R1_CE_LOW();
#if (CI24R1_PLOAD_WIDTH == 0)
// Enable dynamic payload length on pipe 0 and pipe 1
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_DYNPD, 0x03);
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_FEATURE, 0x07);
#else
// Fixed payload length
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_DYNPD, 0x00);
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_FEATURE, 0x03);
// Length of pipe 0
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_RX_PW_P0, CI24R1_PLOAD_WIDTH);
// Length of pipe 1
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_RX_PW_P1, CI24R1_PLOAD_WIDTH);
#endif
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_CONFIG, 0x0E);
// Enable auto ack all pipes
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_EN_AA, 0x3F);
// Enable all pipes
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_EN_RXADDR, 0x3F);
// Address width, 0x1:3bytes, 0x02:4bytes, 0x3:5bytes
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_SETUP_AW, 0x03);
// Resend 500us and 3 times. interval: 250us * ([0, 15] + 1), retries: [0, 15]
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_SETUP_RETR, (0x01 << 4) | 0x03);
// RF Data Rate 250K 11db
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_RF_SETUP, CI24R1_RF_SETUP_1M | CI24R1_RF_SETUP_11DB);
CI24R1_CE_HIGH();
}

Ci24R1 发送

发送沿用了厂商给的例子, 在写入发送内容, 拉高CE后, 立即切换IO到输入状态等待发送结果的中断. 如果是MAX_RT中断, 说明发送失败, 需要清空TX_FIFO和标志位.

void CI24R1_SetTxMode(void)
{
uint8_t value;
value = CI24R1_ReadReg(CI24R1_CMD_R_REGISTER | CI24R1_REG_CONFIG);
value &= 0xFE;
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_CONFIG, value);
} uint8_t CI24R1_Tx(uint8_t *ucPayload, uint8_t length)
{
uint8_t status;
#if (CI24R1_PLOAD_WIDTH == 0)
CI24R1_WriteFromBuf(CI24R1_CMD_W_TX_PAYLOAD, ucPayload, length);
#else
CI24R1_WriteFromBuf(CI24R1_CMD_W_TX_PAYLOAD, ucPayload, CI24R1_PLOAD_WIDTH);
#endif
CI24R1_CE_HIGH();
CI24R1_WriteCmd(CI24R1_CMD_SELIRQ);
CI24R1_DATA_IN();
while (CI24R1_DATA_READ());
CI24R1_DATA_OUT();
CI24R1_WriteCmd(CI24R1_CMD_SELSPI);
status = CI24R1_ReadStatus();
if (status & CI24R1_FLAG_MAX_RT)
{
CI24R1_WriteReg(CI24R1_CMD_FLUSH_TX, CI24R1_CMD_NOP);
}
// Clear status flags
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_STATUS, status);
return status;
}

Ci24R1 接收

也沿用了厂商的例子, 切换到输入状态后, 阻塞等待接收中断. 如果测试中, SPI读写没问题, 距离也够近, 但是一直没中断, 可以检查一下

两个模块的TX地址和RX_P0地址, RF Channel是否一致, 是否开启了对应RX Pipe, 如果是固定宽度, 是否在对应的接收pipe上正确设置了.

void CI24R1_SetRxMode(void)
{
uint8_t value;
value = CI24R1_ReadReg(CI24R1_CMD_R_REGISTER | CI24R1_REG_CONFIG);
value |= 0x01;
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_CONFIG, value);
} uint8_t CI24R1_Rx(void)
{
uint8_t i, status, rxplWidth;
CI24R1_WriteReg(CI24R1_CMD_FLUSH_RX, CI24R1_CMD_NOP);
CI24R1_WriteReg(CI24R1_CMD_SELIRQ, CI24R1_CMD_NOP);
CI24R1_DATA_IN();
while(CI24R1_DATA_READ());
CI24R1_DATA_OUT();
CI24R1_WriteReg(CI24R1_CMD_SELSPI, CI24R1_CMD_NOP);
status = CI24R1_ReadStatus();
UART1_TxChar('>');
UART1_TxHex(status);
if (status & CI24R1_FLAG_RX_READY)
{
#if CI24R1_PLOAD_WIDTH == 0
rxplWidth = CI24R1_ReadReg(CI24R1_CMD_R_RX_PL_WID);
#else
rxplWidth = CI24R1_PLOAD_WIDTH;
#endif
// Read RX to buffer
CI24R1_ReadToBuf(CI24R1_CMD_R_RX_PAYLOAD, xbuf, rxplWidth);
// Clear status flags
CI24R1_WriteReg(CI24R1_CMD_W_REGISTER | CI24R1_REG_STATUS, status);
UART1_TxChar('>');
for (i = 0; i < rxplWidth; i++)
{
UART1_TxHex(*(xbuf_data + i));
}
}
return status;
}

结束

测试中Ci24R1的通信还是比较稳定的, 因为IO转换加上模拟SPI, 通信的速率和4线SPI的nRF24L01和Si24R1比肯定会有差距, 好处是省了一个IO.

这种芯片市场指向非常明显, 就是面向成本和尺寸敏感的市场, 仅需要GPIO就能使用, 几乎所有的MCU都能兼容. 廉价的玩具和家用电器的遥控, 这些产品大量使用8pin的8位MCU, 这种MCU总共只有6个可用IO, 省一个IO就能增加不少可能性. 市场上还有同类型集成了MCU的型号, 例如XL2401, XL2402, SOP16封装连无线带MCU不到1.4CNY, 可以将成本控制到非常低, 集成后也利于生产和品控.

STC8H开发(十五): GPIO驱动Ci24R1More related articles on wireless modules

  1. STC8H开发(五): SPI驱动nRF24L01无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  2. STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  3. STC8H开发(十四): I2C驱动RX8025T高精度实时时钟芯片

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  4. STC8H开发(十): SPI驱动Nokia5110 LCD(PCD8544)

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  5. 嵌入式Linux驱动学习之路(十五)按键驱动-定时器防抖

    in the previous timer driver,We found that when pressing the key continuously,Under normal circumstances, it should be one press and one release.The program sometimes shows that it is pressed twice,Release once.The problem is because when pressed,Because it is a mechanical button,So the voltage signal will generate a certain amount ...

  6. Java微信公众平台开发(十五)--微信JSSDK的使用

    转自:http://www.cuiyongzhi.com/post/63.html 在前面的文章中有介绍到我们在微信web开发过程中常常用到的 [微信JSSDK中Config配置] ,But we are in real ...

  7. SpringBootDevelopment fifteen-发布帖子

    需求介绍 使用 AJAX Asynchronous communication enables web pages to be rendered incrementally on the page without having to refresh the entire page. Now basically all the server returns JSON string to parse 代码实现 使用 JQuery 发送 AJAX 请求. 首先 ...

  8. python运维开发(十五)----JavaScript

    内容目录: HTML补充 javascript HTML补充 1.display标签 display的inline-block Attributes are automatically brought3px的宽度 <span style="di ...

  9. cocos2d-x游戏开发(十五)游戏加载动画loading界面

    个人原创,欢迎转载:http://blog.csdn.net/dawn_moon/article/details/11478885 这个资源加载的loading界面demo是在玩客网做逆转三国的时候随 ...

  10. cocos2d-x游戏开发(十五)游戏载入动画loading界面

    这个资源载入的loading界面demo是在玩客网做逆转三国的时候随手写的,尽管我在那仅仅待了2个礼拜.可是也算參与了一个商业游戏项目了,学到不少东西.当时使用的cocos2d-x还是1.0版的,我用 ...

随机推荐

  1. Evaluate Reverse Polish Notation

    Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...

  2. SQL Union和SQL Union All用法

    SQL UNION 操作符 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每 ...

  3. HTML里面Textareawrapping summary

    Recently I encountered a data transfer to and fromTextreaThe question of whether it can really be stored in rows,在这里总结一下:   问题描写叙述: 比方get数据到一个TextArea里面,如“AAA BBB”,I want to put this text inTextAre ...

  4. 4道过滤菜鸟的iOS面试题

    网上已经有很多针对各种知识点的面试题,面试时有些人未必真正理解也能通过背题看上去很懂.我自己总结了4道面试题,好快速的判断这个人是否是一个合格的工程师,欢迎大家点评. 1.struct和class的区 ...

  5. zoj Grouping(强连通+缩点+关键路径)

    题意: 给你N个人,Mage relationship,Now plan to divide these people into different sets,Make the ages between any two people in each set incomparable.Ask you what is the smallest number of sets? 分析: 首先,假设有一个环,Then between any two points in this ring ...

  6. 【bzoj1031】[JSOI2007]字符加密Cipher

    题目描述 喜欢钻研问题的JS同学,最近又迷上了对加密方法的思考.一天,他突然想出了一种他认为是终极的加密办法:把需要加密的信息排成一圈,显然,它们有很多种不同的读法.例如下图,可以读作:JSOI07 ...

  7. 帝国cmsIn when the current information is insufficient for the call,Continue to fetch other data

    <?php$sql=$empire->query("select * from table1 order by id limit 20"); $num = mysql_ ...

  8. Oracle启动关闭

    启动: [[email protected] ~]$ sqlplus / as sysdba   SQL*Plus: Release 11.2.0.1.0 Production on Wed Nov ...

  9. 找Maven --> pom.xml --> groupId和artifactId的网站

    http://mvnrepository.com/ 在此记录

  10. The General Query Log Binary Log

    https://dev.mysql.com/doc/refman/8.0/en/binary-log.html https://dev.mysql.com/doc/refman/8.0/en/quer ...