历时两周,总算把小车弄好了,总体上来说做的太慢了。自己在32的学习中还不够仔细深入,只是浅面的学习,当真正做一个项目时,暴露的问题就太多了。这次在小车的制作的过程中,遇到了各种各样的问题,软件,硬件,各式各样的问题迎面而来,真的好几次心态崩了。不过还好小车这个项目不只是我一个人在搞,组里的其他成员也在一直在考虑问题,想办法,不断地解决解决,总归小车终于做好了,下面是小车完成图:
是有点灵魂接线(·<·)。
模块清单:
1.stm32f103C8T6开发板
2.HC-SR04超声波测距模块X3
3.TB6612FNG 电机驱动模块X2
4.HC-05主从一体无线蓝牙模块X1
5.智能小车底盘 4WD小车循迹/避障小车 底盘X1
6.LM2596S DC-DC降压电源模块X1
7.电源模块3.3V 5V 12V多路输出 电压转换模块DC-DC 12V转3.3V 5V X1
8.12V电池X1
9.杜邦线 Xn
10.转串口模块X1
11.ST-linkX1
12.面包板X1
完成功能:1.超声波避障 2.蓝牙控制 3.走矩形
各个模块就不再介绍了,具体介绍大家可以自行百度或者看一下我的前几篇Arduino智能小车博客,里面有简单的介绍。
小车的所有详细代码和成品演示在我的资源中,大家可以下载提取(包括代码,引脚图,效果演示):https://download.csdn.net/download/nidie508/11458242
那么,开始吧。
准备工作:
1.首先配置好keil5 c8t6模板(温馨提示:多看看模板的核心驱动,包括sys.h delay.h usart.h等,这些核心文件一定要保证准确无误!)
2.了解开发板和确保开发板无误
3.熟悉怎么用转串口模块和st-link将驱动下载到开发板上
下图示为c8t6开发板的引脚图
1.超声波避障功能
我们设计的思路如下,用三个超声波来避障。为什么用三个超声波呢?我们想的是如果用舵机的话,小车在行进过程中并不好判断,只能将车停下,舵机转动来检测哪个方向无障碍物,而用3个超声波不仅可以在行进中判断,也可以让小车没有停下的动作,显得整个过程比较流畅。这就是我们使用三个超声波的原因了。
我们使用的是定时器二的通道一,通道二,通道三来进行输入捕获的,那么,第一个问题就来了。
我们在写超声波代码时,用一个超声波先测试,发现超声波测试的并没有问题 ,串口显示的数据也并没有问题。但是,但是,在用三个超声波同时测试是,却发现数据显示的总有问题,输出的数据总是毫无规律,且数字都非常大,我们就在想是什么问题。
下面是三个超声波控制的代码:
//main.c
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "dianji.h"
#include "hcsr.h"
u32 DIS_Init(u8 STA,u16 VAL)
{
u32 temp;
u32 lenth;
if((*STA)&0X80)//成功捕获到了一次高电平
{
temp=STA&0X3F;
temp*=65536; //溢出时间总和
temp+=VAL; //得到总的高电平时间
lenth=temp*0.017; //计算长度
STA=0; //开启下一次捕获
}
return lenth;
}
extern u8 TIM2CH2_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH2_CAPTURE_VAL; //输入捕获值
extern u8 TIM2CH3_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH3_CAPTURE_VAL; //输入捕获值
extern u8 TIM2CH4_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH4_CAPTURE_VAL; //输入捕获值
int main(void)
{
u32 temp=0;
u32 length1;
u32 length2;
u32 length3;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化 //初始化与LED连接的硬件接口
TIM_PWM1_Init();//10000-1,36-1);//不分频。PWM频率=72M/(0.036M)=2Khz
Echo1=0;
Echo2=0;
Echo3=0;
HCSR04_Init(0XFFFF,72-1);//以1Mhz的频率计数
/*while(1)
{
}*/
while(1)
{
Echo3=1;
delay_us(20);
Echo3=0;
length1=DIS_Init(&TIM2CH4_CAPTURE_STA,TIM2CH4_CAPTURE_VAL);
delay_ms(1000);
Echo1=1;
delay_us(20);
Echo1=0;
length2=DIS_Init(&TIM2CH2_CAPTURE_STA,TIM2CH2_CAPTURE_VAL);
delay_ms(1000);
Echo2=1;
delay_us(20);
Echo2=0;
length3=DIS_Init(&TIM2CH3_CAPTURE_STA,TIM2CH3_CAPTURE_VAL);
printf("%d %d %drn",length1,length2,length3);
// GO();
delay_ms(500);
}
}
hcsr.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "hcsr.h"
void HCSR04_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<0; //TIM2时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
RCC->APB2ENR|=1<<3; //使能PORTB时钟
GPIOA->CRL&=0XFFFF000F;//PA1 清除之前设置
GPIOA->CRL|=0X00008880;//PA1输入
// GPIOA->ODR|=0<<0;
GPIOA->ODR|=0<<1; //PA1下拉
GPIOA->ODR|=0<<2;
GPIOA->ODR|=0<<3;
GPIOB->CRL&=0X000FFFFF;//PB7清除之前设置
GPIOB->CRL|=0X33300000;//PB7推挽输出
GPIOB->ODR|=1<<7; //PB7 输出高
GPIOB->ODR|=1<<6;
GPIOB->ODR|=1<<5;
TIM2->ARR=arr; //设定计数器自动重装值
TIM2->PSC=psc; //预分频器
TIM2->CCMR1|=1<<8; //CC2S=01 选择输入端IC1映射到TI1
TIM2->CCMR1|=1<<12; //IC2F=0001 配置滤波器 以Fck_int采样,两个事件后有效
TIM2->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<5; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->CCMR2|=1<<0; //CC2S=01 选择输入端IC1映射到TI1
TIM2->CCMR2|=1<<4; //IC2F=0001 配置滤波器 以Fck_int采样,两个事件后有效
TIM2->CCMR2|=0<<2; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<9; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<8; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->CCMR2|=1<<8; //CC2S=01 选择输入端IC1映射到TI1
TIM2->CCMR2|=1<<12; //IC2F=0001 配置滤波器 以Fck_int采样,两个事件后有效
TIM2->CCMR2|=0<<10; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<13; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<12; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->DIER|=1<<2; //允许捕获中断
TIM2->DIER|=1<<3; //允许捕获中断
TIM2->DIER|=1<<4; //允许捕获中断
TIM2->DIER|=1<<0; //允许更新中断
//TIM2->CR1|=0X01; //使能定时器2
MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2
}
u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH1_CAPTURE_VAL; //输入捕获值
u8 TIM2CH2_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH2_CAPTURE_VAL; //输入捕获值
u8 TIM2CH3_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH3_CAPTURE_VAL; //输入捕获值
u8 TIM2CH4_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH4_CAPTURE_VAL; //输入捕获值
//定时器2中断服务程序
void TIM2_IRQHandler(void)
{
u16 tsr;
tsr=TIM2->SR;
if((TIM2CH4_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH4_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH4_CAPTURE_VAL=0XFFFF;
}else TIM2CH4_CAPTURE_STA++;
}
}
if(tsr&0x10)//捕获1发生捕获事件
{
if(TIM2CH4_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH4_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH4_CAPTURE_VAL=TIM2->CCR4;//获取当前的捕获值
TIM2->CCER&=~(1<<13); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH4_CAPTURE_VAL=0;
TIM2CH4_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2->CNT=0; //计数器清空
// TIM2CH4_CAPTURE_VAL=TIM2->CCR4;
TIM2->CCER|=1<<13; //CC1P=1 设置为下降沿捕获
TIM2->CR1|=0x01;
}
}
}
if((TIM2CH2_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH2_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH2_CAPTURE_VAL=0XFFFF;
}else TIM2CH2_CAPTURE_STA++;
}
}
if(tsr&0x04)//捕获1发生捕获事件
{
if(TIM2CH2_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH2_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH2_CAPTURE_VAL=TIM2->CCR2;//获取当前的捕获值
TIM2->CCER&=~(1<<5); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH2_CAPTURE_VAL=0;
TIM2CH2_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2->CNT=0; //计数器清空
TIM2->CCER|=1<<5; //CC1P=1 设置为下降沿捕获
TIM2->CR1|=0x01;
}
}
}
if((TIM2CH3_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH3_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH3_CAPTURE_VAL=0XFFFF;
}else TIM2CH3_CAPTURE_STA++;
}
}
if(tsr&0x08)//捕获1发生捕获事件
{
if(TIM2CH3_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH3_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH3_CAPTURE_VAL=TIM2->CCR3;//获取当前的捕获值
TIM2->CCER&=~(1<<9); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH3_CAPTURE_VAL=0;
TIM2CH3_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2->CNT=0;
// TIM2CH3_CAPTURE_VAL=TIM2->CCR3; //计数器清空
TIM2->CCER|=1<<9; //CC1P=1 设置为下降沿捕获
TIM2->CR1|=0x01;
}
}
}
TIM2->SR=0;//清除中断标志位
}
hcsr.h
#ifndef __HSCR04_H
#define __HSCR04_H
#include "sys.h"
#define Echo1 PBout(7) // PB7
#define Echo2 PBout(6) // PB7
#define Echo3 PBout(5) // PB7
void HCSR04_Init(u16 arr,u16 psc);
#endif
最后发现,输入捕获代码中,中断服务函数中,TIM2->CNT不能清零,因为初始化中,用的是同一个定时器的通道2,3,4。如果每一次中断函数的某个通道函数将TIM2->CNT清0,那么其他通道的记录的TIM2->CNT的值就发生变化,从而导致了各种各样的情况。
问题二就是从串口读取数据,当时挺崩溃的。首先是keil5模板问题,当时串口怎么都显示不出来数据,我们当时都很疑惑。一直反复的看代码,考虑各种情况,但还是显示不出来数据。一开始我们以为是超声波接受的反馈的数据不满足某个条件,所以没有显示。到后面查来查去,又在想是开发板并没有给电压?超声波集体歇B?最终发现,usart.c文件写的串口不是我们接线串口所对应的。当时心态挺炸的,改了之后终于可以测试数据了。
问题三是我们在没接电机驱动之前,超声波接收的数据无论准不准确,最起码能接收到,可是接了电机驱动却发现每次返回的值都是0。我们当时并没有找到原因,又在猜想是不是电机用到的定时器对超声波的定时器有影响?又在想是不是外部电子设备把信号影响了?又在想各种各样的问题?
最终解决的方法是,串口模块给的电压不够。。。(我也不知道这样形容的对不对,但感觉就是)。为什么这样说呢?当时一开始我想的是,超声波的模块在用转串口模块给其供电时,是不是超声波模块的VCC和GND引脚并没有电压,从而导致信号发射不出去。于是我用万用表测试两侧电压,发现是5V没错。又在想是不是发出信号并没有发去,于是我让TRIG引脚一直为1,测试TRIG电压发现为2.6v左右。在此之后,我又用12v的电池降压给开发板供电,发现TRIG电压为3.3V左右。我想验证,是不是电压问题而导致的接收数据为零。所以我想用电池给开发板供电,然后打开串口监视器看数据。但是要想启用串口监视器,必须要用串口给开发板供电。最后我们选用了蓝牙模块,从手机的接收器来观察数据。终于,果然是电压的问题,数据成功出现了!!!(哭了)
2.用PWM调控电机速度
这一过程能稍微简单一些,就是一开始还是电压的问题,四个轮子根本带不起来。反复查看代码后并没有太多问题,可是轮子还是不转,但是发现电机嗡嗡响,去掉一个电机后,将速度调大,发现两个轮子缓缓的动了。验证时,我们将轮子速度调小,发现轮子不转,证明了代码并无问题。
pwm控制部分代码:
#include "dianji.h"
#include "sys.h"
#include "delay.h"
void TIM_PWM1_Init(u16 arr,u16 psc)
{
//此部分需手动修改IO口设置
RCC->APB1ENR|=1<<1; //TIM3时钟使能
RCC->APB2ENR|=1<<2; //GPIOA使能
RCC->APB2ENR|=1<<3; //GPIOB使能
RCC->APB2ENR|=1<<4;
GPIOA->CRL&=0X00FFFFFF; //PA(7)PA(6)做复用,PA(3)是BIN1 PA(4)BIN2 PA(5)STBY
GPIOA->CRL|=0XBB000000;
//GPIOA->ODR|=1<<7;
//GPIOA->ODR|=1<<6;
GPIOC->CRH&=0X000FFFFF;
GPIOC->CRH|=0X33300000;
GPIOB->CRL&=0XFFFFF000; //PB(0)是AIN1 PB(1)是AIN2
GPIOB->CRL|=0X00000333;
TIM3->ARR=arr; //设定计数器3自动重装值
TIM3->PSC=psc; //预分频器设置
TIM3->CCMR1|=6<<4; //CH1 PWM2模式
TIM3->CCMR1|=1<<3; //CH1预装载使能
TIM3->CCMR1|=6<<12; //CH2 PWM2模式
TIM3->CCMR1|=1<<11; //CH2预装载使能
TIM3->CCER|=1<<0; //OC1 输出使能
TIM3->CCER|=1<<4; //OC1 输出使能
// TIM3->BDTR|=1<<15; //MOE 主输出使能
TIM3->CR1=0x0080; //ARPE使能
TIM3->CR1|=0x01; //使能定时器3
RCC->APB2ENR|=1<<11; //TIM1定时器使能
GPIOA->CRH&=0XFFFF0FF0;
GPIOA->CRH|=0X0000B00B;
//GPIOA->ODR|=1<<8;
//GPIOA->ODR|=1<<11;
GPIOB->CRH&=0X0000FFFF;
GPIOB->CRH|=0X33330000;
TIM1->ARR=arr; //设定计数器自动重装值
TIM1->PSC=psc; //预分频器设置
TIM1->CCMR1|=6<<4; //CH1 PWM2模式
TIM1->CCMR1|=1<<3; //CH1预装载使能
TIM1->CCMR2|=6<<12; //CH4 PWM2模式
TIM1->CCMR2|=1<<11; //CH4预装载使能
TIM1->CCER|=1<<0; //OC1 输出使能
TIM1->CCER|=1<<12; //OC4 输出使能
TIM1->BDTR|=1<<15; //MOE 主输出使能
TIM1->CR1=0x0080; //ARPE使能
TIM1->CR1|=0x01; //使能定时器1
STBY=1;
STBY1=1;
}
void GO(u16 a,u16 b)
{
AIN1=0; //AIN1,BIN1,AIN2,BIN2控制轮子方向
AIN2=1;
BIN1=0;
BIN2=1;
AIN3=1;
AIN4=0;
BIN3=1;
BIN4=0;
TIM3->CCR1=a;//右上,控制速度
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
//delay_ms(2000);
}
void STOP(void)
{
AIN1=0;
AIN2=0;
BIN1=0;
BIN2=0;
AIN3=0;
AIN4=0;
BIN3=0;
BIN4=0;
}
void BACK(u16 a,u16 b)
{
AIN1=1;
AIN2=0;
BIN1=1;
BIN2=0;
AIN3=0;
AIN4=1;
BIN3=0;
BIN4=1;
TIM3->CCR1=a;//右上
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
// delay_ms(1000);
}
void RIGHT(u16 a,u16 b)
{
AIN1=1;
AIN2=0;
BIN1=0;
BIN2=1;
AIN3=0;
AIN4=1;
BIN3=1;
BIN4=0;
TIM3->CCR1=a;//右上
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
// delay_ms(1000);
}
void LEFT(u16 a,u16 b)
{
AIN1=0;
AIN2=1;
BIN1=1;
BIN2=0;
AIN3=1;
AIN4=0;
BIN3=0;
BIN4=1;
TIM3->CCR1=a;//右上
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
// delay_ms(1000);
}
3.蓝牙控制
蓝牙控制这一内容是小伙伴写的,大概内容和串口一章内容相似,就是多做出了判断,即没通过蓝牙输入数据来改变小车此时的模式,在蓝牙控制模式下,也可以相对应用蓝牙控制小车的方向。我们用的仍是串口一来控制蓝牙,为什么这样做?一是方便,二是就如前面所说,电机和超声波的分压严重,在电脑上上的串口监视器得出的数据并不准确,所以还不如用蓝牙来看数据,于是我们直接用了串口一来和蓝牙连接。初始化问题并不需要要修改,直接调用uart()函数即可,在中断控制代码下加入接收数据而触发的各种就好了。
蓝牙控制部分代码:
void USART1_IRQHandler(void)
{
char res;
if(USART1->SR&(1<<5))
{
res=USART1->DR;
printf("rn%d",res);
if(res==50) //输入2为前进
{
GO(300,300);
printf("rnGo Stright");
}
else if(res==56) //输入8为后退
{
BACK(300,300);
printf("rnGo Back");
}
else if(res== 52) //输入4为左转
{
LEFT(300,300);
printf("rnTurn Left");
}
else if(res==54) //输入6为右转
{
RIGHT(300,300);
printf("rnTurn Right");
}
else if(res==53) //输入5为停止
{
STOP();
printf("rnStop");
}
else if(res=='9') //进入超声波避障模式
{
opq=0;
}
}
}
3.走矩形
走矩形功能也是小伙伴写的,设计思路大致是通过蓝牙输入长和宽,小车通过接受的数据进行矩形运动。在这唯一遇到的问题就是小车的速度问题和转向时间。这个是他们弄得,自我感觉还是很不好调的,因为要考虑电池可供电压,小车行驶的惯性,不同地面的摩擦程度等等。考虑的方面比较多,根据不同的情况可能还要修改小车的速度和转向时间。我们就是在光滑地砖上测得,摩擦力应该是比较小的。
走矩形代码:
void Juxing()//小车的矩形运动函数
{
u8 chang,kuan,x,y;//chang、kuan分别是小车要走矩形的长和宽的值
delay_ms(200);
printf("Chang:rn");
while(1)
{
printf("Input Chang:rn");
delay_ms(200);
if(USART1->SR&(1<<5))//当串口接收到消息后跳出循环
{
chang=USART1->DR-'0';//将字符型的数据转换为整型数据
break;
}
}
printf("长:%drn",chang);//打印串口接收到的数据
USART1->SR=0;//串口的接收标志位清零,为下一次接收宽度数据做准备
while(1)
{
printf("Input Kuan:rn");
delay_ms(200);
if(USART1->SR&(1<<5))
{
kuan=USART1->DR-'0';//当串口接收到数据后跳出循环
break;
}
}
printf("宽:%drn",kuan);//打印宽度数据
//当前小车速度为0.25米每秒则小车每走1cm要用40ms所以以1cm为单位每走1cm耗时40ms用for函数驱动小车运动
for(x=0;x
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
for(y=0;y
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
for(x=0;x
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
for(y=0;y
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
STOP();
我的想法:
1.自己所学的东西并没有所有吸收,有些东西可能单个实验看着并不重要,一旦做项目了,有些东西起着至关重要的作用。比如,delay_ms()函数,当时我写的一句代码是delay_ms(2000),本想着意思是延迟2s,但是代码并不可行,因为delay_ms()函数设定的参数是小于1864,而2000显然已经超过了这个范围,在串口监视器显示的速度是不准确的。这里就是一个很细微的东西,但是自己并不知道,导致了一系列的错误。
2.和Arduino板子不同的是,不,应该说库函数和寄存器的区别其实还挺大的。起初在学32时,我觉得库函数和寄存器其实没多大的区别,感觉两者都不是和复杂,唯一的就是一些初始化函数,库函数可以直接调用而寄存器你得自己配置,反而成为一个很繁琐的过程。但是小车项目的整个过程彻底改变了我的想法,首先就是四个输入捕获通道问题,我不清楚库函数有没有,但是用寄存器配置时,我真的学的了一些东西。这些东邪可能不只是知识层面上的,更多的是一个训练总体观的过程,即要考虑任何东西。我相信如果只是调用那些已经封装好的函数,可能你应该思考的东西人家已经帮你弄好了,错失了关键点。
3.全面的考虑所有问题。学嵌入式可能和纯软不同,它既要考虑软件代码,又要考虑硬件搭建和硬件的可用,这是一个挺繁琐的过程。在做小车时,真正体会到了这点,从软件一步步排除,再到硬件逐个逐个测电压,一直在排除排除找的真真的解决方法。突然想起几个月前,实验室面试时,我问学长在测试过程中你们应该如何解决问题,他说第一方向是确保软件无误后,接下来开始看硬件。现在我觉得,我能体会到一点了。一定要先排查出软件,接下来入手硬件。
4.沟通与合作。真的随着年龄的增长,自己感觉自己变化挺大的。还记得初中时那种盛气凌人的状态,现在想想真的是井底之蛙那时候得我。现在我有了自己的队友,在遇到问题时,可以相互交流,相互讨论,考虑问题更加全面,学到的东西也更多,弥补自己的短处(感觉真的好讽刺啊,小学就在写,直到大学才真正明白。。。)而且最重要的一点是,我能慢慢听的进去和听懂他人的想法(意思)了,在高中时,一直都在想和那些逻辑思维强的人差距在哪儿,现在我觉得不仅是天赋,强理解力也是非常重要的一点。
5.保持良好的心态。中间有两天,心态真的炸了,小车进度一直被卡,shilian,感觉自己好废物。我还是没有做到,做事认真,乐观,这个我还得好好锻炼吧,以后还有很多的事,这就是冰山一角。
6.我错了,小淳。我还是太任性了,对不起,我很想你。
历时两周,总算把小车弄好了,总体上来说做的太慢了。自己在32的学习中还不够仔细深入,只是浅面的学习,当真正做一个项目时,暴露的问题就太多了。这次在小车的制作的过程中,遇到了各种各样的问题,软件,硬件,各式各样的问题迎面而来,真的好几次心态崩了。不过还好小车这个项目不只是我一个人在搞,组里的其他成员也在一直在考虑问题,想办法,不断地解决解决,总归小车终于做好了,下面是小车完成图:
是有点灵魂接线(·<·)。
模块清单:
1.stm32f103C8T6开发板
2.HC-SR04超声波测距模块X3
3.TB6612FNG 电机驱动模块X2
4.HC-05主从一体无线蓝牙模块X1
5.智能小车底盘 4WD小车循迹/避障小车 底盘X1
6.LM2596S DC-DC降压电源模块X1
7.电源模块3.3V 5V 12V多路输出 电压转换模块DC-DC 12V转3.3V 5V X1
8.12V电池X1
9.杜邦线 Xn
10.转串口模块X1
11.ST-linkX1
12.面包板X1
完成功能:1.超声波避障 2.蓝牙控制 3.走矩形
各个模块就不再介绍了,具体介绍大家可以自行百度或者看一下我的前几篇Arduino智能小车博客,里面有简单的介绍。
小车的所有详细代码和成品演示在我的资源中,大家可以下载提取(包括代码,引脚图,效果演示):https://download.csdn.net/download/nidie508/11458242
那么,开始吧。
准备工作:
1.首先配置好keil5 c8t6模板(温馨提示:多看看模板的核心驱动,包括sys.h delay.h usart.h等,这些核心文件一定要保证准确无误!)
2.了解开发板和确保开发板无误
3.熟悉怎么用转串口模块和st-link将驱动下载到开发板上
下图示为c8t6开发板的引脚图
1.超声波避障功能
我们设计的思路如下,用三个超声波来避障。为什么用三个超声波呢?我们想的是如果用舵机的话,小车在行进过程中并不好判断,只能将车停下,舵机转动来检测哪个方向无障碍物,而用3个超声波不仅可以在行进中判断,也可以让小车没有停下的动作,显得整个过程比较流畅。这就是我们使用三个超声波的原因了。
我们使用的是定时器二的通道一,通道二,通道三来进行输入捕获的,那么,第一个问题就来了。
我们在写超声波代码时,用一个超声波先测试,发现超声波测试的并没有问题 ,串口显示的数据也并没有问题。但是,但是,在用三个超声波同时测试是,却发现数据显示的总有问题,输出的数据总是毫无规律,且数字都非常大,我们就在想是什么问题。
下面是三个超声波控制的代码:
//main.c
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "dianji.h"
#include "hcsr.h"
u32 DIS_Init(u8 STA,u16 VAL)
{
u32 temp;
u32 lenth;
if((*STA)&0X80)//成功捕获到了一次高电平
{
temp=STA&0X3F;
temp*=65536; //溢出时间总和
temp+=VAL; //得到总的高电平时间
lenth=temp*0.017; //计算长度
STA=0; //开启下一次捕获
}
return lenth;
}
extern u8 TIM2CH2_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH2_CAPTURE_VAL; //输入捕获值
extern u8 TIM2CH3_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH3_CAPTURE_VAL; //输入捕获值
extern u8 TIM2CH4_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH4_CAPTURE_VAL; //输入捕获值
int main(void)
{
u32 temp=0;
u32 length1;
u32 length2;
u32 length3;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化 //初始化与LED连接的硬件接口
TIM_PWM1_Init();//10000-1,36-1);//不分频。PWM频率=72M/(0.036M)=2Khz
Echo1=0;
Echo2=0;
Echo3=0;
HCSR04_Init(0XFFFF,72-1);//以1Mhz的频率计数
/*while(1)
{
}*/
while(1)
{
Echo3=1;
delay_us(20);
Echo3=0;
length1=DIS_Init(&TIM2CH4_CAPTURE_STA,TIM2CH4_CAPTURE_VAL);
delay_ms(1000);
Echo1=1;
delay_us(20);
Echo1=0;
length2=DIS_Init(&TIM2CH2_CAPTURE_STA,TIM2CH2_CAPTURE_VAL);
delay_ms(1000);
Echo2=1;
delay_us(20);
Echo2=0;
length3=DIS_Init(&TIM2CH3_CAPTURE_STA,TIM2CH3_CAPTURE_VAL);
printf("%d %d %drn",length1,length2,length3);
// GO();
delay_ms(500);
}
}
hcsr.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "hcsr.h"
void HCSR04_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<0; //TIM2时钟使能
RCC->APB2ENR|=1<<2; //使能PORTA时钟
RCC->APB2ENR|=1<<3; //使能PORTB时钟
GPIOA->CRL&=0XFFFF000F;//PA1 清除之前设置
GPIOA->CRL|=0X00008880;//PA1输入
// GPIOA->ODR|=0<<0;
GPIOA->ODR|=0<<1; //PA1下拉
GPIOA->ODR|=0<<2;
GPIOA->ODR|=0<<3;
GPIOB->CRL&=0X000FFFFF;//PB7清除之前设置
GPIOB->CRL|=0X33300000;//PB7推挽输出
GPIOB->ODR|=1<<7; //PB7 输出高
GPIOB->ODR|=1<<6;
GPIOB->ODR|=1<<5;
TIM2->ARR=arr; //设定计数器自动重装值
TIM2->PSC=psc; //预分频器
TIM2->CCMR1|=1<<8; //CC2S=01 选择输入端IC1映射到TI1
TIM2->CCMR1|=1<<12; //IC2F=0001 配置滤波器 以Fck_int采样,两个事件后有效
TIM2->CCMR1|=0<<10; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<5; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<4; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->CCMR2|=1<<0; //CC2S=01 选择输入端IC1映射到TI1
TIM2->CCMR2|=1<<4; //IC2F=0001 配置滤波器 以Fck_int采样,两个事件后有效
TIM2->CCMR2|=0<<2; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<9; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<8; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->CCMR2|=1<<8; //CC2S=01 选择输入端IC1映射到TI1
TIM2->CCMR2|=1<<12; //IC2F=0001 配置滤波器 以Fck_int采样,两个事件后有效
TIM2->CCMR2|=0<<10; //IC2PS=00 配置输入分频,不分频
TIM2->CCER|=0<<13; //CC2P=0 上升沿捕获
TIM2->CCER|=1<<12; //CC2E=1 允许捕获计数器的值到捕获寄存器中
TIM2->DIER|=1<<2; //允许捕获中断
TIM2->DIER|=1<<3; //允许捕获中断
TIM2->DIER|=1<<4; //允许捕获中断
TIM2->DIER|=1<<0; //允许更新中断
//TIM2->CR1|=0X01; //使能定时器2
MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2
}
u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH1_CAPTURE_VAL; //输入捕获值
u8 TIM2CH2_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH2_CAPTURE_VAL; //输入捕获值
u8 TIM2CH3_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH3_CAPTURE_VAL; //输入捕获值
u8 TIM2CH4_CAPTURE_STA=0; //输入捕获状态
u16 TIM2CH4_CAPTURE_VAL; //输入捕获值
//定时器2中断服务程序
void TIM2_IRQHandler(void)
{
u16 tsr;
tsr=TIM2->SR;
if((TIM2CH4_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH4_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH4_CAPTURE_VAL=0XFFFF;
}else TIM2CH4_CAPTURE_STA++;
}
}
if(tsr&0x10)//捕获1发生捕获事件
{
if(TIM2CH4_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH4_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH4_CAPTURE_VAL=TIM2->CCR4;//获取当前的捕获值
TIM2->CCER&=~(1<<13); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH4_CAPTURE_VAL=0;
TIM2CH4_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2->CNT=0; //计数器清空
// TIM2CH4_CAPTURE_VAL=TIM2->CCR4;
TIM2->CCER|=1<<13; //CC1P=1 设置为下降沿捕获
TIM2->CR1|=0x01;
}
}
}
if((TIM2CH2_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH2_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH2_CAPTURE_VAL=0XFFFF;
}else TIM2CH2_CAPTURE_STA++;
}
}
if(tsr&0x04)//捕获1发生捕获事件
{
if(TIM2CH2_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH2_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH2_CAPTURE_VAL=TIM2->CCR2;//获取当前的捕获值
TIM2->CCER&=~(1<<5); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH2_CAPTURE_VAL=0;
TIM2CH2_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2->CNT=0; //计数器清空
TIM2->CCER|=1<<5; //CC1P=1 设置为下降沿捕获
TIM2->CR1|=0x01;
}
}
}
if((TIM2CH3_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(tsr&0X01)//溢出
{
if(TIM2CH3_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH3_CAPTURE_VAL=0XFFFF;
}else TIM2CH3_CAPTURE_STA++;
}
}
if(tsr&0x08)//捕获1发生捕获事件
{
if(TIM2CH3_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH3_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM2CH3_CAPTURE_VAL=TIM2->CCR3;//获取当前的捕获值
TIM2->CCER&=~(1<<9); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH3_CAPTURE_VAL=0;
TIM2CH3_CAPTURE_STA=0X40; //标记捕获到了上升沿
TIM2->CNT=0;
// TIM2CH3_CAPTURE_VAL=TIM2->CCR3; //计数器清空
TIM2->CCER|=1<<9; //CC1P=1 设置为下降沿捕获
TIM2->CR1|=0x01;
}
}
}
TIM2->SR=0;//清除中断标志位
}
hcsr.h
#ifndef __HSCR04_H
#define __HSCR04_H
#include "sys.h"
#define Echo1 PBout(7) // PB7
#define Echo2 PBout(6) // PB7
#define Echo3 PBout(5) // PB7
void HCSR04_Init(u16 arr,u16 psc);
#endif
最后发现,输入捕获代码中,中断服务函数中,TIM2->CNT不能清零,因为初始化中,用的是同一个定时器的通道2,3,4。如果每一次中断函数的某个通道函数将TIM2->CNT清0,那么其他通道的记录的TIM2->CNT的值就发生变化,从而导致了各种各样的情况。
问题二就是从串口读取数据,当时挺崩溃的。首先是keil5模板问题,当时串口怎么都显示不出来数据,我们当时都很疑惑。一直反复的看代码,考虑各种情况,但还是显示不出来数据。一开始我们以为是超声波接受的反馈的数据不满足某个条件,所以没有显示。到后面查来查去,又在想是开发板并没有给电压?超声波集体歇B?最终发现,usart.c文件写的串口不是我们接线串口所对应的。当时心态挺炸的,改了之后终于可以测试数据了。
问题三是我们在没接电机驱动之前,超声波接收的数据无论准不准确,最起码能接收到,可是接了电机驱动却发现每次返回的值都是0。我们当时并没有找到原因,又在猜想是不是电机用到的定时器对超声波的定时器有影响?又在想是不是外部电子设备把信号影响了?又在想各种各样的问题?
最终解决的方法是,串口模块给的电压不够。。。(我也不知道这样形容的对不对,但感觉就是)。为什么这样说呢?当时一开始我想的是,超声波的模块在用转串口模块给其供电时,是不是超声波模块的VCC和GND引脚并没有电压,从而导致信号发射不出去。于是我用万用表测试两侧电压,发现是5V没错。又在想是不是发出信号并没有发去,于是我让TRIG引脚一直为1,测试TRIG电压发现为2.6v左右。在此之后,我又用12v的电池降压给开发板供电,发现TRIG电压为3.3V左右。我想验证,是不是电压问题而导致的接收数据为零。所以我想用电池给开发板供电,然后打开串口监视器看数据。但是要想启用串口监视器,必须要用串口给开发板供电。最后我们选用了蓝牙模块,从手机的接收器来观察数据。终于,果然是电压的问题,数据成功出现了!!!(哭了)
2.用PWM调控电机速度
这一过程能稍微简单一些,就是一开始还是电压的问题,四个轮子根本带不起来。反复查看代码后并没有太多问题,可是轮子还是不转,但是发现电机嗡嗡响,去掉一个电机后,将速度调大,发现两个轮子缓缓的动了。验证时,我们将轮子速度调小,发现轮子不转,证明了代码并无问题。
pwm控制部分代码:
#include "dianji.h"
#include "sys.h"
#include "delay.h"
void TIM_PWM1_Init(u16 arr,u16 psc)
{
//此部分需手动修改IO口设置
RCC->APB1ENR|=1<<1; //TIM3时钟使能
RCC->APB2ENR|=1<<2; //GPIOA使能
RCC->APB2ENR|=1<<3; //GPIOB使能
RCC->APB2ENR|=1<<4;
GPIOA->CRL&=0X00FFFFFF; //PA(7)PA(6)做复用,PA(3)是BIN1 PA(4)BIN2 PA(5)STBY
GPIOA->CRL|=0XBB000000;
//GPIOA->ODR|=1<<7;
//GPIOA->ODR|=1<<6;
GPIOC->CRH&=0X000FFFFF;
GPIOC->CRH|=0X33300000;
GPIOB->CRL&=0XFFFFF000; //PB(0)是AIN1 PB(1)是AIN2
GPIOB->CRL|=0X00000333;
TIM3->ARR=arr; //设定计数器3自动重装值
TIM3->PSC=psc; //预分频器设置
TIM3->CCMR1|=6<<4; //CH1 PWM2模式
TIM3->CCMR1|=1<<3; //CH1预装载使能
TIM3->CCMR1|=6<<12; //CH2 PWM2模式
TIM3->CCMR1|=1<<11; //CH2预装载使能
TIM3->CCER|=1<<0; //OC1 输出使能
TIM3->CCER|=1<<4; //OC1 输出使能
// TIM3->BDTR|=1<<15; //MOE 主输出使能
TIM3->CR1=0x0080; //ARPE使能
TIM3->CR1|=0x01; //使能定时器3
RCC->APB2ENR|=1<<11; //TIM1定时器使能
GPIOA->CRH&=0XFFFF0FF0;
GPIOA->CRH|=0X0000B00B;
//GPIOA->ODR|=1<<8;
//GPIOA->ODR|=1<<11;
GPIOB->CRH&=0X0000FFFF;
GPIOB->CRH|=0X33330000;
TIM1->ARR=arr; //设定计数器自动重装值
TIM1->PSC=psc; //预分频器设置
TIM1->CCMR1|=6<<4; //CH1 PWM2模式
TIM1->CCMR1|=1<<3; //CH1预装载使能
TIM1->CCMR2|=6<<12; //CH4 PWM2模式
TIM1->CCMR2|=1<<11; //CH4预装载使能
TIM1->CCER|=1<<0; //OC1 输出使能
TIM1->CCER|=1<<12; //OC4 输出使能
TIM1->BDTR|=1<<15; //MOE 主输出使能
TIM1->CR1=0x0080; //ARPE使能
TIM1->CR1|=0x01; //使能定时器1
STBY=1;
STBY1=1;
}
void GO(u16 a,u16 b)
{
AIN1=0; //AIN1,BIN1,AIN2,BIN2控制轮子方向
AIN2=1;
BIN1=0;
BIN2=1;
AIN3=1;
AIN4=0;
BIN3=1;
BIN4=0;
TIM3->CCR1=a;//右上,控制速度
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
//delay_ms(2000);
}
void STOP(void)
{
AIN1=0;
AIN2=0;
BIN1=0;
BIN2=0;
AIN3=0;
AIN4=0;
BIN3=0;
BIN4=0;
}
void BACK(u16 a,u16 b)
{
AIN1=1;
AIN2=0;
BIN1=1;
BIN2=0;
AIN3=0;
AIN4=1;
BIN3=0;
BIN4=1;
TIM3->CCR1=a;//右上
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
// delay_ms(1000);
}
void RIGHT(u16 a,u16 b)
{
AIN1=1;
AIN2=0;
BIN1=0;
BIN2=1;
AIN3=0;
AIN4=1;
BIN3=1;
BIN4=0;
TIM3->CCR1=a;//右上
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
// delay_ms(1000);
}
void LEFT(u16 a,u16 b)
{
AIN1=0;
AIN2=1;
BIN1=1;
BIN2=0;
AIN3=1;
AIN4=0;
BIN3=0;
BIN4=1;
TIM3->CCR1=a;//右上
TIM3->CCR2=b;//左上
TIM1->CCR1=a;//右下
TIM1->CCR4=b;//左下
// delay_ms(1000);
}
3.蓝牙控制
蓝牙控制这一内容是小伙伴写的,大概内容和串口一章内容相似,就是多做出了判断,即没通过蓝牙输入数据来改变小车此时的模式,在蓝牙控制模式下,也可以相对应用蓝牙控制小车的方向。我们用的仍是串口一来控制蓝牙,为什么这样做?一是方便,二是就如前面所说,电机和超声波的分压严重,在电脑上上的串口监视器得出的数据并不准确,所以还不如用蓝牙来看数据,于是我们直接用了串口一来和蓝牙连接。初始化问题并不需要要修改,直接调用uart()函数即可,在中断控制代码下加入接收数据而触发的各种就好了。
蓝牙控制部分代码:
void USART1_IRQHandler(void)
{
char res;
if(USART1->SR&(1<<5))
{
res=USART1->DR;
printf("rn%d",res);
if(res==50) //输入2为前进
{
GO(300,300);
printf("rnGo Stright");
}
else if(res==56) //输入8为后退
{
BACK(300,300);
printf("rnGo Back");
}
else if(res== 52) //输入4为左转
{
LEFT(300,300);
printf("rnTurn Left");
}
else if(res==54) //输入6为右转
{
RIGHT(300,300);
printf("rnTurn Right");
}
else if(res==53) //输入5为停止
{
STOP();
printf("rnStop");
}
else if(res=='9') //进入超声波避障模式
{
opq=0;
}
}
}
3.走矩形
走矩形功能也是小伙伴写的,设计思路大致是通过蓝牙输入长和宽,小车通过接受的数据进行矩形运动。在这唯一遇到的问题就是小车的速度问题和转向时间。这个是他们弄得,自我感觉还是很不好调的,因为要考虑电池可供电压,小车行驶的惯性,不同地面的摩擦程度等等。考虑的方面比较多,根据不同的情况可能还要修改小车的速度和转向时间。我们就是在光滑地砖上测得,摩擦力应该是比较小的。
走矩形代码:
void Juxing()//小车的矩形运动函数
{
u8 chang,kuan,x,y;//chang、kuan分别是小车要走矩形的长和宽的值
delay_ms(200);
printf("Chang:rn");
while(1)
{
printf("Input Chang:rn");
delay_ms(200);
if(USART1->SR&(1<<5))//当串口接收到消息后跳出循环
{
chang=USART1->DR-'0';//将字符型的数据转换为整型数据
break;
}
}
printf("长:%drn",chang);//打印串口接收到的数据
USART1->SR=0;//串口的接收标志位清零,为下一次接收宽度数据做准备
while(1)
{
printf("Input Kuan:rn");
delay_ms(200);
if(USART1->SR&(1<<5))
{
kuan=USART1->DR-'0';//当串口接收到数据后跳出循环
break;
}
}
printf("宽:%drn",kuan);//打印宽度数据
//当前小车速度为0.25米每秒则小车每走1cm要用40ms所以以1cm为单位每走1cm耗时40ms用for函数驱动小车运动
for(x=0;x
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
for(y=0;y
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
for(x=0;x
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
for(y=0;y
{
GO(300,300);
delay_ms(40);
}
RIGHT(300,300);//右拐
delay_ms(785);
STOP();
我的想法:
1.自己所学的东西并没有所有吸收,有些东西可能单个实验看着并不重要,一旦做项目了,有些东西起着至关重要的作用。比如,delay_ms()函数,当时我写的一句代码是delay_ms(2000),本想着意思是延迟2s,但是代码并不可行,因为delay_ms()函数设定的参数是小于1864,而2000显然已经超过了这个范围,在串口监视器显示的速度是不准确的。这里就是一个很细微的东西,但是自己并不知道,导致了一系列的错误。
2.和Arduino板子不同的是,不,应该说库函数和寄存器的区别其实还挺大的。起初在学32时,我觉得库函数和寄存器其实没多大的区别,感觉两者都不是和复杂,唯一的就是一些初始化函数,库函数可以直接调用而寄存器你得自己配置,反而成为一个很繁琐的过程。但是小车项目的整个过程彻底改变了我的想法,首先就是四个输入捕获通道问题,我不清楚库函数有没有,但是用寄存器配置时,我真的学的了一些东西。这些东邪可能不只是知识层面上的,更多的是一个训练总体观的过程,即要考虑任何东西。我相信如果只是调用那些已经封装好的函数,可能你应该思考的东西人家已经帮你弄好了,错失了关键点。
3.全面的考虑所有问题。学嵌入式可能和纯软不同,它既要考虑软件代码,又要考虑硬件搭建和硬件的可用,这是一个挺繁琐的过程。在做小车时,真正体会到了这点,从软件一步步排除,再到硬件逐个逐个测电压,一直在排除排除找的真真的解决方法。突然想起几个月前,实验室面试时,我问学长在测试过程中你们应该如何解决问题,他说第一方向是确保软件无误后,接下来开始看硬件。现在我觉得,我能体会到一点了。一定要先排查出软件,接下来入手硬件。
4.沟通与合作。真的随着年龄的增长,自己感觉自己变化挺大的。还记得初中时那种盛气凌人的状态,现在想想真的是井底之蛙那时候得我。现在我有了自己的队友,在遇到问题时,可以相互交流,相互讨论,考虑问题更加全面,学到的东西也更多,弥补自己的短处(感觉真的好讽刺啊,小学就在写,直到大学才真正明白。。。)而且最重要的一点是,我能慢慢听的进去和听懂他人的想法(意思)了,在高中时,一直都在想和那些逻辑思维强的人差距在哪儿,现在我觉得不仅是天赋,强理解力也是非常重要的一点。
5.保持良好的心态。中间有两天,心态真的炸了,小车进度一直被卡,shilian,感觉自己好废物。我还是没有做到,做事认真,乐观,这个我还得好好锻炼吧,以后还有很多的事,这就是冰山一角。
6.我错了,小淳。我还是太任性了,对不起,我很想你。
举报