目录

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封装(右)

管脚定义

PIN Name I/O 说明
1 CSN DI SPI 片选信号
2 SCK DI SPI 时钟信号
3 DATA/IRQ IO SPI 数据输入/输出/中断信号
4 XC1 AI 晶振输入
5 XC2 AO 晶振输出
6 VDD Power 电源(+2.1 ~ +3.6V,DC)
7 ANT RF 天线接口
8 VSS GND

电路

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驱动Ci24R1无线模块的更多相关文章

  1. STC8H开发(十六): GPIO驱动XL2400无线模块

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

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

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

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

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

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

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

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

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

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

    在之前的定时器驱动程序中,我们发现在连续按下按键的时候,正常情况下应该是一次按下对应一次松开.而程序有时候会显示是两次按下,一次松开.这个问题是因为在按下的时候,因为是机械按键,所以电压信号会产生一定 ...

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

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

  8. SpringBoot开发十五-发布帖子

    需求介绍 使用 AJAX 异步通信实现网页能够增量的更新呈现到页面上而不需要刷新整个页面. 现在基本上都是服务器返回 JSON 字符串来解析 代码实现 使用 JQuery 发送 AJAX 请求. 首先 ...

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

    内容目录: HTML补充 javascript HTML补充 1.display标签 display的inline-block 属性会自动带3px的宽度 <span style="di ...

随机推荐

  1. 好客租房15-jsx中的条件渲染

    jsx中的条件渲染 场景:loding效果 条件渲染:根据条件渲染特定的jsx结构 可以使用if/else或者三元运算符和逻辑和运算符实现 //导入react import React from &q ...

  2. SpringCloud基础概念学习笔记(Eureka、Ribbon、Feign、Zuul)

    SpringCloud基础概念学习笔记(Eureka.Ribbon.Feign.Zuul) SpringCloud入门 参考: https://springcloud.cc/spring-cloud- ...

  3. Fastflow——基于golang的轻量级工作流框架

    Fastflow 是什么?用一句话来定义它:一个 基于golang协程.支持水平扩容的分布式高性能工作流框架. 它具有以下特点: 易用性:工作流模型基于 DAG 来定义,同时还提供开箱即用的 API, ...

  4. 在vue中路径中的@

    1.在Vue的路径中@等于src 2.在css的路径中~@等于src

  5. C++:数的变化

    数的变化 时间限制 : 1.000 sec        内存限制 : 128 MB 题目描述: 小明给你提出了一个问题,即给出两个整数 a 和 b,每次操作可以 a+1 或 a×2,问至少进行多少次 ...

  6. 为什么我在css里使用功能类优先

    前言 我想在我们开始的学CSS语法的时候,都是从以下的流程开始的: 1.写一个CSS类选择器: .my-class { } 2.往选择器里填充CSS语法: .my-class { display fl ...

  7. django框架1

    简介 1.前端    与用户打交道的界面 2.web框架    可以将前端和数据库整合到一起 3.数据库    专门用于存储数据 内容概要 纯手撸web框架 基于wsgiref模块 优化措施 动静态网 ...

  8. 线程安全性-原子性之synchronized锁

    原子性提供了互斥访问:同一时刻只能有一个线程进行操作: 除了Atomic包类之外,还有锁可以实现此功能: synchronized:  java关键字,依赖于jvm实现锁功能,被此关键字所修饰的,都是 ...

  9. Java开发学习(四)----bean的三种实例化方式

    一.环境准备 准备开发环境 创建一个Maven项目 pom.xml添加依赖 resources下添加spring的配置文件applicationContext.xml 最终项目的结构如下:    二. ...

  10. 基于开源流程引擎开发BPM或OA有哪些难点

    前言     如何基于开源流程引擎开发OA系统?开源流程引擎哪个好?把它整合到自己的产品里难不难,有没有啥风险?这是大家经常遇到的问题.笔者从2006年开始参与流程引擎开发,经历了三代流程引擎研发,支 ...