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);// 配置 EXTIEXTI_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/8SysTick_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-PB9GPIO_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模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 使能輸出TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比為0TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//輸出極性高TIM_OC1Init(TIM4, &TIM_OCInitStructure);//TIM4_CH1TIM_OC2Init(TIM4, &TIM_OCInitStructure);//TIM4_CH2TIM_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);//OC1TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC2TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC3TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC4TIM_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-PB9GPIO_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模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;// 使能輸出TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比為0TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//輸出極性高TIM_OC1Init(TIM4, &TIM_OCInitStructure);//TIM4_CH1TIM_OC2Init(TIM4, &TIM_OCInitStructure);//TIM4_CH2TIM_OC3Init(TIM4, &TIM_OCInitStructure);//TIM4_CH3TIM_OC4Init(TIM4, &TIM_OCInitStructure);//TIM4_CH4//使能4個通道的預裝載寄存器TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC1TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC2TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC3TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);//OC4TIM_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// 配置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);// 配置 EXTIEXTI_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();// 初始化USART1USART1_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 毫秒,代碼里也有說明,可以參考,這里拿來再說一遍
下面圖網上抄的
2>供電
L298N 使用 CH340 5V 供電,有點不足夠,在.4.95V-5.10V 有點波動,驅動電機有能力有點差
有能力的還是上2節鋰電池吧,當前只是 測試,無所謂了
4:測試結果 如果對你又幫助,麻煩點個贊,加個關注
結果