[TOC]
需求
实现NRF24L01/SI24R1广播通讯
实现
一、搭建工程
使用STM32CUBEMX创建工程。
使用到的外设有:
USART1----DEBUG调试
SPI1----无线模块连接
USB----上位机通讯
二、修改代码
要使用广播通讯,就不能使用ACK模式,需使用NO ACK模式,注意修改寄存器。
且使用中断接收无线数据,注意中断数据处理
最终无线部分的代码如下
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 这端会接收所有数据,并上传到上位机,具体业务可在上位机这端处理。
上位机是用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吧。
还有啥不明白的可以留言,我修改下。