正点原子延时函数delay_ms延时失效的原因

1、问题陈述

        今天在测试小车程序的时候使用了如下代码,发现延时并没有达到期望的4s,而是仅仅延时了0.4s左右,本来以为少加了个0,最后在我多次测试下来,发现在延时大约超过2s的时候就会失效。

    while(1)
	{
        Set_Pwm(6000,6000);

        printf("%d\t",Read_Encoder(2));
        printf("%d",Read_Encoder(3));
        printf("\r\n");
		
        delay_ms(4000);
        Set_Pwm(-5000,-5000);
        printf("%d\t",Read_Encoder(2));
        printf("%d",Read_Encoder(3));
        printf("\r\n");
        delay_ms(4000);
        
	} 

2、问题解决

        再我重新翻阅了一下不完全手册后,终于发现了问题出在了SysTick-> VAL这个寄存器上:

        我们先看初始化函数::

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;				//每秒钟的计数次数 单位为M  
	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
}	

        我们主要看fac_us和fac_ms ,他们分别表示延时1us、1ms需要多少个SysTick 时钟周期。通过跳转可知,SystemCoreClock为72Mhz,SystemCoreClock/8 代表经过1s需要多少个时钟周期,再除以1000000则表示经过1us需要多少个时钟周期。通过计算可知fac_us=9,fac_ms=9000。

        我们再看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;      					 //清空计数器	 
}

        SysTick是MDK自定义的一个寄存器。SysTick->CTRL主要是开关计数器,SysTick-> LOAD类似于重装载寄存器,在倒数到0后进行重装载,SysTick-> VAL是计数器,进行倒数。

        这个函数先计算出需要倒数的时间nus*fac_us,存到LOAD中,然后清空当前寄存器 VAL 的内容,再开启倒数功能。等到倒数结束,最后关闭 SysTick,清空 VAL 的值。

        但是但是!!!重点的来了,LOAD,VAL是个24位寄存器。也就是最大值为2^24=16777216.也就是说:需要倒数的时间:nus*fac_us必须小于2^24,则nus=2^24/9=1864135.1111111.换算成ms大概就是1800秒

        所以开头我延时4000ms的效果其实就是4000-(1800*2)=400ms,约0.4s左右。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这段代码是 MSP430 单片机的时钟初始化函数。主要的功能是配置时钟源和时钟频率,使得单片机能够正常工作。 具体解释如下: 1. P5SEL |= BIT2 + BIT3; // P5.2和P5.3选择XT2晶振功能 这句代码的作用是将 P5.2 和 P5.3 的功能设置为 XT2 晶振。XT2 是一种外部晶振,用于提供高精度的时钟信号。 2. UCSCTL6 &= ~XT2OFF; // 使能XT2 这句代码的作用是使能 XT2 晶振。 3. UCSCTL3 |= SELREF_2; // FLL模块的参考时钟源选择REFO 这句代码的作用是将 FLL 模块的参考时钟源选择为 REFO。REFO 是 MSP430 单片机内部的振荡器时钟源。 4. UCSCTL4 |= SELA_2; // ACLK=REFO,SMCLK和MCLK为DCO 这句代码的作用是将 ACLK 设置为 REFO,SMCLK 和 MCLK 设置为 DCO。DCO 是 MSP430 单片机内部的数字控制振荡器,可以通过软件控制其频率。 5. do { UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); SFRIFG1 &= ~OFIFG; }while(SFRIFG1 &OFIFG); 这段代码的作用是测试晶振是否产生故障失效,并清除故障失效标志位。如果检测到晶振失效,则需要重新初始化时钟。 6. __bis_SR_register(SCG0); //禁止FLL 这句代码的作用是禁止 FLL。FLL 是一个数字锁相环,用于调节 DCO 的频率。 7. UCSCTL1 = DCORSEL_6; //选择DCO频率范围 这句代码的作用是选择 DCO 的频率范围。DCO 有多种频率范围可供选择,这里选择的是最高频率范围。 8. UCSCTL2 |= 499; // 设置DCO频率为16MHz 这句代码的作用是设置 DCO 的频率为 16MHz。具体的计算公式为:(499 + 1) * 32768 = 16MHz。其中,32768 是 REFO 的频率,499 是用于调节 DCO 频率的参数。 9. __bic_SR_register(SCG0); //启用FLL 这句代码的作用是启用 FLL。经过上述设置后,FLL 会根据参考时钟源 REFO 和 DCO 的频率范围,自动调节 DCO 的频率,使得其稳定在 16MHz。 10. __delay_cycles(250000); // 延时,待DCO工作稳定 这句代码的作用是延时一段时间,等待 DCO 的频率稳定。在 DCO 频率改变后,需要一定的时间才能稳定下来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值