概要
建立在51单片机以及stm32f4单片机上的蓝牙小车,它真的只是一台车没有什么别的功能。(主要原因是我太菜了)
硬件
淘宝搜索
1.蓝牙小车底盘。
2. l298n驱动两个。
3. 51单片机最小核心板。
4. STM32F4核心板。
5. 杜邦线一大堆。
6. 口香糖电池。
51单片机学习过程
买了一本郭天祥老师的51单片机教程以及一块学习板,配合着b站上的教程看,主要是为了初步了解入门单片机。怎么说呢,学习起来还是有点感悟的。
- 开始我是对照着书本进行学习,然后学了怎么操作自己试了一下之后就直接下一章节了,没有得到充分的练习,以至于真正做车车的时候发现有很多东西稀里糊涂的,得翻书重新查。这里的教训就是,学以致用很重要,理论-实践-理论的三明治原理。(后来发现郭天祥老师的视频里留有充足的课后练习)
- 其次就是中间中断了一段时间没有学习,大学生活丰富多彩嘛嘻嘻。。。没人管就很容易滋生堕落的情绪,所以说拥有坚定的信念很重要。
32单片机学习过程
这个就更加曲折了。。一开始看这个32的代码,简直是看天书一样,以至于出现什么一周快速学习stm32从入门到放弃再到不行还是得学之类的事情。
- 没有充分吸取51单片机的学习教训,也是了解差不多就直接下一章了。因为一个是时间紧迫,一个是因为现阶段没有学习学长学姐们口中的《微机原理》balabala…的书,就只能学会这个单片机怎么用,基本的使用原理大概是怎么个回事,实在没法特别深入地学习。
- 发现学习东西啊还是多看看视频,不要闷声看书,有人讲课的效果比自己慢慢慢慢看书的效果更好,书本可以作为对课堂细节的补充,而课堂却可以快速令人掌握一件事物的基本框架。
- 总的来说这次是一次有意义的学习体验,入门一件事情首先先了解框架(51单片机),再通过视频巩固框架(stm32的视频),书本补充细节,然后实践实践再实践,过程中的问题及时问前辈,不要闭门造车,有时候学长的一句提醒顶得上自己翻阅两天的资料。
51的程序
#include<reg52.h>
sbit IN1=P1^0;
sbit IN2=P1^1;
sbit IN3=P1^2;
sbit IN4=P1^3;
sbit IN11=P1^4;
sbit IN22=P1^5;
sbit IN33=P1^6;
sbit IN44=P1^7;
void Left_moto_go() {IN1=0,IN2=1,IN11=0,IN22=1;} //小车运动的正反转电平设置
void Left_moto_back() {IN1=1,IN2=0,IN11=1,IN22=0;}
void Left_moto_Stop() {IN1=0,IN2=0,IN11=0,IN22=0;}
void Right_moto_go() {IN3=0,IN4=1,IN33=0,IN44=1;}
void Right_moto_back() {IN3=1,IN4=0,IN33=1,IN44=0;}
void Right_moto_Stop() {IN3=0,IN4=0,IN33=0,IN44=0;}
unsigned char mode; //用字符来作为运行的指令
unsigned int flag;
void delay(unsigned int k)
{
unsigned int x,y;
for(x=0;x<k;x++)
for(y=0;y<2000;y++);
}
void TIME1() // 选择使用定时器1;
{
TMOD=0x20; //并且工作模式2,8位
TH1=0xfd; //产生9600波特率,与蓝牙模块同步 ,因为当时购买的蓝牙模块出厂设置就是9600波特率。
TL1=0xfd;
SM0=0; //设置串口的工作模式为
SM1=1; //传送8位数据
REN=1; //串口通信允许接收
TR1=1; //开定时器1
EA=1; //允许总中断
ES=1; //允许串口中断
}
int main()
{
TIME1();
while(1)
{
if(flag)
{
ES=0; //这里一定要清零,不然下一个周期定时器中断就没法判断是否接收到信息了
if(mode=='1') //这里采用数字作为通信
{
Left_moto_go() ;
Right_moto_go() ;
delay(20); //延时函数一定要有。
}
if(mode=='3')
{
Left_moto_back();
Right_moto_back(); //back
delay(20);
}
if(mode=='4')
{
Right_moto_go();
Left_moto_Stop(); //left
delay(10);
}
if(mode=='5')
{
Left_moto_go();
Right_moto_Stop(); //right
delay(10);
}
if(mode=='2')
{
Right_moto_Stop();
Left_moto_Stop();
delay(40); //stop
}
ES=1;
flag=0;
}
}
}
void ser() interrupt 4
{
RI=0; //接收中断标志
mode=SBUF; //将接收到的手机发送到缓冲区数据SBUF复制给mode
flag=1; //说明已经接收数据完毕
}
这里比较简单,没有写pwm功能,将在stm32中使用pwm功能。
32单片机
这个花费了比较多的时间,一开始觉得天书,后来慢慢入门发现很多东西前人已经写好了,只要掌握使用的技巧就好了(毕竟我没有足够的专业知识,也不是搞弱电的)所以整个程序是建立在蓝牙模块的程序基础上写的。
主函数
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "pwm.h"
#include "usmart.h"
#include "hc05.h"
#include "usart3.h"
#include "string.h"
int main(void)
{
u16 pwmval=1;
u16 speedlost=1; //转弯的时候用
u16 dang=1;
u8 reclen=0; //检测获取数据
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
TIM2_PWM_Init(500-1,84-1); //84M/84=1Mhz的计数频率,重装载值500,所以PWM频率为 1M/500=2Khz.
TIM3_PWM_Init(500-1,84-1);
TIM9_PWM_Init(500-1,84-1);
usmart_dev.init(84); //初始化USMART
LED_Init(); //初始化LED
delay_ms(1000); //等待蓝牙模块上电稳定
HC05_Init(); //初始化ATK-HC05模块
delay_ms(100);
USART3_RX_STA=0;
while(1)
{
LED0=!LED0;
if(USART3_RX_STA&0X8000) //接收到一次数据了
{
reclen=USART3_RX_STA&0X7FFF; //得到数据长度
USART3_RX_BUF[reclen]=0; //加入结束符
if(reclen!=0) //控制DS1检测
{
if(strcmp((const char*)USART3_RX_BUF,"+SPEED1")==0)
//设置挡位 //必须以+开头才可以啊!
{
delay_ms(1);
LED1=!LED1;
pwmval=1;
dang=1;
}
if(strcmp((const char*)USART3_RX_BUF,"+SPEED2")==0)
{
delay_ms(1);
LED1=!LED1;
pwmval=100;
dang=100;
}
if(strcmp((const char*)USART3_RX_BUF,"+SPEED3")==0)
{
delay_ms(1);
LED1=!LED1;
pwmval=200;
dang=200;
}
if(strcmp((const char*)USART3_RX_BUF,"+FORWARD")==0)
{
LED1=!LED1;//测试用的,到时候可以删掉
TIM_SetCompare1(TIM9,pwmval); //右边两个轮子分别接一个PE5,PE6
TIM_SetCompare2(TIM9,pwmval);
TIM_SetCompare1(TIM2,pwmval); //左边两个轮子分别接一个PA0,PA1
TIM_SetCompare2(TIM2,pwmval);
delay_ms(500);
}
if(strcmp((const char*)USART3_RX_BUF,"+BACK")==0)
{
LED1=!LED1;
TIM_SetCompare1(TIM3,pwmval); //右边两个轮子分别接一个PC6789
TIM_SetCompare2(TIM3,pwmval);
TIM_SetCompare3(TIM3,pwmval);
TIM_SetCompare4(TIM3,pwmval);
delay_ms(500);
}
if(strcmp((const char*)USART3_RX_BUF,"+RIGHT")==0)
{
LED1=!LED1;
speedlost=pwmval+100;
TIM_SetCompare1(TIM9,speedlost); //右边两个轮子分别接一个PE5,PE6 //右边轮子变慢
TIM_SetCompare2(TIM9,speedlost);
TIM_SetCompare1(TIM2,pwmval); //左边两个轮子分别接一个PA0,PA1
TIM_SetCompare2(TIM2,pwmval);
delay_ms(500);
}
if(strcmp((const char*)USART3_RX_BUF,"+LEFT")==0)
{
speedlost=pwmval+100;
LED1=!LED1;//测试用的,到时候可以删掉
TIM_SetCompare1(TIM9,pwmval); //右边两个轮子分别接一个PE5,PE6
TIM_SetCompare2(TIM9,pwmval);
TIM_SetCompare1(TIM2,speedlost); //左边两个轮子分别接一个PA0,PA1 //左边轮子变慢
TIM_SetCompare2(TIM2,speedlost);
delay_ms(500);
}
if(strcmp((const char*)USART3_RX_BUF,"+STOP")==0)
{
LED1=!LED1; //测试用的,到时候可以删掉
pwmval=500;
TIM_SetCompare1(TIM9,pwmval); //右边两个轮子分别接一个PE5,PE6
TIM_SetCompare2(TIM9,pwmval);
TIM_SetCompare1(TIM2,pwmval); //左边两个轮子分别接一个PA0,PA1
TIM_SetCompare2(TIM2,pwmval);
TIM_SetCompare1(TIM3,pwmval); //右边两个轮子分别接一个PC6789
TIM_SetCompare2(TIM3,pwmval);
TIM_SetCompare3(TIM3,pwmval);
TIM_SetCompare4(TIM3,pwmval); //全部停下
delay_ms(500);
pwmval=dang;
}
}
USART3_RX_STA=0;
}
}
}
pwm函数
#include "pwm.h"
#include "led.h"
#include "usart.h"
//TIM4 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTC
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM4); //GPIOC6复用为定时器3
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_TIM4);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_TIM4);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOC6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC6
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOC7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIOC8
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOC9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC9
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器3
//初始化TIM3 Channel 6789 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
void TIM2_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM2); //GPIOA0复用为定时器2
GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM2); //GPIOA1复用为定时器2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA0
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA1
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);//初始化定时器2
//初始化TIM2 Channel 12 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 4OC1
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM2在CCR1上的预装载寄存器
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 4OC2
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM2在CCR2上的预装载寄存器
TIM_ARRPreloadConfig(TIM2,ENABLE);//ARPE使能
TIM_Cmd(TIM2, ENABLE); //使能TIM2
}
void TIM9_PWM_Init(u32 arr,u32 psc)
{
//此部分需手动修改IO口设置
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE); //TIM9时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //使能PORTE
GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_TIM9); //GPIOE5复用为定时器9
GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_TIM9); //GPIOE6复用为定时器9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //GPIOE5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化PE5/6
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOE6
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化PE6
TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM9,&TIM_TimeBaseStructure);//初始化定时器9
//初始化TIM9 Channel 12 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
TIM_OC1Init(TIM9, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM9 4OC1
TIM_OC1PreloadConfig(TIM9, TIM_OCPreload_Enable); //使能TIM9在CCR1上的预装载寄存器
TIM_OC2Init(TIM9, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM9 4OC2
TIM_OC2PreloadConfig(TIM9, TIM_OCPreload_Enable); //使能TIM9在CCR2上的预装载寄存器
TIM_ARRPreloadConfig(TIM9,ENABLE);//ARPE使能
TIM_Cmd(TIM9, ENABLE); //使能TIM2
}
这里之所以调用这么多定时器,是因为另外的几个定时器被项目的其他功能占用了。
这里就只放两个主要的函数吧,蓝牙和其他函数的代码放在总的程序目录里。(懒得拷贝了。。。)
硬件连接
原理图(啊别吐槽我灵魂画手了)
这里只上传51的连接
视频截图。。。
恭喜你司机同志,秋名山一较高下。
最后
再次感谢教我搭建博客,教我做车车,教我开车车的学长学姐。希望自己大学四年能开个好头,继续学习哈。(不说了昨天C语言上机跪了(手动哭泣),得赶紧复习高数把分数捞回来才行。。。)