最近在做一个东西时,发现一个现象。之前一直没有发现过,或者发现也没有仔细研究过,在此为大家分享。
在使用原子哥的延时函数时,发现主函数里面的延时函数失效了。没有起任何作用。下面简单分析一个整个过程。
先直接上代码,很简单的一个实例
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 奇怪的位赋值问题 出错了
下一篇:stm32f103 学习笔记 —— 05 使用SysTick实现硬件延时
推荐阅读最新更新时间:2024-03-16 16:21