- <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函数注册为系统初始化函数,以便在系统启动时自动初始化蜂鸣器。