PWM信号控制电机

1:环境

STM32F103C8T6

KEIL5.38

2个电机

2个轮子

1个L298N

STLINKV2

CH340

1个4位独立按键

杜邦线若干

2:代码

key.h

c 复制代码
#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

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

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

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_CH1
			
			TIM_OC2Init(TIM4, &TIM_OCInitStructure);//TIM4_CH2
			 
			TIM_OC3Init(TIM4, &TIM_OCInitStructure);//TIM4_CH3
				
			TIM_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_CH1
			
			TIM_OC2Init(TIM4, &TIM_OCInitStructure);//TIM4_CH2
			 
			TIM_OC3Init(TIM4, &TIM_OCInitStructure);//TIM4_CH3
				
			TIM_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

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



// 配置LED
void 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_PRESS
		KEY_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_2
			sprintf(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 毫秒,代码里也有说明,可以参考,这里拿来再说一遍

下面图网上抄的

2>供电

L298N 使用 CH340 5V 供电,有点不足够,在.4.95V-5.10V 有点波动,驱动电机有能力有点差

有能力的还是上2节锂电池吧,当前只是 测试,无所谓了

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

相关推荐
清风6666663 小时前
基于单片机的水塔液位检测与智能调节报警系统设计
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
某zhuan4 小时前
STM32中PB4引脚作普通GPIO使用的一个小问题
stm32·单片机·嵌入式硬件
蓝天居士5 小时前
PY32F040单片机介绍(2)
单片机·国产mcu
点灯小铭8 小时前
基于单片机的人体红外传感的步进电机调速自动门智能控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
一月千帆8 小时前
STM32 串口 Bootloader 固件升级方案实现
stm32·单片机·嵌入式硬件
朱嘉鼎8 小时前
GPIO引脚操作方法概述
单片机·嵌入式硬件
小+不通文墨10 小时前
GPIO口输入
stm32·单片机·嵌入式硬件
zzywxc78712 小时前
解锁 Rust 开发新可能:从系统内核到 Web 前端的全栈革命
开发语言·前端·python·单片机·嵌入式硬件·rust·scikit-learn
小莞尔14 小时前
【51单片机】【protues仿真】基于51单片机秒表计时器系统(带存储)
c语言·stm32·单片机·嵌入式硬件·物联网·51单片机
国科安芯15 小时前
ASP3605A电源芯片在高速ADC子卡中的适配性研究
网络·人工智能·单片机·嵌入式硬件·安全