nRF24L01模塊
官網鏈接: https://www.nordicsemi.com/Products/nRF24-series
常見的無線收發模塊, 工作在2.4GHz頻段, 適合近距離遙控和數據傳輸.
nRF24L01是一個能兼顧距離和數據速率的無線模塊, 在空曠環境下,2M速率15米, 1M速率30米, 250K速率能達到50米. 和藍牙相比距離更遠, 和ESP8266這類以太網WiFi相比環境適應力更強.
參數
- 2.4GHz ISM頻段
- 250Kbps, 1Mbps, 2Mbps三種空中傳輸速率
- 輸出功率為 0dBm時發射功耗為11.3mA
- 空中傳輸速率為2Mbps時接收功耗為13.5mA
- Power down模式功耗低至900nA, Standby-I模式功耗低至26uA
- 1.9-3.6V的電壓工作范圍
- 支持6個接收通道(地址)
- IO口能承受5V電壓
- ±60ppm 16MHz晶體振盪器
- 4×4mm QFN封裝
nRF24L01 模塊PIN布局
元件面朝自己,天線朝左(晶振在上), 右側PIN腳的定義為:
IRQ | MISO |
MOSI | CSK |
CSN | CE |
VCC | GND |
PIN腳定義
CE | 數字信號輸入 | Chip Enable, RX和TX模式選擇 |
CSN | 數字信號輸入 | SPI chip select, 低電平使能 |
SCK | 數字信號輸入 | SPI serial clock |
MOSI | 數字信號輸入 | 從機數據輸入 |
MISO | 數字信號輸出 | 從機數據輸出, 有三種狀態選項 |
IRQ | 數字信號輸出 | 可屏蔽中斷腳, 低電平使能 |
VDD | 電源 | 1.9 - 3.6V |
VSS | 電源 | 接地 (0V) |
PIN: IRQ
正常狀態為高電位, 只有當STATUS寄存器的以下三個位被置位(拉高)時會拉低電壓輸出, 要清除中斷, 需要相應地往這三個位寫入1
.
- RX_DR(Received Ready)
- TX_DS (Data Sent)
- MAX_RT(Transmit Failed After Max Retransmits)
可以通過寄存器地址0x00
分別對這三種中斷進行屏蔽
The nRF24L01 has an active low IRQ pin. The IRQ pin has three sources: it is activated when TX_DS (Transmit Data Sent), RX_DR (Receive Data Ready) or MAX_RT (Max Retransmit) bit are set high by the Enhanced ShockBurst in the STATUS register. The IRQ pin resets when MCU writes ‘1’ in the STATUS register on the bit associated to the IRQ active source. As example, we can suppose that a MAX_RT events happens. So, we will detect an IRQ transition from high to low and checking STATUS register we will found that MAX_RT bit is high. So we should take necessary actions and than set MAX_RT to high in the STATUS register to clear IRQ.
To detect IRQ we can use EXT driver from ChibiOS/HAL. This driver launches a callback when detects a transition (on rising, falling or both edge according to its configuration), anyway, we will discuss this callback in detail later.
狀態之間的轉移
- Power Down 模式
在該模式下, nRF24L01+的功耗最小, 不能進行發送或者接收. 但是所有寄存器的值保持不變, SPI處於有效狀態, 允許對寄存器, TX/RX FIFO進行操作, PWR_UP(此位在CONFIG寄存器中)清0即進入該狀態. - Standby-I 模式
將PWR_UP置1, 即進入Standby-I模式, 該模式既降低了nRF24L01+的平均功耗, 同時又保持盡可能短的啟動時間, 將CE置1然后后清0, 就可以進入TX/RX模式, 然后又返回到Standby-I模式. - Standby-II 模式
當nRF24L01+設置為接收機(PTX), 並且CE=1, TX FIFO為空時即進入該模式. 相比Standby-I模式, 這種模式相對耗電, 一旦發送FIFO有新數據, 就會立即將數據打包發送出去. - TX 模式, 進入該模式需要 PWR_UP=1, PRIM_RX=0, TX FIFO不為空, CE=1脈沖寬度超過10us
- RX 模式, 進入該模式需要 PWR_UP=1, PRIM_RX=1, CE=1
工作流程
發送流程
- MCU通過SPI對NRF24L01進行基本配置, 配置自動應答通道使能, 設置自動重發次數不為0(在此設置可以重發數據包)設置為發送模式, 還有其他配置等等
- MCU把要發送的數據和接收數據設備的地址通過SPI寫入NRF24L01
- CE引腳置高, 啟動發送
- 此時有兩種情況
- 在有限時間內收到應答信號, 則TX_DS置高(發送數據成功標志位), 並引發IRQ中斷(引腳IRQ置低), 並清除TX buff(發送緩沖寄存器, 自行寫代碼清除), IRQ中斷需要寫狀態寄存器進行復位(因為此處IRQ由TX_DS引發, 將TX_DS復位即可使IRQ復位)
- 重發數據次數超過設定值, 則 MAX_RT 置高(達到最多重發次數標志位), 並引發IRQ中斷(引腳IRQ置低), 不清除TX buff, IRQ中斷需要寫狀態寄存器進行復位(因為此處IRQ由MAX_RT引發,將MAX_RT復位即可使IRQ復位)
- 接收到應答信號產生中斷或者達到最大重發次數產生中斷后,NRF24L01繼續發下一包數據
- 當TX buff為空時, 進入待機模式二(當CE為高, TX buff為空時進入待機模式二), NRF24L01的工作模式圖表在后面. 只要在適當時候拉高CE進行發送即可, 配置NRF24L01時CE置低)
接收流程
- 與發送模式一樣, 一開始MCU通過SPI對NRF24L01進行基本配置, 設置數據通道自動應答使能(在EN_AA寄存器進行設置, 即收到數據后自動向主機發送應答信號), 還有進行接收數據通道使能(在EN_RXADDR寄存器配置), 即選擇六個接收通道的某一通道來接收數據, 設置為接收模式, 以及其他配置.
- 拉高CE引腳(CE置高), 啟動接收狀態
- 接收到一個有效數據包后, 數據存儲在RX buff, 並產生RX_DR中斷(RX_DR為接收數據成功標志位, 接收成功置1), 中斷和發送模式一樣, 需要復位
- 接收設備自動向發送設備發送確認信號(這步是自動的)
- 設置CE引腳為低, NRF24L01進入待機模式一
- MCU通過SPI讀取NRF24L01收到的數據
發送狀態出現 1E (MAX_RT) 的原因匯總
經過各種環境的測試, 總結一下出現 1E 錯誤的原因
- 發送狀態下, 發送的目標地址 TX_ADDR 必須與RX_ADDR_P0相同!! 這個地址將用於接受對方返回的ACK, 如果RX_ADDR_P0填的不對, 對方依然能收到數據, 但是本地會多次重試后產生MAX_RT中斷
- 如果對方能收到數據, 上面的P0地址也與 TX_ADDR 一致, 仍然出現 MAX_RT, 可以檢查一下發射功率, 經測試, 在1Mbps速率時, 如果功率設置為-12dbm, 就會頻繁出現 MAX_RT 中斷, 如果設為-6dbm和0dbm就不會.
- 還有一種會出現MAX_RT中斷的情況, 就是在處理發送的方法內使用了
printf("%d, ...", u8_variable)
這樣的語句, 使用%x
沒問題, 但是如果使用%d
就會出現, 具體原因未知. - 對於STC89/STC12系列的MCU, 如果以上都沒問題, 可以換一個USB2TTL試試, 最近遇到的一個問題就跟我使用的PL2303的USB2TTL有關, 換成CH340就能正常收到2E狀態.
總結
- 發送過程
- MCU通過SPI對NRF24L01進行基本配置,配置好NRF24L01
- MCU將要發送的數據與接收數據設備的地址寫入NRF24L01
- CE引腳置高,啟動發送
- 接收過程
- MCU通過SPI對NRF24L01進行基本配置,配置好NRF24L01
- CE引腳置高,啟動接收
- MCU對 NRF24L01進行數據讀取
NRF24L01的USB串口調試設備
淘寶上有配套出售的一種USB轉接卡, 用於將NRF24L01通過USB到電腦, 此時NRF24L01相對於電腦成為一個串口設備, 通過AT
命令進行通信.
- 所有命令均為大寫
- 標點符號必須英文狀態下的半角標點
- 無空格
- 不可更改的參數
- 地址長度必須為5位
- 數據長度必須是32個字節
- 發射功率為0dbm
- 如果用上位機和這個USB轉接卡進行調試的話, 要注意這里有個坑: 用戶可用的字節為1-31個, 第0位不可用, 這個字節系統保留, 用於記錄傳輸的數據包長度. 例如: 串口發送
abc
(ASCII碼, 3 bytes), 實際傳輸3abc
(第0個字節就為3), 接收端根據第0字節中的數來判斷收到的數據包長度, 再通過串口TX輸出給電腦的就是abc
.
相關的命令有
- AT? 系統信息查詢
- AT+BAUD=n n為1,2,3,4,5,6,7分別對應4800,9600,14400,19200,38400,115200的波特率
- AT+RATE=n n為1,2,3分別對應250Kbps ,1Mbps,2Mbps的傳輸速率
- AT+RXA=0x??,0x??,0x??,0x??,0x?? 0x??為十六進制本機地址, 英文逗號分隔
- AT+TXA=0x??,0x??,0x??,0x??,0x?? 目標地址, 其他同上
- AT+FREQ=2.xxxG 2. xxx為要設定的頻率, 范圍2.400GHz-2.525GHz, 超過范圍無效, 小數點后面為三位數字, 不足三位需補零, 大寫字母G不可缺少
- AT+CRC=n n等於8或者16, 設置8位或16位CRC校驗
- 發送消息 直接往串口輸出, 且不符合上面命令格式的, 都會發送到目標地址
STM32F103C8T6
接線方式
STM32 | nRF24L01 |
---|---|
PA4 SPI1_NSS | N/A |
PA5 SPI1_SCK | SCK |
PA6 SPI1_MISO | MISO |
PA7 SPI1_MOSI | MOSI |
PB13 | IRQ |
PB14 | CE |
PB15 | CSN |
代碼示例
Github項目: https://github.com/IOsetting/stm32f103-nrf24l01
常量定義
// SPI(nRF24L01) commands
#define NRF24L01_CMD_REGISTER_R 0x00 // Register read
#define NRF24L01_CMD_REGISTER_W 0x20 // Register write
#define NRF24L01_CMD_ACTIVATE 0x50 // (De)Activates R_RX_PL_WID, W_ACK_PAYLOAD, W_TX_PAYLOAD_NOACK features
#define NRF24L01_CMD_RX_PLOAD_WID_R 0x60 // Read RX-payload width for the top R_RX_PAYLOAD in the RX FIFO.
#define NRF24L01_CMD_RX_PLOAD_R 0x61 // Read RX payload
#define NRF24L01_CMD_TX_PLOAD_W 0xA0 // Write TX payload
#define NRF24L01_CMD_ACK_PAYLOAD_W 0xA8 // Write ACK payload
#define NRF24L01_CMD_TX_PAYLOAD_NOACK_W 0xB0 //Write TX payload and disable AUTOACK
#define NRF24L01_CMD_FLUSH_TX 0xE1 // Flush TX FIFO
#define NRF24L01_CMD_FLUSH_RX 0xE2 // Flush RX FIFO
#define NRF24L01_CMD_REUSE_TX_PL 0xE3 // Reuse TX payload
#define NRF24L01_CMD_LOCK_UNLOCK 0x50 // Lock/unlock exclusive features
#define NRF24L01_CMD_NOP 0xFF // No operation (used for reading status register)
// SPI(nRF24L01) register address definitions
#define NRF24L01_REG_CONFIG 0x00 // Configuration register
#define NRF24L01_REG_EN_AA 0x01 // Enable "Auto acknowledgment"
#define NRF24L01_REG_EN_RXADDR 0x02 // Enable RX addresses
#define NRF24L01_REG_SETUP_AW 0x03 // Setup of address widths
#define NRF24L01_REG_SETUP_RETR 0x04 // Setup of automatic re-transmit
#define NRF24L01_REG_RF_CH 0x05 // RF channel
#define NRF24L01_REG_RF_SETUP 0x06 // RF setup
#define NRF24L01_REG_STATUS 0x07 // Status register
#define NRF24L01_REG_OBSERVE_TX 0x08 // Transmit observe register
#define NRF24L01_REG_RPD 0x09 // Received power detector
#define NRF24L01_REG_RX_ADDR_P0 0x0A // Receive address data pipe 0
#define NRF24L01_REG_RX_ADDR_P1 0x0B // Receive address data pipe 1
#define NRF24L01_REG_RX_ADDR_P2 0x0C // Receive address data pipe 2
#define NRF24L01_REG_RX_ADDR_P3 0x0D // Receive address data pipe 3
#define NRF24L01_REG_RX_ADDR_P4 0x0E // Receive address data pipe 4
#define NRF24L01_REG_RX_ADDR_P5 0x0F // Receive address data pipe 5
#define NRF24L01_REG_TX_ADDR 0x10 // Transmit address
#define NRF24L01_REG_RX_PW_P0 0x11 // Number of bytes in RX payload in data pipe 0
#define NRF24L01_REG_RX_PW_P1 0x12 // Number of bytes in RX payload in data pipe 1
#define NRF24L01_REG_RX_PW_P2 0x13 // Number of bytes in RX payload in data pipe 2
#define NRF24L01_REG_RX_PW_P3 0x14 // Number of bytes in RX payload in data pipe 3
#define NRF24L01_REG_RX_PW_P4 0x15 // Number of bytes in RX payload in data pipe 4
#define NRF24L01_REG_RX_PW_P5 0x16 // Number of bytes in RX payload in data pipe 5
#define NRF24L01_REG_FIFO_STATUS 0x17 // FIFO status register
#define NRF24L01_REG_DYNPD 0x1C // Enable dynamic payload length
#define NRF24L01_REG_FEATURE 0x1D // Feature register
// Register bits definitions
#define NRF24L01_CONFIG_PRIM_RX 0x01 // PRIM_RX bit in CONFIG register
#define NRF24L01_CONFIG_PWR_UP 0x02 // PWR_UP bit in CONFIG register
#define NRF24L01_FEATURE_EN_DYN_ACK 0x01 // EN_DYN_ACK bit in FEATURE register
#define NRF24L01_FEATURE_EN_ACK_PAY 0x02 // EN_ACK_PAY bit in FEATURE register
#define NRF24L01_FEATURE_EN_DPL 0x04 // EN_DPL bit in FEATURE register
#define NRF24L01_FLAG_RX_DREADY 0x40 // RX_DR bit (data ready RX FIFO interrupt)
#define NRF24L01_FLAG_TX_DSENT 0x20 // TX_DS bit (data sent TX FIFO interrupt)
#define NRF24L01_FLAG_MAX_RT 0x10 // MAX_RT bit (maximum number of TX re-transmits interrupt)
// Register masks definitions
#define NRF24L01_MASK_REG_MAP 0x1F // Mask bits[4:0] for CMD_RREG and CMD_WREG commands
#define NRF24L01_MASK_CRC 0x0C // Mask for CRC bits [3:2] in CONFIG register
#define NRF24L01_MASK_STATUS_IRQ 0x70 // Mask for all IRQ bits in STATUS register
#define NRF24L01_MASK_RF_PWR 0x06 // Mask RF_PWR[2:1] bits in RF_SETUP register
#define NRF24L01_MASK_RX_P_NO 0x0E // Mask RX_P_NO[3:1] bits in STATUS register
#define NRF24L01_MASK_DATARATE 0x28 // Mask RD_DR_[5,3] bits in RF_SETUP register
#define NRF24L01_MASK_EN_RX 0x3F // Mask ERX_P[5:0] bits in EN_RXADDR register
#define NRF24L01_MASK_RX_PW 0x3F // Mask [5:0] bits in RX_PW_Px register
#define NRF24L01_MASK_RETR_ARD 0xF0 // Mask for ARD[7:4] bits in SETUP_RETR register
#define NRF24L01_MASK_RETR_ARC 0x0F // Mask for ARC[3:0] bits in SETUP_RETR register
#define NRF24L01_MASK_RXFIFO 0x03 // Mask for RX FIFO status bits [1:0] in FIFO_STATUS register
#define NRF24L01_MASK_TXFIFO 0x30 // Mask for TX FIFO status bits [5:4] in FIFO_STATUS register
#define NRF24L01_MASK_PLOS_CNT 0xF0 // Mask for PLOS_CNT[7:4] bits in OBSERVE_TX register
#define NRF24L01_MASK_ARC_CNT 0x0F // Mask for ARC_CNT[3:0] bits in OBSERVE_TX register
// Register masks definitions
#define NRF24L01_MASK_REG_MAP 0x1F // Mask bits[4:0] for CMD_RREG and CMD_WREG commands
#define NRF24L01_ADDR_WIDTH 5 // RX/TX address width
#define NRF24L01_PLOAD_WIDTH 32 // Payload width
基礎方法
初始化
static void NRF24L01_SPI_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;
if(NRF24L01_SPIx == SPI1) {
// A5, A6, A7
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
} else if(NRF24L01_SPIx == SPI2) {
// B13, B14, B15
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CRCPolynomial = 7;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_Direction= SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_Init(NRF24L01_SPIx, &SPI_InitStruct);
SPI_Cmd(NRF24L01_SPIx, ENABLE);
}
void NRF24L01_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// CE CSN Initialize
GPIO_InitStruct.GPIO_Pin = NRF24L01_GPIO_CE | NRF24L01_GPIO_CSN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(NRF24L01_GPIOx, &GPIO_InitStruct);
// IRQ Initialize
GPIO_InitStruct.GPIO_Pin = NRF24L01_GPIO_IRQ;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(NRF24L01_GPIOx, &GPIO_InitStruct);
NRF24L01_SPI_Init();
CSN(1);
}
單次讀寫SPI(所有交互的基礎操作)
/**
* Basic SPI operation: Write to SPIx and read
*/
static u8 SPI_Write_Then_Read(u8 data)
{
while(SPI_I2S_GetFlagStatus(NRF24L01_SPIx, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(NRF24L01_SPIx, data);
while(SPI_I2S_GetFlagStatus(NRF24L01_SPIx, SPI_I2S_FLAG_RXNE)==RESET);
return SPI_I2S_ReceiveData(NRF24L01_SPIx);
}
單個字節的讀寫
/**
* Read a 1-bit register
*/
u8 NRF24L01_Read_Reg(u8 reg)
{
u8 value;
CSN(0);
SPI_Write_Then_Read(reg);
value = SPI_Write_Then_Read(NRF24L01_CMD_NOP);
CSN(1);
return value;
}
/**
* Write a 1-byte register
*/
u8 NRF24L01_Write_Reg(u8 reg, u8 value)
{
u8 status;
CSN(0);
if (reg < NRF24L01_CMD_REGISTER_W) {
// This is a register access
status = SPI_Write_Then_Read(NRF24L01_CMD_REGISTER_W | (reg & NRF24L01_MASK_REG_MAP));
SPI_Write_Then_Read(value);
} else {
// This is a single byte command or future command/register
status = SPI_Write_Then_Read(reg);
if ((reg != NRF24L01_CMD_FLUSH_TX)
&& (reg != NRF24L01_CMD_FLUSH_RX)
&& (reg != NRF24L01_CMD_REUSE_TX_PL)
&& (reg != NRF24L01_CMD_NOP)) {
// Send register value
SPI_Write_Then_Read(value);
}
}
CSN(1);
return status;
}
多個字節的讀寫
/**
* Read a multi-byte register
* reg - register to read
* buf - pointer to the buffer to write
* len - number of bytes to read
*/
u8 NRF24L01_Read_To_Buf(u8 reg, u8 *buf, u8 len)
{
CSN(0);
u8 status = SPI_Write_Then_Read(reg);
while (len--) {
*buf++ = SPI_Write_Then_Read(NRF24L01_CMD_NOP);
}
CSN(1);
return status;
}
/**
* Write a multi-byte register
* reg - register to write
* buf - pointer to the buffer with data
* len - number of bytes to write
*/
u8 NRF24L01_Write_From_Buf(u8 reg, u8 *buf, u8 len)
{
CSN(0);
u8 status = SPI_Write_Then_Read(reg);
while (len--) {
SPI_Write_Then_Read(*buf++);
}
CSN(1);
return status;
}
RX和TX模式配置
/**
* Common configurations of RX and TX, internal function
*/
void _NRF24L01_Config(u8 *tx_addr)
{
// TX Address
NRF24L01_Write_From_Buf(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_TX_ADDR, tx_addr, NRF24L01_ADDR_WIDTH);
// RX P0 Payload Width
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_RX_PW_P0, NRF24L01_PLOAD_WIDTH);
// Enable Auto ACK
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_EN_AA, 0x3f);
// Enable RX channels
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_EN_RXADDR, 0x3f);
// RF channel: 2.400G + 0.001 * x
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_RF_CH, 40);
// 000+0+[0:1Mbps,1:2Mbps]+[00:-18dbm,01:-12dbm,10:-6dbm,11:0dbm]+[0:LNA_OFF,1:LNA_ON]
// 01:1Mbps,-18dbm; 03:1Mbps,-12dbm; 05:1Mbps,-6dbm; 07:1Mbps,0dBm
// 09:2Mbps,-18dbm; 0b:2Mbps,-12dbm; 0d:2Mbps,-6dbm; 0f:2Mbps,0dBm,
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_RF_SETUP, 0x03);
// 0A:delay=250us,count=10, 1A:delay=500us,count=10
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_SETUP_RETR, 0x0a);
}
/**
* Switch NRF24L01 to RX mode
*/
void NRF24L01_RX_Mode(u8 *rx_addr, u8 *tx_addr)
{
CE(0);
_NRF24L01_Config(tx_addr);
// RX Address of P0
NRF24L01_Write_From_Buf(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_RX_ADDR_P0, rx_addr, NRF24L01_ADDR_WIDTH);
/**
REG 0x00:
0)PRIM_RX 0:TX 1:RX
1)PWR_UP 0:OFF 1:ON
2)CRCO 0:8bit CRC 1:16bit CRC
3)EN_CRC Enabled if any of EN_AA is high
4)MASK_MAX_RT 0:IRQ low 1:NO IRQ
5)MASK_TX_DS 0:IRQ low 1:NO IRQ
6)MASK_RX_DR 0:IRQ low 1:NO IRQ
7)Reserved 0
*/
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_CONFIG, 0x0f); //RX,PWR_UP,CRC16,EN_CRC
CE(1);
}
/**
* Switch NRF24L01 to TX mode
*/
void NRF24L01_TX_Mode(u8 *rx_addr, u8 *tx_addr)
{
CE(0);
_NRF24L01_Config(tx_addr);
// On the PTX the **TX_ADDR** must be the same as the **RX_ADDR_P0** and as the pipe address for the designated pipe
// RX_ADDR_P0 will be used for receiving ACK
NRF24L01_Write_From_Buf(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_RX_ADDR_P0, tx_addr, NRF24L01_ADDR_WIDTH);
NRF24L01_Write_Reg(NRF24L01_CMD_REGISTER_W + NRF24L01_REG_CONFIG, 0x0e); //TX,PWR_UP,CRC16,EN_CRC
CE(1);
}
接收和發送操作
/**
* Hold till data received and written to rx_buf
*/
u8 NRF24L01_RxPacket(u8 *rx_buf)
{
u8 status, result = 0;
while(IRQ);
CE(0);
status = NRF24L01_Read_Reg(NRF24L01_REG_STATUS);
printf("Interrupted, status: %02X\r\n", status);
if(status & NRF24L01_FLAG_RX_DREADY) {
NRF24L01_Read_To_Buf(NRF24L01_CMD_RX_PLOAD_R, rx_buf, NRF24L01_PLOAD_WIDTH);
for (int i = 0; i < 32; i++) {
printf("%02X ", RX_BUF[i]);
}
result = 1;
NRF24L01_ClearIRQFlag(NRF24L01_FLAG_RX_DREADY);
}
CE(1);
return result;
}
/**
* Send data in tx_buf and wait till data is sent or max re-tr reached
*/
u8 NRF24L01_TxPacket(u8 *tx_buf, u8 len)
{
u8 status = 0x00;
CE(0);
len = len > NRF24L01_PLOAD_WIDTH? NRF24L01_PLOAD_WIDTH : len;
NRF24L01_Write_From_Buf(NRF24L01_CMD_TX_PLOAD_W, tx_buf, len);
CE(1);
while(IRQ != 0); // Waiting send finish
CE(0);
status = NRF24L01_Read_Reg(NRF24L01_REG_STATUS);
printf("Interrupted, status: %02X\r\n", status);
if(status & NRF24L01_FLAG_TX_DSENT) {
printf("Data sent: ");
for (u8 i = 0; i < len; i++) {
printf("%02X ", tx_buf[i]);
}
printf("\r\n");
NRF24L01_ClearIRQFlag(NRF24L01_FLAG_TX_DSENT);
} else if(status & NRF24L01_FLAG_MAX_RT) {
printf("Sending exceeds max retries\r\n");
NRF24L01_FlushTX();
NRF24L01_ClearIRQFlag(NRF24L01_FLAG_MAX_RT);
}
CE(1);
return status;
}
如果需要使用中斷讀取, 讀方法要改成非阻塞的方式, 就是上面的RX方法去掉了while IRQ的等待.
/**
* Read received data and written to rx_buf, No blocking.
*/
u8 NRF24L01_IRQ_Handler(u8 *rx_buf)
{
u8 status, result = 0;
CE(0);
status = NRF24L01_Read_Reg(NRF24L01_REG_STATUS);
printf("Reg status: %02X\r\n", status);
if(status & NRF24L01_FLAG_RX_DREADY) {
NRF24L01_Read_To_Buf(NRF24L01_CMD_RX_PLOAD_R, rx_buf, NRF24L01_PLOAD_WIDTH);
for (int i = 0; i < 32; i++) {
printf("%02X ", RX_BUF[i]);
}
result = 1;
NRF24L01_FlushRX();
NRF24L01_ClearIRQFlag(NRF24L01_FLAG_RX_DREADY);
} else if(status & NRF24L01_FLAG_TX_DSENT) {
printf("Data sent\r\n");
NRF24L01_FlushTX();
NRF24L01_ClearIRQFlag(NRF24L01_FLAG_TX_DSENT);
} else if(status & NRF24L01_FLAG_MAX_RT) {
printf("Sending exceeds max retries\r\n");
NRF24L01_FlushTX();
NRF24L01_ClearIRQFlag(NRF24L01_FLAG_MAX_RT);
}
CE(1);
return result;
}
一個非常好用的配置打印函數
/**
* Dump nRF24L01 configuration
*/
void NRF24L01_DumpConfig(void) {
uint8_t i,j;
uint8_t aw;
uint8_t buf[5];
// CONFIG
i = NRF24L01_Read_Reg(NRF24L01_REG_CONFIG);
printf("[0x%02X] 0x%02X MASK:%02X CRC:%02X PWR:%s MODE:P%s\r\n",
NRF24L01_REG_CONFIG,
i,
i >> 4,
(i & 0x0c) >> 2,
(i & 0x02) ? "ON" : "OFF",
(i & 0x01) ? "RX" : "TX"
);
// EN_AA
i = NRF24L01_Read_Reg(NRF24L01_REG_EN_AA);
printf("[0x%02X] 0x%02X ENAA: ",NRF24L01_REG_EN_AA,i);
for (j = 0; j < 6; j++) {
printf("[P%1u%s]%s",j,
(i & (1 << j)) ? "+" : "-",
(j == 5) ? "\r\n" : " "
);
}
// EN_RXADDR
i = NRF24L01_Read_Reg(NRF24L01_REG_EN_RXADDR);
printf("[0x%02X] 0x%02X EN_RXADDR: ",NRF24L01_REG_EN_RXADDR,i);
for (j = 0; j < 6; j++) {
printf("[P%1u%s]%s",j,
(i & (1 << j)) ? "+" : "-",
(j == 5) ? "\r\n" : " "
);
}
// SETUP_AW
i = NRF24L01_Read_Reg(NRF24L01_REG_SETUP_AW);
aw = (i & 0x03) + 2;
printf("[0x%02X] 0x%02X EN_RXADDR=%03X (address width = %u)\r\n",NRF24L01_REG_SETUP_AW,i,i & 0x03,aw);
// SETUP_RETR
i = NRF24L01_Read_Reg(NRF24L01_REG_SETUP_RETR);
printf("[0x%02X] 0x%02X ARD=%04X ARC=%04X (retr.delay=%uus, count=%u)\r\n",
NRF24L01_REG_SETUP_RETR,
i,
i >> 4,
i & 0x0F,
((i >> 4) * 250) + 250,
i & 0x0F
);
// RF_CH
i = NRF24L01_Read_Reg(NRF24L01_REG_RF_CH);
printf("[0x%02X] 0x%02X (%.3uGHz)\r\n",NRF24L01_REG_RF_CH,i,2400 + i);
// RF_SETUP
i = NRF24L01_Read_Reg(NRF24L01_REG_RF_SETUP);
printf("[0x%02X] 0x%02X CONT_WAVE:%s PLL_LOCK:%s DataRate=",
NRF24L01_REG_RF_SETUP,
i,
(i & 0x80) ? "ON" : "OFF",
(i & 0x80) ? "ON" : "OFF"
);
switch ((i & 0x28) >> 3) {
case 0x00:
printf("1M");
break;
case 0x01:
printf("2M");
break;
case 0x04:
printf("250k");
break;
default:
printf("???");
break;
}
printf("pbs RF_PWR=");
switch ((i & 0x06) >> 1) {
case 0x00:
printf("-18");
break;
case 0x01:
printf("-12");
break;
case 0x02:
printf("-6");
break;
case 0x03:
printf("0");
break;
default:
printf("???");
break;
}
printf("dBm\r\n");
// STATUS
i = NRF24L01_Read_Reg(NRF24L01_REG_STATUS);
printf("[0x%02X] 0x%02X IRQ:%03X RX_PIPE:%u TX_FULL:%s\r\n",
NRF24L01_REG_STATUS,
i,
(i & 0x70) >> 4,
(i & 0x0E) >> 1,
(i & 0x01) ? "YES" : "NO"
);
// OBSERVE_TX
i = NRF24L01_Read_Reg(NRF24L01_REG_OBSERVE_TX);
printf("[0x%02X] 0x%02X PLOS_CNT=%u ARC_CNT=%u\r\n",NRF24L01_REG_OBSERVE_TX,i,i >> 4,i & 0x0F);
// RPD
i = NRF24L01_Read_Reg(NRF24L01_REG_RPD);
printf("[0x%02X] 0x%02X RPD=%s\r\n",NRF24L01_REG_RPD,i,(i & 0x01) ? "YES" : "NO");
// RX_ADDR_P0
NRF24L01_Read_To_Buf(NRF24L01_REG_RX_ADDR_P0,buf,aw);
printf("[0x%02X] RX_ADDR_P0 \"",NRF24L01_REG_RX_ADDR_P0);
for (i = 0; i < aw; i++) printf("%X ",buf[i]);
printf("\"\r\n");
// RX_ADDR_P1
NRF24L01_Read_To_Buf(NRF24L01_REG_RX_ADDR_P1,buf,aw);
printf("[0x%02X] RX_ADDR_P1 \"",NRF24L01_REG_RX_ADDR_P1);
for (i = 0; i < aw; i++) printf("%X ",buf[i]);
printf("\"\r\n");
// RX_ADDR_P2
printf("[0x%02X] RX_ADDR_P2 \"",NRF24L01_REG_RX_ADDR_P2);
for (i = 0; i < aw - 1; i++) printf("%X ",buf[i]);
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_ADDR_P2);
printf("%X\"\r\n",i);
// RX_ADDR_P3
printf("[0x%02X] RX_ADDR_P3 \"",NRF24L01_REG_RX_ADDR_P3);
for (i = 0; i < aw - 1; i++) printf("%X ",buf[i]);
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_ADDR_P3);
printf("%X\"\r\n",i);
// RX_ADDR_P4
printf("[0x%02X] RX_ADDR_P4 \"",NRF24L01_REG_RX_ADDR_P4);
for (i = 0; i < aw - 1; i++) printf("%X ",buf[i]);
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_ADDR_P4);
printf("%X\"\r\n",i);
// RX_ADDR_P5
printf("[0x%02X] RX_ADDR_P5 \"",NRF24L01_REG_RX_ADDR_P5);
for (i = 0; i < aw - 1; i++) printf("%X ",buf[i]);
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_ADDR_P5);
printf("%X\"\r\n",i);
// TX_ADDR
NRF24L01_Read_To_Buf(NRF24L01_REG_TX_ADDR,buf,aw);
printf("[0x%02X] TX_ADDR \"",NRF24L01_REG_TX_ADDR);
for (i = 0; i < aw; i++) printf("%X ",buf[i]);
printf("\"\r\n");
// RX_PW_P0
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_PW_P0);
printf("[0x%02X] RX_PW_P0=%u\r\n",NRF24L01_REG_RX_PW_P0,i);
// RX_PW_P1
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_PW_P1);
printf("[0x%02X] RX_PW_P1=%u\r\n",NRF24L01_REG_RX_PW_P1,i);
// RX_PW_P2
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_PW_P2);
printf("[0x%02X] RX_PW_P2=%u\r\n",NRF24L01_REG_RX_PW_P2,i);
// RX_PW_P3
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_PW_P3);
printf("[0x%02X] RX_PW_P3=%u\r\n",NRF24L01_REG_RX_PW_P3,i);
// RX_PW_P4
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_PW_P4);
printf("[0x%02X] RX_PW_P4=%u\r\n",NRF24L01_REG_RX_PW_P4,i);
// RX_PW_P5
i = NRF24L01_Read_Reg(NRF24L01_REG_RX_PW_P5);
printf("[0x%02X] RX_PW_P5=%u\r\n",NRF24L01_REG_RX_PW_P5,i);
}
STM32F401CCU6
STM32F4可用的SPI口
SPI1 | SPI2 | SPI3 | SPI4 | |||||
---|---|---|---|---|---|---|---|---|
NSS | PA4 | PA15 | PB12 | PB9 | PA15 | PA4 | PE4 | PE11 |
SCK | PA5 | PB3 | PB13 | PB10 | PC10 | PB3 | PE2 | PE12 |
MISO | PA6 | PB4 | PB14 | PC2 | PC11 | PB4 | PE5 | PE13 |
MOSI | PA7 | PB5 | PB15 | PC3 | PC12 | PB5 | PE6 | PE14 |
因此連接方式與STM32F103完全相同
STM32 | nRF24L01 |
---|---|
PA4 SPI1_NSS | N/A |
PA5 SPI1_SCK | SCK |
PA6 SPI1_MISO | MISO |
PA7 SPI1_MOSI | MOSI |
PB13 | IRQ |
PB14 | CE |
PB15 | CSN |
唯一區別是初始化方式
static void NRF24L01_SPI_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
if(NRF24L01_SPIx == SPI1) {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
// SCK:PA5, MISO:PA6, MOSI:PA7 or PB3, PB4, PB5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE); // reset SPI1
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);// stop reset SPI1
} else if(NRF24L01_SPIx == SPI2) {
// B13, B14, B15
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
} else { // SPI3,4,5,6
RCC_APB1PeriphResetCmd(RCC_APB1Periph_SPI3, ENABLE);
}
SPI_InitTypeDef SPI_InitStructure;
SPI_StructInit(&SPI_InitStructure); // set default settings
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // data is transmitted MSB first
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // set the NSS management to internal and pull internal NSS high
SPI_Init(NRF24L01_SPIx, &SPI_InitStructure);
SPI_Cmd(NRF24L01_SPIx, ENABLE);
}
void NRF24L01_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = NRF24L01_GPIO_CE|NRF24L01_GPIO_CSN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(NRF24L01_GPIOx, &GPIO_InitStructure);
// IRQ Initialize
GPIO_InitStructure.GPIO_Pin = NRF24L01_GPIO_IRQ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_Init(NRF24L01_GPIOx, &GPIO_InitStructure);
NRF24L01_SPI_Init();
/*
CSN is high initially (active low).
CE is low initially (active high).
*/
CSN(1);
printf("## nRF24L01 Initialized ##\r\n");
}
在STM32F401CCU6上, 使用中斷進行接收的例子
void EXTILine13_Config(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* Enable SYSCFG clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect EXTI Line13 to PG13 pin */
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource13);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line13;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI15_10_IRQHandler(void) {
printf("EXTI15_10_IRQHandler\r\n");
/* Make sure that interrupt flag is set */
if (EXTI_GetITStatus(EXTI_Line13) != RESET) {
NRF24L01_IRQ_Handler(RX_BUF);
/* Clear interrupt flag */
EXTI_ClearITPendingBit(EXTI_Line13);
}
}
int main(void)
{
Systick_Init();
USART1_Init();
NRF24L01_Init();
NRF24L01_DumpConfig();
while(NRF24L01_Check() != 0) {
printf("nRF24L01 check failed\r\n");
Systick_Delay_ms(2000);
}
printf("nRF24L01 check succeeded\r\n");
printf("nRF24L01 in RECEIVE mode\r\n");
NRF24L01_RX_Mode(RX_ADDRESS, TX_ADDRESS);
LED_Init();
EXTILine13_Config();
while(1) {}
}
STC89C52
51單片機的連接方式
sbit CE = P1^5;
sbit CSN= P1^4;
sbit SCK= P1^3;
sbit MOSI= P1^2;
sbit MISO= P1^1;
sbit IRQ = P1^0;
常量定義
// SPI(nRF24L01) commands
#define READ_REG 0x00 // Define read command to register
#define WRITE_REG 0x20 // Define write command to register
#define RD_RX_PLOAD 0x61 // Define RX payload register address
#define WR_TX_PLOAD 0xA0 // Define TX payload register address
#define FLUSH_TX 0xE1 // Define flush TX register command
#define FLUSH_RX 0xE2 // Define flush RX register command
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command
#define NOP 0xFF // Define No Operation, might be used to read status register
// SPI(nRF24L01) registers(addresses)
#define CONFIG 0x00 // 'Config' register address
#define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR 0x02 // 'Enabled RX addresses' register address
#define SETUP_AW 0x03 // 'Setup address width' register address
#define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
#define RF_CH 0x05 // 'RF channel' register address
#define RF_SETUP 0x06 // 'RF setup' register address
#define STATUS 0x07 // 'Status' register address
#define OBSERVE_TX 0x08 // 'Observe TX' register address
#define CD 0x09 // 'Carrier Detect' register address
#define RX_ADDR_P0 0x0A // 'RX address pipe0' register address
#define RX_ADDR_P1 0x0B // 'RX address pipe1' register address
#define RX_ADDR_P2 0x0C // 'RX address pipe2' register address
#define RX_ADDR_P3 0x0D // 'RX address pipe3' register address
#define RX_ADDR_P4 0x0E // 'RX address pipe4' register address
#define RX_ADDR_P5 0x0F // 'RX address pipe5' register address
#define TX_ADDR 0x10 // 'TX address' register address
#define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
#define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
#define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
#define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
#define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
#define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address
基礎方法
void init_io(void)
{
CE = 0; // 待機
CSN = 1; // SPI禁止
SCK = 0; // SPI時鍾置低
IRQ = 1; // 中斷復位
LED = 1; // 關閉指示燈
}
void delay_ms(uchar x)
{
uchar i, j;
i = 0;
for(i=0; i<x; i++) {
j = 250;
while(--j);
j = 250;
while(--j);
}
}
uchar SPI_RW(uchar byte)
{
uchar i;
for(i=0; i<8; i++) {
MOSI = (byte & 0x80); // byte最高位輸出到MOSI
byte <<= 1; // 左移一位
SCK = 1; // 拉高SCK,nRF24L01從MOSI讀入1位數據,同時從MISO輸出1位數據
byte |= MISO; // 讀MISO到byte最低位
SCK = 0; // SCK置低
}
return byte;
}
uchar SPI_RW_Reg(uchar reg, uchar value)
{
uchar status;
CSN = 0; // CSN置低,開始傳輸數據
status = SPI_RW(reg); // 選擇寄存器,同時返回狀態字
SPI_RW(value); // 然后寫數據到該寄存器
CSN = 1; // CSN拉高,結束數據傳輸
return(status); // 返回狀態寄存器
}
uchar SPI_Read(uchar reg)
{
uchar reg_val;
CSN = 0; // CSN置低,開始傳輸數據
SPI_RW(reg); // 選擇寄存器
reg_val = SPI_RW(0); // 然后從該寄存器讀數據
CSN = 1; // CSN拉高,結束數據傳輸
return(reg_val); // 返回寄存器數據
}
uchar SPI_Read_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
uchar status, i;
CSN = 0; // CSN置低,開始傳輸數據
status = SPI_RW(reg); // 選擇寄存器,同時返回狀態字
for(i=0; i<bytes; i++)
pBuf[i] = SPI_RW(0); // 逐個字節從nRF24L01讀出
CSN = 1; // CSN拉高,結束數據傳輸
return(status); // 返回狀態寄存器
}
uchar SPI_Write_Buf(uchar reg, uchar * pBuf, uchar bytes)
{
uchar status, i;
CSN = 0; // CSN置低,開始傳輸數據
status = SPI_RW(reg); // 選擇寄存器,同時返回狀態字
for(i=0; i<bytes; i++)
SPI_RW(pBuf[i]); // 逐個字節寫入nRF24L01
CSN = 1; // CSN拉高,結束數據傳輸
return(status); // 返回狀態寄存器
}
STC12C5A60S2
STC12系列帶了硬件SPI, 在代碼上與89C52有區別.
連線
sbit NRF_CE = P3^7;
sbit NRF_CSN = P1^4;
sbit NRF_MISO = P1^6;
sbit NRF_MOSI = P1^5;
sbit NRF_SCK = P1^7;
sbit NRF_IRQ = P3^2;
代碼
#include "STC12C5A60S2.H"
#define uchar unsigned char
#define uint unsigned int
/********** NRF24L01寄存器操作命令 ***********/
#define READ_REG 0x00 //讀配置寄存器,低5位為寄存器地址
#define WRITE_REG 0x20 //寫配置寄存器,低5位為寄存器地址
#define RD_RX_PLOAD 0x61 //讀RX有效數據,1~32字節
#define WR_TX_PLOAD 0xA0 //寫TX有效數據,1~32字節
#define FLUSH_TX 0xE1 //清除TX FIFO寄存器.發射模式下用
#define FLUSH_RX 0xE2 //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL 0xE3 //重新使用上一包數據,CE為高,數據包被不斷發送.
#define NOP 0xFF //空操作,可以用來讀狀態寄存器
/********** NRF24L01寄存器地址 *************/
#define CONFIG 0x00 //配置寄存器地址
#define EN_AA 0x01 //使能自動應答功能
#define EN_RXADDR 0x02 //接收地址允許
#define SETUP_AW 0x03 //設置地址寬度(所有數據通道)
#define SETUP_RETR 0x04 //建立自動重發
#define RF_CH 0x05 //RF通道
#define RF_SETUP 0x06 //RF寄存器
#define STATUS 0x07 //狀態寄存器
#define OBSERVE_TX 0x08 // 發送檢測寄存器
#define CD 0x09 // 載波檢測寄存器
#define RX_ADDR_P0 0x0A // 數據通道0接收地址
#define RX_ADDR_P1 0x0B // 數據通道1接收地址
#define RX_ADDR_P2 0x0C // 數據通道2接收地址
#define RX_ADDR_P3 0x0D // 數據通道3接收地址
#define RX_ADDR_P4 0x0E // 數據通道4接收地址
#define RX_ADDR_P5 0x0F // 數據通道5接收地址
#define TX_ADDR 0x10 // 發送地址寄存器
#define RX_PW_P0 0x11 // 接收數據通道0有效數據寬度(1~32字節)
#define RX_PW_P1 0x12 // 接收數據通道1有效數據寬度(1~32字節)
#define RX_PW_P2 0x13 // 接收數據通道2有效數據寬度(1~32字節)
#define RX_PW_P3 0x14 // 接收數據通道3有效數據寬度(1~32字節)
#define RX_PW_P4 0x15 // 接收數據通道4有效數據寬度(1~32字節)
#define RX_PW_P5 0x16 // 接收數據通道5有效數據寬度(1~32字節)
#define FIFO_STATUS 0x17 // FIFO狀態寄存器
/********* STATUS寄存器bit位定義 *******/
#define MAX_TX 0x10 //達到最大發送次數中斷
#define TX_OK 0x20 //TX發送完成中斷
#define RX_OK 0x40 //接收到數據中斷
/********* 24L01發送接收數據寬度定義 ***********/
#define TX_ADR_WIDTH 5 //5字節地址寬度
#define RX_ADR_WIDTH 5 //5字節地址寬度
#define TX_PLOAD_WIDTH 32 //32字節有效數據寬度
#define RX_PLOAD_WIDTH 32 //32字節有效數據寬度
const uchar TX_ADDRESS[TX_ADR_WIDTH]={0x68,0x86,0x66,0x88,0x28};
const uchar RX_ADDRESS[RX_ADR_WIDTH]={0x68,0x86,0x66,0x88,0x28};
sbit NRF_CE = P3^7;
sbit NRF_CSN = P1^4;
sbit NRF_MISO = P1^6;
sbit NRF_MOSI = P1^5;
sbit NRF_SCK = P1^7;
sbit NRF_IRQ = P3^2;
unsigned char rece_buf[32];
void SPI_Init(void)
{
SPSTAT |= 0XC0;
SPCTL = 0XD0;
}
uchar SPI_RW(uchar tr_data)
{
uchar i=0;
SPSTAT |= 0Xc0; // 清高兩位,
SPDAT=tr_data;
while(((SPSTAT&0X80)!=0X80)&&(i<20))
{
i++;
delay_ms(1);
}
return SPDAT;
}
uchar NRF24L01_Write_Reg(uchar reg,uchar value)
{
uchar status;
NRF_CSN=0; //CSN=0;
status = SPI_RW(reg); //發送寄存器地址,並讀取狀態值
SPI_RW(value);
NRF_CSN=1; //CSN=1;
return status;
}
uchar NRF24L01_Read_Reg(uchar reg)
{
uchar value;
NRF_CSN=0; //CSN=0;
SPI_RW(reg); //發送寄存器值(位置),並讀取狀態值
value = SPI_RW(NOP);
NRF_CSN=1; //CSN=1;
return value;
}
uchar NRF24L01_Read_Buf(uchar reg,uchar *pBuf,uchar len)
{
uchar status,u8_ctr;
NRF_CSN=0; //CSN=0
status=SPI_RW(reg);//發送寄存器地址,並讀取狀態值
for(u8_ctr=0;u8_ctr<len;u8_ctr++)
pBuf[u8_ctr]=SPI_RW(0XFF);//讀出數據
NRF_CSN=1; //CSN=1
return status; //返回讀到的狀態值
}
uchar NRF24L01_Write_Buf(uchar reg, uchar *pBuf, uchar len)
{
uchar status,u8_ctr;
NRF_CSN=0;
status = SPI_RW(reg);//發送寄存器值(位置),並讀取狀態值
for(u8_ctr=0; u8_ctr<len; u8_ctr++)
SPI_RW(*pBuf++); //寫入數據
NRF_CSN=1;
return status; //返回讀到的狀態值
}
uchar NRF24L01_RxPacket(uchar *rxbuf)
{
uchar state;
state=NRF24L01_Read_Reg(STATUS); //讀取狀態寄存器的值
NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中斷標志
if(state&RX_OK)//接收到數據
{
NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//讀取數據
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
return 0;
}
return 1;//沒收到任何數據
}
uchar NRF24L01_TxPacket(uchar *txbuf)
{
uchar state;
NRF_CE=0;//CE拉低,使能24L01配置
NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//寫數據到TX BUF 32個字節
NRF_CE=1;//CE置高,使能發送
while(NRF_IRQ==1);//等待發送完成
state=NRF24L01_Read_Reg(STATUS); //讀取狀態寄存器的值
NRF24L01_Write_Reg(WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中斷標志
if(state&MAX_TX)//達到最大重發次數
{
NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器
return MAX_TX;
}
if(state&TX_OK)//發送完成
{
return TX_OK;
}
return 0xff;//發送失敗
}
uchar NRF24L01_Check(void)
{
uchar check_in_buf[5]={0x11,0x22,0x33,0x44,0x55};
uchar check_out_buf[5]={0x00};
NRF_CE=0;
NRF24L01_Write_Buf(WRITE_REG+TX_ADDR, check_in_buf, 5);
NRF24L01_Read_Buf(READ_REG+TX_ADDR, check_out_buf, 5);
if((check_out_buf[0] == 0x11)&&\
(check_out_buf[1] == 0x22)&&\
(check_out_buf[2] == 0x33)&&\
(check_out_buf[3] == 0x44)&&\
(check_out_buf[4] == 0x55))return 0;
else return 1;
}
void NRF24L01_RT_Init(void)
{
NRF_CE=0;
NRF24L01_Write_Reg(WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//選擇通道0的有效數據寬度
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器
NRF24L01_Write_Buf(WRITE_REG+TX_ADDR,(uchar*)TX_ADDRESS,TX_ADR_WIDTH);//寫TX節點地址
NRF24L01_Write_Buf(WRITE_REG+RX_ADDR_P0,(uchar*)RX_ADDRESS,RX_ADR_WIDTH); //設置TX節點地址,主要為了使能ACK
NRF24L01_Write_Reg(WRITE_REG+EN_AA,0x01); //使能通道0的自動應答
NRF24L01_Write_Reg(WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址
NRF24L01_Write_Reg(WRITE_REG+SETUP_RETR,0x1a);//設置自動重發間隔時間:500us + 86us;最大自動重發次數:10次
NRF24L01_Write_Reg(WRITE_REG+RF_CH,40); //設置RF通道為125
NRF24L01_Write_Reg(WRITE_REG+RF_SETUP,0x27); //7db增益,250kbps
NRF24L01_Write_Reg(WRITE_REG+CONFIG,0x0E); //配置基本工作模式的參數;PWR_UP,EN_CRC,16BIT_CRC,發送模式,開啟所有中斷
NRF_CE=1; //CE置高
}
void main(void)
{
delay_ms(100); // 延時待系統穩定
SPI_Init(); // 初始化SPI口
while(NRF24L01_Check()); // 等待檢測到NRF24L01,程序才會向下執行
NRF24L01_RT_Init(); // 配置NRF24L01為接收模式
rece_buf[0]=1;
rece_buf[1]=0x88;
while (1)
{
delay_ms(500);
NRF24L01_TxPacket(rece_buf); // 無線發送數據
}
}
參考資料
-
百度文庫上一個比較全的資料(51 MCU的) https://wenku.baidu.com/view/45d4ba90dd88d0d233d46a41.html
-
https://blog.csdn.net/qq_38405680/article/details/80456164
1,電源必須在它的電壓符合范圍之內,不能接5V,會燒掉,某寶有1117穩壓模塊比較好。
2,供電電源波紋必須在80mv以內,就是波動不能超過0.08V,某寶有1117穩壓模塊,並聯一個100UF的點解電容和105的瓷片電容。
3,NRF的IRQ腳會壞,表現為發送端正常發送,接受端無法接受到信號,接收端IRQ電平恆高。
4,使用洞洞板時,切記杜邦線會影響NRF之間的通信,如果想要最佳的通信,用銅柱將模塊放到高的位置,並且銅柱接地,用金屬網包裹整個電路(除NRF外)並接地。
5,旁邊不能有強干擾,例如手鑽,電鑽,切割機之類的。 -
這是一個綜合的介紹 https://www.playembedded.org/blog/a-radio-frequency-transceiver-library-nrf24l01-and-chibiosrt/
-
SPI初始化和方法, 與目錄中的
NRF24L01_STM32_code
和STM32F103-Example
結合着看, 模塊部分都是正點原子的代碼
https://blog.csdn.net/weixin_45555616/article/details/110501179 -
這篇對24L01的工作機制有描述
https://www.cnblogs.com/mr-bike/p/3520141.html -
https://github.com/elmot/nrf24l01-lib/blob/master/nrf24l01/nrf24.h
-
用外部中斷處理IRQ
https://github.com/r2aiv/NRF24L01-1/blob/master/inc/nrf24l01.h
https://stackoverflow.com/questions/25932299/stm32-rising-and-falling-button-interrupt-detection -
8051操作nRF24L01 https://blog.csdn.net/fzf1996/article/details/90601375
-
STM32F4的庫文件 https://github.com/MaJerle/stm32f429/blob/master/00-STM32F429_LIBRARIES/tm_stm32f4_nrf24l01.c
-
https://stm32f4-discovery.net/2014/06/library-17-nrf24l01-stm32f4xx/
-
https://github.com/knielsen/stm32f4_wireless_bootloader/blob/master/wireless_bootloader.c
-
https://github.com/AmberHan/STM32F4_Send/blob/main/終端設備A1/HARDWARE/NRF24L01/24l01.c