使用STM32的systick定时器中断实现RTC工作过程出错

开发环境:keil MDK V5.10

操作系统:windows 7(32位)

目标硬件:STM32F103C8

问题描述:使用STM32的systick定时器实现RTC功能。具体方法为systick滴答计时器配置为1ms时间间隔的滴答中断,定义一个RTC结构体,包含年、月、日、时、分、秒。在每进一次systick中断服务程序中更新一次RTC的值。主程序通过不停地获取RTC的时钟,每一秒钟使用printf函数输出当前的时间。测试过程发现绝大多数时间系统正常工作,但依然存在部分情况系统无法进行正常的输出。相关代码和测试结果如下:

/**
  * @brief  系统滴答定时器中断服务程序,主要功能为更新实时时钟
  *         
  * @note   无
  * @param  无
  * @retval 无
  */
void systick_isr(void)
{
  systick.tick_num++;
  rtc.ms++;
  rtc_update(&rtc);  
}

/**
  * @brief  获取系统实时时钟
  *         
  * @note   无
  * @param  用于存放当前系统实时时钟数据的结构体的指针
  * @retval 无
  */
void get_rtc(time_t *ptime)
{	
	*ptime = rtc;
}

时钟测试程序片段

	while(1)
	{
		get_rtc(&tprtc);
		if(j != tprtc.sec)
		{
			printf("time:%02d-%02d-%02d %02d:%02d:%02d\r\n",tprtc.year,tprtc.mon,tprtc.date,tprtc.hour,tprtc.min,tprtc.sec);
			j = tprtc.sec;
			LEDTX_TOGGLE();
			LEDRX_TOGGLE();
			LEDNET_TOGGLE();
                }
	}

测试结果如下:
time:14-04-28 01:55:55
time:14-04-28 01:55:56
time:14-04-28 01:55:57
time:14-04-28 01:55:58
time:14-04-28 01:55:59
time:14-04-28 01:55:00

time:14-04-28 01:56:01
time:14-04-28 01:56:02
time:14-04-28 01:56:03

原因分析:get_rtc函数在使用中断服务程序改变变量rtc时,没有进行保护导致赋值错误。具体原因为,rtc是一个结构体,在get_rtc函数中尽管只使用了一行代码进行赋值操作,实际上应该是很多条指令周期。假设其拷贝数据过程按年、月、日、时、分、秒的顺序进行拷贝,而当get_rtc函数拷贝完“分”的时候,也就是上边测试结果中的“55”这个数值拷贝完之后,发生了中断,中断中秒由原来的59变为0,分变为56,中断服务程序退出时,get_rtc继续拷贝秒,此时秒为0,从而出现以上错误结果。针对该问题可以在进行拷贝前禁止systick中断来处理,代码如下:

void get_rtc(time_t *ptime)
{
	systick_irq_dis();//禁止systick中断	
	*ptime = rtc;
	systick_irq_en();//使能systick中断
}

修改后测试发现比原来更加糟糕,程序几乎无法打印出任何信息来。经过反复分析和仿真发现禁止systick中断函数及使能systick中断函数均为操作寄存器相应的位来使能和禁止中断的。而该寄存器一旦被读或者被写将导致已经产生的中断标志位失效。换言之就是说当在执行"*ptime = rtc"这句代码的时候发生了中断,中断标志置位,理论上当使能中断后就应该进行中断处理,悲哀的是使能中断后中断标志被清空,无法产生中断,从而不能进入中断服务函数,最终导致rtc非常困难才能被更新,程序长期无法正常打印。


经验总结:
1.主程序代码操作中断服务函数需要改变得变量时需要谨慎,一般是在操作前关闭中断,使用之后立即恢复中断。

2.在使用关闭中断和恢复中断函数时务对该操作是否会影响中断标志位进行考虑!

3.如果不关闭中断,那么必须保证中断函数对变量的操作应该在一个指令周期完成,也就是中断操作为原子操作;同时外部程序使用该变量的操作也应该在一个指令周期完成,为原子操作。


你可能感兴趣的:(调试记录,程序设计)