STC8H開發(十五): GPIO驅動Ci24R1無線模塊

来源:https://www.cnblogs.com/milton/archive/2022/08/09/16562622.html
-Advertisement-
Play Games

Ci24R1是Si24R1的SOP8封裝簡化版, 廠商為南京中科微, 他們還有一個比較常見的型號是Si24R1, Si24R1就是應用極廣的nRF24L1的克隆版. Ci24R1的通信協議和Si24R1, nRF24L01是相容的, 另外支持藍牙BLE4.2標準. 本文介紹Ci24R1如何使用GPI... ...


目錄

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, 可以將成本控制到非常低, 集成後也利於生產和品控.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在大部分涉及到資料庫操作的項目裡面,事務控制、事務處理都是一個無法迴避的問題。這裡我們一起探討下關於事務控制相關的一些內容。 ...
  • 來源: blog.csdn.net/fumitzuki/article/details/81630048 volatile關鍵字是由JVM提供的最輕量級同步機制。與被濫用的synchronized不同,我們並不習慣使用它。想要正確且完全的理解它並不容易。 Part1Java記憶體模型 Java記憶體模型 ...
  • 多商戶商城系統,也稱為B2B2C(BBC)平臺電商模式多商家商城系統。可以快速幫助企業搭建類似拼多多/京東/天貓/淘寶的綜合商城。 多商戶商城系統支持商家入駐加盟,同時滿足平臺自營、旗艦店等多種經營方式。平臺可以通過收取商家入駐費,訂單交易服務費,提現手續費,簡訊通道費等多手段方式,實現整體盈利。 ...
  • [演算法1-排序](.NET源碼學習)& LINQ & Lambda 說起排序演算法,在日常實際開發中我們基本不在意這些事情,有API不用不是沒事找事嘛。但必要的基礎還是需要瞭解掌握。 排序的目的是為了讓無序的數據,變得“有序”。此處的有序指的是,滿足當前使用需求的順序,除了自帶的API,我們還可以自定 ...
  • 作為一個沒有系統學習過依賴註入的開發者而言,如果直接在一個使用依賴註入的框架下進行開發,往往對於依賴註入的存在是沒有明顯的察覺,通過代碼追根溯源你都會看不出對象是從哪裡創建的。但這並不影響你進行開發的工作,你可以參照現有代碼的使用形式,將需要使用的對象加入到構造函數的參數列表上,你就可以使用對象,調 ...
  • PDF/X-1a是一種PDF文件規範標準,在製作、使用PDF以及印刷時所需要遵循的技術條件,屬於PDF/X-1標準下的一個子標準。 PDF/X-1標準有由CGATS於1999年制定的PDF/X-1:1999,由ISO於2001年制的PDF/X-1:2001、PDF/X-1a:2001以及PDF/X- ...
  • 二、驅動 2.1.hello world 1.創建項目 2.刪除Driver Files裡面的helloworld.inf文件 3.右鍵屬性 Inf2Cat->General->Run Inf2Cat 改成否 Driver Settings->General->Traget OS VERSION和T ...
  • docker容器網路 容器的四種網路模式 | 網路模式 | 配置 | 說明 | | : | : | : | | host | --network host | 容器和宿主機共用Network namespace | | container | --network container:NAME_OR_ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...