小模块例程:双核AMP进行NRF24L01 2.4GHz无线收发实验 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

小模块例程:双核AMP进行NRF24L01 2.4GHz无线收发实验

说明

  • 使用zynq7020的双核cpu进行无线收发实验
  • 使用了2个AXI QUADSPI模块,一个用作tx,一个用作rx。程序控制上,core0用作tx,控制spi0;core1用作rx,控制spi1
  • 程序移植的是正点原子的stm32的例程。将程序代码拷过来后,删除zynq上没有的东西,并对代码进行移植改造,以适应于zynq。主要是修改了spi的驱动部分。
  • 硬件设计非常简单,几分钟就完成了。软件移植也不复杂,只花了一个上午就调通了。

硬件设计

  • spi的配置,用最简单的模式就可以了
  • gpio使用了6个,主要用于2个无线模块的SPI_CS/CE/IRQ
  • 注意,我们使用的是GPIO来控制SPI的CS。我们没有使用硬件CS,硬件CS非常不灵活。原子的程序里,也是使用的GPIO的CS,因此我们也照搬了。

image image image image

#SPI0
set_property -dict {PACKAGE_PIN G15 IOSTANDARD LVCMOS33} [get_ports spi_rtl_0_io1_io]
set_property -dict {PACKAGE_PIN F16 IOSTANDARD LVCMOS33} [get_ports spi_rtl_0_io0_io]
set_property -dict {PACKAGE_PIN C20 IOSTANDARD LVCMOS33} [get_ports spi_rtl_0_ss_io]
set_property -dict {PACKAGE_PIN F17 IOSTANDARD LVCMOS33} [get_ports spi_rtl_0_sck_io]
#soft_CS0
set_property -dict {PACKAGE_PIN E17 IOSTANDARD LVCMOS33} [get_ports gpio_rtl_0_tri_io[0]]


#SPI1
set_property -dict {PACKAGE_PIN J19 IOSTANDARD LVCMOS33} [get_ports spi_rtl_1_io1_io]
set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVCMOS33} [get_ports spi_rtl_1_io0_io]
set_property -dict {PACKAGE_PIN B20 IOSTANDARD LVCMOS33} [get_ports spi_rtl_1_ss_io]
set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVCMOS33} [get_ports spi_rtl_1_sck_io]
#soft_CS1
set_property -dict {PACKAGE_PIN M19 IOSTANDARD LVCMOS33} [get_ports gpio_rtl_0_tri_io[1]]

#CE1
set_property -dict {PACKAGE_PIN D18 IOSTANDARD LVCMOS33} [get_ports gpio_rtl_0_tri_io[2]]
#CE2
set_property -dict {PACKAGE_PIN M20 IOSTANDARD LVCMOS33} [get_ports gpio_rtl_0_tri_io[3]]
#IRQ1
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports gpio_rtl_0_tri_io[4]]
#IRQ2
set_property -dict {PACKAGE_PIN K19 IOSTANDARD LVCMOS33} [get_ports gpio_rtl_0_tri_io[5]]

软件设计

  • 程序分core0和core1,2个程序是完全一样的,只是mode不一样。
  • 程序的架构如下:从原子的stm32例程中移植过来了delay.c/h, spi.c/h, 24l01.c/h, main.c
  • 从小梅哥移植了ACZ702_Lib库,这个库相当好用,我几乎天天使用
  • 具体程序如下:

image

main.c

//#include "sys.h"
//#include "delay/delay.h"
//#include "usart.h"
//#include "led.h"
//#include "key.h"
//#include "lcd.h"
//#include "sdram.h"
//#include "usmart.h"
//#include "pcf8574.h"
//#include "SPI/24l01.h"
#include "ACZ702_Lib/COMMON.h"
/************************************************
 ALIENTEK 阿波罗STM32H7开发板 实验36
 NRF24L01无线通信实验-HAL库函数版
 技术支持:www.openedv.com
 淘宝店铺:http://eboard.taobao.com 
 关注微信公众平台微信号:"正点原子",免费获取STM32资料。
 广州市星翼电子科技有限公司  
 作者:正点原子 @ALIENTEK
************************************************/

int main(void)
{
	u8 key,mode;
	u16 t=0;			 
	u8 tmp_buf[33];	 
	
    NRF24L01_Init();    		    		//初始化NRF24L01 

	while(NRF24L01_Check())
	{
		xil_printf("NRF24L01 Error\r\n");
		delay_ms(200);
	}
	xil_printf("NRF24L01 OK\r\n");

	mode=1;

	if(mode==0)//RX模式
	{
		xil_printf("NRF24L01 RX_Mode\r\n");
		xil_printf("Received DATA:\r\n");
		NRF24L01_RX_Mode();
		while(1)
		{
			if(NRF24L01_RxPacket(tmp_buf)==0)//一旦接收到信息,则显示出来.
			{
				delay_ms(100);
				xil_printf("Received DATA:\r\n");
				tmp_buf[32]=0;//加入字符串结束符
				for(int i=0; i<32; i++){
					xil_printf("%d ", tmp_buf[i]);
				}
				xil_printf("\r\n");
			}else delay_us(100);
			t++;
		};
	}else//TX模式
	{
		xil_printf("NRF24L01 TX_Mode\r\n");
		NRF24L01_TX_Mode();
		mode=' ';//从空格键开始
		while(1)
		{
			if(NRF24L01_TxPacket(tmp_buf)==TX_OK)
			{
				xil_printf("Sended DATA:\r\n");
				for(int i=0; i<32; i++){
					xil_printf("%c ", tmp_buf[i]);
				}
				xil_printf("\r\n");

				key=mode;
				for(t=0;t<32;t++)
				{
					key++;
					if(key>('~'))key=' ';
					tmp_buf[t]=key;
				}
				mode++;
				if(mode>'~')mode=' ';
				tmp_buf[32]=0;//加入结束符
			}else
			{
 				xil_printf("Send Failed \r\n");
			};
			delay_ms(1500);
		};
	}
}


24l01.c

#include "24l01.h"
#include "../SPI/spi.h"
#include "../delay/delay.h"
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F7开发板
//NRF24L01驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2015/12/30
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
////////////////////////////////////////////////////////////////////////////////// 	

const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址
const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址

/* IO0--CS0
 * IO1--CS1
 * IO2--CE0
 * IO3--CE1
 * IO4--IRQ0
 * IO5--IRQ1
 */
void NRF24L01_CE(u8 n)
{
	AXI_GPIO_SetPin(&AXI_GPIO0, XGPIO_IR_CH1_MASK,2,n);
}

void NRF24L01_CSN(u8 n)
{
	AXI_GPIO_SetPin(&AXI_GPIO0, XGPIO_IR_CH1_MASK,0,n);
}

u8 NRF24L01_IRQ(void)
{
	return AXI_GPIO_GetPin(&AXI_GPIO0, XGPIO_IR_CH1_MASK,4);
}



//针对NRF24L01修改SPI2驱动
void NRF24L01_SPI_Init(void)
{
//    __HAL_SPI_DISABLE(&SPI2_Handler);               //先关闭SPI2
//    SPI2_Handler.Init.CLKPolarity=SPI_POLARITY_LOW; //串行同步时钟的空闲状态为低电平
//    SPI2_Handler.Init.CLKPhase=SPI_PHASE_1EDGE;     //串行同步时钟的第1个跳变沿(上升或下降)数据被采样
//    HAL_SPI_Init(&SPI2_Handler);
//    __HAL_SPI_ENABLE(&SPI2_Handler);                //使能SPI2

	AXI_GPIO_Init(&AXI_GPIO0,XPAR_AXI_GPIO_0_DEVICE_ID);	//初始化AXI GPIO0
	AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH1_MASK, 0b00000, 0);//设置通道CH1.5为输入,其余为输出
}

//初始化24L01的IO口
void NRF24L01_Init(void)
{
//    GPIO_InitTypeDef GPIO_Initure;
//    __HAL_RCC_GPIOG_CLK_ENABLE();			//开启GPIOG时钟
//    __HAL_RCC_GPIOI_CLK_ENABLE();			//开启GPIOI时钟
//
//    GPIO_Initure.Pin=GPIO_PIN_10|GPIO_PIN_12; //PG10,12
//    GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
//    GPIO_Initure.Pull=GPIO_PULLUP;          //上拉
//    GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;     //高速
//    HAL_GPIO_Init(GPIOG,&GPIO_Initure);     //初始化
//
//    GPIO_Initure.Pin=GPIO_PIN_11;           //PI11
//    GPIO_Initure.Mode=GPIO_MODE_INPUT;      //输入
//    HAL_GPIO_Init(GPIOI,&GPIO_Initure);     //初始化
    
    SPI2_Init();    		                //初始化SPI2  
    NRF24L01_SPI_Init();                    //针对NRF的特点修改SPI的设置
	NRF24L01_CE(0); 			            //使能24L01
	NRF24L01_CSN(1);			            //SPI片选取消	 		 	 
}
//检测24L01是否存在
//返回值:0,成功;1,失败	
u8 NRF24L01_Check(void)
{
	u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
	u8 i;
	//SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_32); //spi速度为6.25Mhz(24L01的最大SPI时钟为10Mhz)
	NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.	
	NRF24L01_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
}	 	 
//SPI写寄存器
//reg:指定寄存器地址
//value:写入的值
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
{
	u8 status;	
   	NRF24L01_CSN(0);                //使能SPI传输
  	status =SPI2_ReadWriteByte(reg);//发送寄存器号 
  	SPI2_ReadWriteByte(value);      //写入寄存器的值
  	NRF24L01_CSN(1);                //禁止SPI传输	   
  	return(status);       			//返回状态值
}
//读取SPI寄存器值
//reg:要读的寄存器
u8 NRF24L01_Read_Reg(u8 reg)
{
	u8 reg_val;	    
 	NRF24L01_CSN(0);            //使能SPI传输		
  	SPI2_ReadWriteByte(reg);    //发送寄存器号
  	reg_val=SPI2_ReadWriteByte(0XFF);//读取寄存器内容
  	NRF24L01_CSN(1);            //禁止SPI传输		    
  	return(reg_val);            //返回状态值
}	
//在指定位置读出指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值 
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
{
	u8 status,u8_ctr;	       
  	NRF24L01_CSN(0);            //使能SPI传输
  	status=SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值   	   
 	for(u8_ctr=0;u8_ctr<len;u8_ctr++)pBuf[u8_ctr]=SPI2_ReadWriteByte(0XFF);//读出数据
  	NRF24L01_CSN(1);            //关闭SPI传输
  	return status;              //返回读到的状态值
}
//在指定位置写指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//len:数据长度
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
{
	u8 status,u8_ctr;	    
 	NRF24L01_CSN(0);            //使能SPI传输
  	status = SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
  	for(u8_ctr=0; u8_ctr<len; u8_ctr++)SPI2_ReadWriteByte(*pBuf++); //写入数据	 
  	NRF24L01_CSN(1);            //关闭SPI传输
  	return status;              //返回读到的状态值
}				   
//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:发送完成状况
u8 NRF24L01_TxPacket(u8 *txbuf)
{
	u8 sta;
 	//SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_16); //spi速度为6.25Mhz(24L01的最大SPI时钟为10Mhz)
	NRF24L01_CE(0);
  	NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
 	NRF24L01_CE(1);                         //启动发送	   
	while(NRF24L01_IRQ()!=0);                 //等待发送完成
	sta=NRF24L01_Read_Reg(STATUS);          //读取状态寄存器的值	   
	NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
	if(sta&MAX_TX)                          //达到最大重发次数
	{
		NRF24L01_Write_Reg(FLUSH_TX,0xff);  //清除TX FIFO寄存器 
		return MAX_TX; 
	}
	if(sta&TX_OK)                           //发送完成
	{
		return TX_OK;
	}
	return 0xff;//其他原因发送失败
}
//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:0,接收完成;其他,错误代码
u8 NRF24L01_RxPacket(u8 *rxbuf)
{
	u8 sta;		    							   
	//SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_16); //spi速度为6.25Mhz(24L01的最大SPI时钟为10Mhz)
	sta=NRF24L01_Read_Reg(STATUS);          //读取状态寄存器的值    	 
	NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志
	if(sta&RX_OK)//接收到数据
	{
		NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
		NRF24L01_Write_Reg(FLUSH_RX,0xff);  //清除RX FIFO寄存器 
		return 0; 
	}	   
	return 1;//没收到任何数据
}					    
//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了		   
void NRF24L01_RX_Mode(void)
{
	NRF24L01_CE(0);	  
  	NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址
	  
  	NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);       //使能通道0的自动应答    
  	NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);   //使能通道0的接收地址  	 
  	NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);	        //设置RF通信频率		  
  	NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度 	    
  	NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);    //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  	NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);     //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 
  	NRF24L01_CE(1); //CE为高,进入接收模式 
}						 
//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR
//PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了		   
//CE为高大于10us,则启动发送.	 
void NRF24L01_TX_Mode(void)
{
	NRF24L01_CE(0);	    
  	NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址 
  	NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK	  

  	NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答    
  	NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
  	NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
  	NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40);       //设置RF通道为40
  	NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  	NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
	NRF24L01_CE(1);//CE为高,10us后启动发送
}

spi.c

#include "spi.h"
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32H7开发板
//SPI驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2017/8/15
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved									  
////////////////////////////////////////////////////////////////////////////////// 	

//SPI_HandleTypeDef SPI2_Handler;  //SPI2句柄

//以下是SPI模块的初始化代码,配置成主机模式 						  
//SPI口初始化
//这里针是对SPI2的初始化
void SPI2_Init(void)
{
//    SPI2_Handler.Instance=SPI2;                      //SP2
//    SPI2_Handler.Init.Mode=SPI_MODE_MASTER;          //设置SPI工作模式,设置为主模式
//    SPI2_Handler.Init.Direction=SPI_DIRECTION_2LINES;//设置SPI单向或者双向的数据模式:SPI设置为双线模式
//    SPI2_Handler.Init.DataSize=SPI_DATASIZE_8BIT;    //设置SPI的数据大小:SPI发送接收8位帧结构
//    SPI2_Handler.Init.CLKPolarity=SPI_POLARITY_HIGH; //串行同步时钟的空闲状态为高电平
//    SPI2_Handler.Init.CLKPhase=SPI_PHASE_2EDGE;      //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
//    SPI2_Handler.Init.NSS=SPI_NSS_SOFT;              //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
//    SPI2_Handler.Init.NSSPMode=SPI_NSS_PULSE_DISABLE;//NSS信号脉冲失能
//    SPI2_Handler.Init.MasterKeepIOState=SPI_MASTER_KEEP_IO_STATE_ENABLE;  //SPI主模式IO状态保持使能
//    SPI2_Handler.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256;//定义波特率预分频的值:波特率预分频值为256
//    SPI2_Handler.Init.FirstBit=SPI_FIRSTBIT_MSB;     //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
//    SPI2_Handler.Init.TIMode=SPI_TIMODE_DISABLE;     //关闭TI模式
//    SPI2_Handler.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验
//    SPI2_Handler.Init.CRCPolynomial=7;               //CRC值计算的多项式
//    HAL_SPI_Init(&SPI2_Handler);
//
//    __HAL_SPI_ENABLE(&SPI2_Handler);                 //使能SPI2
	AXI_SPI_Init(&AXI_SPI0, XPAR_SPI_0_DEVICE_ID, XSP_MASTER_OPTION);
    SPI2_ReadWriteByte(0Xff);                        //启动传输
}

//SPI2底层驱动,时钟使能,引脚配置
//此函数会被HAL_SPI_Init()调用
//hspi:SPI句柄
//void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
//{
//    GPIO_InitTypeDef GPIO_Initure;
//    RCC_PeriphCLKInitTypeDef SPI2ClkInit;
//
//    __HAL_RCC_GPIOB_CLK_ENABLE();                   //使能GPIOF时钟
//    __HAL_RCC_SPI2_CLK_ENABLE();                    //使能SPI2时钟
//
//	//设置SPI2的时钟源
//	SPI2ClkInit.PeriphClockSelection=RCC_PERIPHCLK_SPI2;	    //设置SPI2时钟源
//	SPI2ClkInit.Spi123ClockSelection=RCC_SPI123CLKSOURCE_PLL;	//SPI2时钟源使用PLL1Q
//	HAL_RCCEx_PeriphCLKConfig(&SPI2ClkInit);
//
//    //PB13,14,15
//    GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
//    GPIO_Initure.Mode=GPIO_MODE_AF_PP;              //复用推挽输出
//    GPIO_Initure.Pull=GPIO_PULLUP;                  //上拉
//    GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;   //快速
//    GPIO_Initure.Alternate=GPIO_AF5_SPI2;           //复用为SPI2
//    HAL_GPIO_Init(GPIOB,&GPIO_Initure);             //初始化
//}

//SPI速度设置函数
//SPI速度=PLL1Q/分频系数
//@ref SPI_BaudRate_Prescaler:SPI_BAUDRATEPRESCALER_2~SPI_BAUDRATEPRESCALER_256
//PLL1Q时钟一般为200Mhz:
void SPI2_SetSpeed(u32 SPI_BaudRatePrescaler)
{
//    assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
//    __HAL_SPI_DISABLE(&SPI2_Handler);            //关闭SPI
//    SPI2_Handler.Instance->CFG1&=~(0X7<<28);     //位30-28清零,用来设置波特率
//    SPI2_Handler.Instance->CFG1|=SPI_BaudRatePrescaler;//设置SPI速度
//    __HAL_SPI_ENABLE(&SPI2_Handler);             //使能SPI
    
}

//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
    u8 Rxdata;
    //HAL_SPI_TransmitReceive(&SPI2_Handler,&TxData,&Rxdata,1, 1000);
    AXI_SPI_Transfer(&AXI_SPI0, 0, &Rxdata, &TxData, 1);
 	return Rxdata;          		    //返回收到的数据		
}

delay.c

#include "delay.h"
//#include "sys.h"
#include "xil_types.h"
////////////////////////////////////////////////////////////////////////////////// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	  
#endif
//////////////////////////////////////////////////////////////////////////////////  
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32H7开发板
//使用SysTick的普通计数模式对延迟进行管理(支持ucosii)
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2017/6/8
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//修改说明
////////////////////////////////////////////////////////////////////////////////// 

static u32 fac_us=0;							//us延时倍乘数

#if SYSTEM_SUPPORT_OS 	
static u16 fac_ms=0;							//ms延时倍乘数,在os下,代表每个节拍的ms数
#endif

#if SYSTEM_SUPPORT_OS							//如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
//当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
//首先是3个宏定义:
//delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
//delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick
//delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
//然后是3个函数:
//delay_osschedlock:用于锁定OS任务调度,禁止调度
//delay_osschedunlock:用于解锁OS任务调度,重新开启调度
//delay_ostimedly:用于OS延时,可以引起任务调度.

//本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植
//支持UCOSII
#ifdef 	OS_CRITICAL_METHOD						//OS_CRITICAL_METHOD定义了,说明要支持UCOSII				
#define delay_osrunning		OSRunning			//OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec	OS_TICKS_PER_SEC	//OS时钟节拍,即每秒调度次数
#define delay_osintnesting 	OSIntNesting		//中断嵌套级别,即中断嵌套次数
#endif

//支持UCOSIII
#ifdef 	CPU_CFG_CRITICAL_METHOD					//CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII	
#define delay_osrunning		OSRunning			//OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec	OSCfg_TickRate_Hz	//OS时钟节拍,即每秒调度次数
#define delay_osintnesting 	OSIntNestingCtr		//中断嵌套级别,即中断嵌套次数
#endif


//us级延时时,关闭任务调度(防止打断us级延迟)
void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD   			//使用UCOSIII
	OS_ERR err; 
	OSSchedLock(&err);						//UCOSIII的方式,禁止调度,防止打断us延时
#else										//否则UCOSII
	OSSchedLock();							//UCOSII的方式,禁止调度,防止打断us延时
#endif
}

//us级延时时,恢复任务调度
void delay_osschedunlock(void)
{	
#ifdef CPU_CFG_CRITICAL_METHOD   			//使用UCOSIII
	OS_ERR err; 
	OSSchedUnlock(&err);					//UCOSIII的方式,恢复调度
#else										//否则UCOSII
	OSSchedUnlock();						//UCOSII的方式,恢复调度
#endif
}

//调用OS自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD
	OS_ERR err; 
	OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);//UCOSIII延时采用周期模式
#else
	OSTimeDly(ticks);						//UCOSII延时
#endif 
}
 
//systick中断服务函数,使用OS时用到
void SysTick_Handler(void)
{	
    HAL_IncTick();
	if(delay_osrunning==1)					//OS开始跑了,才执行正常的调度处理
	{
		OSIntEnter();						//进入中断
		OSTimeTick();       				//调用ucos的时钟服务程序               
		OSIntExit();       	 				//触发任务切换软中断
	}
}
#endif
			   
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u16 SYSCLK)
{
}								    

#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
//延时nus
//nus:要延时的us数.	
//nus:0~204522252(最大值即2^32/fac_us@fac_us=21)	    								   
void delay_us(u32 nus)
{		
	u32 ticks;
	u32 told,tnow,tcnt=0;
	u32 reload=SysTick->LOAD;				//LOAD的值	    	 
	ticks=nus*fac_us; 						//需要的节拍数 
	delay_osschedlock();					//阻止OS调度,防止打断us延时
	told=SysTick->VAL;        				//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	};
	delay_osschedunlock();					//恢复OS调度											    
}  
//延时nms
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u16 nms)
{	
	if(delay_osrunning&&delay_osintnesting==0)//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)	    
	{		 
		if(nms>=fac_ms)						//延时的时间大于OS的最少时间周期 
		{ 
   			delay_ostimedly(nms/fac_ms);	//OS延时
		}
		nms%=fac_ms;						//OS已经无法提供这么小的延时了,采用普通方式延时    
	}
	delay_us((u32)(nms*1000));				//普通方式延时
}
#else  //不用ucos时
//延时nus
//nus为要延时的us数.	
//注意:nus的值不要大于1000us
void delay_us(u32 nus)
{		
//	u32 ticks;
//	u32 told,tnow,tcnt=0;
//	u32 reload=SysTick->LOAD;				//LOAD的值
//	ticks=nus*fac_us; 						//需要的节拍数
//	told=SysTick->VAL;        				//刚进入时的计数器值
//	while(1)
//	{
//		tnow=SysTick->VAL;
//		if(tnow!=told)
//		{
//			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
//			else tcnt+=reload-tnow+told;
//			told=tnow;
//			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
//		}
//	};
	usleep(nus);
}
 
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{
	u32 i;
	for(i=0;i<nms;i++) delay_us(1000);
}

#endif

实验结果

image image image image

可见接收机一直在轮询,每次轮询的间隔是100us。发射机每隔1500ms发射一次。