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:测试结果 如果对你又帮助,麻烦点个赞,加个关注
结果

相关推荐
悠哉悠哉愿意1 小时前
【电赛学习笔记】MaixCAM 的OCR图片文字识别
笔记·python·嵌入式硬件·学习·视觉检测·ocr
慕容白 MU4 小时前
新唐51单片机看门狗配置流程
c语言·单片机·嵌入式硬件·51单片机
吴烦恼的博客5 小时前
FSMC的配置和应用
c语言·单片机·fsmc
jingjing~6 小时前
STM32 软件模拟 I2C 读写 MPU6050--实现加速度与陀螺仪数据采集
stm32·单片机·嵌入式硬件
wearegogog1238 小时前
Arduino与STM32:初学者该如何选择?
stm32·单片机·嵌入式硬件
EVERSPIN9 小时前
新升级超值型系列32位单片机MM32G0005
单片机·嵌入式硬件
qq_401700419 小时前
stm32的can总线
stm32·单片机·嵌入式硬件
蜡笔小电芯10 小时前
【CAN总线】STM32 的 CAN 总线通信开发笔记(基于 HAL)
笔记·stm32·单片机
景彡先生11 小时前
STM32系统定时器(SysTick)详解:从原理到实战的精确延时与任务调度
stm32·单片机·嵌入式硬件
F1372980155711 小时前
80V降5V,输出电流4A,应用于车载充电器WD5105
stm32·单片机·嵌入式硬件·51单片机