微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 精准延时函数配置+串口配置

精准延时函数配置+串口配置

时间:10-02 整理:3721RD 点击:

大家好,前段时间出了个试用报告是讲解关于F412的时钟函数的配置,不知道大家可否看懂,因为本人是个“原子迷”,所以在学习F103的时候就喜欢用正点原子的那套模板写程序,然而现在接触F412的学习的时候,官方提供的教程都是基于STM32CubeMX生成的模板,总感觉看的很不舒服,所以就想尽办法将正点原子的delay.c & sys.c & usart.c 三个C文件移植过来,其实并没有什么难度,只要把我的上一篇

【NUCLEO-F412ZG试用体验】小项目之大心得(STM32CubeMX+时钟配置+基于HAL库的DHT11驱动)

里边的时钟函数的配置看懂了,基本没啥问题了,因为时钟函数的配置决定系统的运行速度,至于其他有多高大上的配置什么的暂且不管,只要方便我们开发就行。

我是参考正点原子的F429的HAL库开发的例程移植出来的,大家可以到正点原子出的教程里找找。

暂且不废话了,来看看程序吧!

delay.c

#include"delay.h"

#include"sys.h"

//////////////////////////////////////////////////////////////////////////////////     

//如果使用ucos,则包括下面的头文件即可.

#ifSYSTEM_SUPPORT_OS

#include"includes.h"                             //ucos使用   

#endif


static u32fac_us=0;                                             //us延时倍乘数

#ifSYSTEM_SUPPORT_OS      

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

#endif

#ifSYSTEM_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                        

#definedelay_osrunning         OSRunning                 //OS是否运行标记,0,不运行;1,在运行

#definedelay_ostickspersec    OS_TICKS_PER_SEC   //OS时钟节拍,即每秒调度次数

#definedelay_osintnesting    OSIntNesting              //中断嵌套级别,即中断嵌套次数

#endif

//支持UCOSIII

#ifdef    CPU_CFG_CRITICAL_METHOD                            //CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII  

#definedelay_osrunning         OSRunning                 //OS是否运行标记,0,不运行;1,在运行

#definedelay_ostickspersec    OSCfg_TickRate_Hz   //OS时钟节拍,即每秒调度次数

#definedelay_osintnesting    OSIntNestingCtr         //中断嵌套级别,即中断嵌套次数

#endif

//us级延时时,关闭任务调度(防止打断us级延迟)

voiddelay_osschedlock(void)

{

#ifdefCPU_CFG_CRITICAL_METHOD                      //使用UCOSIII

       OS_ERR err;

       OSSchedLock(&err);                                      //UCOSIII的方式,禁止调度,防止打断us延时

#else                                                                     //否则UCOSII

       OSSchedLock();                                             //UCOSII的方式,禁止调度,防止打断us延时

#endif

}

//us级延时时,恢复任务调度

voiddelay_osschedunlock(void)

{      

#ifdefCPU_CFG_CRITICAL_METHOD                      //使用UCOSIII

       OS_ERR err;

       OSSchedUnlock(&err);                                  //UCOSIII的方式,恢复调度

#else                                                                     //否则UCOSII

       OSSchedUnlock();                                          //UCOSII的方式,恢复调度

#endif

}

//调用OS自带的延时函数延时

//ticks:延时的节拍数

voiddelay_ostimedly(u32 ticks)

{

#ifdefCPU_CFG_CRITICAL_METHOD

       OS_ERR err;

       OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);//UCOSIII延时采用周期模式

#else

       OSTimeDly(ticks);                                             //UCOSII延时

#endif

}

//systick中断服务函数,使用OS时用到

voidSysTick_Handler(void)

{      

    HAL_IncTick();

       if(delay_osrunning==1)                                 //OS开始跑了,才执行正常的调度处理

       {

              OSIntEnter();                                          //进入中断

              OSTimeTick();                                  //调用ucos的时钟服务程序               

              OSIntExit();                                     //触发任务切换软中断

       }

}

#endif

                        

//初始化延迟函数

//当使用ucos的时候,此函数会初始化ucos的时钟节拍

//SYSTICK的时钟固定为AHB时钟

//SYSCLK:系统时钟频率

void delay_init(u8SYSCLK)

{

#ifSYSTEM_SUPPORT_OS                                         //如果需要支持OS.

       u32 reload;

#endif

   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK

       fac_us=SYSCLK;                                      //不论是否使用OS,fac_us都需要使用

#ifSYSTEM_SUPPORT_OS                                         //如果需要支持OS.

       reload=SYSCLK;                                  //每秒钟的计数次数 单位为K      

       reload*=1000000/delay_ostickspersec;        //根据delay_ostickspersec设定溢出时间

                                                                             //reload为24位寄存器,最大值:16777216,在180M下,约合0.745s左右  

       fac_ms=1000/delay_ostickspersec;              //代表OS可以延时的最少单位      

       SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断

       SysTick->LOAD=reload;                                //每1/OS_TICKS_PER_SEC秒中断一次      

       SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;//开启SYSTICK

#else

#endif

}                                                           

#ifSYSTEM_SUPPORT_OS                                         //如果需要支持OS.

//延时nus

//nus:要延时的us数.      

//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)                                                                  

void delay_us(u32nus)

{            

       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是一个递减的计数器就可以了.

                     elsetcnt+=reload-tnow+told;      

                     told=tnow;

                     if(tcnt>=ticks)break;                 //时间超过/等于要延迟的时间,则退出.

              }

       };

       delay_osschedunlock();                                 //恢复OS调度                                                                             

}  

//延时nms

//nms:要延时的ms数

//nms:0~65535

void delay_ms(u16nms)

{      

       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:0~190887435(最大值即2^32/fac_us@fac_us=22.5)     

void delay_us(u32nus)

{            

       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是一个递减的计数器就可以了.

                     elsetcnt+=reload-tnow+told;      

                     told=tnow;

                     if(tcnt>=ticks)break;                 //时间超过/等于要延迟的时间,则退出.

              }

       };

}

//延时nms

//nms:要延时的ms数

void delay_ms(u16nms)

{

       u32 i;

       for(i=0;i<nms;i++) delay_us(1000);

}

#endif

                     

usart.c

#include"usart.h"

#include"delay.h"

//////////////////////////////////////////////////////////////////////////////////     

//如果使用os,则包括下面的头文件即可.

#ifSYSTEM_SUPPORT_OS

#include"includes.h"                             //os使用      

#endif


//加入以下代码,支持printf函数,而不需要选择use MicroLIB     

//#definePUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)      

#if 1

#pragmaimport(__use_no_semihosting)            

//标准库需要的支持函数                 

struct __FILE

{

       int handle;

};

FILE__stdout;      

//定义_sys_exit()以避免使用半主机模式   

void _sys_exit(intx)

{

       x = x;

}

//重定义fputc函数

int fputc(int ch,FILE *f)

{     

       while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   

       USART1->DR = (u8) ch;      

       return ch;

}

#endif

#ifEN_USART1_RX   //如果使能了接收

//串口1中断服务程序

//注意,读取USARTx->SR能避免莫名其妙的错误      

u8USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.

//接收状态

//bit15,      接收完成标志

//bit14,      接收到0x0d

//bit13~0,  接收到的有效字节数目

u16USART_RX_STA=0;       //接收状态标记      

u8aRxBuffer[RXBUFFERSIZE];//HAL库使用的串口接收缓冲

UART_HandleTypeDefUART1_Handler; //UART句柄

//初始化IO 串口1

//bound:波特率

void uart_init(u32bound)

{      

       //UART 初始化设置

       UART1_Handler.Instance=USART1;                                //USART1

       UART1_Handler.Init.BaudRate=bound;                           //波特率

       UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B;   //字长为8位数据格式

       UART1_Handler.Init.StopBits=UART_STOPBITS_1;        //一个停止位

       UART1_Handler.Init.Parity=UART_PARITY_NONE;               //无奇偶校验位

       UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;   //无硬件流控

       UART1_Handler.Init.Mode=UART_MODE_TX_RX;               //收发模式

       HAL_UART_Init(&UART1_Handler);                                      //HAL_UART_Init()会使能UART1

      

       HAL_UART_Receive_IT(&UART1_Handler,(u8 *)aRxBuffer, RXBUFFERSIZE);//该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量

  

}

//UART底层初始化,时钟使能,引脚配置,中断配置

//此函数会被HAL_UART_Init()调用

//huart:串口句柄

voidHAL_UART_MspInit(UART_HandleTypeDef *huart)

{

    //GPIO端口设置

       GPIO_InitTypeDef GPIO_Initure;

      

       if(huart->Instance==USART1)//如果是串口1,进行串口1 MSP初始化

       {

              __HAL_RCC_GPIOA_CLK_ENABLE();                   //使能GPIOA时钟

              __HAL_RCC_USART1_CLK_ENABLE();                 //使能USART1时钟

      

              GPIO_Initure.Pin=GPIO_PIN_9;                     //PA9 TX

              GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //复用推挽输出

              GPIO_Initure.Pull=GPIO_PULLUP;                 //上拉

              GPIO_Initure.Speed=GPIO_SPEED_FAST;             //高速

              GPIO_Initure.Alternate=GPIO_AF7_USART1;      //复用为USART1

              HAL_GPIO_Init(GPIOA,&GPIO_Initure);     //初始化PA9

              GPIO_Initure.Pin=GPIO_PIN_10;                   //PA10 RX

              HAL_GPIO_Init(GPIOA,&GPIO_Initure);     //初始化PA10

              

#if EN_USART1_RX

              HAL_NVIC_EnableIRQ(USART1_IRQn);                      //使能USART1中断通道

              HAL_NVIC_SetPriority(USART1_IRQn,3,3);                //抢占优先级3,子优先级3

#endif   

       }

}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)

{

       if(huart->Instance==USART1)//如果是串口1

       {

              if((USART_RX_STA&0x8000)==0)//接收未完成

              {

                     if(USART_RX_STA&0x4000)//接收到了0x0d

                     {

                            if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;//接收错误,重新开始

                            elseUSART_RX_STA|=0x8000; //接收完成了

                     }

                     else //还没收到0X0D

                     {      

                            if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;

                            else

                            {

                                   USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0];

                                   USART_RX_STA++;

                                   if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收        

                            }              

                     }

              }

       }

}

//串口1中断服务程序

voidUSART1_IRQHandler(void)                 

{

       u32 timeout=0;

#ifSYSTEM_SUPPORT_OS       //使用OS

       OSIntEnter();   

#endif

      

       HAL_UART_IRQHandler(&UART1_Handler);      //调用HAL库中断处理公用函数

      

       timeout=0;

    while (HAL_UART_GetState(&UART1_Handler)!= HAL_UART_STATE_READY)//等待就绪

       {

        timeout++;////超时处理

     if(timeout>HAL_MAX_DELAY) break;        

      

       }

     

       timeout=0;

       while(HAL_UART_Receive_IT(&UART1_Handler,(u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//一次处理完成之后,重新开启中断并设置RxXferCount为1

       {

        timeout++; //超时处理

        if(timeout>HAL_MAX_DELAY) break;  

       }

#ifSYSTEM_SUPPORT_OS       //使用OS

       OSIntExit();                                                                              

#endif

}

#endif   

/*下面代码我们直接把中断控制逻辑写在中断服务函数内部。*/

/*

//串口1中断服务程序

voidUSART1_IRQHandler(void)                 

{

       u8 Res;

#if SYSTEM_SUPPORT_OS       //使用OS

       OSIntEnter();   

#endif

       if((__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_RXNE)!=RESET))  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

       {

       HAL_UART_Receive(&UART1_Handler,&Res,1,1000);

              if((USART_RX_STA&0x8000)==0)//接收未完成

              {

                     if(USART_RX_STA&0x4000)//接收到了0x0d

                     {

                            if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

                            elseUSART_RX_STA|=0x8000; //接收完成了

                     }

                     else //还没收到0X0D

                     {      

                            if(Res==0x0d)USART_RX_STA|=0x4000;

                            else

                            {

                                   USART_RX_BUF[USART_RX_STA&0X3FFF]=Res;

                                   USART_RX_STA++;

                                   if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收        

                            }              

                     }

              }                 

       }

       HAL_UART_IRQHandler(&UART1_Handler);      

#ifSYSTEM_SUPPORT_OS       //使用OS

       OSIntExit();                                                                              

#endif

}

#endif   

*/

注释很详细,我就不详细解释了,大家有什么不明白的可以在下边回帖大家一块讨论。下边附上几张我用逻辑分析仪测到的精准延时




没积分不能下载。

没积分不能下载。

最近计划这样做              

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top