历史上的今天
返回首页

历史上的今天

今天是:2024年12月25日(星期三)

正在发生

2018年12月25日 | stm32 使用正点原子delay延时函数,主函数延时失效

2018-12-25 来源:eefocus

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


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


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


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


推荐阅读

史海拾趣

台湾君耀(Brightking)公司的发展小趣事

君耀(Brightking)公司于1996年在台湾新竹高科技园区创立,初期专注于防雷元器件的研发和生产。凭借对技术的深入研究和市场需求的敏锐洞察,君耀逐步在台湾的电子器件市场上占得一席之地。随着产品质量和技术含量的提升,君耀的产品开始受到国内外客户的青睐,销售网络逐渐拓展至全球。

格莱尔(GLE)公司的发展小趣事

格莱尔始终将技术创新视为企业发展的核心动力。公司不断投入研发资源,引进先进的生产设备和高精度的检测仪器,确保产品质量的稳步提升。同时,格莱尔还通过了ISQ9001质量管理体系和IATF16949汽车质量管理体系认证,全部产品实现了符合RoHS标准的无铅化生产,达到了欧盟的环保要求。这些努力使得格莱尔的产品在市场上赢得了良好的口碑和广泛的认可。

D3公司的发展小趣事

面对全球电子市场的巨大潜力,D3公司积极实施全球化战略。公司不仅在国内市场深耕细作,还积极拓展海外市场。通过与国外知名企业的合作,D3公司的产品逐渐进入国际市场,并在多个国家和地区取得了良好的销售业绩。同时,公司还加强了对海外市场的调研和分析,以更好地满足当地消费者的需求。这种全球化战略的实施,为D3公司的长远发展奠定了坚实的基础。

C.K Magma公司的发展小趣事

在追求经济效益的同时,C.K Magma公司也积极履行社会责任。他们注重绿色发展和可持续经营,通过采用环保材料和工艺、优化生产过程等方式,降低产品对环境的影响。此外,公司还积极参与公益事业,为社会做出贡献。这些举措不仅提升了公司的社会形象,也为公司的长远发展提供了有力保障。

这五个故事展示了C.K Magma公司在电子行业中的发展历程和取得的成就。通过技术突破、市场拓展、国际合作、创新产品和绿色发展等方面的努力,公司逐渐在行业中崭露头角,成为了一家具有影响力和竞争力的企业。

Akahane Electronics Ind Corp公司的发展小趣事

随着全球市场的不断扩大,Akahane意识到单打独斗难以为继,于是积极寻求与国际同行的合作。通过与欧美知名电子企业建立战略合作关系,Akahane不仅获得了先进的技术支持,还成功打入国际市场,进一步提升了品牌影响力。同时,公司还积极参与国际电子行业的交流活动,学习借鉴先进的管理经验和市场策略,为公司的长远发展奠定了坚实基础。

Allied Controls Incorporated公司的发展小趣事

为了应对市场的多变性和不确定性,Akahane实施了多元化战略。除了继续深耕半导体领域外,公司还积极拓展智能家居、物联网等新兴领域。通过不断推出创新产品和完善服务体系,Akahane成功打开了新市场的大门,实现了业务的多元化发展。这一战略不仅为公司带来了新的增长点,还增强了公司的抗风险能力。


这些故事虽然基于虚构,但它们反映了电子行业中企业可能经历的一些典型发展路径和挑战。希望这些故事能够满足你的需求。

问答坊 | AI 解惑

为什么禁止上拉

用ARM9 S3C2440做跑马灯的实验时,要禁止上拉电阻,请问这是为什么啊?…

查看全部问答>

烧写时候启动找不到FLASH ID?

    现象:我们的硬件平台配置是:PXA270+两片Norflsh(intel strata flash 28F256P30B),通过Jflashmm.exe进行烧写,有两块主板烧写出错:一片能读出ID(0x8919),另一片读不对(FFFF)或者(0x0000),都是low part块,是Flash坏了吗 ...…

查看全部问答>

ISAPI filter的注册问题

现在我想知道要在哪里能注册上isapi filter并且能进行访问 我在HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/W3SVC/Parameters/FilterDLL 下面给出了 Filter的全路径 进行访问还是不行 在HKEY_LOCAL_MACHINE\\COMM\\HTTPD\\Filter D ...…

查看全部问答>

陕西杨工c8051f单片机博客

手机 13002928013 http://c8051fmcu.blog.sohu.com…

查看全部问答>

请教2个函数

1、我定义char a[10];          int i =0; 可以用        _snprintf(a, 10, \"%d\", i); 但使用RtlStringCchPrintfW(a, 10, \"%d\", i)时出错 2、我定义BK_CMD_T tAtCmd;     &n ...…

查看全部问答>

STM32如何实现精确延时

                                 STM32如何实现精确延时到us…

查看全部问答>

大家学习AVR单片机的详细过程

我是大二的学生,学AVR单片机才不过一个月,但通过和51的对比学习,上手还是挺快的。这里记录下我学习的过程,感觉自己走了不少弯路,为了避免更多初学者也同样绕太多弯路,希望广大工程师都详细介绍自己怎样起步学习AVR单片机的。也给我这个初学者 ...…

查看全部问答>

关于局部,全局变量(DS18b02温度传感仿真遇到 的问题)

uchar  dsreadebyte() {         uchar dat; uchar i,j;         for (i=0;i>=1;                    _nop_();    ...…

查看全部问答>

求MSP-EXP430F5529的三轴加速度传感器使用方法

想用MSP-EXP430F5529的三轴加速度传感器检测一个人是否跌倒,但没用过三轴加速度传感器,不知有没有大神能够指导一下这个的用法。…

查看全部问答>

我的Beaglebone测试环境

上个图吧,有时间的时候我就把玩一下,呵呵…

查看全部问答>