这是智能小车的最后一篇博客,主要讲解一下小车的程序设计。
电机的驱动:
我写了俩种转弯,一种是一个轮朝后一个轮朝前的转弯,另一种是抱死一个轮另一个向前,使用PWM来调速,PWM的占空比不能太小,有效电压低于2.4伏无法驱动298N。
#ifndef __MOTOR_H
#define __MOTOR_H
#include "sys.h"
#include
#define HIGH_SPEED 1
#define LOW_SPEED 0
#define Moter1_CH3_Value TIM2->CCR3
#define Moter1_CH4_Value TIM2->CCR4
#define Moter2_CH3_Value TIM1->CCR1
#define Moter2_CH4_Value TIM1->CCR4
void MOTOR1_PWM_Init(u16 arr,u16 psc);
void MOTOR2_PWM_Init(u16 arr,u16 psc);
void Motor_Forward(u8 speed);
void Motor_Left1(u16 value);
void Motor_Right1(u16 value);
void Motor_Left2(u16 value);
void Motor_Right2(u16 value);
void Motor_Back(void);
void Motor_Stop(void);
#endif
#include "motor.h"
#include "delay.h"
//电机1(左侧)初始化
//PA3(CH4) PA2(CH3)
void MOTOR1_PWM_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<0; //TIM2时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
GPIOA->CRL&=0XFFFF00FF; //PB10、11输出
GPIOA->CRL|=0X0000BB00; //复用功能输出
RCC->APB2ENR|=1<<0; //开启辅助时钟
AFIO->MAPR&=0XFFFFFCFF; //清除MAPR的[9:8]
//AFIO->MAPR|=1<<9; //部分重映像
TIM2->ARR=arr; //设定计数器自动重装值
TIM2->PSC=psc; //预分频器不分频
TIM2->CR1=0x0080; //ARPE使能
TIM2->CCMR2|=7<<12; //CH2 PWM2模式
TIM2->CCMR2|=1<<11; //CH2预装载使能
// TIM2->CCER|=1<<12; //OC4 输出使能
TIM2->CCMR2|=7<<4; //CH3 PWM2模式
TIM2->CCMR2|=1<<3; //CH3预装载使能
//TIM2->CCER|=1<<8; //OC3 输出使能
TIM2->CR1|=0x01; //使能定时器3
}
//电机2(左侧)初始化
//PB7(CH3) PB10(CH4)
void MOTOR2_PWM_Init(u16 arr,u16 psc)
{
RCC->APB2ENR|=1<<11; //TIM1时钟使能
RCC->APB2ENR|=1<<2; //使能PORTB时钟
GPIOA->CRH&=0XFFFF0FF0; //PB0、PB1输出
GPIOA->CRH|=0X0000B00B; //复用功能输出
RCC->APB2ENR|=1<<0; //开启辅助时钟
AFIO->MAPR&=0XFFFFFF3F; //清除MAPR的[7:6]
//AFIO->MAPR|=1<<11; //部分重映像
TIM1->BDTR|=1<<15;
TIM1->ARR=arr; //设定计数器自动重装值
TIM1->PSC=psc; //预分频器不分频
TIM1->CR1=0x0080; //ARPE使能
TIM1->CCMR2|=7<<12; //CH4 PWM2模式
TIM1->CCMR2|=1<<11; //CH4预装载使能
TIM1->CCMR1|=7<<4; //CH3 PWM2模式
TIM1->CCMR1|=1<<3; //CH3预装载使能
TIM1->CR1|=0x01; //使能定时器1
}
/************************************************************
***************************停止*****************************
*****返回值:无
************************************************************/
void Motor_Stop(void)
{
TIM1->CCER|=1<<0; //OC3 输出使能
TIM1->CCER|=1<<12; //OC4 输出使能
TIM2->CCER|=1<<8; //OC3 输出使能
TIM2->CCER|=1<<12; //OC4 输出使能
Moter1_CH3_Value=0;
Moter2_CH3_Value=0;
Moter1_CH4_Value=0;
Moter2_CH4_Value=0;
delay_ms(500);
TIM1->CCER&=~(1<<0); //OC3 输出禁止
TIM1->CCER&=~(1<<12); //OC4 输出禁止
TIM2->CCER&=~(1<<8); //OC3 输出禁止
TIM2->CCER&=~(1<<12); //OC4 输出禁止
}
/************************************************************
**************************向前走*****************************
*****入口参数:speed(0:慢速 1:快速)
*****返回值:无
OUT2+ OUT1- OUT4+ OUT3-
************************************************************/
void Motor_Forward(u8 speed)
{
TIM2->CCER&=~(1<<12); //OC4 输出禁止
TIM1->CCER&=~(1<<12); //OC4 输出禁止
TIM1->CCER|=1<<0; //TIM3 OC1 输出使能
TIM2->CCER|=1<<8; //TIM2 OC1 输出使能
switch (speed)
{
case 0:
{
Moter1_CH3_Value=390;
Moter2_CH3_Value=350;
break;
}
case 1:
{
Moter1_CH3_Value=450;
Moter2_CH3_Value=440;
break;
}
}
}
/************************************************************
***************************左转1*****************************
*****入口参数:无
*****返回值:无
************************************************************/
void Motor_Left1(u16 value)
{
TIM2->CCER|=1<<8; //OC3 输出使能
TIM2->CCER&=~(1<<12); //OC3 输出禁止
TIM1->CCER|=1<<12; //OC4 输出使能
TIM1->CCER&=~(1<<0); //OC3 输出禁止
Moter1_CH3_Value=150;
Moter2_CH4_Value=300;
delay_ms(value);
TIM1->CCER&=~(1<<12); //OC4 输出禁止
TIM2->CCER&=~(1<<8); //OC3 输出使能
}
/************************************************************
***************************右转1*****************************
*****返回值:无
************************************************************/
void Motor_Right1(u16 value)
{
TIM1->CCER|=1<<0; //TIM3 OC1 输出使能
TIM2->CCER&=~(1<<8); //OC3 输出禁止
TIM2->CCER|=1<<12; //OC4 输出使能
TIM1->CCER&=~(1<<12); //OC4 输出禁止
Moter2_CH3_Value=300;
Moter1_CH4_Value=150;
delay_ms(value);
TIM2->CCER&=~(1<<12); //OC4 输出禁止
TIM1->CCER&=~(1<<0); //OC3 输出使能
}
/************************************************************
***************************左转1*****************************
*****入口参数:无
*****返回值:无
************************************************************/
void Motor_Left2(u16 value)
{
Moter2_CH3_Value=990;
Moter1_CH3_Value=410;
delay_ms(value);
}
/************************************************************
***************************右转1*****************************
*****返回值:无
************************************************************/
void Motor_Right2(u16 value)
{
Moter1_CH3_Value=990;
Moter2_CH3_Value=350;
delay_ms(value);
}
/************************************************************
***************************后退*****************************
*****入口参数:无
*****返回值:无
************************************************************/
void Motor_Back(void)
{
TIM1->CCER|=1<<12; //OC4 输出使能
TIM2->CCER|=1<<12; //OC4 输出使能
TIM2->CCER&=~(1<<8); //OC3 输出禁止
TIM1->CCER&=~(1<<0); //OC3 输出禁止
Moter1_CH4_Value=50;
Moter2_CH4_Value=50;
}
红外对管的驱动:
#ifndef __EXTI_H
#define __EXTI_H
void EXTI_Init(void);
#endif
#include "exti.h"
#include "delay.h"
#include "sys.h"
#include "motor.h"
u8 Tracing_Right=0;
u8 Tracing_Left=0;
void EXTI4_IRQHandler(void)
{
delay_ms(10);
Tracing_Right=1;
Motor_Right2(100);
EXTI->PR=1<<4;
}
void EXTI0_IRQHandler(void)
{
delay_ms(10);
Tracing_Left=1;
Motor_Left2(100);
EXTI->PR=1<<0;
}
void EXTI_Init(void){
RCC->APB2ENR|=1<<3;
RCC->APB2ENR|=1<<2;
GPIOB->CRL&=0xFFF00FFF;
GPIOB->CRL|=0x00088000;
GPIOA->CRL&=0xFFFFFFF0;
GPIOA->CRL|=0x00000008;
Ex_NVIC_Config(GPIO_B, 4, RTIR); //PB4 上升沿触发 (右)
Ex_NVIC_Config(GPIO_A, 0, RTIR); //PA0 上升沿触发(左)
MY_NVIC_Init(2, 1, EXTI0_IRQn, 2); //抢占2, 子优先级0, 组2
MY_NVIC_Init(2, 0, EXTI4_IRQn, 2); //抢占2, 子优先级1, 组2
}
HC-SR04的驱动:
输入捕获,采样三次取平均值
#ifndef __HCSR_04_
#define __HCSR_04_
#include "sys.h"
void HCSR_04_TIM3_Init(u16 arr,u16 psc);
u16 HCER_04_Get_Length(void);
#endif
#include "hcsr04.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
//[7] 0表示没有成功捕获 1表示成功捕获一次
//[6] 0表示还没捕获到低电平 1表示捕获到高电平
//[5:0] 捕获高电平后溢出的次数
u8 TIM3CH1_CAPTURE_STA=0; //输入捕获状态
u16 TIM3CH1_CAPTURE_VAL; //输入捕获值
void HCSR_04_TIM3_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<1; //TIM3 时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
GPIOA->CRL&=0x00FFFFFF;
GPIOA->CRL|=0x38000000;
AFIO->MAPR&=0XFFFFF3FF; //清除MAPR的[11:10]
AFIO->MAPR|=1<<11; //部分重映像
GPIOA->ODR|=1<<7;
TIM3->ARR=arr; //设定计数器自动重装值
TIM3->PSC=psc; //预分频器
TIM3->CCMR1|=1<<0; //CC1S=01 选择输入端 IC1映射到TI1上
TIM3->CCMR1|=0<<4; //IC1F=0000 配置输入滤波器 不滤波
TIM3->CCMR1|=0<<2; //IC1PS=00 配置输入分频,不分频
TIM3->CCER|=0<<1; //CC1NP=0 上升沿捕获
TIM3->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中
//TIM3->BDTR|=1<<15;
TIM3->EGR=1<<0; //软件控制产生更新事件,使写入PSC的值立即生效,否则将会要等到定时器溢出才会生效!
TIM3->DIER|=1<<1; //允许捕获1中断
TIM3->DIER|=1<<0; //允许更新中断
TIM3->CR1|=0x01; //使能定时器1
MY_NVIC_Init(2,1,TIM3_IRQn,2);//抢占3,子优先级1,组2 捕获比较中断
}
void TIM3_IRQHandler(void)
{
u16 tsr;
tsr=TIM3->SR;
if((TIM3CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM3CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM3CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM3CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM3CH1_CAPTURE_VAL=0XFFFF;
}else TIM3CH1_CAPTURE_STA++;
}
}
if(tsr&0x02)//捕获1发生捕获事件
{
if(TIM3CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM3CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM3CH1_CAPTURE_VAL=TIM3->CCR1; //获取当前的捕获值.
TIM3->CCER&=~(1<<1); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM3CH1_CAPTURE_STA=0; //清空
TIM3CH1_CAPTURE_VAL=0;
TIM3CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM3->CR1&=~(1<<0) ; //使能定时器2
TIM3->CNT=0; //计数器清空
TIM3->CCER|=1<<1; //CC1P=1 设置为下降沿捕获
TIM3->CR1|=0x01; //使能定时器2
}
}
}
TIM3->SR=0;//清除中断标志位
}
u16 HCER_04_Get_Length()
{
u16 temp=0;
u16 length;
u16 sum=0;
u8 i=1;
u16 result;
while(i<4)
{
GPIOA->ODR|=1<<7; //PA4输出一个高电平
delay_us(20);
GPIOA->ODR&=~(1<<7); //PA4电平拉低
if(TIM3CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
{
temp=TIM3CH1_CAPTURE_STA&0X3F;
temp*=0XFFFF; //溢出时间总和
temp+=TIM3CH1_CAPTURE_VAL; //得到总的高电平时间
length=temp*0.017;
/*均值滤波*/
sum+=length;
if(i==3)
result=sum/3;
i++;
TIM3CH1_CAPTURE_STA=0; //开启下一次捕获
}
}
return result;
}
舵机的驱动:
使用循环+延时翻转电平的方式控制,舵机的代码里加入了HC-SR04的调用,用来测距,遇到障碍后小车停车,左右循找距离远的方向
#ifndef __SERVO_H
#define __SERVO_H
#include "sys.h"
#define SERVO PBout(5)
void Servo_Init(void);
u8 Servo_Find(void);
void Servo_Run(void);
#endif
#include "servo.h"
#include "delay.h"
#include "hcsr04.h"
#include "usart.h"
#include "motor.h"
void Servo_Init(void)
{
RCC->APB2ENR|=1<<3; //使能PORTB时钟
GPIOB->CRL&=0XFF0FFFFF; //PB5输出
GPIOB->CRL|=0X00300000; //复用功能输出
SERVO=0;
}
//ROL 1 向左走,ROL 0 向右走,ROL 2 后退
u8 Servo_Find(void)
{
int i,j;
u16 RightLength=999,LeftLength=999,temp;
u8 ROL;
for(i=0;i<30;i++)
{
SERVO=1;
delay_us(1600);
SERVO=0;
delay_us(18400);
}
//测距
delay_ms(100);
for(j=0;j<5;j++)
{
temp=HCER_04_Get_Length();
if(temp<=RightLength)
RightLength=temp;
}
for(i=0;i<30;i++)
{
SERVO=1;
delay_us(2050);
SERVO=0;
delay_us(17950);
}
for(i=0;i<30;i++)
{
SERVO=1;
delay_us(2400);
SERVO=0;
delay_us(17600);
}
//测距
delay_ms(100);
for(j=0;j<5;j++)
{
temp=HCER_04_Get_Length();
if(temp<=LeftLength)
LeftLength=temp;
}
for(i=0;i<30;i++)
{
SERVO=1;
delay_us(2050);
SERVO=0;
delay_us(17950);
}
if(RightLength>=30&&RightLength>=LeftLength)
return 0;
else if(LeftLength>=30&&LeftLength>=RightLength)
return 1;
else
return 2;
}
u8 flag=1;
void Servo_Run(void)
{
u8 i,ROL;
u16 FrontLength;
//舵机回中
if(flag==1)
{
for(i=0;i<30;i++)
{
SERVO=1;
delay_us(2050);
SERVO=0;
delay_us(17950);
}
flag=0;
}
FrontLength=HCER_04_Get_Length();
if(FrontLength<=30)
{
flag=1;
Motor_Stop();
ROL=Servo_Find();
if(ROL==0)
{
//Motor_Stop();
Motor_Right1(700);
}
else if(ROL==1)
{
//Motor_Stop();
Motor_Left1(700);
}
else if(ROL==2)
{
Motor_Stop();
}
}
}
按键驱动:
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#define KEY0_PRES 1 //KEY0按下
#define KEY1_PRES 2 //KEY1按下
#define KEY2_PRES 3 //KEY2按下
#define KEY_ERROR 5 //按键出错
#define KEY0 PBin(6) //PB6
#define KEY1 PBin(7) //PB7
#define KEY2 PBin(8) //PB8
void KEY_Init(void); //IO初始化
u8 KEY_Scan(u8 mode); //按键扫描函数
//u8 KEY_UP(); //按键松开
#endif
#include "key.h"
#include "delay.h"
//按键初始化函数
void KEY_Init(void)
{
RCC->APB2ENR|=1<<3; //使能PORTB时钟
JTAG_Set(SWD_ENABLE); //关闭JTAG,开启SWD
GPIOB->CRL&=0x00FFFFFF; //PB6、7设置成输入
GPIOB->CRL|=0X88000000;
GPIOB->CRH&=0XFFFFFFF0; //PB8设置成输入
GPIOB->CRH|=0X00000008;
}
//按键处理函数
//返回按下的按键值
//mode:0,不支持连续按;1,支持连续按;
//返回值:
//0,没有任何按键按下
//KEY_ERROR,有多个按键按下
//KEY0_PRES,KEY0按下
//KEY1_PRES,KEY1按下
//WKUP_PRES,WK_UP按下
//注意此函数有响应优先级,KEY0>KEY1>KEY2!!
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==1||KEY1==1||KEY2==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==1&&KEY1==0&&KEY2==0)return KEY0_PRES;
else if(KEY0==0&&KEY1==1&&KEY2==0)return KEY1_PRES;
else if(KEY0==0&&KEY1==0&&KEY2==1)return KEY2_PRES;
else return KEY_ERROR;
}else if(KEY0==0&&KEY1==0&&KEY2==0)key_up=1;
return 0;// 无按键按下
}
蜂鸣器的驱动:
俩种发声模式,分别用于选择模式以及模式选择错误时(多个按键被按下)
#ifndef __BEEP_H
#define __BEEP_H
#include "sys.h"
#define BEEP PBout(9)
void BEEP_Init(void);
void BEEP_Mode1(void); //两声短鸣
void BEEP_Mode2(void); //两声长鸣
#endif
#include "beep.h"
#include "delay.h"
void BEEP_Init(void)
{
RCC->APB2ENR|=1<<3; //使能A口时钟
GPIOB->CRH&=0xFFFFFF0F;
GPIOB->CRH|=0x0000003F; //PA9输出
BEEP=0;
}
//用于模式选择后的提示
void BEEP_Mode1(void)
{
BEEP=1;
delay_ms(300);
BEEP=0;
delay_ms(70);
BEEP=1;
delay_ms(300);
BEEP=0;
}
//模式选择错误时
void BEEP_Mode2(void)
{
BEEP=1;
delay_ms(1000);
BEEP=0;
delay_ms(100);
BEEP=1;
delay_ms(1000);
BEEP=0;
}
USART3(蓝牙)驱动:
#ifndef __USART3_H
#define __USART3_H
#include "sys.h"
#include "stdio.h"
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART3_RX 1 //使能(1)/禁止(0)串口1接收
extern u8 USART3_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART3_RX_STA; //接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
void uart3_init(u32 pclk2,u32 bound);
#endif
#include "usart3.h"
#include "Data_struct.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "motor.h"
u8 BTFlag;
#if EN_USART3_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART3_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART3_RX_STA=0; //接收状态标记
void USART3_IRQHandler(void)
{
char res;
//delay_ms(50);
if(USART3->SR&(1<<5)) //接收到数据
{
//delay_ms(10);
res=USART3->DR;
printf("\r\n%d",res);
if(res ==255)
{
Motor_Forward(0);
printf("\r\nGo Stright");
}
else if(res==254)
{
Motor_Back();
printf("\r\nGo Back");
}
else if(res== 248)
{
Motor_Left1(300);
printf("\r\nTurn Left");
}
else if(res==232)
{
Motor_Right1(300);
printf("\r\nTurn Right");
}
else if(res==200)
{
Motor_Stop();
printf("\r\nStop");
}
}
}
#endif
//初始化IO 串口3
//pclk2:PCLK2时钟频率(Mhz)
//bound:波特率
void uart3_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;
mantissa+=fraction;
RCC->APB2ENR|=1<<3; //使能PORTB口时钟
RCC->APB1ENR|=1<<18; //使能串口3时钟
GPIOB->CRH&=0XFFFF00FF;//IO状态设置
GPIOB->CRH|=0X00008B00;//IO状态设置
RCC->APB2RSTR|=1<<18; //复位串口3
RCC->APB2RSTR&=~(1<<18);//停止复位
//波特率设置
USART3->BRR=mantissa; // 波特率设置
USART3->CR1|=0X200C; //1位停止,无校验位.
//#if EN_USART1_RX //如果使能了接收
//使能接收中断
//USART3->CR1|=1<<8; //PE中断使能
USART3->CR1|=1<<5; //接收缓冲区非空中断使能
USART3_RX_STA=0;
MY_NVIC_Init(3,3,USART3_IRQn,2);//组2,最低优先级
//#endif
}
主程序:
#include "main.h"
#include "motor.h"
#include "exti.h"
#include "beep.h"
#include "key.h"
#include "led.h"
#include "usart.h"
#include "hcsr04.h"
#include "servo.h"
#include "usart3.h"
*/
extern u8 Tracing_Right;
extern u8 Tracing_Left;
extern u8 Flag;
void System_Init(void)
{
Stm32_Clock_Init(9); //初始化系统时钟
delay_init(72);
uart_init(72,9600); //初始化延时
HCSR_04_TIM3_Init(0xFFFF,36-1);
MOTOR1_PWM_Init(1000-1,360-1);
MOTOR2_PWM_Init(1000-1,360-1);
uart3_init(72,9600);
Servo_Init();
EXTI_Init();
BEEP_Init();
KEY_Init();
LED_Init();
BEEP=0;
//关中断
EXTI->IMR&=~(1<<0);
EXTI->IMR&=~(1<<4);
}
int main(void)
{
u8 key=0,SROL,flag1=0,flag2=0,flag3=0;
System_Init();
Motor_Stop();
while(1)
{
key=KEY_Scan(0);
if(key==KEY_ERROR)
{
Motor_Stop();
BEEP_Mode2();
delay_ms(100);
}
//寻迹
else if(key==KEY0_PRES)
{
BEEP_Mode1();
EXTI->IMR|=1<<0;
EXTI->IMR|=1<<4;
//关蓝牙中断
USART3->CR1&=~(1<<5);
delay_ms(2000);
flag1++;
while(1)
{
if(Tracing_Right==1)
{
Tracing_Left=0;
}
else if(Tracing_Left==1)
{
Tracing_Right=0;
}
else
{
Motor_Forward(LOW_SPEED);
}
REDLED=!REDLED;
}
}
//避障碍
else if(key==KEY1_PRES)
{
BEEP_Mode1();
//关寻迹中断
EXTI->IMR&=~(1<<0);
EXTI->IMR&=~(1<<4);
//关蓝牙中断
USART3->CR1&=~(1<<5);
Motor_Forward(LOW_SPEED);
while(1)
{
Servo_Run();
Motor_Forward(LOW_SPEED);
}
}
//蓝牙
else if(key==KEY2_PRES)
{
BEEP_Mode1();
//关寻迹中断
EXTI->IMR&=~(1<<0);
EXTI->IMR&=~(1<<4);
USART3->CR1|=1<<5; //接收缓冲区非空中断使能
}
}
}
小车的效果视频:链接点我