本帖最后由 立创开发板 于 2023-7-24 11:30 编辑
送药小车代码仓库:https://gitee.com/lcsc/medical_car更好的观看体验请去:https://dri8c0qdfb.feishu.cn/wiki/UjwwwO0KZii5bykPcE4cJZafnAg
送药小车立创开源平台资料:https://oshwhub.com/li-chuang-kai-fa-ban/21-dian-sai-f-ti-zhi-neng-song-yao-xiao-che
电机驱动,舵机,蜂鸣器的PWM配置啥是PWM?
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的应用场景非常广泛,例如:
- 电机控制:通过调整PWM的占空比,可以精确控制直流电机的转速。占空比越高,电机转速越快;占空比越低,电机转速越慢。
- LED亮度调节:通过调整PWM的占空比,可以实现对LED灯的亮度调节。占空比越高,LED灯越亮;占空比越低,LED灯越暗。
举个例子:假设我们有一个LED灯,想实现亮度调节。我们可以生成一个PWM信号,频率为1kHz(周期为1ms),然后通过调整占空比来实现LED灯的亮度调节。如果占空比为100%,那么LED灯将一直处于亮的状态;如果占空比为50%,那么LED灯将以1ms为周期,半亮半暗;如果占空比为0%,那么LED灯将一直处于熄灭状态。通过不断调整占空比,就可以实现LED灯的亮度调节。如果PWM信号的频率过低,我们可能会感觉到它在闪烁。所以,在设计PWM驱动的LED灯时,一般会选择较高的频率来避免可见的闪烁。通常我们是看不到它闪烁的,这主要是因为两个原因:第一个是PWM信号的频率够高,第二个是人眼的视觉暂留效应。即当光源瞬间消失时,我们还能在极短的时间内感知到光源。这种效应导致人眼对快速闪烁的光源产生平滑的感觉。当PWM频率足够高时,视觉暂留效应会使我们感觉到LED灯的亮度是连续的。
手机屏幕的亮度是怎么调节的?我们平时一直摸的手机LCD屏幕大部分就是用PWM来调节的。在智能手机屏幕中,PWM调光和DC调光是两种常见的屏幕亮度调节技术。它们的主要区别在于亮度调节的实现方式。
PWM调光:如之前所说,PWM(脉宽调制)调光是通过调整占空比来控制屏幕亮度的。在这种方法中,屏幕背光源会周期性地开启和关闭。占空比越高,背光源亮的时间越长,屏幕亮度越高;占空比越低,背光源亮的时间越短,屏幕亮度越低。
DC调光:DC(直流)调光是通过调整屏幕背光源的电流来实现亮度调节的。在这种方法中,背光源会一直保持开启状态,但电流的大小会改变,从而调整屏幕亮度。
这两种调光技术各有优缺点:
PWM调光优缺点:
- 优点:能实现较高的亮度范围和对比度,通常在高亮度下表现更好。
- 缺点:在低频率下,PWM调光可能导致屏幕闪烁,对部分人来说可能引起眼睛疲劳和不适。此外,对于快速拍照或录像时,PWM调光可能导致出现条纹或闪烁的现象。
DC调光优缺点:
- 优点:在低亮度下表现更好,因为屏幕背光源一直保持开启状态,不会出现闪烁现象,对眼睛更友好。
- 缺点:在高亮度下,对比度和亮度范围可能不如PWM调光好。此外,DC调光可能导致背光源的寿命降低和能耗略高。
各种技术的选择取决于手机制造商的考虑和市场需求。有一些高端手机是用的混合调光,在高亮度的模式下用DC调光,亮度低于一定值后用PWM调光。
获取所用GD32定时器的时钟频率要想输出确定PWM频率的波,首先要知道当前所用定时器的时钟频率,查看GD32F4xx用户手册中的图4-2.时钟树:
送药小车项目中,用的是外部时钟,APB1和APB2配置如下所示:
<div data-page-id="OpjudooEAoYasFxOCz9cAb54ngN" data-docx-has-block-data="false"><pre class="ace-line ace-line old-record-id-MtDHdEqj6obQKwxwf7CcggzVnke"><code class="language-C" data-wrap="false">//截取至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;</code></pre></div><span data-lark-record-data="{"rootId":"OpjudooEAoYasFxOCz9cAb54ngN","text":{"initialAttributedTexts":{"text":{"0":"//截取至system_gd32f4xx.c\n\nsystem_clock_240m_25m_hxtal();\n \n/* AHB = SYSCLK */\nRCU_CFG0 |= RCU_AHB_CKSYS_DIV1;\n/* APB2 = AHB/2 */\nRCU_CFG0 |= RCU_APB2_CKAHB_DIV2;\n/* APB1 = AHB/4 */\nRCU_CFG0 |= RCU_APB1_CKAHB_DIV4;"},"attribs":{"0":"*0|9+52*0+w"}},"apool":{"numToAttrib":{"0":["author","7209868880213491740"]},"nextNum":1}},"type":"text","referenceRecordMap":{},"extra":{"mention_page_title":{},"external_mention_url":{}},"isKeepQuoteContainer":false,"isFromCode":true,"selection":[{"id":33,"type":"text","selection":{"start":0,"end":214},"recordId":"MtDHdEqj6obQKwxwf7CcggzVnke"}],"payloadMap":{},"isCut":false}" data-lark-record-format="docx/text" class="lark-record-clipboard"></span>
可知AHB为240Mhz,定时器的时钟有两个路线,一路定时器的时钟从APB1过来,又因为上面这个TIMERSEL时钟源选择这个位,所以APB1下时钟频率为120MHz,也就是TIMER1,2,3,4,5,6,11,12,13是120MHz。另一路定时器时钟从APB2过来,TIMER0,7,8,9,10时钟频率为240MHz。
电机驱动的PWM配置代码中这里用的是定时器的输出PWM功能,选用EAPWM(边沿对齐PWM),EAPWM 的周期(频率)由 TIMERx_CAR 寄存器值决定,占空比由 TIMERx_CHxCV 寄存器值决定。
上图中的Cx OUT就是对应的引脚输出的PWM波了,在PWM MODE0模式下,当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了。
<div data-page-id="OpjudooEAoYasFxOCz9cAb54ngN" data-docx-has-block-data="false"><pre class="ace-line ace-line old-record-id-SSaRd3GvVoP2TIxXhs8c0fTnnhf"><code class="language-C" data-wrap="false">//定时器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</code></pre></div>
在下面的代码中,配置了定时器的通道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);
其他通道的配置也和这个一样。
详情请看2_code->applications->bsp_motor.c文件。
代码简要介绍:
- motor_pwm_gpio_init():用于初始化电机驱动的PWM控制引脚。该函数通过配置引脚的模式和复用功能,将引脚设置为PWM输出模式。
- motor_timer_init():用于初始化电机驱动的硬件定时器。该函数通过配置定时器的参数,包括时钟分频、计数方向、周期和占空比等,设置定时器为PWM模式。
- motor1_in1_pwm_pulse_set()、motor1_in2_pwm_pulse_set()、motor2_in1_pwm_pulse_set()、motor2_in2_pwm_pulse_set():分别用于设置电机1和电机2的PWM脉宽值。这些函数通过配置定时器的通道和脉冲值,控制对应引脚的PWM输出。
- motor1_pwm_value_set()、motor2_pwm_value_set():用于调节电机1和电机2的PWM值,即控制电机的速度。通过调整对应引脚的PWM脉宽值来实现速度调节。
- motor_test():用于通过控制台输入命令来临时改变PWM值驱动电机转动。根据输入的命令和参数,调用相应的函数来控制电机的运动。
舵机的PWM配置什么是舵机?舵机(Servo Motor)是一种特殊的电机,它可以精确地控制旋转角度。舵机广泛应用于模型、航空模型、遥控车和其他需要精确角度控制的领域。
舵机的工作原理主要基于脉冲宽度调制(PWM)信号。PWM信号由高电平和低电平组成,其中高电平的时长称为脉冲宽度。通过调节脉冲宽度,可以控制舵机的旋转角度。也有通过串口通讯来控制的舵机,会比较贵。
舵机通常由电机、减速器、电子电路和输出轴组成。舵机的基础知识:
- 工作原理:舵机内部有一个电机驱动减速器转动,减速器的输出轴与电位器相连,电位器会随着输出轴的转动而发生变化,变化的电位器信号被反馈给驱动电路,电路会根据电位器的信号调整电机的转速,使输出轴停在设定的位置上。
- 控制信号:舵机的控制信号通常是一个 PWM 脉冲信号,信号的周期为 20ms,脉宽为 1ms 到 2ms 之间,其中 1ms 表示输出角度为最小值,2ms 表示输出角度为最大值,1.5ms 表示输出角度为中间值。
- 输出角度:舵机的输出角度通常为 0 到 180 度之间,不同的舵机有不同的输出角度范围。
- 扭矩:舵机的扭矩是指舵机输出轴能够承受的最大力矩,不同的舵机有不同的扭矩大小。
- 电源电压:小功率舵机的电源电压一般为 4.8V 到 6V 之间,不同的舵机有不同的电源电压范围,这次用的ES08A II舵机就是5V供电的。
具体代码配置定时器7的时钟频率是240MHz,在下面的代码中,舵机需要的周期是20ms也就是50Hz
<div data-page-id="OpjudooEAoYasFxOCz9cAb54ngN" data-docx-has-block-data="false"><pre class="ace-line ace-line old-record-id-N8r9dtnutokku1xv5Yrce0Bonab"><code class="language-C" data-wrap="false">//定时器7时钟频率配置关键代码
timer_param_type.period = 1200-1; //50Hz
timer_param_type.prescaler = 4000-1; //240Mhz/4000=60000Hz</code></pre></div><span data-lark-record-data="{"rootId":"OpjudooEAoYasFxOCz9cAb54ngN","text":{"initialAttributedTexts":{"text":{"0":"//定时器7时钟频率配置关键代码\ntimer_param_type.period = 1200-1; //50Hz\ntimer_param_type.prescaler = 4000-1; //240Mhz/4000=60000Hz"},"attribs":{"0":"*0|2+1o*0+1o"}},"apool":{"numToAttrib":{"0":["author","7209868880213491740"]},"nextNum":1}},"type":"text","referenceRecordMap":{},"extra":{"mention_page_title":{},"external_mention_url":{}},"isKeepQuoteContainer":false,"isFromCode":true,"selection":[{"id":66,"type":"text","selection":{"start":0,"end":120},"recordId":"N8r9dtnutokku1xv5Yrce0Bonab"}],"payloadMap":{},"isCut":false}" data-lark-record-format="docx/text" class="lark-record-clipboard"></span>
PWM的通道配置和上面电机驱动的配置一样。
设置具体的脉宽就是调用timer_channel_output_pulse_value_config(SERVO_TIMER, TIMER_CH_0, SERVO_FIND_LINE_ANGLE);来改变舵机转动的角度。
详情请看2_code->applications->bsp_servo.c文件。
- 首先,包含了一些头文件,包括stdio.h、rtthread.h、board.h等,这些头文件提供了所需的库函数和定义。
- 然后定义了两个静态函数:servo_pwm_gpio_init()和servo_timer_init()。这些函数用于初始化舵机的GPIO和定时器。
- servo_pwm_gpio_init()函数中,首先使能了舵机的GPIO时钟,然后配置了GPIO的模式和复用功能。最后,设置了输出选项,包括输出类型、输出速度等。
- servo_timer_init()函数中,首先使能了舵机所使用的定时器的时钟。然后进行了定时器的初始化设置,包括计数方向、计数模式、计数周期、预分频等参数。接着,配置了定时器的通道输出模式和脉冲值,并设置了定时器的输出极性和空闲状态。
- servo1_pwm_pulse_set()和servo2_pwm_pulse_set()函数分别用于设置舵机1和舵机2的脉冲宽度。这些函数通过调用timer_channel_output_pulse_value_config()函数来设置定时器的通道输出脉冲值,从而控制舵机的角度。
- servo_test()函数是一个用于测试舵机控制的命令函数。通过命令行输入参数来选择要控制的舵机和脉冲宽度,然后调用相应的设置函数来控制舵机。
- 最后,定义了一个servo_init()函数,用于初始化舵机控制。该函数调用了servo_pwm_gpio_init()和servo_timer_init()函数来进行初始化操作。
蜂鸣器的PWM配置什么是蜂鸣器?蜂鸣器是一种常见的声音输出设备,可以发出各种声音或音调。它们广泛应用于家用电器、电子设备、汽车、安全系统等领域。以下是蜂鸣器的主要种类及其使用场景。
蜂鸣器主要分为两大类:有源蜂鸣器和无源蜂鸣器。
有源蜂鸣器(Active Buzzer)有源蜂鸣器内部集成了一个振荡电路,当直接接入电源时就可以发出声音。由于内部已经集成了振荡电路,有源蜂鸣器的控制相对简单,只需要提供一个恒定的电源电压即可。但是,这种蜂鸣器的音调和音量调节较为有限。
无源蜂鸣器(Passive Buzzer)与有源蜂鸣器不同,无源蜂鸣器没有内置振荡电路。要使无源蜂鸣器发声,需要提供一个外部的交流信号(如方波或PWM信号)。这种蜂鸣器的优点是可以通过调整外部信号的频率和占空比来实现更丰富的音调和音量控制。
我这里选用的无源蜂鸣器,他的驱动频率是4000Hz。
具体代码配置定时器12的时钟频率是240MHz,在下面的代码中,无源蜂鸣器需要的发声最佳频率是4000Hz
<pre>//定时器12时钟频率配置关键代码
timer_param_type.period = 1000-1; //4000Hz
timer_param_type.prescaler = 30-1; //120Mhz/30=4000000Hz</pre>
PWM的通道配置和上面电机驱动的配置一样。
详情请看2_code->applications->bsp_beep.c文件。
- beep_pwm_gpio_init函数:该函数用于初始化蜂鸣器的GPIO引脚。具体操作包括使能蜂鸣器所在的GPIO时钟、配置GPIO引脚为复用功能、设置GPIO输出选项等。
- beep_timer_init函数:该函数用于初始化蜂鸣器的定时器(timer)。具体操作包括使能蜂鸣器定时器所在的时钟、重置定时器、配置定时器参数(如计数模式、时钟分频、计数方向、周期和预分频值等)、初始化定时器、配置定时器通道输出参数(如极性、输出使能、闲置状态等)、使能定时器等。
- beep_pwm_pulse_set函数:该函数用于设置蜂鸣器的脉冲宽度。具体操作是通过配置定时器通道的脉冲值来控制蜂鸣器的输出。
- beep函数:该函数用于控制蜂鸣器发出声音。具体操作是设置蜂鸣器的脉冲宽度为500,延时一段时间后再将脉冲宽度设置为0,实现蜂鸣器的开关控制。
- beep_test函数:该函数是一个用于测试的函数,用于通过命令行输入参数来设置蜂鸣器的脉冲宽度。具体操作是通过输入参数获取脉冲宽度值,然后调用beep_pwm_pulse_set函数设置蜂鸣器的脉冲宽度。
- beep_init函数:该函数用于初始化蜂鸣器。具体操作是调用beep_pwm_gpio_init函数和beep_timer_init函数来初始化蜂鸣器的GPIO引脚和定时器。
- INIT_BOARD_EXPORT(beep_init):这是一个用于在系统初始化阶段自动执行的宏,将beep_init函数注册为系统初始化函数,以便在系统启动时自动初始化蜂鸣器。
|
|