1 Star 17 Fork 9

立创开发板/裸机版基于立创梁山派的21年电赛F题智能送药小车

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
3_让电机和舵机动起来.md 22.37 KB
一键复制 编辑 原始数据 按行查看 历史
yzh 提交于 2023-08-29 19:53 . -update

1 什么是PWM?

只简单介绍,更基础的原理请查阅立创梁山派基础课程

PWM_wave

PWM(Pulse Width Modulation,脉宽调制)是一种在嵌入式系统中常用的技术,它可以用来模拟信号,控制设备的功率输出或者实现对设备的精确控制。PWM信号是一种类似于方波的信号,具有固定的频率,但脉冲宽度(占空比)可以调整。在一定频率下,我们可以通过调整这个占空比来改变他的有效电压,在一定程度上可以实现D/A转换(数字量转模拟量,不过一般来说都是用DAC,立创梁山派自带DAC)。

  • 频率(Frequency):指PWM信号在一秒内循环的次数。频率是周期的倒数,单位是赫兹(Hz)。
  • 周期(Period):指一个完整的PWM信号的时间长度,与频率成反比。单位是秒(s)。
  • 脉宽(Pulse Width):指PWM信号中高电平(通常为1)的时间长度。单位是秒(s)或毫秒(ms)。
  • 占空比(Duty Ratio):表示在一个完整的PWM信号周期内,高电平(通常为1)所占的时间比例。占空比 = (脉宽 / 周期)x 100%。
  • 上升沿(Rising Edge):PWM信号从低电平跳变到高电平的瞬间,通常用来作为触发事件。
  • 下降沿(Falling Edge):PWM信号从高电平跳变到低电平的瞬间,也常被用作触发事件。
  • 正脉冲宽度(Positive Pulse Width):PWM信号中高电平的持续时间,一般情况下的脉宽指的就是这个。
  • 负脉冲宽度(Negative Pulse Width):PWM信号中低电平的持续时间。

在嵌入式系统中,PWM的应用场景非常广泛,例如:

  1. 电机控制:通过调整PWM的占空比,可以精确控制直流电机的转速。占空比越高,电机转速越快;占空比越低,电机转速越慢。
  2. LED亮度调节:通过调整PWM的占空比,可以实现对LED灯的亮度调节。占空比越高,LED灯越亮;占空比越低,LED灯越暗。

举个例子:假设我们有一个LED灯,想实现亮度调节。我们可以生成一个PWM信号,频率为1kHz(周期为1ms),然后通过调整占空比来实现LED灯的亮度调节。如果占空比为100%,那么LED灯将一直处于亮的状态;如果占空比为50%,那么LED灯将以1ms为周期,半亮半暗;如果占空比为0%,那么LED灯将一直处于熄灭状态。通过不断调整占空比,就可以实现LED灯的亮度调节。如果PWM信号的频率过低,我们可能会感觉到它在闪烁。所以,在设计PWM驱动的LED灯时,一般会选择较高的频率来避免可见的闪烁。通常我们是看不到它闪烁的,这主要是因为两个原因:第一个是PWM信号的频率够高,第二个是人眼的视觉暂留效应。即当光源瞬间消失时,我们还能在极短的时间内感知到光源。这种效应导致人眼对快速闪烁的光源产生平滑的感觉。当PWM频率足够高时,视觉暂留效应会使我们感觉到LED灯的亮度是连续的。

2 手机屏幕的亮度是怎么调节的?

我们平时一直摸的手机屏幕有一部分就是用PWM来调节的。在智能手机屏幕中,PWM调光和DC调光是两种常见的屏幕亮度调节技术。它们的主要区别在于亮度调节的实现方式。

PWM调光:如之前所说,PWM(脉宽调制)调光是通过调整占空比来控制屏幕亮度的。在这种方法中,屏幕背光源会周期性地开启和关闭。占空比越高,背光源亮的时间越长,屏幕亮度越高;占空比越低,背光源亮的时间越短,屏幕亮度越低。

DC调光:DC(直流)调光是通过调整屏幕背光源的电流来实现亮度调节的。在这种方法中,背光源会一直保持开启状态,但电流的大小会改变,从而调整屏幕亮度。

这两种调光技术各有优缺点:

PWM调光优缺点

  • 优点:能实现较高的亮度范围和对比度,通常在高亮度下表现更好。
  • 缺点:在低频率下,PWM调光可能导致屏幕闪烁,对部分人来说可能引起眼睛疲劳和不适。此外,对于快速拍照或录像时,PWM调光可能导致出现条纹或闪烁的现象。

DC调光优缺点

  • 优点:在低亮度下表现更好,因为屏幕背光源一直保持开启状态,不会出现闪烁现象,对眼睛更友好。
  • 缺点:在高亮度下,对比度和亮度范围可能不如PWM调光好。此外,DC调光可能导致背光源的寿命降低和能耗略高。

各种技术的选择取决于手机制造商的考虑和市场需求。有一些高端手机是用的混合调光,在高亮度的模式下用DC调光,亮度低于一定值后用PWM调光。

3 让电机动起来

做一个小车最基础的要求就是让他能动起来,至少先能前进后退。

3.1 硬件

一般来说,驱动直流减速电机的方式有以下几种,最省时省力还可靠的方式还是使用集成H桥驱动器的方式:

  • 自行构建H桥:如果有特殊需求,也可以自行使用MOSFET构建H桥,难度比较大,新手谨慎尝试。立创开源平台有挺多案例,可以点击链接参考,有多个大MOS管的就是自行构建的H桥驱动,大电流双路直流电机驱动 H桥
  • 使用集成H桥芯片:例如TB6612L298NRZ7899等。这些集成方案通常包括所有必要的保护和功能,只需通过几个控制引脚(用于方向和PWM速度控制)即可驱动电机。

3.1.1 自行搭建H桥驱动电路

优点:

  1. 灵活性:可以根据项目的具体需求选择合适的组件和设计,例如电流、电压、控制逻辑等。
  2. 定制化:适合特殊要求或非标准应用。
  3. 学习价值:通过自己搭建和调试,可以深入理解H桥的工作原理和如何进行电机控制。

缺点:

  1. 复杂性:设计和搭建过程可能涉及很多细节。
  2. 可靠性问题:如果设计或搭建不当,可能存在可靠性和稳定性问题。
  3. 缺乏保护:集成方案通常包括过流保护、热保护等,自行搭建一般会缺少这些保护机制。
  4. 尺寸可能较大:根据设计和元件选择,封装一般都比较大,自行搭建的方案一般会占用电路板更多空间。

3.1.2 使用集成H桥驱动芯片

优点:

  1. 便利性:通常包括必要的功能和保护,使得设计和连接更为方便。
  2. 可靠性高:集成芯片是经过优化和测试的,通常提供较高的可靠性和稳定性。
  3. 尺寸小:集成在一个芯片内的解决方案通常更紧凑,适合空间受限的应用。
  4. 成本:对于普通应用,集成方案通常更为经济,而且可以节省设计和测试时间。

缺点:

  1. 灵活性较低:集成方案的参数和功能通常固定,可能不适合具有特殊要求的项目。
  2. 对特殊应用的适应性较差:如果项目有非标准的电压、电流或其他需求,集成方案就可能不合适。

3.1.3 送药小车实际电路

image-20230808104048573

上面的电路是参考芯片的数据手册设计的,大家在设计原理图时第一参考永远是原厂的手册或原厂的工程。

一个AT8870可以驱动一个直流电机正反转,我们要驱动两个直流电机,所以这里要用两个驱动芯片。他的手册是全中文的,重点要看的参数主要是主供电要求,逻辑供电要求,逻辑输入要求,连续输出电流,峰值输出电流,最大PWM频率等。 了解上面以上参数后我们就可以选电机了,要确保电机的工作电流电压要在电机驱动芯片的工作范围以下,我选的是轮趣科技的MG513P20_12V ,这个电机减速比是20,工作电压是12V,额定电流是0.36A,堵转电流是3.2A。

我们所用的AT8870峰值输出电流是3.6A,那还是有可能在长时间堵转的情况下烧毁的,所以我们需要设置驱动芯片的保护,这款芯片的ISEN引脚可以设置芯片的电流控制电流(这个电阻的取值和参考电压以及要设置的电流有关)。详细介绍可以看AT8870的datasheet的第8页,有详细的介绍和公式。在上面原理图中我就选择0.15欧姆了。代表参考电压为3.3V时,目标电流为2.2A。

因为AT8870目前已经在立创买不到了,所以我们可以找个PINtoPIN的料,AT8236,查看手册可以看到,他的峰值输出电流是6A,为了保护电机和驱动芯片,设置了电流保护,这个芯片的ISEN引脚接入一个电阻就可以设置芯片的电流,当这个电流达到设置的阈值时,驱动器就会输出关断(这个电阻的取值和参考电压以及要设置的电流有关)。详细介绍可以看AT8236的datasheet的第6页,有详细的介绍和公式。在上面原理图中我就选择0.15欧姆了。代表参考电压为3.3V时,目标电流为2.2A。

image-20230829120740686

3.3/(10*0.15)=2.2A ,实测这样配置电流就算电机运行在堵转,最大速度瞬间切换到最小速度,最大速度瞬间外力刹车等情况下均不会导致驱动芯片或电机损坏。

再有就是我们看一下手册中快衰减和慢衰减说的是什么,这里的快慢指的是电流,而不是电机转动的速度。

image-20230808104548142

由于电机是感性负载(当通电时,线圈内会形成磁场。这个线圈的磁场会产生自感电动势,抵抗电流的改变。),当断开电机两端的电压时,电流会产生反向电动势。这可能会对驱动芯片造成损坏。因此,要让电机停下来,除了断开电源,还需要建立一个续流回路来释放电机中的能量。慢衰减相当于加在电机(感性原件)两端电压消失,将电机两端正负短接。快衰减相当于加在电机(感性原件)两端电压消失,将电机两端快速接上与驱动电流相反的电流。如果大家小时候鼓捣过四驱车的马达,就能发现如果把马达两端短接,再用手去转动这个马达就会明显感受到阻力,转的越快阻力越大。

在送药小车中,我们希望小车在停止时电机就立马停止而不是向前滑行一段,所以我这里在电机停止转动时就选择慢衰竭(刹车)模式。

近期发现立创商城上面AT8870停止进货了,那可以选一个更好的,AT8236(网页下面可以直接看手册),最大峰值驱动输出电流可达6A,连续输出驱动电流可达4A,还是PINtoPIN的,可以直接替换,实际焊接的时候直接替换就好了,这也就提醒了我们,设计时要尽量选用有替代方案的物料,不然做成产品出货时后有可能会被一个物料卡住脖子。

3.2 软件

3.2.1 获取GD32定时器的时钟频率

要想输出确定PWM频率的信号,首先要知道当前所用定时器的时钟频率,查看GD32F4xx用户手册中的4-2.时钟树

image-20230629115836505

送药小车项目中,用的是外部时钟,APB1和APB2配置如下所示:

//截取至system_gd32f4xx.c

system_clock_240m_25m_hxtal()
    
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB/2 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;
/* APB1 = AHB/4 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;

image-20230629121121537

可知AHB为240Mhz,定时器的时钟有两个路线,一路定时器的时钟从APB1过来,又因为上面这个TIMERSEL时钟源选择这个位,APB1是四分频,定时器的时钟要是APB时钟的两倍。所以APB1下时钟频率为120MHz,也就是TIMER1,2,3,4,5,6,11,12,13是120MHz。另一路定时器时钟从APB2过来,这些定时器也要乘以2,所以TIMER0,7,8,9,10时钟频率为240MHz。

3.2.2 电机驱动的PWM配置

代码中这里用的是定时器的输出PWM功能,选用EAPWM(边沿对齐PWM),EAPWM 的周期(频率)由 TIMERx_CAR 寄存器值决定,占空比由 TIMERx_CHxCV 寄存器值决定。

image-20230629112402349

上图中的Cx OUT就是对应的引脚输出的PWM波了,当CNT(计数器值)小于CHxVAL时引脚输出高有效电平,当CNT(计数器值)大于CHxVAL引脚就输出低有效电平。

该项目所用到的电机驱动芯片AT8870||AT8236最高可以接收100KHz的频率(频率越高驱动电机也就越顺滑)。所以配置PWM频率时不能超过100KHz。

首先我们需要知道当前所用定时器的时钟频率,在这个项目中,要驱动两个电机,每个电机驱动需要两路PWM值。电机驱动1用的是定时器8,结合上面的基础知识,他的时钟频率是240MHz。电机驱动2用的是定时器11。他的时钟频率是120MHz。分别都是用的相应定时器的通道1和通道2,详情看代码沃。

在下面的代码中,预分频(prescaler)和周期(period)都是从0开始计数的,所以一分频就是0,四分频是3,写成下面的形式只是为了表示更直观。定时器周期(period)是定时器计数的最大值,当计数器的值到达这个值时会重新开始计数。一般的电机驱动PWM频率20KHz就够用,这里我就都配置成60KHz了。

//定时器8时钟频率配置关键代码
timer_param_type.period = 1000-1;   //60kHz
timer_param_type.prescaler = 4-1;   //240Mhz/4=60MHz

//定时器11时钟频率配置关键代码
timer_param_type.period = 1000-1;   //60kHz
timer_param_type.prescaler = 2-1;   //120Mhz/2=60MHz

在下面的代码中,配置了定时器的通道0,关键的配置设置了ocpolarity = TIMER_OC_POLARITY_HIGH;timer_channel_output_pulse_value_config(M1_MOTOR_TIMER, TIMER_CH_0, 1000);相当于把占空比设置为了100%。相当于把这个引脚拉高了。当电机驱动的两个引脚都为高电平时,会进入刹车模式,这时候转动车轮能感觉到明显的阻力。

timer_channel_output_struct_para_init(&timer_oc_init_struct);
timer_oc_init_struct.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_oc_init_struct.outputstate = TIMER_CCX_ENABLE;
timer_oc_init_struct.outputnstate = TIMER_CCXN_DISABLE;
timer_oc_init_struct.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
timer_oc_init_struct.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
timer_oc_init_struct.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

timer_channel_output_config(M1_MOTOR_TIMER, TIMER_CH_0, &timer_oc_init_struct);
timer_channel_output_pulse_value_config(M1_MOTOR_TIMER, TIMER_CH_0, 1000);
timer_channel_output_mode_config(M1_MOTOR_TIMER,TIMER_CH_0,TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(M1_MOTOR_TIMER,TIMER_CH_0,TIMER_OC_SHADOW_DISABLE);

其他通道的配置也和这个一样。

详情请看bsp->bsp_motor.c文件。

代码简要介绍:

  1. motor_pwm_gpio_init():用于初始化电机驱动的PWM控制引脚。通过配置引脚的模式和复用功能,将引脚设置为PWM输出模式。
  2. motor_timer_init():用于初始化电机驱动的硬件定时器。该函数通过配置定时器的参数,包括时钟分频、计数方向、周期和占空比等,设置定时器为PWM模式。
  3. motor1_in1_pwm_pulse_set()motor1_in2_pwm_pulse_set()motor2_in1_pwm_pulse_set()motor2_in2_pwm_pulse_set():分别用于设置电机1和电机2的PWM脉宽值。这些函数通过配置定时器的通道和脉冲值,控制对应引脚的PWM输出。
  4. motor1_pwm_value_set()motor2_pwm_value_set():用于调节电机1和电机2的PWM值,即控制电机的速度。通过调整对应引脚的PWM脉宽值来实现速度调节。

3.3 实际测试

3.3.1 打开工程

打开1_turn_the_motor下的工程,编译并烧录到立创梁山派。

3.3.2 观察现象

如果供电正常,此时两个电机就会开始转动。

3.3.3 查看波形

结合原理图,可以知道我们电机1的PWM驱动在PA2和PA3上,我们把示波器的两个通道连接到这两个引脚上,接地也要接好。

CH1连接到了PA2,CH2连接到了PA3

motor_pwm_wave

通过测量可以知道他的频率约为60.002Khz,周期约为16.666us,满足我们之前计算出来的频率,这两个引脚都在高电平的时候驱动芯片处于刹车状态,当PA2变为低电平的时候驱动芯片就会给电机供电,不过这个频率足够高而且电机旋转也有惯性,所以实际运行中我们是感觉不到电机旋转时的顿挫感的。

再测量一下他的低电平占空比,测量可以知道占空比约为20.07%。结合程序中,我们设置的PWMCHxCV 寄存器为200,周期为1000。所以程序设置的占空比为20%,符合实际预期。

4 让舵机动起来

舵机(Servo Motor)是一种特殊的电机,它可以精确地控制旋转角度。舵机广泛应用于模型船、航空模型、遥控车和其他需要精确角度控制的领域。

舵机的工作原理主要基于脉冲宽度调制(PWM)信号。PWM信号由高电平和低电平组成,其中高电平的时长称为脉冲宽度。通过调节脉冲宽度,可以控制舵机的旋转角度。也有通过串口通讯来控制的舵机,会比较贵。

舵机通常由电机、减速器、电子电路和输出轴组成。舵机的基础知识:

  1. 工作原理:舵机内部有一个电机驱动减速器转动,减速器的输出轴与电位器相连,电位器会随着输出轴的转动而发生变化,变化的电位器信号被反馈给驱动电路,电路会根据电位器的信号调整电机的转速,使输出轴停在设定的位置上。
  2. 控制信号:舵机的控制信号通常是一个 PWM 脉冲信号,信号的周期为 20ms(即频率为50Hz),脉宽为 1ms 到 2ms 之间,其中 1ms 表示输出角度为最小值,2ms 表示输出角度为最大值,1.5ms 表示输出角度为中间值。
  3. 输出角度:舵机的输出角度通常为 0 到 180 度之间,不同型号的舵机有不同的输出角度范围。
  4. 扭矩:舵机的扭矩是指舵机输出轴能够承受的最大力矩,扭矩的大小具体要看所选舵机的参数。
  5. 电源电压:小功率舵机的电源电压一般为 4.8V 到 6V 之间,不同的舵机有不同的电源电压范围,这次用的ES08A II舵机就是5V供电的。

因为K210的摄像头要识别地图上的红线,也要识别数字,所以需要微调摄像头的角度来切换这两种模式。

4.1 硬件

image-20230808142256327

这里就是用了一个双排侧插的排针把 地,+5V,PWM引脚给引出来了。舵机的内部已经有控制电路了,所以我们要用的话就给电源和PWM信号就可以了。需要注意的是我这里用的舵机是5V舵机,因为带一个小摄像头旋转也不需要多大的力矩,而市面上扭矩大的舵机一般都是7v以上供电的,在选型的时候就要注意这个的供电。

4.2 软件

定时器7的时钟频率是240MHz(怎么得出来的查看上面的 获取GD32定时器的时钟频率部分),在下面的代码中,舵机需要的周期是20ms也就是50Hz

//定时器7时钟频率配置关键代码
timer_param_type.period = 1200-1;   //50Hz
timer_param_type.prescaler = 4000-1;   //240Mhz/4000=60000Hz

PWM的通道配置和上面电机驱动的配置一样。

设置具体的脉宽就是调用timer_channel_output_pulse_value_config(SERVO_TIMER, TIMER_CH_0, SERVO_FIND_LINE_ANGLE);来改变舵机转动的角度。

详情请看bsp->bsp_servo.c文件。

  1. 定义了两个静态函数:servo_pwm_gpio_init()servo_timer_init()。这些函数用于初始化舵机的GPIO和定时器。
  2. servo_pwm_gpio_init()函数中,首先使能了舵机的GPIO时钟,然后配置了GPIO的模式和复用功能。最后,设置了输出选项,包括输出类型、输出速度等。
  3. servo_timer_init()函数中,首先使能了舵机所使用的定时器的时钟。然后进行了定时器的初始化设置,包括计数方向、计数模式、计数周期、预分频等参数。接着,配置了定时器的通道输出模式和脉冲值,并设置了定时器的输出极性和空闲状态。
  4. servo1_pwm_pulse_set()servo2_pwm_pulse_set()函数分别用于设置舵机1和舵机2的脉冲宽度。这些函数通过调用timer_channel_output_pulse_value_config()函数来设置定时器的通道输出脉冲值,从而控制舵机的角度。
  5. 最后,定义了一个servo_init()函数,用于初始化舵机控制。该函数调用了servo_pwm_gpio_init()servo_timer_init()函数来进行初始化操作。

4.3 实际测试

4.3.1 打开工程

打开1_turn_the_motor下的工程,编译并烧录到立创梁山派。

4.3.2 观察现象

在上电前把舵机用手转到偏一点的位置,上电后舵机会自动转到中位上(这个具体和你安装的角度有关)

4.3.3 查看波形

结合原理图,可以知道我们舵机的PWM驱动引脚在PC6上,我们把示波器的通道1连接到这个引脚上,接地接好。

servo_pwm_wave

通过测量可以知道他的周期约为20.001ms,频率约为49.998Hz,满足对舵机控制的要求。测上面的高电平脉宽约为1.423ms,在程序中,我设置的巡红线环角度值寄存器为85,这个定时器的周期寄存器值为1200,在这里1200就代表了50Hz ,也就是20ms,理论值为(85/1200)*20ms=1.41ms,和实际预期符合。

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/lcsc/public_bare_medical_car.git
git@gitee.com:lcsc/public_bare_medical_car.git
lcsc
public_bare_medical_car
裸机版基于立创梁山派的21年电赛F题智能送药小车
master

搜索帮助