刚接触STM32,还只看了时钟系统、中断、GPIO和通用定时器的普通定时功能。
编译器MDK4.0,借固件库写个了用TIM2控制灯闪的程序。不优化时正常,优化后(优化等级1~3都试过)闪烁频率翻倍了!本来一秒一次,现在一秒两次。
使用RCC_GetClocksFreq(&RCC_clock_value)函数读出来的时钟值如下:f[SYSCLK]=8MHz,f[HCLK]=8MHz,f[PCLK1]=8MHz,f[PCLK2]=8MHz,f[ADCCLK]=4MHz。读出的TIM2->PSC值为99(即TIM2分频系数为100)
这个问题找得我头大了。各位帮忙看看。下面是相关部分程序:
void RCC_config(void)
{
RCC_HSEConfig(RCC_HSE_ON); //HES开启;
if(SUCCESS==RCC_WaitForHSEStartUp()); //等待时钟准备好;
{
// while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE); //使用HSE时钟;
while(RCC_GetSYSCLKSource()!= 0x04); //检查时钟源是否已是HSE;
RCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB时钟分频系数为1;
RCC_PCLK2Config(RCC_HCLK_Div1); //APB2时钟分频系数为1;
RCC_PCLK1Config(RCC_HCLK_Div1); //APB1时钟分频系数为1;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //开启GPIOB时钟;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2时钟;
}
}
void TIM2_config(void)
{
TIM_InternalClockConfig(TIM2); //TIM2使用内部时钟;
TIM_SetAutoreload(TIM2,4000); //设置自动重装载寄存器,定时50ms;
TIM_CounterModeConfig(TIM2,TIM_CounterMode_Up); //TIM2向上计数,
即从0到TIM2_ARR;
TIM_PrescalerConfig(TIM2,99,TIM_PSCReloadMode_Immediate); //f[TIM2]=
f[CLK_APB1]/(99+1),即100分频,并立即产生更新;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //此次更新不产生中断;
TIM_Cmd(TIM2,ENABLE); //开启计数器;
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启TIM2的更新中断;
}
//中断配置;
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//开TIM2全局中断;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择TIM2通道;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void device_init(void)
{
RCC_config();
GPIO_config();
TIM2_config();
NVIC_Configuration();
}
//TIM2 50ms中断函数;
void TIM2_IRQHandler(void)
{
static vu8 t500ms_for_PCB_shine=0;
if(++t500ms_for_PCB_shine>=10)
{
LED1_SHINE();
t500ms_for_PCB_shine=0;
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志;
}
int main(void)
{
device_init();
while(1);
}
大家都没遇到过这问题呀?一天又过去了,我还没找出来,急晕了。
看时钟以及中断没有什么问题的,我建议device_init()函数走完后看下timer寄存器是否和预期一样,如果一样说明定时器没问题。
LED1_SHINE()函数怎么实现的呢,里面没有delay之类的吧?
再不行,就调用DBGMCU_Config(DBGMCU_TIM2_STOP, ENABLE);把定时器设置成调试运行,实际的跟踪下
TO 5楼:
谢谢你的建议。TIM2的所有寄存器我都对比过,哪怕是没有用到的那些捕捉、输出类的。优化等级不同寄存器读出的结果是一样的。包括RCC的所有寄存器。
LED1_SHINE()是一个预定义:
#define LED1_ON() GPIO_ResetBits(GPIOB,GPIO_Pin_12) //GPIOB->BRR=GPIO_Pin_12
#define LED1_OFF() GPIO_SetBits(GPIOB,GPIO_Pin_12) //GPIOB->BSRR=GPIO_Pin_12
#define LED1_SHINE() GPIOB->ODR ^=GPIO_Pin_12
今天上午才发现DATASHEET里面有讲APB1分频系数为1的话,f[TIM2]=f[APB1],
否则f[TIM2]=2f[APB1],因此找到了一线暑光,但后来试了,不管怎样调APB1的分频系数,优化级别调高后频率就变了。。。
还有一个怪问题:
还是1楼所述代码,优化等级为0,将TIM2中断程序中的清除中断标志
“TIM_ClearITPendingBit(TIM2,TIM_IT_Update)”去掉,改成如下:
先定义一个函数:
void test_fanc(void)
{
TIM2->SR &= 0xfffe;
}
TIM2中断程序最后调用test_fanc();测试结果正常。
再改一种方式:不定义上述函数,直接在TIM2中断程序最后写“TIM2->SR &= 0xfffe;”灯的闪烁频率翻倍了,经测试正好是前面频率的两倍!注意这里的优化等级还是0!
RCC和TIM2相关的寄存器、读频率函数读出的频率两种方式下完全一样!
看了汇编,调用test_fanc()的多了一句
0x080001AE F000F82D BL.W test_func (0x0800020C)
然后再有不同的地方就是main()开始之前的一些。本人汇编水平太烂,根据全篇汇篇来找出受影响的地方太困难。~~~~
另外,device_init()函数中RCC_config()、GPIO_config()、TIM2_config()这三个函数我任意调换一下位置灯都不会亮的,只有这个排列顺序灯才会正常闪烁。
7楼这个问题是因为总线的延迟,造成外设的状态变化与CPU不同步所导致。
解决的办法是,如果清中断标志的操作是中断处理程序中的最后一条语句时,需要在这条语句之后加以个小的延迟,一般为2~3个NOP。
香帅呀,你咋不早出现呢?这个下午真高兴!
优化的问题也解决了!以后进中断就清标志了。
不过有一点没弄明白,为什么频率正好是正常的2倍呢?如果说是因为没有清除又引发了一次中断,那么灯闪烁的占空比不会正好是1:1吧(经过测试发现不管怎样调占空比能保持1:1)。
可能这一块的处理机制我还是没了解。
哈哈,我这个人比较懒,不愿意看程序(很久没看,也不大看得懂),
顶香帅,原来是这样,怪不得中断处理中,都是一进入中断函数就清标志呢