无线连接任务
void radiolinkTask(void* param)
{
u8 rx_len;
atkp_t rx_p;
while(1)
{
nrf_txPacket((u8*)&tx_p, tx_p.dataLen+2);
xSemaphoreTake(nrfIT, 1000);
nrfEvent_e status = nrf_checkEventandRxPacket((u8*)&rx_p, &rx_len);
if(status == RX_DR)
{
LED_BLUE = 0;
LED_RED = 1;
statusCount = 0;
connectStatus = true;
if(rx_p.dataLen <= ATKP_MAX_DATA_SIZE)
{
xQueueSend(rxQueue, &rx_p, portMAX_DELAY);
}
if(xQueueReceive(txQueue, &tx_p, 0) == pdFALSE)
{
tx_p.msgID = DOWN_RADIO;
tx_p.dataLen = 1;
tx_p.data[0] = D_RADIO_HEARTBEAT;
}
}
else if(status == MAX_RT)
{
LED_BLUE = 1;
LED_RED = 0;
failRxCount++;
if(++statusCount > 10)
{
statusCount = 0;
connectStatus = false;
}
}
if(connectStatus==true && xTaskGetTickCount()>=failRxcountTime+1000)
{
failRxcountTime = xTaskGetTickCount();
failReceiveNum = failRxCount;
failRxCount = 0;
}
}
}
调用了几个函数:函数内部实现看我的另一个博客NRF几个函数详解
1,调用
nrf_txPacket((u8*)&tx_p, tx_p.dataLen+2);
tx_p为结构体变量,u8 *tx_buf 为待发送数据首地址
做len次循环,每次发送一个字节,MsfID(1字节)+DataLen(1字节)=2字节,所以将tx_p.dataLen+2传给len
void nrf_txPacket(u8 *tx_buf,u8 len)
{
NRF_CE_L();
writeBuf(CMD_W_TX_PAYLOAD,tx_buf,len);
NRF_CE_H();
}
通过该函数发送ATKP数据给四轴,而tx_p为结构体变量,其成员恰好都是u8,该函数形参为u8类型的指针,故需转换为(u8*)即(u8*)&tx_p。
1.1nrf_txPacket函数里面调用
writeBuf(CMD_W_TX_PAYLOAD,tx_buf,len);
CMD_W_TX_PAYLOAD 写待发数据指令,status=SPI_RWByte(NRF_SPI,cmd)获取状态,(stm32告诉NRF,要发数据给你了,准备好了没?然后返回是否准备好
static u8 writeBuf(u8 cmd,u8 *pBuf,u8 len)
{
u8 status,i;
SPI2_CSN_L();
status=SPI_RWByte(NRF_SPI,cmd);
for(i=0;i<len;i++)
SPI_RWByte(NRF_SPI,*pBuf++);
SPI2_CSN_H();
return status;
}
1.1.1writeBuf函数里面又调用
static u8 SPI_RWByte(SPI_TypeDef* SPIx , u8 TxData)
发送数据,同时返回最近收到的数据
static u8 SPI_RWByte(SPI_TypeDef* SPIx , u8 TxData)
{
SPI_I2S_SendData(SPIx, TxData);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPIx);
}
该函数为最终的“干活者”,它调用的是官方SPI库函数SPI_I2S_SendData(SPIx, TxData)和SPI_I2S_ReceiveData(SPIx)。
其通过SPI协议,发送一个个字节,u8 TxData可以是NRF SPI寄存器指令,也可以是发送给NRF的数据。
该函数具有读写功能,当发送命令给NRF时,可通过其return功能返回数据,起到了读的作用,当发送数据时,一次发送一个字节(u8),利用循环可发送多个数据(字节)。
在此时它功能实现的过程是:拉低片选引脚SPI2_CSN_L()也就是PAout(4) = 0 ,NRF被选中,然后发送一个待写命令(准备向NRF发送数据),然后循环len次将数据发送到NRF发送缓冲区,通过2.4G发送出去,最后失能,关闭SPI通信。
NRF SPI寄存器指令传给形参cmd,可为以下宏定义
小结:
1)nrf_txPacket函数的作用:把发送队列的消息(atkp_t 结构体)通过writeBuf将tx_p.dataLen+2长度的数据写入TX_FIFO,发送出去
2)xSemaphoreTake(nrfIT, 1000);//take二值信号量
3)nrf_checkEventandRxPacket((u8*)&rx_p, &rx_len);
2,调用xSemaphoreTake(nrfIT, 1000);//take二值信号量
说明radiolinkTask任务要等到这个二值信号量才能运行
nrfIT二值信号量在哪创建的呢?
同时该函数也创建了发送队列txQueue,接收队列rxQueue。
nrfIT二值信号量在哪释放(Give)的呢?
当外部中断EXTI_Line13发生,调用该函数释放二值信号量。
调用线配置初始化函数,初始化时,将遥控器设置为发送模式
流程图如下:
下降沿触发中断,这个和外部中断怎么联系上了????
答:1)原来是这个NRF_IRQ连接的是PC13,NRF_IRQ平时是高电平,三种情况能将其拉低,
1:达到最大重发次数;
2:有数据发送过来了(Rx FIFO 收到数据);
3,Tx FIFO 发完并且收到ACK(使能ACK 情况下)。
NRF_IRQ由高到低,就产生了下降沿,此时PC13设置为外部中断模式,GPIO引脚配置为上拉输入。
这3句联系起来就是:
1,发送一个包nrf_txPacket((u8*)&tx_p, tx_p.dataLen+2);,此时可能发送两种情况
1)达到最大次数,发送失败,此时 MAX_RT为1,IRQ拉低
2)发送完成,收到应答,此时Rx FIFO 收到数据,RX_DR,TX_DS为1,IRQ拉低
2,等IRQ拉低,触发外部中断13(PC13引脚)产生中断,中断函数中释放(Give二值信号量),然后xSemaphoreTake(nrfIT, 1000);take到了信号量,下方代码才继续执行
3,如果是收到了应答包,包存到rx_p这个局部变量,包长度存到rx_len,返回RX_DR,如果重发达到最大次数(设置为3次)都没成功,则返回MAX_RT
4,如果是接收了应答包(返回RX_DR),则点亮led指示灯,如果包没问题就存到接收队列raidolink.c的rxQueue。从txQueue队列提取包存到全局变量tx_p,准备无线发送出去,队列为空,则会取出失败,这说明没有“有效数据”要发送,但通信得保持着,所以发送心跳包,让对方知道自己链路没问题,只是我没有“有效数据”发给你。就将待发送的全局变量(包)改装成心跳包,下次循环就发心跳包。
如果是达到最大发送次数还无应答则代表通讯失败,点亮led指示灯,记录一次失败failRxCount++;,连续10次重启发送,每次启动有3次自动重发,间隔15000us,还是没成功则说明通讯失败
为什么能自动重启?
因为在前面调用这个函数时,软件清除TX FIFO中的数据和REG_STATUS里的MAX_RT标志位。
每次启动TX FIFO都被清空,说明第二次启动后发的不是第一次启动的值,而是心跳包。
最后1000ms统计一次收发失败次数,
其他函数
void radiolinkInit(void)
{
if (isInit) return;
radioInit();
txQueue = xQueueCreate(RADIOLINK_TX_QUEUE_SIZE, sizeof(atkp_t));
ASSERT(txQueue);
rxQueue = xQueueCreate(RADIOLINK_RX_QUEUE_SIZE, sizeof(atkp_t));
ASSERT(rxQueue);
nrfIT = xSemaphoreCreateBinary();
tx_p.msgID = DOWN_RADIO;
tx_p.dataLen = 1;
tx_p.data[0] = D_RADIO_HEARTBEAT;
connectStatus = false;
isInit = true;
}
调用radioInit();
static void radioInit(void)
{
uint64_t addr = (uint64_t)configParam.radio.addressHigh<<32 | configParam.radio.addressLow;
if(nrf_check() == SUCCESS)
{
nrfInit(PTX_MODE);
nrf_setIterruptCallback(nrf_interruptCallback);
的interruptCb函数指针指向nrf_interruptCallback函数,然后有外部中断了就触发Give二值信号量
}
else
{
oledInit();
oled_showString(0,0,(u8*)"NRF24L01 CHECK FAIL !",6,12);
oled_refreshGram();
while(1);
}
nrf_setAddress(addr);
nrf_setChannel(configParam.radio.channel);
nrf_setDataRate(configParam.radio.dataRate);
}
radioInit(void)函数主要两个作用
1)检查模块是否正常,正常就设置为发送模式
2)调用“设置nrf回调函数”,也就是设置中断函数里调用哪个函数来响应中断。
过程:将函数nrf_interruptCallback(void)传给函数指针,这样当PC13引脚发生NRF-IRQ中断,则中断函数就会调用nrf_interruptCallback(void)
u16 radioinkFailRxcount(void)
{
return failReceiveNum;
}
bool radioinkConnectStatus(void)
{
return connectStatus;
}
void radiolinkEnable(FunctionalState state)
{
if(state == ENABLE)
vTaskResume(radiolinkTaskHandle);
else
vTaskSuspend(radiolinkTaskHandle);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)