小模块例程:双核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,因此我们也照搬了。
#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库,这个库相当好用,我几乎天天使用
- 具体程序如下:
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
实验结果
可见接收机一直在轮询,每次轮询的间隔是100us。发射机每隔1500ms发射一次。