Ci24R1是Si24R1的SOP8封裝簡化版, 廠商為南京中科微, 他們還有一個比較常見的型號是Si24R1, Si24R1就是應用極廣的nRF24L1的克隆版. Ci24R1的通信協議和Si24R1, nRF24L01是相容的, 另外支持藍牙BLE4.2標準. 本文介紹Ci24R1如何使用GPI... ...
目錄
- 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無線模塊
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上測試, 發現不可行, 主要存在兩個問題
- Ci24R1僅僅提供了一個DATA口, 對應SPI的MOSI, 但是還復用IRQ, 所以使用硬體SPI的話, 需要隨時切換MOSI pin的工作狀態
- STC8H的硬體SPI驅動時, 會有一半概率無法正確讀取, 得到的全是0xFF
- 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
示例代碼
代碼下載地址
- GitHub https://github.com/IOsetting/FwLib_STC8/tree/master/demo/gpio/ci24r1
- Gitee https://gitee.com/iosetting/fw-lib_-stc8/tree/master/demo/gpio/ci24r1
基礎巨集定義
切換收發模式, 通過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 的初始化
初始化有幾個需要註意的點
- CONFIG的最後一個bit標識是TX還是RX
- 地址寬度沒有特殊情況都用5 bytes
- payload是否變寬, 如果是, 則不需要定義每個pipe的payload寬度, 如果否, 則必須定義對應pipe的payload寬度(CI24R1_REG_RX_PW_Px), 否則不會有接收
- 是否變寬還會影響到 CI24R1_REG_FEATURE 中的一位
- 如果開啟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, 可以將成本控制到非常低, 集成後也利於生產和品控.