> 技术文档 > PWM信号控制电机

PWM信号控制电机

1:环境
STM32F103C8T6
KEIL5.38
2个电机
2个轮子
1个L298N
STLINKV2
CH340
1个4位独立按键
杜邦线若干

2:代码

key.h

#ifndef __KEY_H#define __KEY_H#include \"stm32f10x.h\"extern volatile uint8_t key_t ;extern volatile uint8_t key_t0;// 函数声明void Key_Init(void);uint8_t Key_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);void KEY_Configuration(void);#endif

key.c

#include \"key.h\"#include \"delay.h\"#include \"usart.h\"/** * 按键初始化 * 配置 PA0、PB1、PA2、PA3 为浮空输入模式 */void Key_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能 GPIOB 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置 PA0、PA1、PA2、PA3 为浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//// 上拉输入(未按为高,按下为低) GPIO_Init(GPIOB, &GPIO_InitStructure);}// 按键 GPIO 和中断配置void KEY_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能 GPIOA 和 AFIO 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 配置 PA1 和 PA2 为浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入(未按为高,按下为低) GPIO_Init(GPIOA, &GPIO_InitStructure); // 连接 EXTI 线路 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 配置 EXTI EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Rising_Falling ; //EXTI_Trigger_Rising 上升沿触发,根据实际按键修改 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // 配置 NVIC // 配置 EXTI1 中断 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 配置 EXTI2 中断 NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x05; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}/** * 检测按键状态(带消抖) * @param GPIOx: GPIO 端口 (GPIOA, GPIOB 等) * @param GPIO_Pin: GPIO 引脚 (GPIO_Pin_0, GPIO_Pin_1 等) * @return: 1-按键按下,0-按键未按下 */uint8_t Key_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 0) { // 检测到按键按下 delay_ms(20); // 消抖延时 if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 0) { // 确认按下 while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == 0); // 等待释放 return 1; // 返回按键按下状态 } } return 0; // 按键未按下}static uint32_t key1_count =0;static uint32_t key2_count =0;static uint8_t key1_state = 0; //0 未按下,1 按下static uint8_t key2_state = 0;volatile uint8_t key_t =0; volatile uint8_t key_t0 =0; void EXTI1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line1) != RESET) { //SET:表示对应中断线有未处理的中断请求 RESET:表示无中断请求 // 按键处理代码uint8_t pinState1 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); delay_ms(20); uint8_t pinState2 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); if (pinState1 == pinState2) { if (pinState2 == 0) { // 确认是按下事件 下降沿 //Key6_PressedCallback(); key1_state= 1; //if(key1_state ==0) } else { // 确认是释放事件 //Key6_ReleasedCallback();if(key1_state == 1){key1_state =0;key_t++;USART1_SendString(\"key1 press\\r\\n\");//,[%d]++key1_count);} } } // ...// printf(\"key1 pinState1[%d]pinState2[%d]\\r\\n\",pinState1,pinState2); // 清除中断标志位 EXTI_ClearITPendingBit(EXTI_Line1); }}// EXTI1 中断服务函数void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) != RESET) { // 按键处理代码 uint8_t pinState1 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); delay_ms(20); uint8_t pinState2 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); if (pinState1 == pinState2) { if (pinState2 == 0) { // 确认是按下事件 下降沿 //Key6_PressedCallback(); key2_state= 1; //if(key2_state ==0) } else { // 确认是释放事件 //Key6_ReleasedCallback();if(key2_state == 1){key2_state=0;key_t0++;USART1_SendString(\"key2 press\");//\"[%d]\\r\\n\",++key2_count);} } }// printf(\"key2 pinState1[%d]pinState2[%d]\\r\\n\",pinState1,pinState2); // 清除中断标志位 EXTI_ClearITPendingBit(EXTI_Line0); }}delay.h```c#ifndef DELAY_H#define DELAY_H#include \"stm32f10x.h\"void SysTick_Init(void);// 精确延时函数(毫秒)void delay_ms(uint32_t nms);void delay_s(uint32_t ns);//精确延时函数(毫秒)没上限void delay_ms_safe(uint32_t nms);#endif

delay.c

#include \"delay.h\"int f_us=9;//1us 的次数int f_ms=9000; //1ms的次数// SysTick初始化 - 配置为HCLK/8 (72MHz/8 = 9MHz) //即 9,000,000 次 / 秒 或 9,000 次 / 毫秒void SysTick_Init(void) { // 设置SysTick时钟源为HCLK/8 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);}//STM32 的 SysTick 定时器是一个24 位递减计数器,其最大值为 2^24 - 1 = 16,777,215。因此,SysTick->LOAD 的值不能超过这个范围。 16,777,215/9000 = 1864.135//nms <= 1864// 精确延时函数(毫秒)void delay_ms(uint32_t nms) { uint32_t temp; // 设置重载值 SysTick->LOAD = nms * f_ms - 1; // 清空当前值 SysTick->VAL = 0x00; // 使能SysTick定时器 SysTick->CTRL |= (0x01 << 0); do { temp = SysTick->CTRL; } while ((temp & (0x01 << 0)) && (!(temp & (0x01 << 16)))); // 关闭SysTick定时器 SysTick->CTRL &= ~(0x01 << 0);}void delay_s(uint32_t ns){uint32_t n =0;for(n=0;n<ns;n++){delay_ms(1000);}}////精确延时函数(毫秒)没上限void delay_ms_safe(uint32_t nms) { while (nms > 1864) { delay_ms(1864); nms -= 1864; } delay_ms(nms);}
motor.h```c#ifndef __MOTOR_H#define __MOTOR_H#include \"stm32f10x.h\"// 函数声明void Motor_Init(void);//void LeftMotor_SetSpeed(int16_t speed);//void RightMotor_SetSpeed(int16_t speed);void Car_Forward(uint16_t speed,uint16_t delayms);void Car_Backward(uint16_t speed,uint16_t delayms);void Car_TurnLeft(uint16_t speed,uint16_t delayms);void Car_TurnRight(uint16_t speed,uint16_t delayms);void Car_Stop(uint16_t delayms);//急速/旋转 void Car_SpinLeft(uint16_t speed,uint16_t delayms);void Car_SpinRight(uint16_t speed,uint16_t delayms);#endif

motor.c

#include \"motor.h\"#include \"delay.h\"const uint16_t CAR_MAX_SPEED = 100; //最大速度/** * 电机控制初始化 * 配置TIM4_CH1(PB6)到TIM4_CH3(PB9)为PWM输出 */void Motor_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置PWM输出引脚 (PB6和PB8)// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_8; // PB6(TIM4_CH1), PB8(TIM4_CH3)// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;// GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置方向控制引脚 //TIM4 ch1 pb6 ch2 pb7 ch3 pb8 ch4 pb9 查看手册的GPIO 引脚定义 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; // PB6-PB9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 //GPIO_Mode_Out_PP; // 通用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//1. 合适的频率范围//普通直流电机:推荐频率为 10kHz ~ 20kHz。//低于 10kHz 时,电机可能会产生可听噪声(蜂鸣声),且转矩波动较大。//高于 20kHz 时,人耳听不到噪声,但电机驱动电路(如 H 桥)的开关损耗会增加,可能导致发热。//步进电机:通常需要更高的频率(20kHz ~ 50kHz),以保证平滑运行。//伺服电机:一般使用固定频率(如 50Hz),但通过改变脉冲宽度(占空比)控制角度。//2. 频率对电机的影响//频率过低(如低于 5kHz)://电机可能抖动或发出明显噪音,因为电流变化跟不上 PWM 切换速度,导致转矩不连续。//频率过高(如高于 30kHz)://驱动电路的 MOSFET 或三极管开关损耗增加,效率降低,甚至可能因过热损坏。//3. 常见智能小车的选择//玩具级小车:5kHz ~ 10kHz(成本低,但可能有噪音)。//普通 DIY 小车:15kHz ~ 20kHz(兼顾静音和效率)。//高性能小车:20kHz ~ 30kHz(追求极致平滑,但需优化散热)。// 配置TIM4时基 //初始化TIM4,PWM频率=72MHz/36/100=20k //一秒有 2万 个方块波//PWM频率=:系统时钟/(TIM_Prescaler+1)/(TIM_Period+1)//PWM 频率计算//系统时钟:STM32F103 默认系统时钟为 72MHz(APB1 定时器时钟 = 72MHz)。//预分频系数:TIM_Prescaler = 36 - 1 → 实际分频为 36。//自动重装值:TIM_Period = 100 - 1 → 实际周期为 100。//void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);//void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4)//TIM_SetCompare1(TIM4, speed); 0<=speed <=TIM_Period //speed 的取值范围 TIM_TimeBaseStructure.TIM_Period = CAR_MAX_SPEED-1;//100 - 1;//arr;//自动重装值TIM_TimeBaseStructure.TIM_Prescaler =36 - 1;//psc; //时钟预分频数TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //初始化TIM4//初始化TIM4_CH1的PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 使能输出 TIM_OCInitStructure.TIM_Pulse = 0; //初始占空比为0 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高 TIM_OC1Init(TIM4, &TIM_OCInitStructure);//TIM4_CH1TIM_OC2Init(TIM4, &TIM_OCInitStructure);//TIM4_CH2 TIM_OC3Init(TIM4, &TIM_OCInitStructure);//TIM4_CH3TIM_OC4Init(TIM4, &TIM_OCInitStructure);//TIM4_CH4// //初始化TIM4_CH2的PWM模式// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 使能输出// TIM_OCInitStructure.TIM_Pulse = 0;//初始占空比为0// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高// //TIM4_CH2初始化,OC2// TIM_OC2Init(TIM4, &TIM_OCInitStructure);// //初始化TIM4_CH3的PWM模式// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// TIM_OCInitStructure.TIM_Pulse = 0;// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// TIM_OC3Init(TIM4, &TIM_OCInitStructure);// //初始化TIM4_CH4的PWM模式// TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;// TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// TIM_OCInitStructure.TIM_Pulse = 0;// TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;// TIM_OC4Init(TIM4, &TIM_OCInitStructure); //使能4个通道的预装载寄存器 TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC1 TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC2 TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC3 TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC4 TIM_ARRPreloadConfig(TIM4, ENABLE); //使能重装寄存器 TIM_Cmd(TIM4, ENABLE);//使能定时器TIM4,准备工作 }void Motor_Init_2(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 配置方向控制引脚 //TIM4 ch1 pb6 ch2 pb7 ch3 pb8 ch4 pb9 查看手册的GPIO 引脚定义 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; // PB6-PB9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 通用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = CAR_MAX_SPEED-1;//100 - 1;//arr;//自动重装值TIM_TimeBaseStructure.TIM_Prescaler =36 - 1;//psc; //时钟预分频数TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//TIM向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //初始化TIM4//初始化TIM4_CH1到CH4的PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 使能输出 TIM_OCInitStructure.TIM_Pulse = 0; //初始占空比为0 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高 TIM_OC1Init(TIM4, &TIM_OCInitStructure);//TIM4_CH1TIM_OC2Init(TIM4, &TIM_OCInitStructure);//TIM4_CH2 TIM_OC3Init(TIM4, &TIM_OCInitStructure);//TIM4_CH3TIM_OC4Init(TIM4, &TIM_OCInitStructure);//TIM4_CH4//使能4个通道的预装载寄存器 TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC1 TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC2 TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC3 TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC4 TIM_ARRPreloadConfig(TIM4, ENABLE); //使能重装寄存器 TIM_Cmd(TIM4, ENABLE);//使能定时器TIM4,准备工作 }////* 控制左电机//// * @param speed: 速度值 (-1000~1000),负号表示反转//void LeftMotor_SetSpeed(int16_t speed) {// // 限制速度范围// if (speed > 1000) speed = 1000;// if (speed < -1000) speed = -1000;// // 控制方向// if (speed >= 0) {// GPIO_SetBits(GPIOA, GPIO_Pin_1); // IN1 = HIGH// GPIO_ResetBits(GPIOA, GPIO_Pin_2); // IN2 = LOW// } else {// GPIO_ResetBits(GPIOA, GPIO_Pin_1); // IN1 = LOW// GPIO_SetBits(GPIOA, GPIO_Pin_2); // IN2 = HIGH// speed = -speed;  // 转为正数用于PWM// }// // 设置PWM占空比// TIM_SetCompare1(TIM4, speed);//}//// * 控制右电机//// * @param speed: 速度值 (-1000~1000),负号表示反转// //void RightMotor_SetSpeed(int16_t speed) {// // 限制速度范围// if (speed > 1000) speed = 1000;// if (speed < -1000) speed = -1000;// // 控制方向// if (speed >= 0) {// GPIO_SetBits(GPIOA, GPIO_Pin_3); // IN3 = HIGH// GPIO_ResetBits(GPIOA, GPIO_Pin_4); // IN4 = LOW// } else {// GPIO_ResetBits(GPIOA, GPIO_Pin_3); // IN3 = LOW// GPIO_SetBits(GPIOA, GPIO_Pin_4); // IN4 = HIGH// speed = -speed;  // 转为正数用于PWM// }// // 设置PWM占空比 (使用TIM4_CH3)// TIM_SetCompare3(TIM4, speed);//}// 前进void Car_Forward(uint16_t speed,uint16_t delayms) {if(speed > CAR_MAX_SPEED){speed = CAR_MAX_SPEED;}TIM_SetCompare1(TIM4, speed);TIM_SetCompare2(TIM4, 0);TIM_SetCompare3(TIM4, speed);TIM_SetCompare4(TIM4, 0);delay_ms(delayms); //延迟 表示 当前操作 持续多久 ms}// 后退void Car_Backward(uint16_t speed,uint16_t delayms) { if(speed > CAR_MAX_SPEED){speed = CAR_MAX_SPEED;}TIM_SetCompare1(TIM4, 0);TIM_SetCompare2(TIM4, speed);TIM_SetCompare3(TIM4, 0);TIM_SetCompare4(TIM4, speed);delay_ms(delayms);}// 左转 //左轮不动,右轮动void Car_TurnLeft(uint16_t speed,uint16_t delayms) { if(speed > CAR_MAX_SPEED){speed = CAR_MAX_SPEED;}TIM_SetCompare1(TIM4, 0);TIM_SetCompare2(TIM4, 0);TIM_SetCompare3(TIM4, speed);TIM_SetCompare4(TIM4, 0);delay_ms(delayms);}// 右转 //左轮动,右轮不动void Car_TurnRight(uint16_t speed,uint16_t delayms) { if(speed > CAR_MAX_SPEED){speed = CAR_MAX_SPEED;}TIM_SetCompare1(TIM4, speed);TIM_SetCompare2(TIM4, 0);TIM_SetCompare3(TIM4, 0);TIM_SetCompare4(TIM4, 0);delay_ms(delayms);}// 停止void Car_Stop(uint16_t delayms) { TIM_SetCompare1(TIM4, 0);TIM_SetCompare2(TIM4, 0);TIM_SetCompare3(TIM4, 0);TIM_SetCompare4(TIM4, 0);delay_ms(delayms);}//急速/旋转 左转void Car_SpinLeft(uint16_t speed,uint16_t delayms){if(speed > CAR_MAX_SPEED){speed = CAR_MAX_SPEED;}TIM_SetCompare1(TIM4, 0);TIM_SetCompare2(TIM4, speed);TIM_SetCompare3(TIM4, speed);TIM_SetCompare4(TIM4, 0);delay_ms(delayms);}//急速/旋转 右转void Car_SpinRight(uint16_t speed,uint16_t delayms){ if(speed > CAR_MAX_SPEED){speed = CAR_MAX_SPEED;}TIM_SetCompare1(TIM4, speed);TIM_SetCompare2(TIM4, 0);TIM_SetCompare3(TIM4, 0);TIM_SetCompare4(TIM4, speed);delay_ms(delayms);}

main.c

#include \"stm32f10x.h\"#include \"usart.h\"#include \"delay.h\"#include \"key.h\"#include \"motor.h\"#define LED_PIN GPIO_Pin_13#define LED_PORT  GPIOC// 配置LEDvoid LED_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin = LED_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(LED_PORT, &GPIO_InitStructure); GPIO_SetBits(LED_PORT, LED_PIN); // 初始熄灭LED}#ifdef SETUP_KEY_PRESS// 按键 GPIO 和中断配置void KEY_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能 GPIOA 和 AFIO 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); // 配置 PA1 和 PA2 为浮空输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入(未按为高,按下为低) GPIO_Init(GPIOA, &GPIO_InitStructure); // 连接 EXTI 线路 GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2); // 配置 EXTI EXTI_InitStructure.EXTI_Line = EXTI_Line1 | EXTI_Line2; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Rising_Falling ; //EXTI_Trigger_Rising 上升沿触发,根据实际按键修改 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); // 配置 NVIC // 配置 EXTI1 中断 NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 配置 EXTI2 中断 NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}static uint32_t key1_count =0;static uint32_t key2_count =0;static uint8_t key1_state = 0; //0 未按下,1 按下static uint8_t key2_state = 0;//2次中断//按下前 按下瞬间 保持按下 松开瞬间 松开后// 高电平 下降沿 低电平  上升沿 高电平// (1) ↓  (0)  ↑  (1)// EXTI0 中断服务函数//双边沿触发 + 状态机://捕获完整按下 - 释放周期//通过状态机过滤抖动和异常触发//消抖策略://软件延时:20ms 通常足够//硬件滤波:并联 0.1μF 电容到地void EXTI1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line1) != RESET) { //SET:表示对应中断线有未处理的中断请求 RESET:表示无中断请求 // 按键处理代码uint8_t pinState1 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); delay_ms(20); uint8_t pinState2 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); if (pinState1 == pinState2) { if (pinState2 == 0) { // 确认是按下事件 下降沿 //Key6_PressedCallback(); key1_state= 1; //if(key1_state ==0) } else { // 确认是释放事件 //Key6_ReleasedCallback();if(key1_state == 1){key1_state =0;printf(\"key1 press[%d]\\r\\n\",++key1_count);} } } // ...// printf(\"key1 pinState1[%d]pinState2[%d]\\r\\n\",pinState1,pinState2); // 清除中断标志位 EXTI_ClearITPendingBit(EXTI_Line1); }}// EXTI1 中断服务函数void EXTI2_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line2) != RESET) { // 按键处理代码 uint8_t pinState1 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2); delay_ms(20); uint8_t pinState2 = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2); if (pinState1 == pinState2) { if (pinState2 == 0) { // 确认是按下事件 下降沿 //Key6_PressedCallback(); key2_state= 1; //if(key2_state ==0) } else { // 确认是释放事件 //Key6_ReleasedCallback();if(key2_state == 1){key2_state=0;printf(\"key2 press[%d]\\r\\n\",++key2_count);} } }// printf(\"key2 pinState1[%d]pinState2[%d]\\r\\n\",pinState1,pinState2); // 清除中断标志位 EXTI_ClearITPendingBit(EXTI_Line2); }}#endif#include \"motor.h\"#include \"key.h\"int main(void) { // 初始化系统时钟(需根据实际电路配置,此处省略,可参考标准库例程) SystemInit();SysTick_Init(); LED_Configuration(); // 初始化USART1 USART1_Init();// 初始化电机控制 // Motor_Init(); // 初始化按键 // Key_Init();KEY_Configuration(); Motor_Init(); // 初始状态:停止// Car_Stop(1000);#ifdef SETUP_KEY_PRESSKEY_Configuration();#endif GPIO_ResetBits(LED_PORT, LED_PIN); // 点亮LED指示错误delay_s(1);GPIO_SetBits(LED_PORT, LED_PIN); // 打印日志 // printf(\"USART Test: Hello, F103C8T6!\\r\\n\");//char szbuf[BUFFER_SIZE+24]={0,}; USART1_SendString(\"USART Test:Hello,F103C8T6[car8]!\"); int i = 0;uint8_t last_key_1 =0; while (1) { // 循环打印示例#ifdef __RC_MSG_2sprintf(szbuf,\"current num=%d \\n\",i++);printf((const char*)szbuf); // printf(\"Log: Running...\\r\\n\");GPIO_ResetBits(LED_PORT, LED_PIN); // 点亮LED指示错误delay_ms(800);GPIO_SetBits(LED_PORT, LED_PIN);delay_ms(200);#else // 检查是否收到消息// if (messageReceived) {// messageReceived = 0; // 清除标志////sprintf(szbuf,\"%s-[%d]\\r\\n\",rxBuffer,i++);// printf((const char*)szbuf);////USART1_SendString(szbuf);// // GPIO_ResetBits(LED_PORT, LED_PIN); // 点亮LED指示错误// delay_ms(500);// GPIO_SetBits(LED_PORT, LED_PIN);//// }//if(Key_Scan(GPIOA,GPIO_Pin_0) ==1){// USART1_SendString(\"car forward \\n\");//Car_Forward(80,100); // 前进,速度80%// delay_ms(500); // 防止连续触发//}else if(Key_Scan(GPIOA,GPIO_Pin_1) ==1){//USART1_SendString(\"car backward \\n\");// Car_Backward(80,100); // 后退,速度80%// delay_ms(500); // 防止连续触发//}else{//delay_ms(100);//}if(key_t > last_key_1){last_key_1 = key_t;USART1_SendString(\"car forward \\n\");Car_Forward(90,100); // 前进,速度80% //线接反了,实际位退delay_ms(200);Car_Stop(1000);}if(key_t0 > 0){key_t0 =0;USART1_SendString(\"car backward \\n\");Car_Backward(90,100); // 后退,速度80% //线接反了,实际位前进delay_ms(200);Car_Stop(1000);}delay_ms(50); #endif }}

3:说明
1>GPIO 引脚规划
这里PWM 使用的TIM4 PB6-PB9
4位按钮使用 PA0-PA3
USART串口使用的 USART1,PA9 PA10
LED 使用是 PC13 自带的哪个

PWM 主要是通过定时器 分片控制电机 假如IN1 IN2 控制左边电机(如果实际的情况刚好反了,可以把线调换下-就是输出口的2根线)
设置指定通道的占空比后,需要 delay_ms 维持一段时间,一般50-100ms 足够了

IN1 NI2 说明
H L 前进
L H 后退
L L 停止

EG: 假如接线没接反 假如 TIM_TimeBaseStructure.TIM_Period = CAR_MAX_SPEED-1;//100 - 1;//arr;//自动重装值 CAR_MAX_SPEED 假定位100 speed 假定位80
TIM_SetCompare1(TIM4, 0); //IN1 输出低电平
TIM_SetCompare2(TIM4, speed);//IN2 80%输出高电平
TIM_SetCompare3(TIM4, 0); //IN3 输出低电平
TIM_SetCompare4(TIM4, speed); //IN4 80%输出高电平
delay_ms(delayms); //上面的状态持续多久

结果为 后退 持续 delayms 毫秒,代码里也有说明,可以参考,这里拿来再说一遍

下面图网上抄的
PWM信号控制电机
PWM信号控制电机

2>供电
L298N 使用 CH340 5V 供电,有点不足够,在.4.95V-5.10V 有点波动,驱动电机有能力有点差
有能力的还是上2节锂电池吧,当前只是 测试,无所谓了
PWM信号控制电机

在这里插入图片描述

4:测试结果 如果对你又帮助,麻烦点个赞,加个关注
结果