单片机 :STM32F407
开发板:DMF407电机开发板
平台:keil   V5.31

HSE 为8MHZ
HSI为16MHZ

一、中断实验

主函数:

int main(void)
{     
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);     /* 设置时钟,168Mhz */
    delay_init(168);                        /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    led_init();                             /* 初始化LED */
    gtim_timx_int_init(5000 - 1, 8400 - 1); /* 10Khz的计数频率,计数5K次为500ms */
   
    while (1)
    {
        LED0_TOGGLE();                      /*LED0(红灯) 翻转*/
        delay_ms(200);
    }
}

配置:

#define GTIM_TIMX_INT                       TIM3
#define GTIM_TIMX_INT_IRQn                  TIM3_IRQn
#define GTIM_TIMX_INT_IRQHandler            TIM3_IRQHandler
#define GTIM_TIMX_INT_CLK_ENABLE()          do{ __HAL_RCC_TIM3_CLK_ENABLE(); }while(0)      /* TIM3 时钟使能 */

void gtim_timx_int_init(uint16_t arr, uint16_t psc)
{
    GTIM_TIMX_INT_CLK_ENABLE();                                 /* 使能TIMx时钟 */

    g_timx_handle.Instance = GTIM_TIMX_INT;                     /* 通用定时器x */
    g_timx_handle.Init.Prescaler = psc;                         /* 预分频系数 */
    g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 递增计数模式 */
    g_timx_handle.Init.Period = arr;                            /* 自动装载值 */
    HAL_TIM_Base_Init(&g_timx_handle);

    HAL_NVIC_SetPriority(GTIM_TIMX_INT_IRQn, 1, 3);             /* 设置中断优先级,抢占优先级1,子优先级3 */
    HAL_NVIC_EnableIRQ(GTIM_TIMX_INT_IRQn);                     /* 开启ITMx中断 */

    HAL_TIM_Base_Start_IT(&g_timx_handle);                      /* 使能定时器x和定时器x更新中断 */
}

中断:

void GTIM_TIMX_INT_IRQHandler(void)
{
    /* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */
    if(__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET)
    {
        LED1_TOGGLE();
        __HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE);      /* 清除定时器溢出中断标志位 */
    }
}

实验结果:

500msLED1翻转一次。

二、通用定时器PWM输出

主函数

int main(void)
{     
    uint8_t t = 0;
    uint8_t dir = 1;
    uint16_t ledrpwmval = 0;

    
    HAL_Init();                                     /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);             /* 设置时钟,168Mhz */
    delay_init(168);                                /* 延时初始化 */
    usart_init(115200);                             /* 串口初始化为115200 */
    led_init();                                     /* 初始化LED */
    gtim_timx_pwm_chy_init(5000 - 1, 8400 - 1);        /* 1M Hz的计数频率,2Khz的PWM */
   
    while (1)
    {
        if (dir)ledrpwmval++;                       /* dir==1 ledrpwmval递增 */
        else ledrpwmval--;                          /* dir==0 ledrpwmval递减 */

        if (ledrpwmval > 300)dir = 0;               /* ledrpwmval到达300后,方向为递减 */
        if (ledrpwmval == 0)dir = 1;                /* ledrpwmval递减到0后,方向改为递增 */

        /* 修改比较值控制占空比 */
        __HAL_TIM_SET_COMPARE(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY, ledrpwmval);
        
        t++;
        if (t > 50)
        {
            t = 0;
            LED0_TOGGLE();                          /* 控制LED0闪烁, 提示程序运行状态 */
        }
        delay_ms(10);
    }
}

配置:

/* TIMX PWM输出定义 */

#define GTIM_TIMX_PWM_CHY_GPIO_PORT         GPIOD
#define GTIM_TIMX_PWM_CHY_GPIO_PIN          GPIO_PIN_12
#define GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)         /* PD口时钟使能 */

/* TIMX 引脚复用设置
 * 因为PD12默认并不是TIM4的功能脚, 必须开启复用, PD12才能用作TIM4的PWM输出引脚。
 */
#define GTIM_TIMX_PWM_CHY_GPIO_AF           GPIO_AF2_TIM4                                       /* 端口复用到TIM4 */

#define GTIM_TIMX_PWM                       TIM4                                                /* TIM4 */
#define GTIM_TIMX_PWM_CHY                   TIM_CHANNEL_1                                       /* 通道Y,  1<= Y <=4 */
#define GTIM_TIMX_PWM_CHY_CLK_ENABLE()      do{ __HAL_RCC_TIM4_CLK_ENABLE(); }while(0)          /* TIM4 时钟使能 */

void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef timx_oc_pwm_chy  = {0};                                              /* 定时器PWM输出配置 */
    
    g_timx_pwm_chy_handle.Instance = GTIM_TIMX_PWM;                                         /* 定时器x */
    g_timx_pwm_chy_handle.Init.Prescaler = psc;                                             /* 预分频系数 */
    g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                            /* 向上计数模式 */
    g_timx_pwm_chy_handle.Init.Period = arr;                                                /* 自动重装载值 */
    g_timx_pwm_chy_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;                      /* 时钟分频因子 */
    HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);                                               /* 初始化PWM */

    timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;                                               /* 模式选择PWM1 */
    timx_oc_pwm_chy.Pulse = ( arr + 1 ) / 2;                                                /* 设置比较值,此值用来确定占空比 */
                                                                                            /* 默认比较值为自动重装载值的一半,即占空比为50% */
    timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW;                                        /* 输出比较极性为低 */
    HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, GTIM_TIMX_PWM_CHY); /* 配置TIMx通道y */
    HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY);                           /* 开启对应PWM通道 */
}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_PWM)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE();                                                /* 开启通道y的GPIO时钟 */
        GTIM_TIMX_PWM_CHY_CLK_ENABLE();                                                     /* 开启定时器X时钟 */

        gpio_init_struct.Pin = GTIM_TIMX_PWM_CHY_GPIO_PIN;                                  /* 通道y的GPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                                      /* 高速 */
        gpio_init_struct.Alternate = GTIM_TIMX_PWM_CHY_GPIO_AF;                             /* IO复用为定时器功能 */
        HAL_GPIO_Init(GTIM_TIMX_PWM_CHY_GPIO_PORT, &gpio_init_struct);                      /* 初始化IO */
    }
}

实验结果:

在PD12与GND加上一个LED

gtim_timx_pwm_chy_init(5000 - 1, 8400 - 1);        /* 10K Hz的计数频率,2hz的PWM */

三、通用定时器输入捕获实验

主函数:

int main(void)
{  
    uint32_t temp = 0;
    uint8_t t = 0;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    gtim_timx_pwm_chy_init(5000-1,8400-1);      /* 10k HZ计数频率,500ms的周期,占空比为50%,也就是高电平时间 = 250ms = 250000us */
    gtim_timx_cap_chy_init(0xFFFF, 84 - 1);     /* 以1Mhz的频率计数 捕获 */

    while (1)
    {
        if (g_timxchy_cap_sta & 0X80)           /* 成功捕获到了一次高电平 */
        {
            temp = g_timxchy_cap_sta & 0X3F;
            temp *= 0xFFFF;                     /* 溢出时间总和 */
            temp += g_timxchy_cap_val;          /* 得到总的高电平时间 */
            printf("HIGH:%d us\r\n", temp);     /* 打印总的高点平时间 */
            g_timxchy_cap_sta = 0;              /* 开启下一次捕获 */
        }

        t++;
        if (t > 20)                             /* 200ms进入一次 */
        {
            t = 0;
            LED0_TOGGLE();                      /* LED0闪烁 ,提示程序运行 */
        }
        delay_ms(10);
    }
}

配置:

/* TIMX PWM输出定义 */

#define GTIM_TIMX_PWM_CHY_GPIO_PORT         GPIOD
#define GTIM_TIMX_PWM_CHY_GPIO_PIN          GPIO_PIN_12
#define GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)         /* PD口时钟使能 */

/* TIMX 引脚复用设置
 * 因为PD12默认并不是TIM4的功能脚, 必须开启复用, PD12才能用作TIM4的PWM输出引脚
 */
#define GTIM_TIMX_PWM_CHY_GPIO_AF           GPIO_AF2_TIM4                                       /* 端口复用到TIM4 */

#define GTIM_TIMX_PWM                       TIM4                                                /* TIM4 */
#define GTIM_TIMX_PWM_CHY                   TIM_CHANNEL_1                                       /* 通道Y,  1<= Y <=4 */
#define GTIM_TIMX_PWM_CHY_CLK_ENABLE()      do{ __HAL_RCC_TIM4_CLK_ENABLE(); }while(0)          /* TIM4 时钟使能 */

 /* TIMX 输入捕获定义 
  * 这里的输入捕获使用定时器TIM5_CH1
  */
#define GTIM_TIMX_CAP_CHY_GPIO_PORT         GPIOH
#define GTIM_TIMX_CAP_CHY_GPIO_PIN          GPIO_PIN_10                                         /* 输入捕获引脚:PH10 */
#define GTIM_TIMX_CAP_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0)         /* GPIOH时钟使能 */

/* TIMX 引脚复用设置
 * 因为PH10默认并不是TIM5的功能脚, 必须开启复用, PH10才能用作TIM5的输入捕获引脚
 */
#define GTIM_TIMX_CAP_CHY_GPIO_AF           GPIO_AF2_TIM5                                       /* 端口复用到TIM5 */

#define GTIM_TIMX_CAP                       TIM5                                                /* 定时器5 */
#define GTIM_TIMX_CAP_IRQn                  TIM5_IRQn
#define GTIM_TIMX_CAP_IRQHandler            TIM5_IRQHandler
#define GTIM_TIMX_CAP_CHY                   TIM_CHANNEL_1                                       /* 通道Y,  1<= Y <=4 */
#define GTIM_TIMX_CAP_CHY_CLK_ENABLE()      do{ __HAL_RCC_TIM5_CLK_ENABLE(); }while(0)          /* TIM5 时钟使能 */
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef timx_oc_pwm_chy  = {0};                                              /* 定时器PWM输出配置 */
    
    g_timx_pwm_chy_handle.Instance = GTIM_TIMX_PWM;                                         /* 定时器x */
    g_timx_pwm_chy_handle.Init.Prescaler = psc;                                             /* 定时器分频 */
    g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                            /* 向上计数模式 */
    g_timx_pwm_chy_handle.Init.Period = arr;                                                /* 自动重装载值 */
    g_timx_pwm_chy_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;                      /* 时钟分频因子 */
    HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);                                               /* 初始化PWM */

    timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1;                                               /* 模式选择PWM1 */
    timx_oc_pwm_chy.Pulse = (arr + 1) / 2;                                                  /* 设置比较值,此值用来确定占空比 */
                                                                                            /* 默认比较值为自动重装载值的一半,即占空比为50% */
    timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW;                                        /* 输出比较极性为低 */
    HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, GTIM_TIMX_PWM_CHY); /* 配置TIMx通道y */
    HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, GTIM_TIMX_PWM_CHY);                           /* 开启对应PWM通道 */
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_PWM)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        GTIM_TIMX_PWM_CHY_GPIO_CLK_ENABLE();                                                /* 开启通道y的GPIO时钟 */
        GTIM_TIMX_PWM_CHY_CLK_ENABLE();                                                     /* 开启定时器X时钟 */

        gpio_init_struct.Pin = GTIM_TIMX_PWM_CHY_GPIO_PIN;                                  /* 通道y的GPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                                      /* 高速 */
        gpio_init_struct.Alternate = GTIM_TIMX_PWM_CHY_GPIO_AF;                             /* IO复用为定时器功能 */
        HAL_GPIO_Init(GTIM_TIMX_PWM_CHY_GPIO_PORT, &gpio_init_struct);                      /* 初始化IO */
    }
}
void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc)
{
    TIM_IC_InitTypeDef timx_ic_cap_chy = {0};

    g_timx_cap_chy_handle.Instance = GTIM_TIMX_CAP;                     /* 定时器5 */
    g_timx_cap_chy_handle.Init.Prescaler = psc;                         /* 定时器分频 */
    g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 递增计数模式 */
    g_timx_cap_chy_handle.Init.Period = arr;                            /* 自动重装载值 */
    HAL_TIM_IC_Init(&g_timx_cap_chy_handle);

    timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;                 /* 上升沿捕获 */
    timx_ic_cap_chy.ICSelection = TIM_ICSELECTION_DIRECTTI;             /* 映射到TI1上 */
    timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;                       /* 配置输入分频,不分频 */
    timx_ic_cap_chy.ICFilter = 0;                                       /* 配置输入滤波器,不滤波 */
    HAL_TIM_IC_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, GTIM_TIMX_CAP_CHY);  /* 配置TIM5通道1 */

    __HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE);         /* 使能更新中断 */
    HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY);     /* 开始捕获TIM5的通道1 */
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_CAP)                                /* 输入通道捕获 */
    {
        GPIO_InitTypeDef gpio_init_struct;
        GTIM_TIMX_CAP_CHY_CLK_ENABLE();                                 /* 使能TIMx时钟 */
        GTIM_TIMX_CAP_CHY_GPIO_CLK_ENABLE();                            /* 开启捕获IO的时钟 */

        gpio_init_struct.Pin = GTIM_TIMX_CAP_CHY_GPIO_PIN;              /* 输入捕获的GPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                        /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLDOWN;                          /* 下拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                  /* 高速 */
        gpio_init_struct.Alternate = GTIM_TIMX_CAP_CHY_GPIO_AF;         /* 端口复用 */ 
        HAL_GPIO_Init(GTIM_TIMX_CAP_CHY_GPIO_PORT, &gpio_init_struct);  /* 初始化IO */

        HAL_NVIC_SetPriority(GTIM_TIMX_CAP_IRQn, 2, 0);                 /* 抢占优先级2,子优先级0 */
        HAL_NVIC_EnableIRQ(GTIM_TIMX_CAP_IRQn);                         /* 开启ITMx中断 */
    }
}

处理:

void GTIM_TIMX_CAP_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_cap_chy_handle);                         /* 定时器HAL库共用处理函数 */
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_CAP)
    {
        if ((g_timxchy_cap_sta & 0X80) == 0)                            /* 还未成功捕获 */
        {
            if (g_timxchy_cap_sta & 0X40)                               /* 捕获到一个下降沿 */
            {
                g_timxchy_cap_sta |= 0X80;                              /* 标记成功捕获到一次高电平脉宽 */
                g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY);  /* 获取当前的捕获值 */
                TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY);                      /* 一定要先清除原来的设置 */
                TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY, TIM_ICPOLARITY_RISING); /* 配置TIM5通道1上升沿捕获 */
            }
            else                                                        /* 还未开始,第一次捕获上升沿 */
            {
                g_timxchy_cap_sta = 0;                                  /* 清空 */
                g_timxchy_cap_val = 0;
                g_timxchy_cap_sta |= 0X40;                              /* 标记捕获到了上升沿 */
                __HAL_TIM_DISABLE(&g_timx_cap_chy_handle);              /* 关闭定时器5 */
                __HAL_TIM_SET_COUNTER(&g_timx_cap_chy_handle, 0);       /* 定时器5计数器清零 */
                TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY);                       /* 一定要先清除原来的设置 */
                TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY, TIM_ICPOLARITY_FALLING); /* 定时器5通道1设置为下降沿捕获 */
                __HAL_TIM_ENABLE(&g_timx_cap_chy_handle);               /* 使能定时器5 */
            }
        }
    }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_CAP)
    {
        if ((g_timxchy_cap_sta & 0X80) == 0)                            /* 还未成功捕获 */
        {
            if (g_timxchy_cap_sta & 0X40)                               /* 已经捕获到高电平了 */
            {
                if ((g_timxchy_cap_sta & 0X3F) == 0X3F)                 /* 高电平太长了 */
                {
                    TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY);                     /* 一定要先清除原来的设置 */
                    TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, GTIM_TIMX_CAP_CHY, TIM_ICPOLARITY_RISING);/* 配置TIM5通道1上升沿捕获 */
                    g_timxchy_cap_sta |= 0X80;                          /* 标记成功捕获了一次 */
                    g_timxchy_cap_val = 0XFFFF;
                }
                else                                                    /* 累计定时器溢出次数 */
                {
                    g_timxchy_cap_sta++;
                }
            }
        }
    }
}

测试结果:

将PD12和PH10连接在一起。

四、通用定时器输入实验

主函数

int main(void)
{  
    uint8_t t = 0;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */
    delay_init(168);                            /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    gtim_timx_pwm_chy_init(5000-1,8400-1);      /* 10k Hz计数频率,500ms的周期,其中高电平占一半也就是250ms = 250000us */
    gtim_timx_pwmin_chy_init();                 /* 初始化PWN输入,以1Mhz的频率计数,最大捕获周期 2^32 us */

    while (1)
    {
        t++;
        if(t % 20 ==0)
        {
            LED0_TOGGLE();
        }
        if(g_timxchy_pwmin_sta == 1)            /* 成功捕获 */
        {
            g_timxchy_pwmin_sta = 0;            /* 清除成功捕获标志 */
            
            printf("\r\n");
            printf("高电平时间:%d us\r\n",g_timxchy_pwmin_hval);
            printf("PWM周期:%d us\r\n",g_timxchy_pwmin_cval );
            printf("PWM频率:%d Hz\r\n",1000000/g_timxchy_pwmin_cval);
        }
        delay_ms(10);
    }
}

配置:与上个实验一样

测试结果:

五、通用定时器输出比较实验:

主函数:

int main(void)
{
    HAL_Init();                                     /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);             /* 设置时钟,168Mhz */
    delay_init(168);                                /* 延时初始化 */
    usart_init(115200);                             /* 串口初始化为115200 */
    led_init();                                     /* 初始化LED */
    atim_timx_comp_pwm_init( 0xFFFF - 1, 84 - 1);   /* 1Mhz的计数频率 */
   
    while (1)
    {
        LED0_TOGGLE();                              /* LED0(红灯) 翻转 */
        delay_ms(10);
    }
}

配置:

/* TIMX PWM输出定义 */

#define GTIM_TIMX_COMP_CHY_GPIO_PORT         GPIOD
#define GTIM_TIMX_COMP_CHY_GPIO_PIN          GPIO_PIN_12
#define GTIM_TIMX_COMP_CHY_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)      /* PD口时钟使能 */

/* TIMX 引脚复用设置
 * 因为PD12默认并不是TIM4的功能脚, 必须开启复用, PD12才能用作TIM4的PWM输出引脚
 */
#define GTIM_TIMX_COMP_CHY_GPIO_AF           GPIO_AF2_TIM4                                    /* 端口复用到TIM4 */

#define GTIM_TIMX_COMP                       TIM4                                             /* TIM4 */
#define GTIM_TIMX_COMP_IRQn                  TIM4_IRQn
#define GTIM_TIMX_COMP_IRQHandler            TIM4_IRQHandler
#define GTIM_TIMX_COMP_CHY                   TIM_CHANNEL_1                                    /* 通道Y,  1<= Y <=4 */
#define GTIM_TIMX_COMP_CHY_CLK_ENABLE()      do{ __HAL_RCC_TIM4_CLK_ENABLE(); }while(0)       /* TIM4 时钟使能 */
void atim_timx_comp_pwm_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef timx_oc_comp_pwm = {0};
    
    g_timx_comp_pwm_handle.Instance = GTIM_TIMX_COMP;                                           /* 定时器4 */
    g_timx_comp_pwm_handle.Init.Prescaler = psc;                                                /* 预分频系数 */
    g_timx_comp_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;                               /* 递增计数模式 */
    g_timx_comp_pwm_handle.Init.Period = arr;                                                   /* 自动重装载值 */
    HAL_TIM_OC_Init(&g_timx_comp_pwm_handle);

    timx_oc_comp_pwm.OCMode = TIM_OCMODE_TOGGLE;                                                /* 比较输出模式翻转功能 */
    timx_oc_comp_pwm.Pulse = 0;                                                                 /* 默认比较值为0 */
    timx_oc_comp_pwm.OCPolarity = TIM_OCPOLARITY_LOW;                                           /* 输出比较极性为低 */
    HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &timx_oc_comp_pwm, GTIM_TIMX_COMP_CHY);   /* 初始化TIMx通道y */
    HAL_TIM_OC_Start_IT(&g_timx_comp_pwm_handle, GTIM_TIMX_COMP_CHY);                           /* 开启比较输出通道及其中断 */
    
}
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == GTIM_TIMX_COMP)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        GTIM_TIMX_COMP_CHY_GPIO_CLK_ENABLE();                                                   /* 开启IO时钟 */
        GTIM_TIMX_COMP_CHY_CLK_ENABLE();                                                        /* 开启TIM4时钟 */

        gpio_init_struct.Pin = GTIM_TIMX_COMP_CHY_GPIO_PIN;                                     /* PD12 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                                                /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                                          /* 高速 */
        gpio_init_struct.Alternate = GTIM_TIMX_COMP_CHY_GPIO_AF;                                /* IO复用为定时器功能 */
        HAL_GPIO_Init(GTIM_TIMX_COMP_CHY_GPIO_PORT, &gpio_init_struct);                         /* 初始化IO */
        
        HAL_NVIC_SetPriority(GTIM_TIMX_COMP_IRQn, 2, 0);                                        /* 抢占优先级2,子优先级0 */
        HAL_NVIC_EnableIRQ(GTIM_TIMX_COMP_IRQn);                                                /* 开启ITMx中断 */
    }
}
void GTIM_TIMX_COMP_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_timx_comp_pwm_handle);                                                /* 定时器共用处理函数 */
}
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint16_t tim_oc_val;
    
    if( htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1 )
    {
        tim_oc_val = __HAL_TIM_GetCompare( htim,GTIM_TIMX_COMP_CHY ) + 1;                          /* 获取当前比较值 */
        
        /* 设置比较值,在上一次比较值的基础上递增200
         * 定时器采用1M Hz的计数频率,计数值递增200需要200us
         * 当计数值等于比较值的时候输出会发生翻转,因此,每200us会翻转一次
         * 输出波形的周期 = 2 * 200 = 400us,频率 = 1/周期 = 2.5k Hz
         * 需要改变频率的话只需要改变递增值即可
         */
        __HAL_TIM_SetCompare( htim ,GTIM_TIMX_COMP_CHY,( tim_oc_val + 200 ) % g_timx_comp_pwm_handle.Init.Period );
    }
}

实验结果:

LED0和新增加的PD12LED均无闪烁。

在while里加断点,PD12的LED按照设定的时间闪烁。

修改一下:

int main(void)
{
    HAL_Init();                                     /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);             /* 设置时钟,168Mhz */
    delay_init(168);                                /* 延时初始化 */
    usart_init(115200);                             /* 串口初始化为115200 */
    led_init();                                     /* 初始化LED */
    atim_timx_comp_pwm_init( 0xFFFF - 1, 8400 - 1);   /* 1Mhz的计数频率 */
   
    while (1)
    {
        LED0_TOGGLE();                              /* LED0(红灯) 翻转 */
			printf("LED0\n");
        delay_ms(1000);
    }
}

void GTIM_TIMX_COMP_IRQHandler(void)
{
		printf("PD12\n");
    HAL_TIM_IRQHandler(&g_timx_comp_pwm_handle);                                                /* 定时器共用处理函数 */
}

void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint16_t tim_oc_val;
    
    if( htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1 )
    {
			printf("callback\n");
        tim_oc_val = __HAL_TIM_GetCompare( htim,GTIM_TIMX_COMP_CHY ) + 1;                          /* 获取当前比较值 */
        
        /* 设置比较值,在上一次比较值的基础上递增200
         * 定时器采用1M Hz的计数频率,计数值递增200需要200us
         * 当计数值等于比较值的时候输出会发生翻转,因此,每200us会翻转一次
         * 输出波形的周期 = 2 * 200 = 400us,频率 = 1/周期 = 2.5k Hz
         * 需要改变频率的话只需要改变递增值即可
         */
        __HAL_TIM_SetCompare( htim ,GTIM_TIMX_COMP_CHY,( tim_oc_val + 200 ) % g_timx_comp_pwm_handle.Init.Period );
    }
}

串口数据如下:

[22:20:00.637]收←◆LED0

[22:20:01.638]收←◆LED0

[22:20:02.638]收←◆LED0

[22:20:03.634]收←◆LED0

[22:20:04.634]收←◆LED0

[22:20:05.634]收←◆LED0

[22:20:06.639]收←◆LED0

[22:20:07.189]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.272]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.372]收←◆PD12
callback

[22:20:07.389]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.472]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.573]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.639]收←◆PD12
callback
LED0
PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.772]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:07.872]收←◆PD12
callback

[22:20:07.889]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:07.955]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:08.056]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:08.156]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:08.256]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:08.356]收←◆PD12
callback
PD12
callback

[22:20:08.389]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:08.458]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:08.518]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:08.578]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:08.638]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback
LED0
PD12
callback
PD12
callback
PD12
callback

[22:20:08.779]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:08.860]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:08.920]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:09.000]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:09.061]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:09.141]收←◆PD12
callback
PD12
callback
PD12
callback

[22:20:09.201]收←◆PD12
callback
PD12
callback
PD12
callback
PD12
callback

[22:20:09.282]收←◆PD12
callback
PD12
callback

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐