litin326 发表于 2013-3-9 09:42:16

STM32 的定时器延时为何软件仿真误差那么大

这个定时器2是春风电源里的 数码管动态扫描和按键扫描中断函数
在中段函数里放置断点,仿真界面里观察每次的断点间隔时间都是在2MS以上
/*********************************************************************************
* 函数名称: Time2_Configuration        数码管动态扫描和按键扫描
* 功    能: 定时器2初始化,3mS (其前面的时钟设为72M啊应该1mS?,软件仿真为2MS)
                        72000000HZ / 36分频 = 2000000HZ   1 / 2000000HZ = 0.0000005s = 0.5us
                        每个加1的脉冲周期为0.5us * 2000设定值 =1ms
* 参    数: 无
* 返回值: 无
**********************************************************************************/
void Time2_Configuration(void)
{
                TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
                //TIM_Period设置了在下一个更新事件装入活动的自动重装载寄存器周期的值。它的取值必须在0x0000和0xFFFF之间
                TIM_TimeBaseStructure.TIM_Period = 2000;                                                              //设置定时器周期
                //TIM_Prescaler设置了用来作为TIMx时钟频率除数的预分频值。它的取值必须在0x0000和0xFFFF之间
                TIM_TimeBaseStructure.TIM_Prescaler = 36;                                                  //设置定时器时钟分频值        它的取值必须在0x0000和0xFFFF之间
                TIM_TimeBaseStructure.TIM_ClockDivision = 0;                                                //时钟分割
                TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;              //设置定时器向上计数
                TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;                                     //设置定时器重装载值
                TIM_TimeBaseInit(TIM2, & TIM_TimeBaseStructure);                                   //配置定时器2       
               
                TIM_ClearFlag(TIM2, TIM_FLAG_Update);                                                //清除中断标志(UIF)防止配置完成打开中断时立即产生中断
                TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE );                 //使能定时器2允许更新中断(UIE)
                //*******************************************************************************
                //*使能定时器2功能(CEN)
                //*在软件设置了CEN位后,外部时钟、门控模式和编码器模式才能工作。触发模式可以自动地
                //*通过硬件设置CEN位。 在单脉冲模式下,当发生更新事件时,CEN被自动清除。
                //********************************************************************************/       
                TIM_Cmd(TIM2, ENABLE);                                                                                                   //使能定时器2功能(CEN)
}

/*******************************************************************************
* Function Name: TIM2_IRQHandler
* Description    : This function handles TIM2 global interrupt request.
                   数码管动态扫描和按键扫描
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void TIM2_IRQHandler(void)
{       
        static vu8 W = 0;                 //Static 局部静态变量
        static vu8 keyscan = 0;       
        static vu16 key5del = 100;//按键5禁止延时       
        static vu16 key6del = 100;//按键6禁止延时       
        vu8 a,c,d;                                  //a为延时?/c,d为判断编码器?拨动
        vu8 e;                                          //用来设定4个调节发光二极管指示

        static vu8 Key0Circs = 0;//按键0状态
        static vu8 Key1Circs = 0;//按键1状态
        static vu8 Key2Circs = 0;//按键2状态
        static vu8 Key3Circs = 0;//按键3状态
        static vu8 Key4Circs = 0;//按键4状态
        static vu8 Key5Circs = 0;//按键5状态
        static vu8 Key6Circs = 0;//按键6状态
        static vu8 Key7Circs = 0;//按键7状态


    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

    GPIOE->ODR^=GPIO_Pin_7;   ******************************在此处放置断点,在仿真界面里观察每次的断点间隔时间都是在2MS以上***************************************

        if(I_time > 0)
          I_time--;
        W ++;
        if(W >= 8) W = 0;

        keyscan ++;
        if(keyscan >= 6)    keyscan = 0;

        if(DispDelay)DispDelay --;   //静态显示延时

        //b = keyscan;
        if(keyscan == 5)   keyscan = 7;

        //4个调节发光二极管指示
        if(AdjustSign == 0)        e = 0xE0;
        if(AdjustSign == 1)        e = 0xD0;
        if(AdjustSign == 2)        e = 0xB0;
        if(AdjustSign == 3)        e = 0x70;

        SER_SetOut();                  //配置输出                                                                          
        /*将各数据移入595中*/                   //                                          595的HGFEDCBA 8位并口
        HC595_Tx_BYTE(0x01 << 5);      //按键扫描,向595写入一个字节 00100000 直接给0X20也行
        HC595_Tx_BYTE(DispNum]);//后4位段码 电流?               
        HC595_Tx_BYTE(DispNum]);   //前4位段码 电压?
        HC595_Tx_BYTE((0x01 << (W % 4)) | e);             //位码,LED (1%4=1),小于4的被除数结果为被除数
        /*将移入的数据输出到并口上*/
        RCK_H();                                     //没这两条旋转编码器不工作,但按键都工作
        RCK_L();
        SER_SetIn();                             //配置输入
    for(a = 0;a < 50;a++);              //延时?

    if(Key_IN == 0)c = 0;      //key_IN 被宏定义为一个读取指定端口管脚输入的库函数
        else             c = 1;             //c为1时就说明编码器KA按下

        SER_SetOut();                  //配置输出   595的 HGFEDCBA 8位并口
        HC595_Tx_BYTE(0x01 << 6);      //按键扫描              01000000
        HC595_Tx_BYTE(DispNum]);//后4位段码       
        HC595_Tx_BYTE(DispNum]);   //前4位段码
        HC595_Tx_BYTE((0x01 << (W % 4)) | e);             //位码,LED
        RCK_H();
        RCK_L();
        SER_SetIn();                             //配置输入
        for(a = 0;a < 50;a++);

        if(Key_IN == 0)d = 0;
        else             d = 1;                   //d为1时就说明编码器KB按下

        if(key5del > 0) key5del --;    //按键5禁止延时?
        if(key6del > 0) key6del --;    //按键6禁止延时

        //Key5Circs 按键5状态   
        if((c == 0)&&(d == 0)&&(Key5Circs == 0)&&(key5del == 0))Key5Circs = 1; //c0,d0
    else if((c == 0)&&(d == 1)&&(Key5Circs == 1))                           Key5Circs = 2; //c0,d1
        else if((c == 1)&&(d == 1)&&(Key5Circs == 2))                           Key5Circs = 3; //c1,d1
        else if((c == 1)&&(d == 0)&&(Key5Circs == 3))                      Key5Circs = 4; //c1,d0   
        else if((c == 0)&&(d == 0)&&(Key5Circs == 4))
               {
                        Key5Circs = 0;Key5Sign = 1;Key6Circs = 0;key6del = 100;                     //c0,d0 按键有效
              }                         //          Key5Sign :全局键5标志
                                                                                                                          
        //Key6Circs 按键6状态   
        if((c == 0)&&(d == 0)&&(Key6Circs == 0)&&(key6del == 0))Key6Circs = 1; //c0,d0
    else if((c == 1)&&(d == 0)&&(Key6Circs == 1))Key6Circs = 2;            //c1,d0
        else if((c == 1)&&(d == 1)&&(Key6Circs == 2))Key6Circs = 3;            //c1,d1
        else if((c == 0)&&(d == 1)&&(Key6Circs == 3))Key6Circs = 4;            //co,d1   
        else if((c == 0)&&(d == 0)&&(Key6Circs == 4))
                {
                        Key6Circs = 0;Key6Sign = 1;Key5Circs = 0;key5del = 100;                       //c0,d0 按键有效
                }                                                                                                                        

        //if(((c == 0) && (d == 0)) && (Key6Circs == 2)) Key6Circs = 0;
        //if(((c == 0) && (d == 0)) && (Key5Circs == 2)) Key5Circs = 0;

        SER_SetOut();//配置输出
        HC595_Tx_BYTE(0x01 << keyscan);                         //按键扫描
        HC595_Tx_BYTE(DispNum]);//后4位段码       
        HC595_Tx_BYTE(DispNum]);   //前4位段码
        HC595_Tx_BYTE((0x01 << (W % 4)) | e);             //位码,LED
    RCK_H();
        RCK_L();
        SER_SetIn();//配置输入
    for(a = 0;a < 50;a++);

        if(keyscan == 0)
        {
                if((Key_IN == 0)&&(Key0Circs == 0))       Key0Circs = 1;               //按键释放状态0
                else if((Key_IN == 1)&&(Key0Circs == 1))Key0Circs = 2;               //按键按下状态1
                else if((Key_IN == 0)&&(Key0Circs == 2)){Key0Circs = 0;Key0Sign = 1;}//按键释放状态2
        }
        else if(keyscan == 1)
        {
            if((Key_IN == 0)&&(Key1Circs == 0))       Key1Circs = 1;               //按键释放状态0
                else if((Key_IN == 1)&&(Key1Circs == 1))Key1Circs = 2;               //按键按下状态1
                else if((Key_IN == 0)&&(Key1Circs == 2)){Key1Circs = 0;Key1Sign = 1;}//按键释放状态2
        }
    else if(keyscan == 2)
        {
                if((Key_IN == 0)&&(Key2Circs == 0))       Key2Circs = 1;               //按键释放状态0
                else if((Key_IN == 1)&&(Key2Circs == 1))Key2Circs = 2;               //按键按下状态1
                else if((Key_IN == 0)&&(Key2Circs == 2)){Key2Circs = 0;Key2Sign = 1;}//按键释放状态2
        }
        else if(keyscan == 3)
        {
                if((Key_IN == 0)&&(Key3Circs == 0))       Key3Circs = 1;               //按键释放状态0
                else if((Key_IN == 1)&&(Key3Circs == 1))Key3Circs = 2;               //按键按下状态1
                else if((Key_IN == 0)&&(Key3Circs == 2)){Key3Circs = 0;Key3Sign = 1;}//按键释放状态2          
        }
        else if(keyscan == 4)
        {
                if((Key_IN == 0)&&(Key4Circs == 0))       Key4Circs = 1;               //按键释放状态0
                else if((Key_IN == 1)&&(Key4Circs == 1))Key4Circs = 2;               //按键按下状态1
                else if((Key_IN == 0)&&(Key4Circs == 2)){Key4Circs = 0;Key4Sign = 1;}//按键释放状态2
        }
        else if(keyscan == 7)
        {
                if((Key_IN == 0)&&(Key7Circs == 0))       Key7Circs = 1;               //按键释放状态0
                else if((Key_IN == 1)&&(Key7Circs == 1))Key7Circs = 2;               //按键按下状态1
                else if((Key_IN == 0)&&(Key7Circs == 2)){Key7Circs = 0;Key7Sign = 1;}//按键释放状态2
        }

        if(LockKey) //按键锁定标志有效
        {
          Key0Sign = 0;//把没有执行的按键标志清掉
                //Key1Sign = 0;
                Key2Sign = 0;
                Key3Sign = 0;
                Key4Sign = 0;
                Key5Sign = 0;
                Key6Sign = 0;
                Key7Sign = 0;
        }
        if(KeyEn == 0) //按键失能
        {
                  Key0Sign = 0;//把所有按键标志清掉
                Key1Sign = 0;
                Key2Sign = 0;
                Key3Sign = 0;
                Key4Sign = 0;
                Key5Sign = 0;
                Key6Sign = 0;
                Key7Sign = 0;
        }
}

litin326 发表于 2013-3-12 15:11:53

本帖最后由 litin326 于 2013-3-12 15:13 编辑

找到原因,是我对STM32时钟树的不太了解


/*********************************************************************************
* 函数名称: RCC_Configuration
* 功    能: 设置各个系统时钟
* 参    数: 无
* 返回值: 无
**********************************************************************************/
void RCC_Configuration(void)
{

ErrorStatus HSEStartUpStatus;
RCC_DeInit(); /* RCC system reset(for debug purpose) */
RCC_HSEConfig(RCC_HSE_ON);//使能外部晶振          
HSEStartUpStatus = RCC_WaitForHSEStartUp();//等待外部晶振稳定
if (HSEStartUpStatus == SUCCESS)        //如果外部晶振启动成功,则进行下一步操作
{
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* Enable Prefetch Buffer使能或者失能FLASH半周期访问 */
    FLASH_SetLatency(FLASH_Latency_2);/* Flash 2 wait state设置代码延时值 */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);/* HCLK = SYSCLK 设置AHB时钟(HCLK)*/

    RCC_PCLK2Config(RCC_HCLK_Div1);   /* PCLK2 = HCLK设置高速 APB2 时钟(PCLK2)
        注意:1:ADC TIME1和TIME8的时钟都从其分支而来
                  2:TIME1/8的时钟只有当APB2的预分频系数 > 2时,其计数脉冲才会小于72M参RCC时钟树。
              3:其分频系数只有5档 1/2/4/8/16.                           
                                                                         */

    RCC_PCLK1Config(RCC_HCLK_Div4);          /* PCLK1 = HCLK/4设置低速 APB1 时钟
    注意:1:当APB1预分频=1时送给定时器的时钟频率不变.当有分频时,送给定时器的
                     时钟频率=分频后的频率 * 2 .比如这里APB1 / 4 = 18M(可看clock control观
             察窗内的PCLK1:),送给定时器的时钟计数脉冲18M * 2 = 36M(可看clock control
                 观察窗内的TIMXCL:)
                  2:TIME2-TIME7的计数时钟 = SYSCLK > AHB > APB1(参上面细节) > TIMXCL
                  3:技术手册里的RCC时钟树和TIME时钟树有详细介绍
                                                                          */

    RCC_ADCCLKConfig(RCC_PCLK2_Div8);/* ADCCLK = PCLK2(APB2)/8设置ADC时钟(ADCCLK)*/
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);   /* PLLCLK = 8MHz * 9 = 24 MHz (应该是72MHZ吧??)*/
    RCC_PLLCmd(ENABLE); /* Enable PLL */

wugq_sh 发表于 2013-10-6 08:29:02

相对于初学者很有帮助。

jj632856828 发表于 2013-10-10 13:48:49

原来是这样啊,之前也遇到过,不知道为啥
页: [1]
查看完整版本: STM32 的定时器延时为何软件仿真误差那么大