打印

STM32延时函数的几种方法

[复制链接]
721|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 王小琪 于 2023-1-9 18:59 编辑

延时函数是基本上在所有的项目中都会遇到的一个功能、和软件工程师的HELLO WORLD!一样,几乎每个玩单片机的上手第一个项目就是点灯,因此许多人自称“点灯工程师”,点灯常用的闪烁基本逻辑就是亮0.5s,灭0.5s,重复动作就是闪烁的效果。上面就用到了延时函数,由此可见一斑,延时函数是多么的重要,于是我在这里抛砖引玉,介绍几种延时函数的方式,下面用野火核心板进行演示,演示的功能都是一样的,间隔一段时间LED状态进行翻转,同时进行串口打印。
一、软件延时
 //  软件延时函数,使用不同的系统时钟,延时不一样
void Delay(__IO uint32_t nCount)        
{
        for(; nCount != 0; nCount--);
}

int main(void)
{        
        HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK  8*9=72M
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
        LED_Init();
           while(1)
        {
                LED0=!LED0;
                printf("\r\n今天是个好日子~");
                Delay(5000000);
        }         
}

通过下面的对比可以看到,通过改变系统时钟SYSCLK,时间间隔改变了,因为单片机执行一步指令大概在几ns,频率越快,执行的动作越快,相对应的时间就越短。
还有一个问题就是Delay(5000000)差不多是0.8s,如果我希望延时刚好1s,很难找到这个临界值,导致延时不准。
总结:
优点:移植方便,无论你用51,还是PIC,还是STM32基本都可以用这个延时函数,移植性强。
缺点:不精准,对不同的单片机,延时时间也是不一样的,只能大概知道延时时间。


二、滴答定时器
下面展示的是main.cdelay.c部分函数
 int main(void)
{        
        HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK  8*5=40M
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
        LED_Init();
        delay_init();
           while(1)
        {
                LED0=!LED0;
                printf("\r\n今天是个好日子~");
                delay_ms(1000);
        }         
}
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        //选择外部时钟  HCLK/8
        fac_us=9;//该处的值为SYSCLK/8M,对应关系为72M--9;64M--8;56M--7…………
        fac_ms=(u16)fac_us*1000;//代表每个ms需要的systick时钟数   
}                                                                    
//延时nus,nus为要延时的us数.                                                                                       
void delay_us(u32 nus)
{               
        u32 temp;                     
        SysTick->LOAD=nus*fac_us;                                         //时间加载                           
        SysTick->VAL=0x00;                                                //清空计数器
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数         
        do
        {
                temp=SysTick->CTRL;
        }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
        SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
        SysTick->VAL =0X00;                                               //清空计数器         
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{                                    
        u32 temp;                  
        SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)
        SysTick->VAL =0x00;                                                        //清空计数器
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数  
        do
        {
                temp=SysTick->CTRL;
        }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
        SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
        SysTick->VAL =0X00;                                               //清空计数器                     
}




可以看到滴答定时器延时比较准,delay_ms(1000)就刚好是1s,有一点需要注意的是,修改了SYSCLK之后,在delay.c文件里面的系数也要修改。
总结:
优点:延时精准。
缺点:不适用其他型号单片机,不方便移植。




三、使用定时器进行定时,不使用中断

 int main(void)
{        
        HSE_SetSysClock(RCC_PLLMul_9);//SYSCLK  8*9=72M
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        uart_init(115200);         //串口初始化为115200
        LED_Init();
        TIM3_Init();
           while(1)
        {
                LED0=!LED0;
                printf("\r\n今天是个好日子~");
                TIM3_Delayms(1000);
        }         
}
void TIM3_Init(void)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟
        TIM_TimeBaseInitStructure.TIM_Period    = 50000-1;         //自动重装载值
        TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;     //定时器分频
        TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
        TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
        TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
}

//微秒级延时
void TIM3_Delayus(u16 xus)
{
        TIM_Cmd(TIM3,ENABLE); //启动定时器
        while(TIM3->CNT < xus);
        TIM3->CNT = 0;
        TIM_Cmd(TIM3,DISABLE); //关闭定时器
}

//毫秒级延时
void TIM3_Delayms(u16 xms)
{
        int i;
        for(i=0;i<xms;i++)
        {
                TIM3_Delayus(1000);
        }
}







可以看到延时也比较准,TIM3_Delayms(1000)就刚好是1s,有一点需要注意的是,修改了SYSCLK之后,在time.c文件里面的预分频系数需要改。
总结:
优点:延时精准。
缺点:不适用其他型号单片机,不方便移植。



当然延时函数的方法有很多种,也欢迎其他小伙伴一起谈论。

4889163bbec6f412c9.png (328.39 KB )

4889163bbec6f412c9.png

9274263bbee67aa494.png (324.29 KB )

9274263bbee67aa494.png

780563bbf1a2da9ae.png (137.71 KB )

780563bbf1a2da9ae.png

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

227

主题

578

帖子

6

粉丝