主函数调用的无线通信初始化函数,最终肯定要配置NRF24L01的,无线通信功能的配置总共设置了两个.C文件:
radiolink.c
24l01.c
下边是从主函数调用到引脚配置的过程:
关于无线模块NRF24L01的配置:
radiolinkInit(); /*无线通信初始化*///在文件radiolink.c中
⬇
radioInit(); //也在文件radiolink.c中
⬇
/* 初始化NRF24L01配置 */
/* model: PTX_MODE、PRX_MODE */
void nrfInit(enum nrfMode model);//在文件24l01.c中
⬇
/* NRF初始化,使用STM32的SPI1 */
static void NRF_Init(void);//也在文件24l01.c中,真正配置了引脚初始化了SPI
相信这样设置是自有用处的。
先来看看底层文件24L01里的东西吧,下边的是头文件里的:
/* 初始化NRF24L01配置 */
void nrfInit(enum nrfMode model);
/* 检查nrf是否通讯正常 */
ErrorStatus nrf_check(void);
/* 发送数据包(PTX模式) */
void nrf_txPacket(u8 *tx_buf,u8 len);
/* 发送NO_ACK数据包(PTX模式) */
void nrf_txPacketNoACK(u8 *tx_buf,u8 len);
/* 发送ACK数据包,设置0通道(PRX模式) */
void nrf_txPacket_AP(u8 *tx_buf,u8 len);
/* 发送NO_ACK数据包(PTX模式) */
void nrf_sendPacketNoACK(u8 *sendBuf,u8 len);
/* 发送数据包,并等待接收ACK (PTX模式) */
u8 nrf_sendPacketWaitACK(u8 *sendBuf ,u8 len ,
u8 *ackBuf ,u8 *acklen);
/* 查询事件并接收数据包 */
nrfEvent_e nrf_checkEventandRxPacket(u8 *ackBuf, u8 *acklen);
/*设置nrf中断回调函数*/
void nrf_setIterruptCallback(void(*cb)(void));
/**************************NRF24L01+配置函数***********************************/
void nrf_setAddress(uint64_t address);
void nrf_setChannel(u8 channel);
void nrf_setDataRate(enum nrfRate dataRate);
void nrf_setPower(enum nrfPower power);
void nrf_setArd(void);
void nrf_setArc(u8 arc);
u8 nrf_getRpd(void);
u8 nrf_getTxRetry(void);
void nrf_txPacket(u8 *tx_buf,u8 len);
u8 nrf_rxPacket(u8 *rx_buf);
#endif
见笑了,又长见识了:
静态函数https://blog.csdn.net/zhangqianqian57/article/details/117588572我说咋没看见定义初始化函数呢……
这么一看,.c文件里边还有不少静态函数呢,这些应该就是最基本的对NRF24L01芯片的驱动程序了,就是对命令、数据的读和写。stm32f103和NRF24L01之间使用SPI方式通信,从最底层的引脚配置开始:
static void NRF_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* 配置SPI1的SCK(PA5),MISO(PA6),MOSI(PA7)引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置NRF的CE(PA3),CSN(PA4)引脚 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 配置NRF的IRQ引脚(PC13) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* 配置外IRQ外部中断 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13);
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_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; /* 设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 */
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; /* 设置SPI工作模式:设置为主SPI */
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* 设置SPI的数据大小:SPI发送接收8位帧结构 */
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; /* 选择了串行时钟的稳态:时钟悬空低 */
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; /* 数据捕获于第一个时钟沿 */
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /* NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 */
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; /*定义波特率预分频的值:波特率预分频值为4 */
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; /* 指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 */
SPI_InitStructure.SPI_CRCPolynomial = 7; /* CRC值计算的多项式 */
SPI_Init(SPI1, &SPI_InitStructure); /* 根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 */
SPI_Cmd(SPI1, ENABLE); /*使能SPI外设*/
SPI2_CSN_H();
NRF_CE_L();
}
这里不明白的是 ,这里的SPI使用了SPI1,倒数第二行 SPI2_CSN_H(); 为什么要命名成SPI2,为什么不随着下图的引脚复用定义来命名?
至于那些读写,全部是根据NRF24L01的芯片读写时序来设置的:
不重要,这些东西都是固定的,抄。
那么,头文件中定义的哪些函数是干什么的?
首先看第一个函数,void nrfInit(enum nrfMode model); 根据定义,其并不是对NRF_Init()简单的封装,而是在调用了引脚初始化函数后又对通信参数进行了配置。有
void nrfInit(enum nrfMode model)
{
NRF_Init();
nrf_setAddress(nrfAddress);
nrf_setChannel(DEFAULT_CHANNEL);
nrf_setDataRate(DEFAULT_DATARATE);
nrf_setPower(DEFAULT_POWR);
nrf_setArd();
nrf_setArc(3);
if(model==PRX_MODE)
{
writeReg(REG_CONFIG, 0x0f); /* IRQ收发完成中断开启,16位CRC,PRX */
writeReg(REG_DYNPD,0x01); /* 使能RX_P0动态长度PLAYLOAD */
writeReg(REG_FEATURE,0x06); /* 使能动态长度PLAYLOAD、发送ACK PLAYLOAD */
writeReg(REG_EN_AA,0x01); /* 使能通道0的自动应答 */
writeReg(CMD_FLUSH_TX,0xff); /* 冲洗TX_FIFO */
writeReg(CMD_FLUSH_RX,0xff);
}
else
{
writeReg(REG_CONFIG, 0x0e); /* IRQ收发完成中断开启,16位CRC,PTX */
writeReg(REG_DYNPD,0x01); /* 使能RX_P0动态长度PLAYLOAD */
writeReg(REG_FEATURE,0x07); /* 使能动态长度、ACK PLAYLOAD发送、W_TX_PAYLOAD_NOACK */
writeReg(CMD_FLUSH_TX,0xff); /* 冲洗TX_FIFO */
writeReg(CMD_FLUSH_RX,0xff);
}
}
检查通讯是否正常,因为这个函数里有个最基本的初始化函数。若是硬件连接正常检查一遍就算初始化了,不过,这个仅仅是检查MCU与24l01是否通讯正常 ,置于两个设备能够正常通讯那是上述初始化函数还有其他模式软件配置的事情。
/* 检查MCU与24l01是否通讯正常 */
/* 方法:写入读出地址是否一致 */
ErrorStatus nrf_check(void)
{
uint64_t addr = 0;
NRF_Init();
writeBuf(CMD_W_REG |REG_TX_ADDR,(u8*)&nrfAddress,5);
readBuf(CMD_R_REG|REG_TX_ADDR,(u8*)&addr,5);
if(nrfAddress==addr)
return SUCCESS;
else
return ERROR;
}
接着往下边几个函数很明显,是让NRF24L01执行常用的通讯动作的,比如发个应答啥的。
/* 发送数据包(PTX模式) */
void nrf_txPacket(u8 *tx_buf,u8 len)
{
NRF_CE_L();
writeBuf(CMD_W_TX_PAYLOAD,tx_buf,len);
NRF_CE_H();
}
/* 发送NO_ACK数据包(PTX模式) */
void nrf_txPacketNoACK(u8 *tx_buf,u8 len)
{
NRF_CE_L();
writeBuf(CMD_W_TX_PAYLOAD_NO_ACK,tx_buf,len);
NRF_CE_H();
}
/* 发送ACK数据包,设置0通道(PRX模式) */
void nrf_txPacket_AP(u8 *tx_buf,u8 len)
{
NRF_CE_L();
writeBuf(CMD_W_ACK_PAYLOAD(0),tx_buf,len);
NRF_CE_H();
}
/* 发送NO_ACK数据包(PTX模式) */
void nrf_sendPacketNoACK(u8 *sendBuf,u8 len)
{
while((readReg(REG_STATUS)&0x01)!=0); /* 等待TX_FIFO不为full */
nrf_txPacketNoACK(sendBuf,len); /* 发送NO_ACK数据包 */
}
/* 查询事件并接收数据包 */
nrfEvent_e nrf_checkEventandRxPacket(u8 *ackBuf, u8 *acklen)
{
nrfEvent_e nrfEvent = IDLE;
*acklen = 0;
u8 status = readReg(REG_STATUS);/*读事件标志寄存器*/
if(status&BIT_MAX_RT)/*重发失败*/
{
writeReg(CMD_FLUSH_TX,0xff);
nrfEvent = MAX_RT;
}
else if(status&BIT_RX_DR)/*接收数据到RX_FIFO*/
{
*acklen = nrf_rxPacket(ackBuf);
nrfEvent = RX_DR;
}
else if(status&BIT_TX_DS)/*发送数据至TX_FIFO成功*/
{
nrfEvent = TX_DS;
}
writeReg(REG_STATUS,0x70);/*清除标志*/
u8 status1 = readReg(REG_STATUS);/*读事件标志寄存器*/
status1 = status1;
return nrfEvent;
}
/* 发送数据包,并等待接收ACK(PTX模式) */
/* 返回值:1成功、0失败*/
u8 nrf_sendPacketWaitACK(u8 *sendBuf, u8 len, u8 *ackBuf, u8 *acklen)
{
if(len==0) return 0;
nrf_txPacket(sendBuf,len);
while((readReg(REG_STATUS)&0x70) == 0);/* 等待事件 */
nrfEvent_e nrfEvent = nrf_checkEventandRxPacket(ackBuf, acklen);
if(nrfEvent == MAX_RT)
return 0;
return 1;
}
/*设置nrf中断回调函数*/
void nrf_setIterruptCallback(void(*cb)(void))
{
interruptCb = cb;
}
而且这个东西免不了被其他函数程序调用,因为根据实际情况让NRF24L01干什么就得调用这些,所以说肯定不能定义成static。
这些就是 发射接收地址配置、频率通道配置、传输速率配置、发射功率配置、设置重发时间间隔,根据速率及收发字节大小设置、设置重发次数等。这些东西都需要在初始化的时候配置好,就像在初始化LCD屏幕时,配置完引脚后要写入一大堆命令一样。
/**************************NRF24L01+配置函数***********************************/
/* 设置发射接收地址,这里收发地址一致 */
void nrf_setAddress(uint64_t address)
{
writeBuf(CMD_W_REG |REG_RX_ADDR_P0,(u8*)&address,5);//接收使用P0节点
writeBuf(CMD_W_REG |REG_TX_ADDR,(u8*)&address,5);
}
/* 设置频率通道,channel:0~125 */
void nrf_setChannel(u8 channel)
{
if(channel<=125)
writeReg(REG_RF_CH, channel);
}
/* 设置传输速率,dr:0->250KHz、1->1MHz、2->2MHz。 */
void nrf_setDataRate(enum nrfRate dataRate)
{
u8 reg_rf = readReg(REG_RF_SETUP);
reg_rf &= ~((1<<5)|(1<<3));/* 清除原设速率 */
switch(dataRate)
{
case DATA_RATE_250K:
reg_rf |= 0x20;
break;
case DATA_RATE_1M:
reg_rf |= 0x00;
break;
case DATA_RATE_2M:
reg_rf |= 0x08;
break;
}
writeReg(REG_RF_SETUP,reg_rf);
}
/* 设置发射功率,power: 0->-18dB 1->-12dB 2->-6dB 3->0dB */
void nrf_setPower(enum nrfPower power)
{
u8 reg_rf = readReg(REG_RF_SETUP);
reg_rf &= 0xF8;/* 清除原设功率 */
switch(power)
{
case POWER_M18dBm:
reg_rf |= 0x00;
break;
case POWER_M12dBm:
reg_rf |= 0x02;
break;
case POWER_M6dBm:
reg_rf |= 0x04;
break;
case POWER_0dBm:
reg_rf |= 0x07;
break;
}
writeReg(REG_RF_SETUP,reg_rf);
}
/* 设置重发时间间隔,根据速率及收发字节大小设置 */
/* 详细说明参考nrf24l01.datasheet的P34. */
void nrf_setArd(void)
{
u8 reg_rf,reg_retr;
reg_rf=readReg(REG_RF_SETUP);
reg_retr=readReg(REG_SETUP_RETR);
if(!(reg_rf&0x20)) /* 速率不是250K(寄存器0x20) */
reg_retr|= 1<<4;/* (1+1)*250=500us,在接收32字节时 */
else
reg_retr|= 5<<4;/* (5+1)*250=1500us,在接收32字节时 */
writeReg(REG_SETUP_RETR,reg_retr);
}
/* 设置重发次数,arc:0~15 */
void nrf_setArc(u8 arc)
{
u8 reg_retr;
if(arc>15)
return;
reg_retr=readReg(REG_SETUP_RETR);
reg_retr|= arc;
writeReg(REG_SETUP_RETR,reg_retr);
}
/* 获取接收功率检测 */
u8 nrf_getRpd(void)
{
return readReg(REG_RPD);
}
/* 获取重发失败次数 */
u8 nrf_getTxRetry(void)
{
return readReg(REG_OBSERVE_TX)&0x0F;
}
/* 接收数据包,返回包长度len */
u8 nrf_rxPacket(u8 *rx_buf)
{
u8 rx_len = readReg(CMD_RX_PL_WID);
if(rx_len>0 && rx_len<33)
{
NRF_CE_L();
readBuf(CMD_R_RX_PAYLOAD,rx_buf,rx_len);
NRF_CE_H();
}
else
rx_len = 0;
writeReg(CMD_FLUSH_RX,0xff);/* 冲洗RX_FIFO */
return rx_len;
}
到这里24l01.h文件里的函数就讲完了,24l01.c文件里还有个中断服务函数和一个函数指针定义,就是在定义引脚的时候初始化的那个外部中断。如下:
/*外部中断服务函数*/
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line13)==SET)
{
if(interruptCb)
{
interruptCb();
}
EXTI_ClearITPendingBit(EXTI_Line13);
}
}
这个interruptCb();的定义也在24l01.c文件中,如下:
static void (*interruptCb)(void) = 0;
这就是定义了一个函数指针。
函数指针、回调函数详解https://blog.csdn.net/nbu_dahe/article/details/117434237?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-2-117434237-blog-127455998.235%5Ev32%5Epc_relevant_increate_t0_download_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-2-117434237-blog-127455998.235%5Ev32%5Epc_relevant_increate_t0_download_v2&utm_relevant_index=3也就是中断服务函数里没有什么真实的内容,由nrf中断回调函数nrf_setIterruptCallback(void(*cb)(void))来把一个函数的入口地址传送给这个函数指针,那么发生nrf中断后就会执行传入的函数里的内容。现在还看不出中断到底干了什么。
到这儿24l01.c和24l01.h里的函数就说完了。
那么,看看radiolink.c和radiolink.h里的东西,听着名字就像是射频链路通信协议。既然是从main中射频的初始化往底层看的还是先找初始化。
/*无线连接初始化*/
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;
}
找到了初始化,也定位在了radiolink.c文件中,为了一览这个文件,打开它的头文件:
extern xTaskHandle radiolinkTaskHandle;
void radiolinkInit(void);
bool radiolinkSendPacket(const atkp_t *p);
bool radiolinkSendPacketBlocking(const atkp_t *p);
bool radiolinkReceivePacket(atkp_t *p);
bool radiolinkReceivePacketBlocking(atkp_t *p);
void radiolinkTask(void* param);
u16 radioinkFailRxcount(void);
bool radioinkConnectStatus(void);
void radiolinkEnable(FunctionalState state);
从头文件看出,没有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);
}
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);
}
是个静态函数,在这个函数里,调用24l01.c里的nrf_check()检查了单片机与NRF24L01通讯是否正常,是的话调用24l01.c里的nrfInit(); 初始化芯片,若是不正常就把oled给初始化了,提示不正常的信息,然后进入死循环等待。
初始化之后会设置之前提到的中断函数,这个中断函数是:
/*nrf外部中断回调函数*/
static void nrf_interruptCallback(void)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(nrfIT, &xHigherPriorityTaskWoken);
}
这也是一个静态函数,作用是当中断发生时释放一个信号量,定义了变量xHigherPriorityTaskWoken 并初始化为 pdFALSE来记录时候在此函数退出后需要任务切换。这里的信号量的定义以后碰到再说。
radioInit()函数中,正常初始化后还会再次设置地址、通道、传输速率等值。而在24l01.c里的nrfInit()函数里有设置这些量的语句,也许两次的值不一样,但不知道为什么要设置两次?
调用从最底层往上数第三层初始化后,作为第四层也就是最高层的radiolinkInit()函数有创建了两个队列txQueue和txQueue,很明显应该是用来存放atkp_t数据的,用来队列创建的参数也可以体现出来:RADIOLINK_TX_QUEUE_SIZE, sizeof(atkp_t),具体定义也在radiolink.c中:
/*发送和接收队列信息个数*/
#define RADIOLINK_TX_QUEUE_SIZE 10
#define RADIOLINK_RX_QUEUE_SIZE 10
static xQueueHandle txQueue;
static xQueueHandle rxQueue;
回到radiolinkInit(),接下来他又做创建了一个二值信号量,很明显这个信号量就是射频芯片中断发生后执行的中断任务中释放的那个信号量。
还有一个通讯数据结构体tx_p;为ATK结构体,为通讯结构体,在atkp.h中定义:
/*通讯数据结构*/
typedef struct
{
u8 msgID;
u8 dataLen;
u8 data[ATKP_MAX_DATA_SIZE];
}atkp_t;
即:
然后初始化tx_p包,初始化成心跳包。
完成后这里有个连接状态叫connectStatus的值会被赋值false,也不知道是啥意思……
很可能时初始化先来个没链接
至此,最大的、最高层的、直接被main函数调用的初始化函数就定义完了,它做了初始化硬件、创建队列、创建信号量、初始化通讯数据结构体tx_p、赋值连接状态等事儿。
接着看其他的函数,radiolink.h中第二个声明的是bool radiolinkSendPacket(const atkp_t *p);它的内容是:
/*无线发送atkpPacket*/
bool radiolinkSendPacket(const atkp_t *p)
{
ASSERT(p);
ASSERT(p->dataLen <= ATKP_MAX_DATA_SIZE);
return xQueueSend(txQueue, p, 0);
}
这个函数的作用是用无线的方式发送数据。其实就是把该数据发送到txQueue队列中。 他的数据格式为ATK格式。
radiolink.h中第三个定义的函数是bool radiolinkSendPacketBlocking(const atkp_t *p)
bool radiolinkSendPacketBlocking(const atkp_t *p)
{
ASSERT(p);
ASSERT(p->dataLen <= ATKP_MAX_DATA_SIZE);
return xQueueSend(txQueue, p, 100);//portMAX_DELAY
}
这个函数跟bool radiolinkSendPacket(const atkp_t *p)一样,只不过加上了xTicksToWait时间。至于在什么场景下使用以后再看。
在往下就是接收的两个函数:
/*无线接收atkpPacket*/
bool radiolinkReceivePacket(atkp_t *p)
{
ASSERT(p);
return xQueueReceive(rxQueue, p, 0);
}
bool radiolinkReceivePacketBlocking(atkp_t *p)
{
ASSERT(p);
return xQueueReceive(rxQueue, p, portMAX_DELAY);
}
跟发送的定义差不多,也是一个没加等待时间,另一个加了等待时间,此处是最大等待时间portMAX_DELAY,其定义在portmacro.h中:
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
接下来这个可就是大头了,这个是任务级函数:
/*无线连接任务*/
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)//连续10次无应答则通讯失败
{
statusCount = 0;
connectStatus = false;
}
}
/*1000ms统计一次收发失败次数*/
if(connectStatus==true && xTaskGetTickCount()>=failRxcountTime+1000)
{
failRxcountTime = xTaskGetTickCount();
failReceiveNum = failRxCount;
failRxCount = 0;
}
}
}
也就是在系统起始函数运行后定义的各个任务中无线通信任务函数,
xTaskCreate(radiolinkTask, "RADIOLINK", 100, NULL, 6, &radiolinkTaskHandle);/*创建无线连接任务*/
这个函数在定了一个接收数据包后,就进入了循环,循环中首先发送在初始换定义的tx_p数据包,此处注意,一个数据通讯结构体的长度为 结构体➡dataLen+2 ,根据数据结构一眼就能看出来。
然后获取二值信号量,阻塞时间是1000,八成是1秒,这个地方是可能会发生任务切换的。
等等,这个地方信号量时二值信号量,可是只会在射频中断中才会产生的,很可能就是射频芯片收到数据了。
接下来是检测当前事件,其中nrf_checkEventandRxPacket((u8*)&rx_p, &rx_len);中的参数仅仅是为了收到数据时传入数据使用的,其他状态用不到的。
再往下就是判断当前状态了,如果状态为RX_DR则 蓝灯亮 、 红灯不亮(这俩灯为灌电流方式连接)、statusCount状态计数为0、connectStatus连接状态为真、 向接收队列中写入该数据(写入时会判断收到的数据是否超过最大长度)、读取发送队列,若读取如不成功则设置发送数据包tx_p为心跳包。
这里有个疑惑点,RX_DR状态好像是接收成功,难道是在发送一个数据包后会接收到一个数据包?
如果状态为MAX_RT,说明发送失败,则 蓝灯不亮、红灯亮、failRxCount(定义在radiolink.c中)接收失败次数加一,若是连续10发送失败,则状态计数为零、连接状态为假。
在无线通信任务的主循环中最后一项就是1秒统计一次这一秒期间的收发失败次数。汇总到失败接收次数failReceiveNum中,此时在符合发送失败条件中累加的failRxCount也会清零,也就是1秒清零一次。
至此无线通信任务就完成了。大致是发送tx_p、读取信号量(会产生阻塞)、判断时间状态、事件为接收成功,变灯,把收到的消息放队列中,判断发送队列如果为空,设置tx_p为心跳包、事件若为发送失败则变灯,统计失败次数,10此以上则状态为断开、统计一次1秒内通信失败次数。
接着radiolink.h的函数定往下走,只剩下3个函数了:
/*获取丢包个数*/
u16 radioinkFailRxcount(void)
{
return failReceiveNum;
}
这个就简单了,肯定是给别的任务用的,因为无线通信任务只产生了,也没有使用。
/*获取无线连接状态*/
bool radioinkConnectStatus(void)
{
return connectStatus;
}
同上
/*使能radiolink*/
void radiolinkEnable(FunctionalState state)
{
if(state == ENABLE)
vTaskResume(radiolinkTaskHandle);
else
vTaskSuspend(radiolinkTaskHandle);
}
这个就牛了,直接系统插手,挂起和恢复,不知道哪个有这么高的权限来调用此函数。
没了,关于MiniFly的Remoter的跟NRF24L01芯片的代码就这些了。