stm32 使用正点原子delay延时函数,主函数延时失效
2017-10-31 14:18
1136 查看
最近在做一个东西时,发现一个现象。之前一直没有发现过,或者发现也没有仔细研究过,在此为大家分享。
在使用原子哥的延时函数时,发现主函数里面的延时函数失效了。没有起任何作用。下面简单分析一个整个过程。
先直接上代码,很简单的一个实例
主循环里面做一个电平翻转,一个LED灯一亮一灭。
但是发现没有执行延时函数,LED一直快闪。
经过调试发现,我在一个定时器中断函数里面有个延时函数造成了主函数里面的延时失效。
因为实际需要操作4个超声波模块,所以我在一个定时中断里面选择了延时函数来进行操作。
后来经发现这也是造成主函数延时失效的原因
下面进行分析
延时初始化函数,SysTick 的时钟源自 HCLK 的 8 分频,我所使用是外部晶振为 8M,然后倍频到 72M,那么 SysTick 的时钟即为 9Mhz,也就是 SysTick 的计数器 VAL 每减 1,就代表时间过了 1/9us。
原子哥提供的延时函数
通过查看SysTick控制及状态寄存器
上图对应的寄存器名称分别为:
CTRL :控制和状态寄存器
LOAD :重新加载值寄存器
VAL :当前值寄存器
CALIB :校准寄存器
在主循环中,执行delay_ms(1000)函数,要延时的 ms 数换算成 SysTick 的时钟数,然后写入 LOAD 寄存器。然后清空当前寄存器 VAL 的内容,再开启倒数
功能。等到倒数结束。 此时进入中断服务函数,执行delay_us(20)函数,又重新将延时的us数换算成SysTick 的时钟数,然后写入 LOAD 寄存器。然后清空当前寄存器 VAL 的内容,再开启倒数。
当中断执行完后,主函数继续执行,但是此时LOAD寄存器里面的值已经被改变(被中断函数里面的延时函数改变),不在是最初计算的那个值。
所以就会出现上面的情况。
所以
最后我直接写了一个类似51里面的延时函数去解决这个问题,虽然我觉得很low。
在使用原子哥的延时函数时,发现主函数里面的延时函数失效了。没有起任何作用。下面简单分析一个整个过程。
先直接上代码,很简单的一个实例
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。
相关文章推荐
- OLED使用测试-参照STM32正点原子和电机控制原理(一)
- 改进的延时函数Delay(使用MsgWaitForMultipleObjects等待消息或超时的到来)
- 给正点原子的STM32工程加入类似Arduino编程里的micros()、millis()函数
- 23、STM32 上系统精确延时与获取 micro 时间函数的实现(FreeRTOS 中也能移植)
- 关于正点原子开发的基于stm32的USMART部分源码分析
- Delphi 目前使用delay函数功能
- STM32 使用通用计时器实现微秒延时
- STM32上使用UCOSII--软件定时器和任务延时
- STM32 SysTick定时器做延时函数
- 利用KERNEL32 API函数实现的通用延时函数DELAY
- Delay 延时函数
- 延时函数和Timer的使用
- STM32使用C标准库time.h里面的时间时钟函数
- STM32使用freertos时在中断中调用系统函数的注意点
- STM32学习笔记之fatfs文件系统接口函数使用
- STM32串口采样与仪表步进电机控制程序(主程序与串口程序,需用正点原子定时器中断实验程序模板)
- 精确延时函数DelayTime .
- LED显示(4):使用内核IO操作及延时函数
- 中断中处理延时及一些函数的调用规则(中断调i2c驱动有感)--中断中的延迟delay与printk函数的冲突【转】
- DSP delay延时函数