欢迎光临
我们一直在努力

基于FreeRtos stm32 使用NRF24L01进行双向通讯时的一些调试心得

本片使用stm32 的硬件spi和软件io控制片选。原发送端在本篇为A代号,原接收端在本篇为B代号

本篇主要解决以下问题:

1.在FreeRtos 下两个NRF24L01进行双向握手通信测试的时A接收不到B回传来的应答数据包。

2.B进行回传数据包发送失败即状态寄存器最大TX重传中断MAX_RT置1,这时可能不是A的问题。

双向握手通信主要思路

建议在初始化之后通信双方NRF24L01 都为接收模式

A发送数据给B,若B接收到数据并返回应答,A端并且状态寄存器TX_DS(数据已发送TX FIFO中断 当数据包成功发送时该位置1,若激活自动应答,则需收到应答信号才置1) 置1,此时A转为接收模式,在转为接收模式之前最好,清空TX FIFO 和RX FIFO 并且清空所有状态标志位。B端在接收到数据包后转为发送模式,回传A端所需的数据包,回传成功状态寄存器TX_DS置1,此时可以重新回到接收模式,A端在接收到数据之后重新回到原始状态(发送模式或接收模式)都可。

关键时序问题:这个思路下就存在一个时序问题,若B端在转为发送模式之后A端因为一些任务调度或者其他原因,并没有第一时间进入接收模式,错过了B端的回传数据包,这时候就会导致通讯错乱 解决办法:通过阻塞任务让A端转为接收状态就绪 这时候B端再进行数据包回传。


spi初始化部分:

void my_spi_Init(void)
{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*CE*/
GPIO_InitTypeDef GPIO_InitStructureA;
GPIO_InitStructureA.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructureA.GPIO_Pin =GPIO_Pin_8;
GPIO_InitStructureA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructureA);
GPIO_ResetBits(GPIOA,GPIO_Pin_8);

/*clk MOSI */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽复用输出
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_13|GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*NSS*/

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
/*MISO*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_8;
SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge; //CPHA 0
SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;//CPOL 0
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(SPI2,&SPI_InitStruct);

SPI_Cmd(SPI2,ENABLE);

}

以下为部分代码实现:

包含部分调试代码如printf串口 oled显示

以下是A端(原发送端)主要代码实现

void NRF24L01_Send_Task(void * pvParameters)
{
TickType_t WaitTime;
uint8_t status;
uint8_t PS2_Instruct,i;
while(1)
{
xSemaphoreTake(PS2_DMA_TC_FLAG,portMAX_DELAY); //死等信号量
taskENTER_CRITICAL();//进入临界区
NRF24L01_TxMode();
taskEXIT_CRITICAL();//退出临界区
if(xQueueReceive(PS2_Instruct_Queue,&PS2_Instruct,0)==pdFALSE)
{
Get_PS2_Data((uint8_t)write,(uint8_t)null);
NRF24L01_Send(PS2_Data);

}else
{

Get_PS2_Data((uint8_t)read,(uint8_t)PS2_Instruct);
NRF24L01_Send(PS2_Data);
i=1;
}
Delay_us(135);
WaitTime=xTaskGetTickCount();
do
{
taskENTER_CRITICAL();//进入临界区
status=NRF24L01_get_status();
OLED_ShowNum(1,10,status,3); //状态寄存器
OLED_ShowNum(2,10,spi_nrf240l_Receive(CONFIG),3);
taskEXIT_CRITICAL();//退出临界区
if(status&0x30){break;}

}while(xTaskGetTickCount()-WaitTime<=3); //如果小于3个tick(3ms) 则为真继续等待
if (status&0x20) // 发送成功
{
taskENTER_CRITICAL();//进入临界区
spi_nrf240l_Send(STATUS,0x20);
taskEXIT_CRITICAL();//退出临界区
if(i==1)
{
i=0;
taskENTER_CRITICAL();
//NRF24L01_TX_FIFOclear();
//NRF24L01_RX_FIFOclear();
//spi_nrf240l_Send(STATUS,0x70);
NRF24L01_RxMode();
taskEXIT_CRITICAL();
vTaskDelay(5);
}
}
else if(status&0x10) // 发送失败
{
taskENTER_CRITICAL();//进入临界区
spi_nrf240l_Send(STATUS, 0x10);
NRF24L01_TX_FIFOclear(); // 清空 TX FIFO,否则锁死
OLED_ShowString(4,1,"send no!");
taskEXIT_CRITICAL();//退出临界区
}else{
// 超时或未知状态,同时清 TX_DS 和 MAX_RT,并冲刷 FIFO
taskENTER_CRITICAL();
spi_nrf240l_Send(STATUS, 0x70); // 写 1 清除 bit4 和 bit5
NRF24L01_TX_FIFOclear();
OLED_ShowString(4,1,"timer out!");
taskEXIT_CRITICAL();
}
}
}
void NRF24L01_Receive_Task( void * pvParameters )
{

while(1)
{
xSemaphoreTake(PS2_ITReceive_FLAG,portMAX_DELAY); //死等信号量
NRF24L01_CE_L();//ce低电平 暂停接收
taskENTER_CRITICAL();//进入临界区
spi_nrf240l_Send(STATUS,0x40); //写1 清除
taskEXIT_CRITICAL();//退出临界区
NRF24L01_Receive(10);
if(rx_data[0]==0x01)
{
taskENTER_CRITICAL();
OLED_ShowString(4,1,"Receivet yes!");
taskEXIT_CRITICAL();
}else
{
taskENTER_CRITICAL();
OLED_ShowString(4,1,"Receivet no");
taskEXIT_CRITICAL();
}
NRF24L01_CE_H();//ce低电平 暂停接收
}
}

以下是B端(原接收端)主要代码实现;

void NRF24L01_Receive_Task(void *pvParameters )
{

uint8_t i;
while(1)
{
xSemaphoreTake(PS2_ITReceive_FLAG,portMAX_DELAY); //死等信号量
NRF24L01_CE_L();//ce低电平 暂停接收
taskENTER_CRITICAL();//进入临界区
spi_nrf240l_Send(STATUS,0x40); //写1 清除
taskEXIT_CRITICAL();//退出临界区
NRF24L01_Receive(10);
/*判断数据位*/
if(rx_data[8]==0x01){i=1;}
if(rx_data[9]==33)
{
rx_data[9]=0;
gpio_trun(GPIOA,GPIO_Pin_0);
}
/*数据合并*/
Receive_PS2_Data();//数据合并处理 16数据存入PS2_RX_Data[4]
if(i==1)
{
i=0;
vTaskDelay(10);
xSemaphoreGive(send);
}else{NRF24L01_CE_H();}
}
}

void NRF24L01_Send_Task(void *pvParameters )
{
TickType_t WaitTime;

uint8_t status;
uint8_t buff[10]={1,2,3,4,5,6,7,8,9,10};
while(1)
{
xSemaphoreTake(send,portMAX_DELAY); //死等信号量
//gpio_trun(GPIOA,GPIO_Pin_0);
taskENTER_CRITICAL();//进入临界区
NRF24L01_TxMode();
taskEXIT_CRITICAL();//退出临界区
NRF24L01_Send(buff);
Delay_us(135);
WaitTime=xTaskGetTickCount();
do
{
taskENTER_CRITICAL();//进入临界区
status=NRF24L01_get_status();
taskEXIT_CRITICAL();//退出临界区
if(status&0x30){break;}

}while(xTaskGetTickCount()-WaitTime<=3); //如果小于3个tick(3ms) 则为真继续等待
if (status&0x20) // 发送成功
{
taskENTER_CRITICAL();//进入临界区
spi_nrf240l_Send(STATUS,0x20);
taskEXIT_CRITICAL();//退出临界区
printf("send yes! \\n");
}
else if(status&0x10) // 发送失败
{
taskENTER_CRITICAL();//进入临界区
spi_nrf240l_Send(STATUS, 0x10);
NRF24L01_TX_FIFOclear(); // 清空 TX FIFO,否则锁死
printf("send no! \\n");
taskEXIT_CRITICAL();//退出临界区
}else{
// 超时或未知状态,同时清 TX_DS 和 MAX_RT,并冲刷 FIFO
taskENTER_CRITICAL();
spi_nrf240l_Send(STATUS, 0x70); // 写 1 清除 bit4 和 bit5
NRF24L01_TX_FIFOclear();
printf("timer out! \\n");
taskEXIT_CRITICAL();
}
taskENTER_CRITICAL();
NRF24L01_TX_FIFOclear();
NRF24L01_RX_FIFOclear();
spi_nrf240l_Send(STATUS, 0x70);
NRF24L01_RxMode();
taskEXIT_CRITICAL();
}
}

经过测试发现,在B端接收成功后经过vTaskDelay(10); 延时后进入发送模式确保A端完全就绪,A端发送成功之后经过vTaskDelay(5);确保在A发送的时候处于接收模式。AB端的延时时长具体需根据自己的代码量而定。

另外优先级,原发送端的接收任务优先级要高于发送任务,原接收端的发送任务优先级要高于接收任务。确保可以抢占有限执行如下仅供参考(为B接收端):

#define uxStackDepth1128 //任务大小
#define uxPriority14 //优先级
TaskHandle_t NRF24L01_Receive_Task_Handlet;
void NRF24L01_Receive_Task(void *pvParameters );

#define uxStackDepth4128 //任务大小
#define uxPriority43 //优先级
TaskHandle_t NRF24L01_Send_Task_Handlet;
void NRF24L01_Send_Task(void *pvParameters );

赞(0)
未经允许不得转载:171主机测评 » 基于FreeRtos stm32 使用NRF24L01进行双向通讯时的一些调试心得
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址