今天同事测试我之前写的一个小程序,发生了奇怪的错误,先是Uart通讯接收操作,出现了接收数据不全的问题:2个字节的应答帧,在实际运行中只能收到1个字节,导致程序死循环。检查后发现,是接收部分代码留的延时太短,造成了芯片误以为通讯已结束,但实际应答帧尚未传输完毕。(此处接收代码的工作模式是:当Uart接收到1个字节后,即开始一个定长的延时,该延时长度与通讯波特率相关,当正常通讯还在继续时,则应在延时结束前收到下一个字节数据,如延时结束仍未收到下一个字节数据,说明当前一帧数据已完成,可开始对已接收数据进行处理)
发现了问题后,进行相应的针对性操作,对延时长度进行了增加,即解决了此问题。但仍然觉得疑惑,因此段程序是已经通过测试的,运行正常,不应突然出现这种奇怪的错误,因此怀疑芯片自身的延时程序存在问题。
而后此程序继续出现的错误证实了之前的怀疑:新出现的错误是显示部分程序的延时明显不够,因此可以断定是延时部分出了问题。此程序的延时功能由滴答定时器的1ms延时函数来实现,对该函数进行排查,果然发现了问题根源。
stm32的滴答定时器设置主要有以下寄存器:
其中SysTick->CTRL寄存器包含了对滴答定时器的时钟频率来源设置和分频设置。前述小程序中,采用的是STM32F107芯片,外部时钟,工作频率为72MHz,在此程序中,为了让滴答定时器的工作压力稍减,使用了8分频的时钟设置,计数(72000000/8000)=9000,时长为1ms。代码如下:
void SystemTick_Configuration(void)
{
RCC_ClocksTypeDef RCC_Clocks;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
RCC_GetClocksFreq(&RCC_Clocks);
// SystTick configuration: an interrupt every 1 ms
if(SysTick_Config(RCC_Clocks.SYSCLK_Frequency / 8000))
{
while(1) FEED_WWDG;
}
}
此程序在我的电脑编译下是正常工作的,但在同事电脑编译下出了问题,滴答定时器的延时明显缩短,原因在于core_cm3.h文件。此文件位置在C:\Keil\ARM\CMSIS\Include文件夹下,此文件中的SysTick_Config函数包含以下操作:
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
此处对滴答定时器的时钟来源进行了操作,使其恢复了最高频率(72MHz),不分频:
<pre name="code" class="cpp">SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk |
在我的电脑上,因为对core_cm3.h文件做了以下修改,屏蔽了其对滴答定时器时钟的操作,所以可以正常运行:
SysTick->CTRL |= //SysTick_CTRL_CLKSOURCE_Msk |
但在同事的电脑上没有做此操作,从而导致了1ms延时实际只有0.125ms,于是出现了前述的种种错误。
解决办法:
1、使用滴答定时器时,不再考虑减轻芯片负担,直接采用原始频率,可以保证不会出现此问题。
2、修改文件调用指向,不调用工程外的相关头文件,防止出现移植时,不同电脑间,工程外文件的不同导致的错误。