一种使用通用定时器实现独立通道输出指定数量脉冲的方法
- 一种使用通用定时器实现独立通道输出指定数量脉冲的方法
- 概述
- 实验平台
- 配置步骤
- 1. 初始化定时器与GPIO
- 2. 设置定时器工作模式
- 3. 编写脉冲计数逻辑
- 4. 调整参数以满足要求
- 注意事项
- 代码实现
- 电机结构体配置,GPIO配置不单独给出
- TIM2配置
- TIM2中断服务配置
- TIM3配置
- TIM3中断服务配置
- TIM4配置与TIM4中断服务配置
- 输出效果
- 结论
一种使用通用定时器实现独立通道输出指定数量脉冲的方法
概述
本文档描述了如何在STM32F103ZET6微控制器上,通过配置其内部通用定时器(如TIM2、TIM3等),实现每个定时器通道独立输出指定数量的脉冲。此方法适用于需要精确控制电机、LED亮度调节等应用场景。
实验平台
- MCU: STM32F103ZET6
- IDE: Keil MDK
- 开发方式: 标准库
- 编程语言: C语言
配置步骤
1. 初始化定时器与GPIO
首先,在代码中初始化相关定时器和GPIO引脚(hal库可使用STM32CubeMX进行相关配置)。确保选择的定时器支持PWM输出模式,并且对应的GPIO引脚已正确配置为复用功能输出。
2. 设置定时器工作模式
对于每个需要输出脉冲的定时器通道,需将其设置为PWM模式。具体来说:
- 选择PWM模式1或2(根据需求)。
- 设置自动重装载值(ARR),这决定了PWM周期。
- 设置比较值(CCR),用于控制单个脉冲宽度。
3. 编写脉冲计数逻辑
为了使每个通道能够输出指定数量的脉冲,需要编写一个简单的计数逻辑。每当一个PWM周期结束时(即定时器更新事件发生时),检查当前已输出的脉冲数是否达到设定的目标值。如果未达到,则继续输出;否则,停止对应通道的PWM输出。
4. 调整参数以满足要求
根据实际应用调整定时器的频率、PWM周期以及每个脉冲的宽度,以满足特定的应用需求。
注意事项
在调整定时器参数时,请注意不要超出硬件限制。
对于不同的应用场合,可能需要对上述方案进行适当调整,比如增加中断处理函数来提高实时性等。
代码实现
以电机应用为例,本案例使用TIM2为主定时器,TIM3、TIM4为从定时器,控制8个独立电机运动
电机结构体配置,GPIO配置不单独给出
u8 CNTtag = 0;
u8 ADD = 0;
struct DJ
{u8 num; //使能(0失能1使能)u8 ena; //使能(0失能1使能)u8 dir; //方向(0反转1正转)u8 state; //电机状态(0堵转1正常)u32 cycle_pulse; //步进电机转一周的脉冲数(=细分数)u32 pulse_set; //PWM脉冲设定值u32 pulse_cnt; //PWM脉冲计数值(用于单次转动脉冲计数)int pulse_tot_cnt; //PWM脉冲总计数值(保存电机位置以及用于角度计算)char string;
};
#define ALL_DJ_Stop (dj[0].state == DISABLE && dj[1].state == DISABLE) && (dj[2].state == DISABLE && dj[3].state == DISABLE) && (dj[4].state == DISABLE && dj[5].state == DISABLE) && (dj[6].state == DISABLE && dj[7].state == DISABLE)
//电机是否旋转代号
#define ENABLE 1 //ena-停止旋转 state-运动中
#define DISABLE 0 //ena-允许旋转 state-运动结束
//电机旋转方向代号
#define Forward 1 //dir-正方向移动
#define Revers 0 //dir-负方向移动 //电机操作定时器
#define DJ1_Timer TIM3
#define DJ2_Timer TIM3
#define DJ3_Timer TIM3
#define DJ4_Timer TIM3#define DJ5_Timer TIM4
#define DJ6_Timer TIM4
#define DJ7_Timer TIM4
#define DJ8_Timer TIM4//电机操作定时器通道
#define DJ1_Channel TIM_Channel_1
#define DJ2_Channel TIM_Channel_2
#define DJ3_Channel TIM_Channel_3
#define DJ4_Channel TIM_Channel_4#define DJ5_Channel TIM_Channel_1
#define DJ6_Channel TIM_Channel_2
#define DJ7_Channel TIM_Channel_3
#define DJ8_Channel TIM_Channel_4
TIM2配置
//通用定时器2中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器2!
void TIM2_Int_Init(u16 arr,u16 psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能//定时器TIM3初始化TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM2中断,允许更新中断//中断优先级NVIC设置NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器TIM_Cmd(TIM2, ENABLE); //使能TIMx
}
TIM2中断服务配置
//定时器2中断服务程序
void TIM2_IRQHandler(void) //TIM2中断
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查TIM2更新中断发生与否{TIM_ClearITPendingBit(TIM2, TIM_IT_Update ); //清除TIMx更新中断标志 //LED1=!LED1;TIM_CCxCmd(TIM3, TIM_Channel_1, ENABLE);TIM_CCxCmd(TIM3, TIM_Channel_2, ENABLE);TIM_CCxCmd(TIM3, TIM_Channel_3, ENABLE);TIM_CCxCmd(TIM3, TIM_Channel_4, ENABLE);TIM_Cmd(TIM3, ENABLE); //使能TIM3TIM_Cmd(TIM3, ENABLE); //使能TIM3TIM_CCxCmd(TIM4, TIM_Channel_1, ENABLE);TIM_CCxCmd(TIM4, TIM_Channel_2, ENABLE);TIM_CCxCmd(TIM4, TIM_Channel_3, ENABLE);TIM_CCxCmd(TIM4, TIM_Channel_4, ENABLE);TIM_Cmd(TIM4, ENABLE); //使能TIM4TIM_Cmd(TIM4, ENABLE); //使能TIM4}
}
TIM3配置
//TIM3 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{ NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCInitTypeDef TIM_OCInitStructureCh1;TIM_OCInitTypeDef TIM_OCInitStructureCh2;TIM_OCInitTypeDef TIM_OCInitStructureCh3;TIM_OCInitTypeDef TIM_OCInitStructureCh4;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5 //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //TIM_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIORCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟//设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; //TIM_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOTIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位// 开启通道1的PWM模式TIM_OCStructInit(&TIM_OCInitStructureCh1);TIM_OCInitStructureCh1.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructureCh1.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructureCh1.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OC1Init(TIM3, &TIM_OCInitStructureCh1);// 开启通道2的PWM模式TIM_OCStructInit(&TIM_OCInitStructureCh2);TIM_OCInitStructureCh2.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructureCh2.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructureCh2.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OC2Init(TIM3, &TIM_OCInitStructureCh2);// 开启通道3的PWM模式TIM_OCStructInit(&TIM_OCInitStructureCh3);TIM_OCInitStructureCh3.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructureCh3.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructureCh3.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OC3Init(TIM3, &TIM_OCInitStructureCh3);// 开启通道4的PWM模式TIM_OCStructInit(&TIM_OCInitStructureCh4);TIM_OCInitStructureCh4.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructureCh4.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructureCh4.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OC4Init(TIM3, &TIM_OCInitStructureCh4);TIM_OC1Init(TIM3, &TIM_OCInitStructureCh1); //根据T指定的参数初始化外设TIM3 OC1TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器TIM_OC2Init(TIM3, &TIM_OCInitStructureCh2); //根据T指定的参数初始化外设TIM3 OC2TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器TIM_OC3Init(TIM3, &TIM_OCInitStructureCh3); //根据T指定的参数初始化外设TIM3 OC3TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器TIM_OC4Init(TIM3, &TIM_OCInitStructureCh4); //根据T指定的参数初始化外设TIM3 OC4TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器NVIC_EnableIRQ(TIM3_IRQn);// 配置TIM3通道1,并使能比较匹配中断TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); // PWM模式下一般选择更新事件触发输出// TIM_SetCompare1(TIM3, dutyCycle); // 设置比较值(占空比)TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); // 使能通道1的比较匹配中断TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE); // 使能通道1的比较匹配中断TIM_ITConfig(TIM3, TIM_IT_CC3, ENABLE); // 使能通道1的比较匹配中断TIM_ITConfig(TIM3, TIM_IT_CC4, ENABLE); // 使能通道1的比较匹配中断TIM_Cmd(TIM3, DISABLE); //使能TIM3}
TIM3中断服务配置
void TIM3_IRQHandler(void) //TIM3中断
{if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);//脉冲计数if(dj[0].ena == DISABLE){dj[0].pulse_cnt++;if(dj[0].dir == Revers){dj[0].pulse_tot_cnt-=1;}else if(dj[0].dir == Forward){dj[0].pulse_tot_cnt+=1;}}//结束停止if(dj[0].pulse_cnt > dj[0].pulse_set + ADD){TIM_CCxCmd(TIM3, TIM_Channel_1, DISABLE); // TIM_Channel_1对应通道1dj[0].pulse_cnt = 0;dj[0].ena = ENABLE;}}if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);//脉冲计数if(dj[1].ena == DISABLE){dj[1].pulse_cnt++;if(dj[1].dir == Revers){dj[1].pulse_tot_cnt-=1;}else if(dj[1].dir == Forward){dj[1].pulse_tot_cnt+=1;}}//结束停止if(dj[1].pulse_cnt > dj[1].pulse_set + ADD){TIM_CCxCmd(TIM3, TIM_Channel_2, DISABLE); // TIM_Channel_1对应通道1dj[1].pulse_cnt = 0;dj[1].ena = ENABLE;}}if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_CC3);//脉冲计数if(dj[2].ena == DISABLE){dj[2].pulse_cnt++;if(dj[2].dir == Revers){dj[2].pulse_tot_cnt-=1;}else if(dj[2].dir == Forward){dj[2].pulse_tot_cnt+=1;}}//结束停止if(dj[2].pulse_cnt > dj[2].pulse_set + ADD){TIM_CCxCmd(TIM3, TIM_Channel_3, DISABLE); // TIM_Channel_1对应通道1dj[2].pulse_cnt = 0;dj[2].ena = ENABLE;}}if (TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_CC4);//脉冲计数if(dj[3].ena == DISABLE){dj[3].pulse_cnt++;if(dj[3].dir == Revers){dj[3].pulse_tot_cnt-=1;}else if(dj[3].dir == Forward){dj[3].pulse_tot_cnt+=1;}}//结束停止if(dj[3].pulse_cnt > dj[3].pulse_set + ADD){TIM_CCxCmd(TIM3, TIM_Channel_4, DISABLE); // TIM_Channel_1对应通道1dj[3].pulse_cnt = 0;dj[3].ena = ENABLE;}}if(dj[0].pulse_cnt==0&&dj[1].pulse_cnt==0&&dj[2].pulse_cnt==0&&dj[3].pulse_cnt==0){TIM_Cmd(TIM3, DISABLE);dj[0].pulse_cnt = 1;dj[1].pulse_cnt = 1;dj[2].pulse_cnt = 1;dj[3].pulse_cnt = 1;dj[0].pulse_set = 1;dj[1].pulse_set = 1;dj[2].pulse_set = 1;dj[3].pulse_set = 1;dj[0].state = DISABLE;dj[1].state = DISABLE;dj[2].state = DISABLE;dj[3].state = DISABLE;//printf("--cccccwwww--\r\n");
// printf("0,%d,%d,%d,%d,%d,%d,%d\r\n",
// dj[0].ena, dj[0].dir, dj[0].state, dj[0].cycle_pulse,
// dj[0].pulse_set, dj[0].pulse_cnt, dj[0].pulse_tot_cnt);
// printf("1,%d,%d,%d,%d,%d,%d,%d\r\n",
// dj[1].ena, dj[1].dir, dj[1].state, dj[1].cycle_pulse,
// dj[1].pulse_set, dj[1].pulse_cnt, dj[1].pulse_tot_cnt);
// printf("2,%d,%d,%d,%d,%d,%d,%d\r\n",
// dj[2].ena, dj[2].dir, dj[2].state, dj[2].cycle_pulse,
// dj[2].pulse_set, dj[2].pulse_cnt, dj[2].pulse_tot_cnt);
// printf("3,%d,%d,%d,%d,%d,%d,%d\r\n",
// dj[3].ena, dj[3].dir, dj[3].state, dj[3].cycle_pulse,
// dj[3].pulse_set, dj[3].pulse_cnt, dj[3].pulse_tot_cnt);
// delay_us(5);
// printf("-------------\r\n");}
}
TIM4配置与TIM4中断服务配置
类似TIM3实现,不再单独写出
输出效果
结论
通过合理配置STM32F103ZET6上的通用定时器资源,可以方便地实现多通道独立输出指定数量脉冲的功能。这对于控制外部设备如电机速度、灯光强度等提供了灵活有效的手段。