stm32 使用正点原子delay延时函数,主函数延时失效

发布者:糖三角最新更新时间:2018-12-25 来源: eefocus关键字:stm32  正点原子  delay  延时函数 手机看文章 扫描二维码
随时随地手机看文章

最近在做一个东西时,发现一个现象。之前一直没有发现过,或者发现也没有仔细研究过,在此为大家分享。


在使用原子哥的延时函数时,发现主函数里面的延时函数失效了。没有起任何作用。下面简单分析一个整个过程。


先直接上代码,很简单的一个实例


int main(void)

{

 

delay_init();                              //延时函数初始化   

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

uart_init(115200);              //串口初始化为115200

  LED_Init();      //LED端口初始化

UltrasonicWave_Configuration();      //IO口初始化

  TIM5_Cap_Init(0XFFFF,72-1);      //以1Mhz的频率计数 

        TIM7_Int_Init(99,7199);              //10ms 超声波定时

        while(1)

{

LED1=!LED1;        

delay_us(50);

  delay_ms(1000);

        delay_ms(1000);

delay_ms(1000);

 

}


}


主循环里面做一个电平翻转,一个LED灯一亮一灭。


但是发现没有执行延时函数,LED一直快闪。


经过调试发现,我在一个定时器中断函数里面有个延时函数造成了主函数里面的延时失效。


void TIM7_IRQHandler(void)

{

if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断

{    

TIM_ClearITPendingBit(TIM7, TIM_IT_Update  );  //清除TIM7更新中断标志  

time_count++;

                switch (time_count)

{

case 1:

GPIO_SetBits(TRIG_PORT,TRIG_PIN_1);   //送>10US的高电平

        delay_us(20);                           //延时20US

GPIO_ResetBits(TRIG_PORT,TRIG_PIN_1);

break;


case 2:

        GPIO_SetBits(TRIG_PORT,TRIG_PIN_2);   //送>10US的高电平

                          delay_us(20);                           //延时20US

                          GPIO_ResetBits(TRIG_PORT,TRIG_PIN_2);

break;


case 3:

GPIO_SetBits(TRIG_PORT,TRIG_PIN_3);   //送>10US的高电平

                                delay_us(20);                       //延时20US

                                GPIO_ResetBits(TRIG_PORT,TRIG_PIN_3);

break;


case 4:

GPIO_SetBits(TRIG_PORT,TRIG_PIN_4);   //送>10US的高电平

                                delay_us(20);                       //延时20US

                                GPIO_ResetBits(TRIG_PORT,TRIG_PIN_4);

break;

}

if(time_count==4)

time_count=0;

}     

}


因为实际需要操作4个超声波模块,所以我在一个定时中断里面选择了延时函数来进行操作。


后来经发现这也是造成主函数延时失效的原因


下面进行分析


//初始化延迟函数

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

//SYSTICK的时钟固定为HCLK时钟的1/8

//SYSCLK:系统时钟

void delay_init()

{

#if SYSTEM_SUPPORT_OS  //如果需要支持OS.

u32 reload;

#endif

SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟  HCLK/8

fac_us=SystemCoreClock/8000000; //为系统时钟的1/8  

#if SYSTEM_SUPPORT_OS          //如果需要支持OS.

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

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

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

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

 

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

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

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

 

#else

fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数   

#endif

}


延时初始化函数,SysTick 的时钟源自 HCLK 的 8 分频,我所使用是外部晶振为 8M,然后倍频到 72M,那么 SysTick 的时钟即为 9Mhz,也就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/9us。


原子哥提供的延时函数


void delay_us(u32 nus)

{

u32 temp;      

SysTick->LOAD=nus*fac_us; //时间加载      重新加载寄存器值从这个值开始进行倒数

SysTick->VAL=0x00;        //清空计数器       当前寄存器值清0

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;               //清空计数器       


通过查看SysTick控制及状态寄存器 


上图对应的寄存器名称分别为:


CTRL  :控制和状态寄存器


LOAD  :重新加载值寄存器


VAL :当前值寄存器


CALIB :校准寄存器


在主循环中,执行delay_ms(1000)函数,要延时的 ms 数换算成 SysTick 的时钟数,然后写入 LOAD 寄存器。然后清空当前寄存器 VAL 的内容,再开启倒数功能。等到倒数结束。 此时进入中断服务函数,执行delay_us(20)函数,又重新将延时的us数换算成SysTick 的时钟数,然后写入 LOAD 寄存器。然后清空当前寄存器 VAL 的内容,再开启倒数。


当中断执行完后,主函数继续执行,但是此时LOAD寄存器里面的值已经被改变(被中断函数里面的延时函数改变),不在是最初计算的那个值。


所以就会出现上面的情况。


所以,最后我直接写了一个类似51里面的延时函数去解决这个问题,虽然我觉得很low


关键字:stm32  正点原子  delay  延时函数 引用地址:stm32 使用正点原子delay延时函数,主函数延时失效

上一篇:stm32 奇怪的位赋值问题 出错了
下一篇:stm32f103 学习笔记 —— 05 使用SysTick实现硬件延时

推荐阅读最新更新时间:2024-03-16 16:21

STM32之父谈2019产品蓝图:性价比、AI、开发体验
日前,在2019年STM32峰会上,STM32之父,意法半导体微控制器事业部全球市场总监Daniel Colonna介绍了STM32产品规划及生态战略。 Daniel几十年来的工作一直围绕着微控制器。自2006年起任意法半导体微控制器事业部全球市场总监 。他于1982年加入德州仪器,担任微控制器应用工程师;1986年加入Thomson-Mostek,负责8位微控制器业务;三年后,加入意法半导体法国公司,担任8位微控制器产品市场经理,并于90年代后期致力于启用ARM32位架构。 2002年,意法半导体推出首个基于ARM7的产品系列STR7,Daniel对此作出了重要贡献。后来,他带领团队启用新的Cortex-M架构,为意法半导体
[单片机]
<font color='red'>STM32</font>之父谈2019产品蓝图:性价比、AI、开发体验
STM32看门狗的使用
为什么使用看门狗 事情很简单先前做的一款采集数据的产品不知道为何异常,陷入死循环然后“死机”,分析了很多次,没发现原因,但是每次重新上点后就能正常采集到数据。后来找到了解决方法:看门狗!目的是当程序走入死循环或者硬件异常时,可以自动复位,这样就可以得到跟重新上电后差不多的效果了。 使用的平台:stm32f103系列单片机 使用的烧写调试模式:Jlink SWD 模式。 使用STM32官方模板库。 ST系列单片机看门狗分为两种: 1.独立看门狗,2.窗口看门狗。 独立看门狗: 可参看RM(reference Manual)的Independent watchdog (IWDG) 当然,只是简要查看
[单片机]
<font color='red'>STM32</font>看门狗的使用
STM32 AI实验课程—HAR在STM32L4上的实现
课程目的:旨在为AI深度学习解决方案提供关于ST生态系统的实践体验 实验步骤: 下载AI演示项目到ST开发板上 借助智能手机/平板电脑上的应用程序,将传感器数据和标签记录在ST开发板上 给定的Python脚本将处理数据集并训练NN模型 在STM32CubeMX.AI工具的帮助下,将神经网络训练好的模型导入到STM32项目中 相关软硬件简介 软硬件需求列表 IoT开发板—B-L475E-IOT01A FP-AI-SENSING1 AI特定功能:训练用的数据集、数据标注、STM32模型转换对应代码 其他功能:使用RTOS实现低功耗、手机应用软件“ST BLE Sensor” 实验1:STM32固件下载和演示测
[单片机]
<font color='red'>STM32</font> AI实验课程—HAR在STM32L4上的实现
stm32 rtc 误差实验
1. stm32f103re,使用外部晶振32.768k,没有校准rtc时钟,使用串口打印时间,用带时间戳的串口工具记录串口接收的时间。 2. 测试时间一天。 数据如下: 开始计时:电脑时间 stm32时间 00:03:20 结束计时:电脑时间 stm32时间 25:35:50 历时:电脑 25:32:01:641 stm32时间 25:32:30 3. 结论 单片机快28秒, 28/25=1.12 stm32平均每小时快1.12秒 4. 按照AN2604.pdf描述的原理,RTC 的校准值应在0-127之间。可实现的校准误差对应为0-121ppm。相当于每30天跑快的秒数为0-3
[单片机]
STM32学习札记--ADC的有关函数个人见解
STM32的ADC功能较为完善,个人理解的还不是很深入。一点点的吃透! 在学习ADC之前我们需要知道相关的ADC的配置 一:ADC的最关键的时钟需要使能,用的是HSI:RCC_HSICmd(ENABLE);//时钟源选择及配置,参见时钟树 二:既然是ADC采集,当然需要你设置需要采集的通道,需要利用的I/O口 1,使能相应IO口的时钟---- 配置 I/O的引脚,模式,速率,输出类型及是否上下拉! 2,使能相应ADC的时钟--- 配置ADC的工作方式等 三:基础工作做完后,当然是要准备去采集数据, 包含两种方式: 规则通道顺序配置和注入通道配置 个人理解两者的区别在于前者是连续转换已经设定的通道,后者是等
[单片机]
STM8|STM32 看门狗使用
STM8和STM32都配备了独立看门狗,其作用之大不言而喻。以下为STM8及STM32的独立看门狗使用例: 对于STM32单片机: #define SYS_IWDG_OPEN IWDG- KR=0xCCCC; #define SYS_IWDG_FEED IWDG- KR=0xAAAA; void SystemIWDG_Config(uint32 OverTime) { RCC- CSR|=RCC_CSR_LSION; while((RCC- CSR&RCC_CSR_LSIRDY)==0); IWDG- KR=0x5555; //使能模块访问 while(IWDG- SR&IWDG_S
[单片机]
STM32——流水灯
stm32f10x_conf.h:打开stm32f10x_gpio.h和stm32f10x_rcc.h; stm32f10x_gpio.c 和 stm32f10x_rcc.c加入工程模板中,只说重点。 【stm32f10x_rcc.c用于配置系统时钟 和外设时钟,由于每个外设都要配置时钟,所以它是每个外设都需要用到的库文件。】 // 新建led.h led.c #ifndef _LED_H_ #define _LED_H_ #include stm32f10x.h #define ON 0 #define OFF 1 #define LED1(a) if (a) GPIO
[单片机]
采用STM32 单片机的太阳能LED街灯解决方案
  随着化石类能源的日益减少,以及温室气体的过度排放导致全球变暖问题越来越受到重视,人们一方面在积极开发各类可再生新能源,另一方面也在倡导节能减排的绿色环保技术。太阳能作为取之不尽、用之不竭的清洁能源,成为众多可再生能源的重要代表;而在照明领域,寿命长、节能、安全、绿色环保、色彩丰富、微型化的LED固态照明也已被公认为世界一种节能环保的重要途径。太阳能-LED街灯同时整合了这两者的优势,利用清洁能源以及高效率的LED实现绿色照明。   本文介绍的太阳能-LED街灯方案,能自动检测环境光以控制路灯的工作状态,最大功率点追踪(MPPT)保证最大太阳能电池板效率,恒电流控制LED,并带有蓄电池状态输出以及用户
[电源管理]
采用<font color='red'>STM32</font> 单片机的太阳能LED街灯解决方案
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved