正点原子Minifly遥控器V1.1—代码分析一:radiolink.c

2023-05-16

无线连接任务

void radiolinkTask(void* param)
{
	u8 rx_len;
	atkp_t rx_p;
	while(1)
	{
		nrf_txPacket((u8*)&tx_p, tx_p.dataLen+2);//全局变量结构体tx_p为发送的数据包
		xSemaphoreTake(nrfIT, 1000);//take二值信号量,IRQ触发中断时Give
		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次无应答则通讯失败,每次TX FIFO都被清空
			{
				statusCount = 0;
				connectStatus = false;
			}
		}		
		/*1000ms统计一次收发失败次数*/
		if(connectStatus==true && xTaskGetTickCount()>=failRxcountTime+1000)
		{
			failRxcountTime = xTaskGetTickCount();//记录当前的节拍
			failReceiveNum = failRxCount;//记录失败次数,也就是丢包数
			failRxCount = 0;//从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

/* 发送数据包(PTX模式) */
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++)//连续写入len个数据
		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)
{			
	/* 通过外设SPIx发送一个数据 */
	SPI_I2S_SendData(SPIx, TxData);
	/* 检查指定的SPI标志位设置与否:发送缓存空标志位*/
	while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);//空标志为假,代表缓存不是空的,还没发送完,等待发完
	
	/* 检查指定的SPI标志位设置与否:接受缓存非空标志位 */
	while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);//非空标志为假,代表接收缓存没有空,上次的还没移走,等空了可以接收数据
	
	/* 返回通过SPIx最近接收的数据 */
	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();//线配置初始化(地址、通道、速率),做好Give二值信号量的准备
	
	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)//检查MCU与24l01是否通讯正常
	{
		nrfInit(PTX_MODE);//NRF设置为发送模式
		nrf_setIterruptCallback(nrf_interruptCallback);//参数为函数指针,使外部中断函数EXTI15_10_IRQHandler(void)里\
		                                         的interruptCb函数指针指向nrf_interruptCallback函数,然后有外部中断了就触发Give二值信号量
	}
	else//不正常,则通过OLED显示
	{
		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;
}

/*使能radiolink*/
void radiolinkEnable(FunctionalState state)
{
	if(state == ENABLE)
		vTaskResume(radiolinkTaskHandle);
	else
		vTaskSuspend(radiolinkTaskHandle);
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

正点原子Minifly遥控器V1.1—代码分析一:radiolink.c 的相关文章

随机推荐

  • Linux内核之 printk 打印

    Linux内核之 printk 打印 前言一 printk 介绍1 printk 消息级别2 内核 printk 文件 二 调整打印级别1 在 menuconfig 中修改2 在系统中修改 xff08 常用 xff09 三 使用示例四 查看
  • Linux ALSA 之四:Tinyalsa->Alsa Driver Flow分析

    Tinyalsa gt Alsa Driver Flow 一 概述二 Tinyalsa2 1 tinypcminfo2 2 tinymix2 3 tinyplay2 4 tinycap 三 Tinyalsa gt alsa driver f
  • Linux ALSA 之十:ALSA ASOC Machine Driver

    ALSA ASOC Machine Driver 一 Machine 简介二 ASoC Machine Driver2 1 Machine Driver 的 Platform Driver amp Platform Device 驱动模型2
  • Linux ALSA 之十一:ALSA ASOC Path 完整路径追踪

    ALSA ASOC Path 完整路径追踪 一 ASoc Path 简介二 ASoc Path 完整路径2 1 tinymix 设置2 2 完整路径 route 一 ASoc Path 简介 如前面小节所描述 xff0c ASoc 中 Ma
  • 【STM32】关于Clion+STM32cubeMX环境搭建过程中所遇到的一些问题·其一

    目录 一 前言 二 正文 stm32 cubemx的安装 建立工程 配置openOCD进行编译烧录 关于烧录失败的事 三 小结 四 参考文章 一 前言 近日在网上冲浪时无意间发现了稚晖君在知乎发布的一篇关于如何使用clion 43 stm3
  • 端口扫描 1

    TCP端口扫描是通过SYN数据包进行的 xff0c 用于扫描目标机器的端口上是否存在程序监听 xff0c 通常意义上 xff0c 普通个人机器上的某个端口如果有程序监听的话 xff0c 那么它一般是系统漏洞 由于TCP是一个有连接的可靠协议
  • linux Ubuntu终端快捷键

    终端操作快捷键 Tab 自动补全 Ctrl 43 a 光标移动到开始位置 Ctrl 43 e 光标移动到最末尾 Ctrl 43 k 删除此处至末尾的所有内容 Ctrl 43 u 删除此处至开始的所有内容 Ctrl 43 d 删除当前字符 C
  • Linux ALSA 之十二:ALSA ASOC Kcontrol

    ALSA ASOC Kcontrol 一 结构体 snd kcontrol new二 ASoC 系统定义的 kcontrol 宏2 1 SOC SINGLE2 2 SOC SINGLE TLV2 3 SOC DOUBLE2 4 Mixer
  • (2022.12.12 )完成mavros配置+PX4配置

    一 mavros 安装配置 在安装之前 xff0c 请先更新软件库 xff1a sudo apt get update sudo apt get upgrade 遇到问题 无法安全地用该源进行更新 xff0c 所以默认禁用该源 N 参见 a
  • 基于ROS平台的STM32小车--汇总

    一切为了实现利用ros通过串口控制小车简单运动 基于ROS平台的STM32小车 4 上位机控制器 https blog csdn net weixin 39752599 article details 86552511 下载串口通信的ROS
  • Docker容器可视化

    文章目录 本地安装X11界面工具 添加参数创建容器测试容器尝试运行Matplotlib绘制柱状图 参考 Docker容器中运行程序 xff0c 有时候需要显示容器中的图像 xff0c 或在容器中运行一些图形界面的软件 调用摄像头等 xff0
  • 什么是枚举

    什么是枚举 简单来说 xff0c 枚举就是一个类有有限个确定的对象 有限个 就不用解释了 xff0c 如果说枚举有无限个对象 xff0c 那它和普通的类又有什么区别呢 xff1f 确定的 就是指枚举类的所有对象都是不可变的 这里我给个例子解
  • Centos7如何安装sogo输入法

    安装epel依赖源 sudo yum install epel release xff1b 卸载 ibus软件 rpm e nodeps ibus 安装fcitx相关 yum y install fcitx fcitx pinyin fci
  • 安卓系统无线投屏到win10

    安卓系统无线投屏到win10 xff0c 在电脑上操作手机 共有两种方法 xff0c 第一种方法需要先使用数据线连接电脑 xff0c 再断开数据线使用 xff0c 第二种方法是使用安卓11的无线调试 xff0c 全程不需要数据线连接电脑 x
  • FreeRTOS对系统异常优先级寄存器的PendSV和SysTick 的优先级设置过程

    为什么要设置PendSV为最低优先级 xff1f 应用程序执行SVC 时都是希望所需的请求立即得到响应 PendSV 则不同 xff0c 它是可以像普通的中断一样被抢占挂起的 xff08 不像SVC 那样会上访 xff09 操作系统 可以利
  • 如何看懂FreeRTOS里面的函数指针typedef BaseType_t (*TaskHookFunction_t)( void * );

    看懂这个 span class token keyword typedef span span class token keyword long span BaseType t span class token punctuation sp
  • 正点原子MiniFly遥控器V1.1—电路图原理分析

    MiniFly 遥控器系统框架 xff1a STM32F103C8T6 作为控制 MCU xff0c 外围硬件主要有蓝色 0 96 寸 OLED NRF24L01 43 RF 功率放大 2 4G 天线 摇杆 按键 蜂鸣器 LED 等 1 x
  • 使用谷歌学术检索论文小技巧

    写在最前 当然 xff0c 最最关键的一点是 xff0c 得上得了谷歌学术 xff0c 如果没法上谷歌学术 xff0c 请移步这里 xff1a 修改hosts使用谷歌服务 另外说说我们的需求 xff0c 就是需要查找某个方面的文章 xff0
  • 正点原子MiniFly遥控器V1.1—通信链路

    如果遥控器连接了上位机 xff0c 遥控器会将接收到的应答数据包先解析 xff0c 解析完成后转发给上位机 同样 xff0c 上位机发下来的数据遥控器也会先解析再转发 给四轴 Radiolink 链路中 遥控器定周期发送控制命令 xff0c
  • 正点原子Minifly遥控器V1.1—代码分析一:radiolink.c

    无线连接任务 span class token keyword void span span class token function radiolinkTask span span class token punctuation span