找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2224|回复: 3
打印 上一主题 下一主题
收起左侧

零基础制作平衡小车【连载】3---STM32生成PWM控制电机旋转

[复制链接]
跳转到指定楼层
楼主
ID:223481 发表于 2020-11-27 14:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

制作平衡小车肯定会用到电机,那么怎么控制呢?最简单的就是直接加电压,这样电机就能转动,但是至于转多少圈,转的快慢是不能控制的。这就不符合我们平衡小车的控制要求。这就需要用到PWM模式来控制电压的大小,从而控制转的快慢。至于转了多少圈,就要用到编码器了。今天先来看看怎么控制小车转的快慢吧,一步一步来。

直流有刷电机的原理

简单说下有刷电机的工作原理,其实很简单,稍微懂点物理的就能看懂吧。

上面这张图应该都见过吧(因为你现在就在看着它呢,手动滑稽)。小时候四驱赛车的那种黄色小马达也都见过吧,是不是只要将电压加载电刷的两极上,电机就会转了。那个电机的内部结构就是上图这样。这一种里面带有电刷的电机叫有刷电机,又因为是用的直流电,所以叫直流有刷电机。
我们只需要将电压加到AB端,电机就能转,电机转动的速度和电压有关,电压越大,转速越高。具体电机旋转不懂的百度吧。我就不搬运了,重点是都后面的PWM。

有一点需要注意的是,直流有刷电机的驱动频率的问题,过高或者过低都不好,网上找了资料说是根据电机的不同频率有所不同,一般10k-20k。我暂且信了吧。

PWM是什么?

既然知道了电压和转速成正比,那我们是不是只需要控制电压就能控制转速了。这就需要用到PWM技术了。PWM又名脉冲宽度调制,从名字上来看是调节脉冲宽度的。到底是怎么调节的呢?我是这样理解的。
本来我们直接给电压,电机就会转,现在我们通过控制电压,让他一会有输出,一会不输出。假设占空比为50%,也就是说,在一定时间内,电压有一半的时间是闭合的,一半的时间是打开的,这样求出来的平均电压值应该是原电压值的一半。(不相信的同学可以一会试试,我后面把代码传上来)这样我们就能控制电压了。但是只控制电压还不行呀,单片机引脚不能直接接到电机上呀,这就需要电机驱动器了。

TB6612直流有刷电机驱动


从淘宝截了一张图,上面有引脚和使用说明,接线也很简单。原理的话我没细看,大致应该是通过PWM来控制VM的电压值,之后会有一个像H桥一样的电路来控制正反转,当然了都是集成到芯片内部了。

STM32F103ZET6生成PWM代码/*使用通用定时器2  通道1  PA0引脚输出PWM------------ PWM信号 周期和占空比的计算 --------------- ARR :自动重装载寄存器的值 CLK_cnt:计数器的时钟,等于 Fck_int / (psc+1) = 72M/(psc+1) PWM 信号的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M 占空比P=CCR/(ARR+1) 时钟分频71,系统总线时钟72M,可以算出定时器时钟为72/(71+1)=1M在这里我们需要设定PWM频率为10Khz=0.1ms,因此自动重装载的值ARR=100.因为定时器时钟为1M=0.001ms,因此只需要累加100次,就等于0.1ms=10Khz了。占空比我们设定50%因此我们配置如下TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 71;                   //定时器时钟分频TIM_TimeBaseInitTypeStructure.TIM_Period = (100 - 1);                //自动重装载的值 TIM_OCInitTypeStructure.TIM_Pulse = 50;                              //设置占空比大小*/void PWM_Init(void){    //定义结构体变量    GPIO_InitTypeDef GPIO_InitTypeStructure;    TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitTypeStructure;    TIM_OCInitTypeDef TIM_OCInitTypeStructure;        //使能引脚时钟    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);    //使能引脚时钟    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);            //引脚配置    GPIO_InitTypeStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //复用推挽输出模式    GPIO_InitTypeStructure.GPIO_Pin = GPIO_Pin_0;           //PA0    GPIO_InitTypeStructure.GPIO_Speed = GPIO_Speed_50MHz;   //速度为50M    GPIO_Init(GPIOA, &GPIO_InitTypeStructure);              //写入到寄存器中            //定时器基本配置    TIM_TimeBaseStructInit(&TIM_TimeBaseInitTypeStructure);                             //恢复默认值    //自动重装载寄存器的值,当TIM_Period+1个频率后产生一个更新或者中断    TIM_TimeBaseInitTypeStructure.TIM_Period = (100 - 1);                               //自动重装载的值    //驱动CNT计数器的时钟 = Fck_int/(psc+1)    TIM_TimeBaseInitTypeStructure.TIM_Prescaler = 71;                                   //定时器时钟分频    //时钟分频因子 ,配置死区时间时需要用到    TIM_TimeBaseInitTypeStructure.TIM_ClockDivision = TIM_CKD_DIV1;                     //时钟分割    //向上计数模式    TIM_TimeBaseInitTypeStructure.TIM_CounterMode = TIM_CounterMode_Up;                 //计数器模式    //重复计数器的值,没用到不用管    TIM_TimeBaseInitTypeStructure.TIM_RepetitionCounter = 0;                            //重复计数器的值.    //写入到寄存器    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitTypeStructure);            //PWM模式配置    TIM_OCStructInit(&TIM_OCInitTypeStructure);                         //恢复默认值    TIM_OCInitTypeStructure.TIM_OCMode = TIM_OCMode_PWM1;               //选择PWM1模式    TIM_OCInitTypeStructure.TIM_OCIdleState = TIM_OCIdleState_Set;      //空闲时间的引脚电平    TIM_OCInitTypeStructure.TIM_OCPolarity = TIM_OCPolarity_High;       //指定输出极性    TIM_OCInitTypeStructure.TIM_OutputState = TIM_OutputState_Enable;   //输出比较使能    TIM_OCInitTypeStructure.TIM_Pulse = 80;                             //设置占空比大小        //初始化通道1    TIM_OC1Init(TIM2, &TIM_OCInitTypeStructure);        //启用CCR1上的TIMx外设预加载寄存器。    TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);        //使能定时器    TIM_Cmd(TIM2,ENABLE);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
示波器测量

1.当占空比为50%时

测量电压值

2.占空比为80%时

测量值

总结

1.PWM配置过程
①配置GPIO引脚,用的哪个引脚就配置哪个引脚,注意别忘了开启时钟使能
②配置完引脚,配置定时器基本参数
注意:
在配置TIM_Period的值的时候一定不要忘记最终计算的值是TIM_Period+1;
TIM_Prescaler在最终计算的值也是TIM_Prescaler+1。

TIM_TimeBaseStructInit(&TIM_TimeBaseInitTypeStructure); //恢复默认值
上面这个函数可要可不要,规范化代码最好是写上去。
③配置PWM输出通道
④启用CCR1上的TIMx外设预加载寄存器
⑤使能定时器

特别特别注意

下面这个函数

//启用CCR1上的TIMx外设预加载寄存器。
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);

进入到函数主题

void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload){  uint16_t tmpccmr1 = 0;  /* Check the parameters */  assert_param(IS_TIM_LIST8_PERIPH(TIMx));  assert_param(IS_TIM_OCPRELOAD_STATE(TIM_OCPreload));  tmpccmr1 = TIMx->CCMR1;  /* Reset the OC1PE Bit */  tmpccmr1 &= (uint16_t)~((uint16_t)TIM_CCMR1_OC1PE);  /* Enable or Disable the Output Compare Preload feature */  tmpccmr1 |= TIM_OCPreload;  /* Write to TIMx CCMR1 register */  TIMx->CCMR1 = tmpccmr1;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

中文手册中的寄存器说明


函数进来首先复位OC1PE位,之后再根据形参来决定该位置高还是置低。如果我们使能该位,则当我们在改变CCR1寄存器中的值的时候,他不会立即改变,而是将这个周期走完,下个周期才会更新CCR1 的值。

比如我们设定ARR=100,CCR1=50,累加器从0开始往上加,当OC1PE=0,我们在累加器加到30的时候突然改变CCR1的值,让CCR1=70,此时当累加器加到50的时候,电平是不会跳变的,因为OC1PE=0时,改变CCR1则会立即生效,此时已经将CCR1=70了,因此达到70才会跳变。

当OC1PE=1时,他不会在这个周期生效,累加器还会在50的时候跳变,在下一个周期才会从70跳变。

因为我写的这个程序中没有改变CCR1的值,所以该行代码写不写都没有什么影响。只不过我写的时候不明白这个函数是什么意思,就去搜索了一下,顺便把他记录下来。


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:223481 发表于 2020-11-27 14:13 | 只看该作者
原文链接:
https://blog.csdn.net/D_SEngineer?spm=1000.2115.3001.5113
去掉表情
回复

使用道具 举报

板凳
ID:666561 发表于 2020-11-27 18:17 | 只看该作者
楼主加油,正好跟着学习
回复

使用道具 举报

地板
ID:223481 发表于 2020-12-1 15:28 | 只看该作者
听檐雨 发表于 2020-11-27 18:17
楼主加油,正好跟着学习

在这里发表代码全乱了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表