打印
[活动]

【APM32F411V Tiny Board测评】5 定时器的使用(基础定时+PWM)

[复制链接]
184|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 孤独的单行者 于 2024-5-28 10:30 编辑

       本章节主要是进行定时器的基本使用以及PWM的产生与控制,结合前面的按键以及LED的控制,再加上串口的打印功能,就可以实现,不连接其他外部器件进行定时器的使用和测试。
我们首先看一下定时器的基本使用,在使用基本定时功能时,我们通常需要进行定时器的初始化后,在定时器的中断中进行处理,我们现在通过现有的资源实现LED的非阻塞闪烁,一个按键对应一个LED,并通过串口打印状态。
       这里我们还要了解一个点,也就是系统的时钟频率是多少,注意之前的测试可以不考虑,这里要计算定时器的时间则必须要考虑了,下面是本芯片的时钟树,板载的外部高速时钟是8Mhz:

       我们找到system_apm32f4xx.C中有关时钟的配置,注意一点,才发现这里的配置真的很乱,我不知道对于官方的SDK为什么会这样配置,可能只是为了能达到最大时钟吧,原始配置如下:
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
      咱们简单计算一下,外部时钟8Mhz,按照如上的计算,PLL_VCO=200M,USB OTG FS = 28.57 M,SYSCLK= 100M,为了后续的USB功能的测试(一般USB的时钟要求为48M),我们重新梳理一下时钟配置,最大时钟不得超过120Mhz,为了比较好计算,SYSCLK取96中,一种参数计算配置如下:
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_B) * PLL_A */
#define PLL_B      8
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLL_D */
#define PLL_D      4
#define PLL_A      192
/* SYSCLK = PLL_VCO / PLL_C */
#define PLL_C      2
      接下来继续进行定时器的初始化配置:
void APM_TMR1_Init(void)
{
    TMR_BaseConfig_T TMR_BaseConfigStruct;

    /* Enable TMR1 Periph Clock */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
   
    APM_HCLKFreq = RCM_ReadHCLKFreq();
    /* 定时10ms */
    APM_TMR1_DIV = APM_HCLKFreq/1000*10/1000-1;

    /* Config TMR1 */
    TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
    TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
    TMR_BaseConfigStruct.division = APM_TMR1_DIV;
    TMR_BaseConfigStruct.period = 999;
    TMR_BaseConfigStruct.repetitionCounter = 0;
    TMR_ConfigTimeBase(TMR1, &TMR_BaseConfigStruct);

    /* Enable TMR1 Interrupt */
    TMR_EnableInterrupt(TMR1, TMR_INT_UPDATE);
    NVIC_EnableIRQRequest(TMR1_UP_TMR10_IRQn, 0, 0);

    TMR_Enable(TMR1);
}
      在中断处理中加入按键判断,KEY2控制灯的闪烁:
void TMR1_UP_TMR10_IRQHandler(void)
{
    if(TMR_ReadIntFlag(TMR1, TMR_INT_UPDATE) == SET)
    {
        TMR_ClearIntFlag(TMR1, TMR_INT_UPDATE);
            
        if(APM_EINT0_flag == 1)
        {
        TMR1_cnt++;
        if(TMR1_cnt%50 == 0)
        {
        APM_LED3_Toggle;  
        APM_LED2_Toggle;
        }
        }
               
    }
}
      接下来是通过定时器的PWM功能实现呼吸灯的效果,LED对应的PE5和PE6的引脚功能如下:

       可以看到对应的是定时器9的CH1和CH2,我们用的产生PWM的定时器为定时器9,使用的时钟依然是APB2.
       初始化如下:
void APM_TMR9_Init(void)
{
    TMR_BaseConfig_T TMR_BaseConfigStruct;
    TMR_OCConfig_T OCcongigStruct;
    GPIO_Config_T GPIO_ConfigStruct;
        
   
    /* Enable TMR1 Periph Clock */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR9);
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
   
   
    /* TMR9_CH1 */
    GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_5, GPIO_AF_TMR9);
    /* TMR9_CH2 */
    GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_6, GPIO_AF_TMR9);
   
//        /* Config TMR2 GPIO for output PWM */
//        GPIO_ConfigStruct.pin = GPIO_PIN_5|GPIO_PIN_6;
//        GPIO_ConfigStruct.mode = GPIO_MODE_AF;
//        GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
//        GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
//        GPIO_Config(GPIOE, &GPIO_ConfigStruct);
   
   
    APM_HCLKFreq = RCM_ReadHCLKFreq();
    /* 频率200Hz */
    APM_TMR1_DIV = APM_HCLKFreq/200/1000-1;

    /* Config TMR1 */
    TMR_BaseConfigStruct.clockDivision = TMR_CLOCK_DIV_1;
    TMR_BaseConfigStruct.countMode = TMR_COUNTER_MODE_UP;
    TMR_BaseConfigStruct.division = 999;
    TMR_BaseConfigStruct.period = APM_TMR1_DIV;
    TMR_ConfigTimeBase(TMR9, &TMR_BaseConfigStruct);
   
    /* Configure channel1 */
    TMR_ConfigOCStructInit(&OCcongigStruct);

    OCcongigStruct.mode = TMR_OC_MODE_PWM1;
    OCcongigStruct.outputState = TMR_OC_STATE_ENABLE;
    OCcongigStruct.polarity = TMR_OC_POLARITY_HIGH;
   

    OCcongigStruct.pulse = 500;
    TMR_ConfigOC1(TMR9, &OCcongigStruct);
    TMR_ConfigOC1Preload(TMR9, TMR_OC_PRELOAD_ENABLE);

    /* Configure channel2 */
    OCcongigStruct.pulse = 200;
    TMR_ConfigOC2(TMR9, &OCcongigStruct);
    TMR_ConfigOC2Preload(TMR9, TMR_OC_PRELOAD_ENABLE);

    TMR_EnableAutoReload(TMR9);
//    TMR_Enable(TMR9);

}
      注意,我们在初始化里面没有开启定时器和引脚的初始化,这里主要因为LED的引脚的功能是复用的,在正常状态默认是输出控制,只有开启后才是PWM控制,这里面的切换在按键中断中进行:
void EINT1_IRQHandler(void)
{
    if(EINT_ReadIntFlag(EINT_LINE_1))
    {
            
            /*Clear EINT_LINE0 interrupt flag*/
       EINT_ClearIntFlag(EINT_LINE_1);
            
            if(APM_EINT1_flag == 0)
            {
                if(APM_EINT0_flag == 0)
                {
                    GPIO_Config_T GPIO_ConfigStruct;
                    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
                    /* Config TMR2 GPIO for output PWM */
                    GPIO_ConfigStruct.pin = GPIO_PIN_5|GPIO_PIN_6;
                    GPIO_ConfigStruct.mode = GPIO_MODE_AF;
                    GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
                    GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
                    GPIO_Config(GPIOE, &GPIO_ConfigStruct);
                    
                    TMR_EnableAutoReload(TMR9);
                    TMR_Enable(TMR9);
                    
                    APM_EINT1_flag = 1;
                    
                    
                    printf("APM_LED breathing start \r\n");
                }
                else
                {
                    printf("Plase stop APM_LED bilinking \r\n");
                }
            }
            else
            {
                APM_EINT1_flag = 0;
                TMR_Disable(TMR9);
               
                GPIO_Config_T GPIO_ConfigStruct;
                RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
            
                GPIO_ConfigStruct.pin = GPIO_PIN_5|GPIO_PIN_6;
                GPIO_ConfigStruct.mode = GPIO_MODE_OUT;
                GPIO_ConfigStruct.otype = GPIO_OTYPE_PP;
                GPIO_ConfigStruct.speed = GPIO_SPEED_100MHz;
                GPIO_Config(GPIOE, &GPIO_ConfigStruct);
               
                APM_LED2_Off;
                APM_LED3_Off;
               
               
                printf("APM_LED breathing stop \r\n");
            }  
    }
}
      定时器中断中加入呼吸灯程序:
if(APM_EINT1_flag == 1)
{
        TMR_OCConfig_T OCcongigStruct;
        TMR1_breaking_cnt++;
        
        TMR_ConfigOCStructInit(&OCcongigStruct);
        OCcongigStruct.mode = TMR_OC_MODE_PWM1;
        OCcongigStruct.outputState = TMR_OC_STATE_ENABLE;
        OCcongigStruct.polarity = TMR_OC_POLARITY_HIGH;
   
        if(TMR1_breaking_cnt >= 200)
        {
            TMR1_breaking_cnt = 0;
        }
   
        if(TMR1_breaking_cnt<=100)
        {
            OCcongigStruct.pulse = TMR1_breaking_cnt*10;
        }
        else if(TMR1_breaking_cnt>100)
        {
            OCcongigStruct.pulse = (200 - TMR1_breaking_cnt)*10;
        }
   
        TMR_ConfigOC1(TMR9, &OCcongigStruct);
        TMR_ConfigOC2(TMR9, &OCcongigStruct);
        TMR_ConfigOC1Preload(TMR9, TMR_OC_PRELOAD_ENABLE);
        TMR_ConfigOC2Preload(TMR9, TMR_OC_PRELOAD_ENABLE);
   
}
      可以看到串口打印基本操作:

       实际上咱们得按键采集没有加入防抖,所以会有出现连键的情况,不过和现象还是一致的,效果如下:

使用特权

评论回复
沙发
星辰大海不退缩| | 2024-6-22 20:52 | 只看该作者
PWM的产生与控制进行呼吸灯的测试非常简单

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

103

帖子

0

粉丝