NRF24L01/SI24R1广播通讯

[TOC]

需求

实现NRF24L01/SI24R1广播通讯

image

实现

一、搭建工程

使用STM32CUBEMX创建工程。

image

使用到的外设有:

USART1----DEBUG调试

SPI1----无线模块连接

USB----上位机通讯

二、修改代码

要使用广播通讯,就不能使用ACK模式,需使用NO ACK模式,注意修改寄存器。

image
image

且使用中断接收无线数据,注意中断数据处理

image

最终无线部分的代码如下

static uint8_t SPIx_ReadWriteByte(SPI_HandleTypeDef *hspi, uint8_t byte) {
    uint8_t d_read, d_send = byte;
    if (HAL_SPI_TransmitReceive(hspi, &d_send, &d_read, 1, 0xFF) != HAL_OK) {
        d_read = 0xFF;
    }
    return d_read;
}

static uint8_t SI24R1_Write_Reg(uint8_t reg, uint8_t value) {
    uint8_t status;
    SI24R1_SPI_CS_ENABLE();
    status = SPIx_ReadWriteByte(&hspi1, reg);
    SPIx_ReadWriteByte(&hspi1, value);
    SI24R1_SPI_CS_DISABLE();
    return (status);
}

static uint8_t SI24R1_Read_Reg(uint8_t reg) {
    uint8_t reg_val;
    SI24R1_SPI_CS_ENABLE();
    SPIx_ReadWriteByte(&hspi1, reg);
    reg_val = SPIx_ReadWriteByte(&hspi1, 0XFF);
    SI24R1_SPI_CS_DISABLE();
    return (reg_val);
}

// 在指定位置读出指定长度的数据
static uint8_t SI24R1_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len) {
    uint8_t status, uint8_t_ctr;

    SI24R1_SPI_CS_ENABLE();
    status = SPIx_ReadWriteByte(&hspi1, reg);
    for (uint8_t_ctr = 0; uint8_t_ctr < len; uint8_t_ctr++) {
        pBuf[uint8_t_ctr] = SPIx_ReadWriteByte(&hspi1, 0XFF);
    }
    SI24R1_SPI_CS_DISABLE();
    return status;
}

// 在指定位置写指定长度的数据
static uint8_t SI24R1_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len) {
    uint8_t status, uint8_t_ctr;
    SI24R1_SPI_CS_ENABLE();
    status = SPIx_ReadWriteByte(&hspi1, reg);
    for (uint8_t_ctr = 0; uint8_t_ctr < len; uint8_t_ctr++) {
        SPIx_ReadWriteByte(&hspi1, *pBuf++);
    }
    SI24R1_SPI_CS_DISABLE();
    return status;
}

void SI24R1_Config(void) {
    SI24R1_SPI_CS_ENABLE();
    SI24R1_CE_LOW();
    SI24R1_Write_Reg(NRF_WRITE_REG + SETUP_RETR, 0);
    SI24R1_Write_Reg(NRF_WRITE_REG + FEATURE, 0x01);
    SI24R1_Write_Reg(NRF_WRITE_REG + EN_AA, 0);
    SI24R1_Write_Reg(NRF_WRITE_REG + EN_RXADDR, 0x01);
    SI24R1_Write_Reg(NRF_WRITE_REG + RF_CH, 40);
    SI24R1_Write_Reg(NRF_WRITE_REG + RF_SETUP, 0x0f);
    SI24R1_Write_Reg(NRF_WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH);

    SI24R1_Write_Buf(NRF_WRITE_REG + RX_ADDR_P0, (uint8_t *)RX_ADDRESS,
                     RX_ADR_WIDTH);
    SI24R1_Write_Buf(NRF_WRITE_REG + TX_ADDR, (uint8_t *)TX_ADDRESS,
                     TX_ADR_WIDTH);

    SI24R1_Write_Reg(STATUS, 0x70);
    SI24R1_Write_Reg(FLUSH_TX, 0xff);
    SI24R1_Write_Reg(FLUSH_RX, 0xff);

    SI24R1_CE_HIGH();
    SI24R1_SPI_CS_DISABLE();
}

// 检测24L01是否存在
uint8_t SI24R1_Check(void) {
    uint8_t buf[5] = {0XA5, 0XA5, 0XA5, 0XA5, 0XA5};
    uint8_t i;
    SI24R1_Write_Buf(NRF_WRITE_REG + TX_ADDR, buf, 5);
    SI24R1_Read_Buf(TX_ADDR, buf, 5);
    for (i = 0; i < 5; i++)
        if (buf[i] != 0XA5) break;
    if (i != 5) return 1;  //检测24L01错误
    return 0;              //检测到24L01
}

void SI24R1_TxPacket(uint8_t *txbuf) {
    SI24R1_CE_LOW();
    SI24R1_Write_Buf(WR_TX_PLOAD_NOACK, txbuf, TX_PLOAD_WIDTH);
    SI24R1_CE_HIGH();
    return;
}

void SI24R1_RX_Mode(void) {
    SI24R1_CE_LOW();
    SI24R1_Write_Reg(NRF_WRITE_REG + CONFIG, 0x0F);
    SI24R1_CE_HIGH();
}

void SI24R1_TX_Mode(void) {
    SI24R1_CE_LOW();
    SI24R1_Write_Reg(NRF_WRITE_REG + CONFIG, 0x0E);
    SI24R1_CE_HIGH();
}

static uint8_t NRF_RX_DATA[RX_PLOAD_WIDTH + 1];
void SI24R1_RX_IQR(void) {
    uint8_t status = 0;

    SI24R1_CE_LOW();
    status = SI24R1_Read_Reg(STATUS);

    if (status & RX_OK) {
        SI24R1_Read_Buf(RD_RX_PLOAD, NRF_RX_DATA, RX_PLOAD_WIDTH);
        NRF_RX_DATA[RX_PLOAD_WIDTH] = SI24R1_Read_Reg(NRF_WRITE_REG + RSSI);

        extern MessageList si24r1RecvDataMessageList;

        AddMessageListMsgData(&si24r1RecvDataMessageList, NRF_RX_DATA,
                              RX_PLOAD_WIDTH + 1);
    }
    if (status & 0xF0) {
        SI24R1_Write_Reg(NRF_WRITE_REG + STATUS, status);
    }

    SI24R1_Write_Reg(FLUSH_RX, 0xff);
    SI24R1_Write_Reg(FLUSH_TX, 0xff);

    SI24R1_CE_HIGH();
}
    

main.c代码如下

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_IWDG_Init();
    MX_SPI1_Init();
    MX_TIM4_Init();
    MX_USART1_UART_Init();
    MX_USB_DEVICE_Init();
    while (SI24R1_Check()) {
        HAL_Delay(1000);
        printf("Si24r1 err !!\r\n");
    }
    SI24R1_Config();
    SI24R1_RX_Mode();
    printf("Si24r1 init ok!\r\n");

    CreatMessageList(&usbRecvDataMessageList);
    CreatMessageList(&si24r1RecvDataMessageList);

    Message msg;
    uint8_t recvDataBuf[32];
    uint8_t len;
    uint8_t whileCnt = 0;

    while (1) {
        if (GetMessageList(&usbRecvDataMessageList, &msg) != 0) {
            len = msg.messageSize;
            memset(recvDataBuf, 0, sizeof(recvDataBuf));
            len = len < 32 ? len : 32;
            memcpy(recvDataBuf, msg.message, len);
            SI24R1_TX_Mode();
            HAL_Delay(1);
            SI24R1_TxPacket(recvDataBuf);
            HAL_Delay(1);
            SI24R1_RX_Mode();
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
            free(msg.message);
        }
        if (GetMessageList(&si24r1RecvDataMessageList, &msg) != 0) {
            CDC_Transmit_FS(msg.message, msg.messageSize);
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
            free(msg.message);
        }
        if (whileCnt++ >= 100) {
            HAL_IWDG_Refresh(&hiwdg);
            whileCnt = 0;
        }
        HAL_Delay(1);
    }
}

三、上位机

STM32 这端会接收所有数据,并上传到上位机,具体业务可在上位机这端处理。

image

上位机是用C#写的,部分代码如下:

int portDataSend(int localAddr, int destAddr, int len, byte[] buf)
        {
            if (!checkAddr(localAddr)) {
                return -1;
            }
            if (!checkAddr(destAddr))
            {
                return -2;
            }
            if (!checkLen(len)) {
                return -3;
            }

            byte[] serialSendBuf = new byte[64];

            serialSendBuf[0] = (byte)((localAddr & 0xFF00) >> 8);
            serialSendBuf[1] = (byte)((localAddr & 0x00FF));
            serialSendBuf[2] = (byte)((destAddr & 0xFF00) >> 8);
            serialSendBuf[3] = (byte)((destAddr & 0x00FF));
            serialSendBuf[4] = (byte)((len & 0x00FF));

            for (int i = 0; i < len; i++) {
                serialSendBuf[5 + i] = buf[i];
            }
            sptMain.Write(serialSendBuf,0, 64);

            return 0;
        }
private void portDataRecvived(Object sender, SerialDataReceivedEventArgs e)
        {
            int n = sptMain.BytesToRead;
            byte[] data = new byte[n];
            sptMain.Read(data, 0, n);

            if (n < 32) {
                return;
            }

            if (data[4] > DATA_MAX_LEN) {
                return;
            }

            int srcAddr, desAddr, len;
            
            srcAddr = (data[0] << 8) + data[1];
            desAddr = (data[2] << 8) + data[3];
            len = data[4];

            string recvString = System.Text.Encoding.Default.GetString(data, 5, len);

            if (!chbData.Checked) {
                if ((desAddr != 0) && (desAddr != 0xFFFF) && (desAddr != localAddr)) {
                    return;
                }
            }

            
            string nowTime = DateTime.Now.ToString("HH:mm:ss", DateTimeFormatInfo.InvariantInfo);

            if (n == 33) {
                string rssiString = "bad";
                if (data[32] == 0) {
                    rssiString = "good";
                }
                txbRecvData.AppendText(nowTime + " -> Src:0x" + srcAddr.ToString("X") + ", Des:0x" + desAddr.ToString("X") + ", Data:" + recvString + ", Rssi:" + rssiString + "\r\n");
            }
            else if (n == 32){
                txbRecvData.AppendText(nowTime + " -> Src:0x" + srcAddr.ToString("X") + ", Des:0x" + desAddr.ToString("X") + ", Data:" + recvString + "\r\n");
            }


            
        }
private void btnSendData_Click(object sender, EventArgs e)
        {
            
            if (!sptMain.IsOpen) {
                MessageBox.Show("请先打开串口", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }

            if (txbSendData.Text == "") {
                MessageBox.Show("输入数据为空!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
            else {
                if (txbSendData.Text.Length > DATA_MAX_LEN) {
                    MessageBox.Show("数据过长\r\n最大27Bytes", "错误", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
                
            }

            portDataSend(localAddr, destAddr, txbSendData.Text.Length, System.Text.Encoding.Default.GetBytes(txbSendData.Text));
        }

        private void txbLocal_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar != '\b')
            {
                if (!(((e.KeyChar >= '0') && (e.KeyChar <= '9')) ||
                    ((e.KeyChar >= 'a') && (e.KeyChar <= 'f')) ||
                ((e.KeyChar >= 'A') && (e.KeyChar <= 'F'))))
                {
                    e.Handled = true;
                }
            }
        }

        private void txbDest_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar != '\b')
            {
                if (!(((e.KeyChar >= '0') && (e.KeyChar <= '9')) ||
                    ((e.KeyChar >= 'a') && (e.KeyChar <= 'f')) ||
                ((e.KeyChar >= 'A') && (e.KeyChar <= 'F'))))
                {
                    e.Handled = true;
                }
            }
        }

STM32用的USB使用的CDC模拟串口,底层USB每次发送的包为64Bytes;

总结

使用这种NO ACK模式,就类似于广播,不在局限于芯片的6V1这种模式,可以各种自定义。

也就类似于 UDP之于TCP吧。

还有啥不明白的可以留言,我修改下。

你可能感兴趣的:(NRF24L01/SI24R1广播通讯)