<分享>STM32输出可控数量的pwm波形
在做步进电机驱动程序,想让电机按一定速度运行一定角度,于是采用了一个主定时器产生pwm,另外一个从定时器负责发送脉冲数量的计数
STM32F103VE 时钟72MHz
实现:输出可控数量与频率的脉冲(pwm)
使用一个(主)定时器作为另一个(从)定时器的预分频器(外部时钟触发源)
本例子通过<df_MAIN_TIM_1_OR_8>修改可以实现TIM1和TIM8主的切换产生PWM信号,TIM2作为从定时器,用于对主定时器的计数,计数到达设定数量,停止pwm输出
TIM8已验证功能-ok;TIM1仿真运行正常,输出未验证
#define df_MAIN_TIM_1 0 //主定时器1
#define df_MAIN_TIM_8 1 //主定时器8
#define df_MAIN_TIM_1_OR_8 df_MAIN_TIM_8 //定时器选择
void TIM1_MasterPwmOutInit(void);
void TIM8_MasterPwmOutInit(void);
//TIM1_主pwm输出初始化
void TIM1_MasterPwmOutInit(void){
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
TIM_OCInitTypeDefTIM_OCInitStructure;
//pwm频率=72000000/psc/arr这里为了测试设置了1Hz用于led观测
unsigned short arr=10000;//周期
unsigned short psc=7200;//分频
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_TIM1 , ENABLE); //时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //TIM1_CH4 PA11
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr-1; //使用Cycle来控制频率(f=72/(71+1)/Cycle)当Cycle为100时脉冲频率为10KHZ
TIM_TimeBaseStructure.TIM_Prescaler = psc-1; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS= Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //重复计数,一定要=0!!!(高级定时器特有)
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = psc/2-1; //设置待装入捕获寄存器的脉冲值(占空比:默认50%,这可也可以调节如果需要的话将它作为一个参数传入即可)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性
TIM_OC4Init(TIM1, &TIM_OCInitStructure); //使能通道4
TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable); //设置为主从模式
TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); //选择定时器1的触发方式(使用更新事件作为触发输出)
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //使能通道4预装载寄存器
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIM1在ARR上的预装载寄存器
//TIM_CtrlPWMOutputs(TIM1, ENABLE); //高级定时器一定要加上,主输出使能
}
//TIM8_pwm输出初始化
void TIM8_MasterPwmOutInit(void){
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
TIM_OCInitTypeDefTIM_OCInitStructure;
//pwm频率=72000000/psc/arr这里为了测试设置了1Hz用于led观测
unsigned short arr=10000;//周期
unsigned short psc=7200;//分频
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);//使能GPIO外设和AFIO复用功能模块时钟使能
//PC6-CH1
//PC7-CH2
//PC8-CH3 cut_pwm
//PC9-CH4 out_pwm
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;//初始化GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值72MHz/(psc+1)/(arr+1)=频率
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //重复计数,一定要=0!!!(高级定时器特有)<<<这里必须设置否则从计数器不正常
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
// {//CH1
// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能/失能OCx管脚
// TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //使能/失能OCxN管脚(使用OCxN必须使能TIM_OutputState)
// TIM_OCInitStructure.TIM_Pulse = arr+1; //设置待装入捕获比较寄存器的脉冲值
// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
// TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; //输出极性
// TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //BDTR寄存器的MOE为0时对应管脚状态
// TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; //BDTR寄存器的MOE为0时对应管脚状态
// TIM_OC1Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
//
// TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器
// }
// {//CH2
// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能/失能OCx管脚
// TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //使能/失能OCxN管脚(使用OCxN必须使能TIM_OutputState)
// TIM_OCInitStructure.TIM_Pulse = arr+1; //设置待装入捕获比较寄存器的脉冲值
// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
// TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; //输出极性
// TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //BDTR寄存器的MOE为0时对应管脚状态
// TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; //BDTR寄存器的MOE为0时对应管脚状态
// TIM_OC2Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
//
// TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器
// }
{//CH3
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能/失能OCx管脚
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //使能/失能OCxN管脚(使用OCxN必须使能TIM_OutputState)
TIM_OCInitStructure.TIM_Pulse = arr/2-1; //设置待装入捕获比较寄存器的脉冲值 占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; //输出极性
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; //BDTR寄存器的MOE为0时对应管脚状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; //BDTR寄存器的MOE为0时对应管脚状态
TIM_OC3Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC3PreloadConfig(TIM8, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器
}
{//CH4
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能/失能OCx管脚
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //使能/失能OCxN管脚(使用OCxN必须使能TIM_OutputState)
TIM_OCInitStructure.TIM_Pulse = arr/2-1; //设置待装入捕获比较寄存器的脉冲值 占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; //输出极性
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; //BDTR寄存器的MOE为0时对应管脚状态
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; //BDTR寄存器的MOE为0时对应管脚状态
TIM_OC4Init(TIM8, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC4PreloadConfig(TIM8, TIM_OCPreload_Enable); //使能TIMx在CCR2上的预装载寄存器
}
TIM_ARRPreloadConfig(TIM8, ENABLE); //使能TIMx在ARR上的预装载寄存器
// TIM_CtrlPWMOutputs(TIM8, ENABLE); //TIM8->BDTR|=1<<15; 主输出MOE
// TIM_Cmd(TIM8, ENABLE); //使能TIMx外设
TIM_SetCompare3(TIM8,arr/2-1); //TIM8->CCR2 = x;更改占空比(设置范围0~arr)
TIM_SetCompare3(TIM8,arr/2-1); //TIM8->CCR2 = x;更改占空比(设置范围0~arr)
}
{//设置为主从模式
TIM_SelectMasterSlaveMode(TIM8, TIM_MasterSlaveMode_Enable); //设置为主从模式
TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update); //选择定时器1的触发方式(使用更新事件作为触发输出)
}
}
//TIM2从计数定时器初始化
//设定脉冲数量后面可以通过TIM_SetAutoreload(TIM2,X);修改,这里预设了一个数值
void TIM2_SlaveCountInit(void){
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
unsigned short PulseNum = 65535; //预设脉冲数
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定时器2的时钟
TIM_TimeBaseStructure.TIM_Period = PulseNum-1; //脉冲数
TIM_TimeBaseStructure.TIM_Prescaler =0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
#if (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_1)
TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0); //选择定时器2的输入触发源(内部触发(TIM1))
#elif (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_8)
TIM_SelectInputTrigger(TIM2, TIM_TS_ITR1); //选择定时器2的输入触发源(内部触发(TIM8))
#endif //df_MAIN_TIM_1_OR_8
TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_External1); //TIM2->SMCR|=0x07;设置从模式寄存器(SMS:111 外部时钟模式1)
TIM_ITConfig(TIM2,TIM_IT_Update,DISABLE); //更新中断失能
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); //定时器2中断初始化
}
//定时器_主从_pwm计数_初始化
void TIM_MasterSlave_PWMCount_Init(void){
TIM2_SlaveCountInit(); //初始化 (从定时器)
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志位
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能更新中断
#if (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_1)
TIM1_MasterPwmOutInit(); //使能定时器1(主定时器)
#elif (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_8)
TIM8_MasterPwmOutInit(); //使能定时器1(主定时器)
#endif //df_MAIN_TIM_1_OR_8
}
//定时器_主从_pwm计数_开始
//PulseNum :用于设定输出脉冲的数量
void TIM_MasterSlave_PWMCount_Start(unsigned short PulseNum){
PulseNum = (PulseNum>1?PulseNum-1:PulseNum); //这里的数值不能为0,为0后TIM2将失效; 设置数-1为需要的脉冲数
TIM_Cmd(TIM2, ENABLE); //使能TIM2 (从定时器)
TIM_SetAutoreload(TIM2,PulseNum); //设置脉冲数量
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能更新中断
#if (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_1)
TIM_Cmd(TIM1, ENABLE); //使能定时器1
TIM_CtrlPWMOutputs(TIM1, ENABLE); //高级定时器一定要加上,主输出使能
#elif (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_8)
TIM_Cmd(TIM8, ENABLE); //使能定时器8
TIM_CtrlPWMOutputs(TIM8, ENABLE); //高级定时器一定要加上,主输出使能
#endif //df_MAIN_TIM_1_OR_8
}
//当TIM的CNT寄存器的值到达设定的Update值会触发更新中断,此时设定的脉冲数已输出完毕,关闭TIM1和TIM2
void TIM2_IRQHandler(void){
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){ //TIM_IT_Update
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志位
#if (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_1)
TIM_CtrlPWMOutputs(TIM1, DISABLE); //主输出失能
TIM_Cmd(TIM1, DISABLE); //关闭定时器
#elif (df_MAIN_TIM_1_OR_8 == df_MAIN_TIM_8)
TIM_CtrlPWMOutputs(TIM8, DISABLE); //主输出失能
TIM_Cmd(TIM8, DISABLE); //关闭定时器
#endif //df_MAIN_TIM_1_OR_8
TIM_Cmd(TIM2, DISABLE); //关闭定时器
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); //关闭TIM2更新中断
}
}
u8 ksw=0;
void tim_test_co(void){
TIM_MasterSlave_PWMCount_Init();//初始化
while(1)
{
if(ksw){
ksw = 0;
TIM_MasterSlave_PWMCount_Start(3);//启动3个pwm
}
}
}
感谢分享。。。 感谢分享。。 18年VIP++,牛 whatcanitbe 发表于 2022-10-30 13:11
18年VIP++,牛
(引用自4楼)
一直到我退休了{:lol:} zhcj66 发表于 2022-10-31 11:16
一直到我退休了
(引用自5楼)
可能会多几个脉冲,你可以控控多种数量的,比如从几个到几千,印象中有这毛病。
这个控制方法以前用,后来参考别人的帖子,改成DMA 方式了看看 我的帖子占用资源少,一路可控只用一个定时器就能实现了,而且用DMA 方式,不占CPU kyq_linux 发表于 2022-10-31 15:25
可能会多几个脉冲,你可以控控多种数量的,比如从几个到几千,印象中有这毛病。
...
(引用自6楼)
启用了CCR1和CCR2,在CCR1=0CCR2 = 50(方波 占空比50%) ,启动输出,CCR1管脚会出现几个脉冲就消失了,不知道你遇到过吗?
磊磊映画 发表于 2022-11-3 16:37
这个控制方法以前用,后来参考别人的帖子,改成DMA 方式了看看 我的帖子占用资源少,一路可控只用一个 ...
(引用自7楼)
https://www.amobbs.com/thread-5758539-1-1.html
<STM32驱动步进电机精确可控输出脉冲个数方案及对比>
7.定时器CHx独立通道dma,改变dma传输长度控制个数(上一个用的溢出dma一个定时器只能产生一路有点浪费,改成用通道dma)
一个定时器可以产生3路或4路,dma限制不能是4,例如定时器5、8可以4路
优点:能读取剩余脉冲个数
可变占空比,周期,用于可以调速等 缺点:好像没哈缺点
这个DMA确实更好 zhcj66 发表于 2022-11-4 08:18
https://www.amobbs.com/thread-5758539-1-1.html
(引用自9楼)
嗯,做了两个项目实战得来的
页:
[1]