本帖最后由 口天土立口 于 2026-3-13 08:52 编辑
#申请原创# #技术资源#
@21小跑堂
1. 外设介绍
APM32F103拥有多个定时器,不同的定时器存在功能差异,其中TMR2/3/4定时器有1路外部触发输入引脚和4路非互补的通道,而TMR1拥有3路互补通道,并且支持1路刹车输入信号。 注意:计算TMR时钟源时,不一定等于对应的APB总线时钟。
2. 硬件 APM32F103VB MINI板
3. 驱动介绍 TIMER的时基初始化很简单,只需要配置分频值、计数模式和自动重装载值即可。 注意:分频值的使用为(PSC + 1)方式;又因为定时器计数计到AUTORLD再跳到0也需要1个计数时长,所以需要配置1000个计数值时,实际传递给AUTORLD的值应该为(1000 - 1)。 - /*
- * @brief 定时器初始化
- *
- * @param ms: 定时间隔
- *
- * @retval None
- *
- */
- void bsp_tim_init(uint16_t ms)
- {
- TMR_BaseConfig_T baseConfig;
-
- /* 寄存器AUTORLD为16bit */
- if ((ms >= 1) && (ms <= 6553)) {
- RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR2);
- TMR_Reset(TIM_INS);
- TMR_ConfigTimeBaseStructInit(&baseConfig);
- baseConfig.countMode = TMR_COUNTER_MODE_UP;
- /* CK_CNT = fCK_PSC / (PSC + 1) */
- baseConfig.clockDivision = TMR_CLOCK_DIV_1;
- baseConfig.period = (ms * 100) - 1;
- baseConfig.division = 720 - 1; /* 100KHz(时钟源为: 72MHz) */
- baseConfig.repetitionCounter = 0x0000;
- TMR_ConfigTimeBase(TIM_INS, &baseConfig);
- /* 使能更新中断 */
- TMR_EnableInterrupt(TIM_INS, TMR_INT_UPDATE);
- /* 使能中断 */
- NVIC_EnableIRQRequest(TMR2_IRQn, 0, 0);
- /* 使能定时器 */
- TMR_Enable(TIM_INS);
- }
- }
中断处理函数采用注册方式的好处是实现底层代码与应用层代码的隔离:当任一层代码需要更改时,都不影响另一层代码的使用。 - /*
- * @brief 中断处理函数
- *
- * @param None
- *
- * @retval None
- *
- */
- void TMR2_IRQHandler(void)
- {
- if (TMR_ReadIntFlag(TIM_INS, TMR_INT_UPDATE) == SET) {
- TMR_ClearIntFlag(TIM_INS, TMR_INT_UPDATE);
- (tim_update_deal_func != NULL) ? tim_update_deal_func() : NULL;
- }
- }
- /*
- * @brief 定时器更新处理函数注册
- *
- * @param func: 定时器更新中断处理函数
- *
- * @retval None
- *
- */
- void bsp_tim_update_deal_func_register(tim_update_deal_func_f func)
- {
- tim_update_deal_func = func;
- }
TIMER的PWM输出,需要增加初始化想应的GPIO,初始化为对应的IO复用模式。因为前面配置定时器时基时,配置了计数器按照向上计数的方式工作,所以输出比较模式为PWM1时,按照OCxMOD的介绍,通道先输出高电平,比较值匹配时,再切为低电平,即配置的通道比较值为通道的高电平占空比值;如果需要通道的比较值为低电平占空比值,则可配置输出比较模式为PWM2模式。 注意:在配置各个通道的PWM输出占空比时,要按照(AUTORLD + 1)作为单个周期进行计算。
- /*
- * @brief PWM引脚初始化
- *
- * @param None
- *
- * @retval None
- *
- */
- static void bsp_tim_pwm_gpio_init(void)
- {
- GPIO_Config_T gpioConfig;
-
- /* PA6 -> TMR3_CH1
- * PA7 -> TMR3_CH2
- * PB0 -> TMR3_CH3
- * PB1 -> TMR3_CH4
- */
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOA);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOB);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_AFIO);
- GPIO_ConfigStructInit(&gpioConfig);
- gpioConfig.pin = GPIO_PIN_6;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- gpioConfig.mode = GPIO_MODE_AF_PP;
- GPIO_Config(GPIOA, &gpioConfig);
- gpioConfig.pin = GPIO_PIN_7;
- GPIO_Config(GPIOA, &gpioConfig);
- gpioConfig.pin = GPIO_PIN_0;
- GPIO_Config(GPIOB, &gpioConfig);
- gpioConfig.pin = GPIO_PIN_1;
- GPIO_Config(GPIOB, &gpioConfig);
- }
- /*
- * @brief 定时器初始化
- *
- * @param ms: 定时间隔
- *
- * @retval None
- *
- */
- void bsp_tim_init(uint16_t ms)
- {
- TMR_BaseConfig_T baseConfig;
- TMR_OCConfig_T OCConfig;
-
- /* 寄存器AUTORLD为16bit */
- if ((ms >= 1) && (ms <= 6553)) {
- bsp_tim_pwm_gpio_init();
-
- RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
- TMR_Reset(TIM_INS);
- TMR_ConfigTimeBaseStructInit(&baseConfig);
- baseConfig.countMode = TMR_COUNTER_MODE_UP;
- /* CK_CNT = fCK_PSC / (PSC + 1) */
- baseConfig.clockDivision = TMR_CLOCK_DIV_1;
- baseConfig.period = (ms * 100) - 1;
- baseConfig.division = 720 - 1; /* 100KHz(时钟源为: 72MHz) */
- baseConfig.repetitionCounter = 0x0000;
- TMR_ConfigTimeBase(TIM_INS, &baseConfig);
-
- /* PWM */
- DUTY_100 = baseConfig.period;
- TMR_ConfigOCStructInit(&OCConfig);
- /* PWM 模式 1(计数器值 < 输出比较值时置为高,否则反之) */
- OCConfig.mode = TMR_OC_MODE_PWM1;
- /* 输出使能 */
- OCConfig.outputState = TMR_OC_STATE_ENABLE;
- OCConfig.outputNState = TMR_OC_NSTATE_DISABLE;
- /* 默认50%占空比 */
- OCConfig.pulse = (DUTY_100 + 1) / 2;
- OCConfig.polarity = TMR_OC_POLARITY_HIGH;
- OCConfig.nPolarity = TMR_OC_NPOLARITY_HIGH;
- OCConfig.idleState = TMR_OC_IDLE_STATE_RESET;
- OCConfig.nIdleState = TMR_OC_NIDLE_STATE_RESET;
- TMR_ConfigOC1(TIM_INS, &OCConfig);
- TMR_ConfigOC2(TIM_INS, &OCConfig);
- TMR_ConfigOC3(TIM_INS, &OCConfig);
- TMR_ConfigOC4(TIM_INS, &OCConfig);
- /* 使能预装载功能: 写入比较寄存器CC的值在更新事件后才起作用 */
- TMR_ConfigOC1Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
- TMR_ConfigOC2Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
- TMR_ConfigOC3Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
- TMR_ConfigOC4Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
-
- /* 使能更新中断 */
- TMR_EnableInterrupt(TIM_INS, TMR_INT_UPDATE);
- /* 使能中断 */
- NVIC_EnableIRQRequest(TMR3_IRQn, 0, 0);
-
- /* 使能定时器 */
- TMR_Enable(TIM_INS);
- }
- }
TMR1支持输出互补通道加入死区功能,应用于电机场景。 TMR1使能了自动输出功能,则每次刹车信号无效时,PWM都能自动恢复输出;而不使用自动输出,每次重新输出,都需要调用TMR_EnablePWMOutputs函数手动输出PWM。 - /*
- * @brief 定时器初始化
- *
- * @param ms: 定时间隔
- *
- * @retval None
- *
- */
- void bsp_tim_init(uint16_t ms)
- {
- TMR_BaseConfig_T baseConfig;
- TMR_OCConfig_T OCConfig;
- TMR_BDTConfig_T BDTConfig;
-
- /* 寄存器AUTORLD为16bit */
- if ((ms >= 1) && (ms <= 6553)) {
- bsp_tim_pwm_gpio_init();
-
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_TMR1);
- TMR_Reset(TIM_INS);
- TMR_ConfigTimeBaseStructInit(&baseConfig);
- baseConfig.countMode = TMR_COUNTER_MODE_UP;
- /* CK_CNT = fCK_PSC / (PSC + 1) */
- baseConfig.clockDivision = TMR_CLOCK_DIV_1;
- baseConfig.period = (ms * 100) - 1;
- baseConfig.division = 720 - 1; /* 100KHz(时钟源为: 72MHz) */
- baseConfig.repetitionCounter = 0x0000;
- TMR_ConfigTimeBase(TIM_INS, &baseConfig);
-
- /* PWM */
- DUTY_100 = baseConfig.period;
- TMR_ConfigOCStructInit(&OCConfig);
- /* PWM 模式 1(计数器值 < 输出比较值时置为高,否则反之) */
- OCConfig.mode = TMR_OC_MODE_PWM1;
- /* 输出使能 */
- OCConfig.outputState = TMR_OC_STATE_ENABLE;
- OCConfig.outputNState = TMR_OC_NSTATE_ENABLE;
- /* 默认50%占空比 */
- OCConfig.pulse = (DUTY_100 + 1) / 2;
- OCConfig.polarity = TMR_OC_POLARITY_HIGH;
- OCConfig.nPolarity = TMR_OC_NPOLARITY_HIGH;
- OCConfig.idleState = TMR_OC_IDLE_STATE_RESET;
- OCConfig.nIdleState = TMR_OC_NIDLE_STATE_RESET;
- TMR_ConfigOC1(TIM_INS, &OCConfig);
- TMR_ConfigOC2(TIM_INS, &OCConfig);
- TMR_ConfigOC3(TIM_INS, &OCConfig);
- TMR_ConfigOC4(TIM_INS, &OCConfig);
- /* 使能预装载功能: 写入比较寄存器CC的值在更新事件后才起作用 */
- TMR_ConfigOC1Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
- TMR_ConfigOC2Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
- TMR_ConfigOC3Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
- TMR_ConfigOC4Preload(TIM_INS, TMR_OC_PRELOAD_ENABLE);
-
- /* 刹车 */
- TMR_ConfigBDTStructInit(&BDTConfig);
- BDTConfig.RMOS = TMR_RMOS_STATE_DISABLE;
- BDTConfig.IMOS = TMR_IMOS_STATE_DISABLE;
- BDTConfig.lockLevel = TMR_LOCK_LEVEL_OFF;
- /* 定时器时钟源为72MHz, 死区时间1us = 72 */
- BDTConfig.deadTime = 72;
- /* 使能刹车功能 */
- BDTConfig.BRKState = TMR_BRK_STATE_ENABLE;
- /* 刹车信号高电平有效 */
- BDTConfig.BRKPolarity = TMR_BRK_POLARITY_HIGH;
- /* 刹车信号无效时自动恢复输出 */
- BDTConfig.automaticOutput = TMR_AUTOMATIC_OUTPUT_ENABLE;
- TMR_ConfigBDT(TIM_INS, &BDTConfig);
- /* 不使能刹车信号无效自动恢复输出功能时,需调用此函数手动开启互补输出 */
- // TMR_EnablePWMOutputs(TIM_INS);
-
- /* 使能更新中断 */
- TMR_EnableInterrupt(TIM_INS, TMR_INT_UPDATE);
- /* 使能刹车中断 */
- TMR_EnableInterrupt(TIM_INS, TMR_INT_BRK);
- /* 使能中断 */
- NVIC_EnableIRQRequest(TMR1_UP_IRQn, 0, 0);
- NVIC_EnableIRQRequest(TMR1_BRK_IRQn, 0, 0);
-
- /* 使能定时器 */
- TMR_Enable(TIM_INS);
- }
- }
TIMER的输入捕获功能,相比时基配置,需要增加初始化想应的GPIO,初始化为对应的IO复用模式。输入捕获模式,可以1个IO同时使用2个捕获通道(2个通道内部连接),所以可以实现捕获PWM的频率的同时又计算占空比。 注意:需要使能对应IO的输入功能。 注意:输入捕获的脉宽是有范围限制的,具体取决于配置的AUTORLD和PSC值。
- /* 捕获精度us */
- #define CAPTURE_PRECISION (100)
- /* 最大周期 */
- static uint32_t period_max;
- /*
- * @brief PWM引脚初始化
- *
- * @param None
- *
- * @retval None
- *
- */
- static void bsp_tim_capture_gpio_init(void)
- {
- GPIO_Config_T gpioConfig;
-
- /* PB6 -> TMR4_CH1
- * PB8 -> TMR4_CH3
- */
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOB);
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_AFIO);
- GPIO_ConfigStructInit(&gpioConfig);
- gpioConfig.pin = GPIO_PIN_6;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- gpioConfig.mode = GPIO_MODE_IN_FLOATING;
- GPIO_Config(GPIOB, &gpioConfig);
- gpioConfig.pin = GPIO_PIN_8;
- GPIO_Config(GPIOB, &gpioConfig);
- }
- /*
- * @brief 定时器初始化
- *
- * @param None
- *
- * @retval None
- *
- */
- void bsp_tim_capture_init(void)
- {
- TMR_BaseConfig_T baseConfig;
- TMR_ICConfig_T ICConfig;
-
- /* 寄存器AUTORLD为16bit */
- bsp_tim_capture_gpio_init();
-
- RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR4);
- TMR_Reset(TIM_INS);
- TMR_ConfigTimeBaseStructInit(&baseConfig);
- baseConfig.countMode = TMR_COUNTER_MODE_UP;
- /* CK_CNT = fCK_PSC / (PSC + 1) */
- baseConfig.clockDivision = TMR_CLOCK_DIV_1;
- /* 最大捕获PWM周期为6s
- * 因AUTORLD寄存器为16位,所以period最大为65535
- * 同时AUTORLD寄存器限制,提高捕获精度,需同时缩短最大捕获PWM周期
- */
- baseConfig.period = 60000 - 1;
- baseConfig.division = 7200 - 1; /* 10KHz(时钟源为: 72MHz) */
- baseConfig.repetitionCounter = 0x0000;
- TMR_ConfigTimeBase(TIM_INS, &baseConfig);
- period_max = baseConfig.period + 1;
-
- /* 捕获PWM1:
- * 捕获通道1: 输入来源为TMR4_CH1
- * 捕获通道2: 输入来源为TMR4_CH1
- */
- TMR_ConfigICStructInit(&ICConfig);
- ICConfig.channel = TMR_CHANNEL_1;
- ICConfig.polarity = TMR_IC_POLARITY_RISING;
- ICConfig.selection = TMR_IC_SELECTION_DIRECT_TI; /* TMR4_CH1 */
- ICConfig.prescaler = TMR_IC_PSC_1;
- ICConfig.filter = 0x00;
- TMR_ConfigIC(TIM_INS, &ICConfig);
- ICConfig.channel = TMR_CHANNEL_2;
- /* 捕获下降沿 */
- ICConfig.polarity = TMR_IC_POLARITY_FALLING;
- ICConfig.selection = TMR_IC_SELECTION_INDIRECT_TI; /* TMR4_CH1 */
- TMR_ConfigIC(TIM_INS, &ICConfig);
- /* 捕获PWM2:
- * 捕获通道3: 输入来源为TMR4_CH3
- * 捕获通道4: 输入来源为TMR4_CH3
- */
- ICConfig.channel = TMR_CHANNEL_3;
- /* 捕获上升沿 */
- ICConfig.polarity = TMR_IC_POLARITY_RISING;
- ICConfig.selection = TMR_IC_SELECTION_DIRECT_TI; /* TMR4_CH3 */
- TMR_ConfigIC(TIM_INS, &ICConfig);
- ICConfig.channel = TMR_CHANNEL_4;
- /* 捕获下降沿 */
- ICConfig.polarity = TMR_IC_POLARITY_FALLING;
- ICConfig.selection = TMR_IC_SELECTION_INDIRECT_TI; /* TMR4_CH3 */
- TMR_ConfigIC(TIM_INS, &ICConfig);
-
- /* 使能捕获中断 */
- TMR_EnableInterrupt(TIM_INS, TMR_INT_CC1);
- TMR_EnableInterrupt(TIM_INS, TMR_INT_CC2);
- TMR_EnableInterrupt(TIM_INS, TMR_INT_CC3);
- TMR_EnableInterrupt(TIM_INS, TMR_INT_CC4);
- /* 使能中断 */
- NVIC_EnableIRQRequest(TMR4_IRQn, 0, 0);
-
- /* 使能定时器 */
- TMR_Enable(TIM_INS);
- }
捕获功能TIMER的中断服务函数内部计算捕获的PWM频率和占空比。 注意:当定时器的计数在0和AUTORLD之间跳变时,计数需要+1。 - /*
- * @brief 中断处理函数
- *
- * @param None
- *
- * @retval None
- *
- */
- void TMR4_IRQHandler(void)
- {
- /* 通道捕获值 */
- static uint16_t capture1 = 0;
- uint16_t capture1_tmp;
- static uint16_t capture2 = 0;
- static uint8_t capture2_ok;
- static uint16_t capture3 = 0;
- uint16_t capture3_tmp;
- static uint16_t capture4 = 0;
- static uint8_t capture4_ok;
-
- /* PWM占空比 */
- uint16_t pwm1_duty;
- uint16_t pwm2_duty;
- /* PWM频率 */
- uint32_t pwm1_hz;
- uint32_t pwm2_hz;
-
- /* 通道1捕获中断 */
- if (TMR_ReadIntFlag(TIM_INS, TMR_INT_CC1) == SET) {
- TMR_ClearIntFlag(TIM_INS, TMR_INT_CC1);
- capture1_tmp = TMR_ReadCaputer1(TIM_INS);
- /* 上升沿和下降沿均已捕获 */
- if (capture2_ok != 0) {
- /* 周期定时器计数 */
- pwm1_hz = (capture1 < capture1_tmp) ? (capture1_tmp - capture1) : (period_max + capture1_tmp - capture1);
- /* 高电平时长定时器计数 */
- pwm1_duty = (capture1 < capture2) ? (capture2 - capture1) : (period_max + capture2 - capture1);
- /* 转换为百分比和频率 */
- pwm1_duty = ((uint32_t)pwm1_duty) * 1000 / pwm1_hz; /* x10% */
- pwm1_hz = 100000000 / CAPTURE_PRECISION / pwm1_hz; /* x100Hz */
- (tim_capture_data_func_1 != NULL) ? tim_capture_data_func_1(pwm1_duty, pwm1_hz) : NULL;
- capture2_ok = 0;
- }
- capture1 = capture1_tmp;
- }
- /* 通道2捕获中断 */
- if (TMR_ReadIntFlag(TIM_INS, TMR_INT_CC2) == SET) {
- TMR_ClearIntFlag(TIM_INS, TMR_INT_CC2);
- capture2 = TMR_ReadCaputer2(TIM_INS);
- capture2_ok = 1;
- }
- /* 通道3捕获中断 */
- if (TMR_ReadIntFlag(TIM_INS, TMR_INT_CC3) == SET) {
- TMR_ClearIntFlag(TIM_INS, TMR_INT_CC3);
- capture3_tmp = TMR_ReadCaputer3(TIM_INS);
- /* 上升沿和下降沿均已捕获 */
- if (capture4_ok != 0) {
- /* 周期定时器计数 */
- pwm2_hz = (capture3 < capture3_tmp) ? (capture3_tmp - capture3) : (period_max + capture3_tmp - capture3);
- /* 高电平时长定时器计数 */
- pwm2_duty = (capture3 < capture4) ? (capture4 - capture3) : (period_max + capture4 - capture3);
- /* 转换为百分比和频率 */
- pwm2_duty = ((uint32_t)pwm2_duty) * 1000 / pwm2_hz; /* x10% */
- pwm2_hz = 100000000 / CAPTURE_PRECISION / pwm2_hz; /* x100Hz */
- (tim_capture_data_func_2 != NULL) ? tim_capture_data_func_2(pwm2_duty, pwm2_hz) : NULL;
- capture4_ok = 0;
- }
- capture3 = capture3_tmp;
- }
- /* 通道4捕获中断 */
- if (TMR_ReadIntFlag(TIM_INS, TMR_INT_CC4) == SET) {
- TMR_ClearIntFlag(TIM_INS, TMR_INT_CC4);
- capture4 = TMR_ReadCaputer4(TIM_INS);
- capture4_ok = 1;
- }
- }
4. 测试 TIMER的时基和PWM输出测试代码如下:定时器更新中断翻转IO测试时基,PWM输出测试占空比。 - // 外设初始化
- void bsp_init(void)
- {
- bsp_tim_init(100);
- }
- #include "apm32f10x_rcm.h"
- #include "apm32f10x_gpio.h"
- // IO初始化
- void gpio_test_init(void)
- {
- GPIO_Config_T gpioConfig;
-
- RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_GPIOE);
- GPIO_ConfigStructInit(&gpioConfig);
- gpioConfig.pin = GPIO_PIN_6;
- gpioConfig.speed = GPIO_SPEED_50MHz;
- gpioConfig.mode = GPIO_MODE_OUT_PP;
- GPIO_Config(GPIOE, &gpioConfig);
- }
- // IO翻转
- void gpio_toggle(void)
- {
- if (GPIO_ReadOutputBit(GPIOE, GPIO_PIN_6) == BIT_SET) {
- GPIO_ResetBit(GPIOE, GPIO_PIN_6);
- } else {
- GPIO_SetBit(GPIOE, GPIO_PIN_6);
- }
- }
- uint8_t duty1 = 10;
- uint8_t duty2 = 20;
- uint8_t duty3 = 30;
- uint8_t duty4 = 40;
- // 应用初始化
- void app_init(void)
- {
- gpio_test_init();
- bsp_tim_update_deal_func_register(gpio_toggle);
- }
- // 应用任务
- void app_task(void)
- {
- bsp_pwm_duty_set(duty1, duty2, duty3, duty4);
- }
互补通道的死区时间刚好为1us。
TIMER的输入捕获功能测试代码如下:
|