怎样把正点儿童原子能实验室的输入捕获实验改成测量编码器转速实验

两轮自平衡小车DIY进程贴-电子产品世界论坛
两轮自平衡小车DIY进程贴
欢迎大家来本帖交流,本人长期在线,ID就是QQ号,有任何问题可以跟帖或者私聊,我会将我我所能做到的一切资源包括代码完全开源,尽可能的仔细讲解,大家能从我的帖子中学到哪怕一点点的知识就是对我最大的肯定了,最后希望大家在最后几天好好完成小车作品,我与你们同在,愿大家都能拿到王叔叔的补助。
& & & & & & & & & & & & & & & & & 目录&list
& & & & 实验一 小车开箱与组装
直接来组装吧,大家都开箱很多了,我第一次拿回来的时候由于与电机坏了一个所以杨哥有给我换了第二个,所以这是第二个了,这里要十分感谢@777-passoni,嘿嘿!
我们先把两个轮子上上去,这样比较容易固定住,然后把四个双通铜柱装上,记住,是双通,双通,双通,重要的话说三遍,然后装上电池,这个电池还是很给力的,上面标称1300ma,比之前说明的还要大一些,事实证明还是可以玩很久的。
& & 然后我们将OLED焊上排针,插在主控板上,其实我们可以匀一颗螺钉用来固定OLED,然后我们把主控板放在电池上面,对准孔位,找准机会插上四个单通铜柱,最后用螺钉固定上亚克力板,就成了!
整个小车还是很小巧的,接下来附上完整图:
& & &实验二 &小车测试与keil的安装程序下载
小车安装完毕之后,我们就可以插上电源线了,就是红色的那个超难插进去的那个接头,可能是方式松掉的设计吧,上好电后就可以波动那个铁的电源开关,按下白色按键,若果没有什么意外的话,小车这和时候就可以平衡了。不然就是你的车有些其他的问题了,小车的OLED共显示了6排信息如下:
第一排&的Kalman&问一下度娘就知道是卡尔曼的意思,当然初始的是方式1,即dmp方式
第二排显然就是温度了;
第三排第四排就是左右轮编码器的值了
第五排的就是当前电压值了,若果看到它低于12v,就说明你的小车已经饿了,你懂的!
第六排就是当前小车的偏角了
& &这些我们先不管了,今后再讨论,然后我们现在来说说keil5怎么去安装吧,群里面经常有人安装出现各种问题,我这里有一些方法希望能帮到大家,由于本人热衷于装系统,从xp到win10,基本上一个月会换一次,多的时候是按星期算的(ps现在的win10&10240RTM版本非常的好用,兼容性好切稳定)偏题了,回归正题,如果我每次装完系统都配置一下,那我现在就不会在这里发帖了,早疯了估计,所以我都会将我安装好的keil保存在硬盘里就好,随时可以拿出来用的,现在我将我的keil5上传到云盘了,大家下载下来就直接能用了,不需要在经历那么多繁琐的步骤了,希望大家不要再为keil而烦恼了!
keil5:链接: 密码:hyt6
下载完了后解压在D盘根目录,如果没有解压在D盘的话就需要改一下这里
先点一下紫色框框选择的按钮,然后若果你的软件在c盘,就把绿色框框选中的地方改成C:\Keil\Keil_v5\ARM\ 就行了,接下来需要用注册机破解一下 注册机:
注册方法:运行keygen.exe(需要先解压,如果杀毒软件报毒,则暂时退出杀毒软件先),在CID栏,输入MDK License Management里面的CID号,点击Genrate,即可获得注册码,输入MDK License Management里面,完成注册。MDK License Management在keil左上角的File里面,这里就不再啰嗦了,至此,大家打开只需要两分钟的时间就可以搞定keil所有的安装问题了!
& 接着简单的说一下程序的下载吧,其实就用提供的串口线就挺好的了,下面附上下载的方法 :
对了,下载的时候千万不要给串口线的vcc,gnd供电哦,那连个角是悬空的,切记,不然电脑坏了找谁修去啊!
& &刚开始下载的时候我们会遇到这样的问题,那就是串口下载的插口真的是太紧了,插进去容易拔出来难(嘿嘿),于是我就试试稍微的将串口线碰到插口,发现这样也能的,而且下载完后红色开关可以不用拨到ON档就可以直接运行平衡了,这样我们下次下程序是就方便多了,特别是在调试pid的时候不用拔来拔去了!
ps(为什么排好的文字再次编辑时全变了,太忧桑了 ,可惜了在云笔记上的精心的排版了,争取再怒更两帖。)
& & & & & & & & & &&实验三 & 蓝牙控制及app微改动
& &今天我们继续来看一下分析下一蓝牙的相关知识,首先,我们先来看看蓝牙是如何控制小车的,以下这段代码就是小车接收端的蓝牙控制程序:
#include "usart3.h"
u8 Usart3_R
u8 mode_data[8];
u8 six_data_1[4]={6,5,4,0};
u8 six_data_2[4]={4,5,6,0};
/**************************************************************************
函数功能:串口3初始化
入口参数:pclk2:PCLK2 时钟频率(Mhz)
bound:波特率
**************************************************************************/
void uart3_init(u32 pclk2,u32 bound)
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
//得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa&&=4;
mantissa+=
RCC-&APB2ENR|=1&&3;
//使能PORTB口时钟
RCC-&APB1ENR|=1&&18;
//使能串口时钟
GPIOB-&CRH&=0XFFFF00FF;
GPIOB-&CRH|=0X00008B00;//IO状态设置
GPIOB-&ODR|=1&&10;
RCC-&APB1RSTR|=1&&18;
//复位串口1
RCC-&APB1RSTR&=~(1&&18);//停止复位
//波特率设置
USART3-&BRR= // 波特率设置
USART3-&CR1|=0X200C;
//1位停止,无校验位.
//使能接收中断
USART3-&CR1|=1&&8;
//PE中断使能
USART3-&CR1|=1&&5;
//接收缓冲区非空中断使能
MY_NVIC_Init(3,3,USART3_IRQn,2);//组2,最低优先级
/**************************************************************************
函数功能:串口3接收中断
入口参数:无
**************************************************************************/
void USART3_IRQHandler(void)
if(USART3-&SR&(1&&5))//接收到数据
static int uart_receive=0;//蓝牙接收相关变量
uart_receive=USART3-&DR;
if(uart_receive&10)
mode_data[0]=uart_
if((mode_data[0]==six_data_2[0]
&&mode_data[1]==six_data_2[1]
&&mode_data[2]==six_data_2[2]
&&mode_data[3]==six_data_2[3])
||(mode_data[0]==six_data_1[0]
&&mode_data[1]==six_data_1[1]
&&mode_data[2]==six_data_1[2]
&&mode_data[3]==six_data_1[3]))
Flag_Stop=!Flag_S
mode_data[0]=0; mode_data[1]=0; mode_data[2]=0; mode_data[3]=0;
if(uart_receive==0x00) Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////刹车
if(uart_receive==0x01) Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;//////////////前
if(uart_receive==0x05) Flag_Qian=0,Flag_Hou=1,Flag_Left=0,Flag_Right=0;//////////////后
else if(uart_receive==0x02||uart_receive==0x03||uart_receive==0x04)
Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;
else if(uart_receive==0x06||uart_receive==0x07||uart_receive==0x08)
Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;
mode_data[7]=mode_data[6];
mode_data[6]=mode_data[5];
mode_data[5]=mode_data[4];
mode_data[4]=mode_data[3];
mode_data[3]=mode_data[2];
mode_data[2]=mode_data[1];
mode_data[1]=mode_data[0];
这段代码可以在HARDWARE文件夹下面的串口3『usart3.c』上找到,我们收手机上的app是一个圆圈可以向四周拨动,从代码上我们可以得到以下示意图:
从图上我们可以看到这个元被划分为八个部分,当我们在手机app上将圆圈波动到相应的方位,就会发送相应的指令,而小车的接收相应的指令后就可以控制完成动作了,从第63行到第69行中我们可以看到,当小车收到0x01时,小车前进标志位置1,其余置0,便是完成前进动作;当小车收到0x05时,小车后退标志位置1,其余置0,便是完成后退动作;当小车收到0x02、0x03、0x04时,小车右转标志位置1,其余置0,便是完成右转动作;当小车收到0x06、0x07、0x08时,小车左转标志位置1,其余置0,便是完成左转动作;这也就解释了为什么我们向右上方或者右下方拨动时依然是右转的。
接下来我们分析一下为什么在底部向右滑动会关闭电机,而向左滑动就会重新启动,这段代码其实很好看懂的,大家看看第51行到第60行代码中,six_data_2数组中的4个数据做与运算,当six_data_2或者six_data_1数组中的数据被小车同时接收到数组中
的四个数据,那么 Flag_Stop=!Flag_Stop(第60行),每收到一次就取反一次,而在第4行和第5行中定义了这两个数组为4、5、6、0,因此是底部的三个滑动过去就刚好,如果你改成2、3、4、0,那就是从右侧向下滑动了,所以,其实我认为这里并不需要定义两个数组,定义一个数组,滑动一次关闭电机,再来一次,就开启,亲测可用,大家可以试试!
然后贴出我的APP图片:
这里有两处小小的突变和文字的改动,打开小车,在蓝色led闪烁的时候,手机端按菜单键链接蓝牙,然后可以输入配对密码默认1234,连接上就可以控制了,接下来奉上小车的首秀测试视频分享给大家。
& & & & & &实验四
PID并且尝试想让小车静止不动,论坛里面也有很多人来询问,群里面更是讨论的热火朝天。
所以这一次进程我想来一发大家十分关心的PID参数的整定的一点个人见解,希望大家可以互相交流交流,大家有什么疑问或者我说的不正确的地方可以跟帖我们一起来讨论,谢谢。
在讲PID参数的整定之前,我们先来看看什么叫做PID参数:
P 就是比例,就是输入偏差乘以一个系数;
I 就是积分,就是对输入偏差进行积分运算;
D就是微分,就是对输入偏差进行微分运算;
而输入偏差又是什么鬼呢?
输入偏差是被调量减去设定值的插值,当然前提是设定值是不变的。
并且在一个系统中可以只用到P,PI,PD,或者PID其中的一种,并不是完全的要用上的。
很显然,在这段直立环控制函数中:
int&balance(float&Angle,float&Gyro)
float&Bias,kp=50,kd=0.13;
Bias=Angle-4;
balance=kp*Bias+Gyro*
在Bias=Angle-4;这行代码中,Bias我们可以理解为“相对重心角”,也就是我们所说的当前角度相对重心所在角度的差值,即小车的输入偏差,而我们的重心角度即设定值;
接下来,我给大家扽想两篇非常好的文章,大家仔细阅读之后,一定会对PID整定有所悟吧
第一篇文章是让大家对PID有一个更加全面的认识,而且作者叙事的方式也十分幽默有趣,重点突出,大家可以从第二章开始看起。
这第二篇文档是由版主提供给大家的,我之所以拿出来,是希望引起大家的重视,因为这边文档里面讲的实在是太好了,对于平衡车的PID调试里面给出了详细的指导性意见。
当然了,如果大家认为读这两篇文档需要很长时间的话(ps其实大致的读一读也浪费不了太多时间),我还给大家准备了配套的视屏,是不是突然觉得我很贴心有木有啊。我相信大家看完这段短短半个小时的视频之后,一定会掌握一些基本的PID调试技巧的。
看不了全屏的点右下角的优酷就可以全屏了哈
&&& 什么,你说半个小时还是太长了怎么半?好吧,只要大家不觉得我讲的不好的话,可以抽出五分钟不到的时间listen一下我的个人看法咯。小车程序中有直立环、速度环、转向环,其中直立环和转向环用到了比例微分参数,速度环用到了比例积分参数。之前看到很多的朋友说,为什么我屏蔽了速度环转向环,直立环调的好好的,都快可以静止了,可是加上速度环之后,小车就疯了呢,素的,你的直立环确实调好了,然而你加上速度环的时候,有没有想过它会影响你的直立环,其实直立环和速度环是存在耦合关系的,因此我们不能够完全将他们分开来调整,我认为我们在调试直立环的时候,不应该直接屏蔽速度环,而是应该保留,不过转向环就无所谓了,可以屏蔽掉。在直立环中,我们用到了比例参数和微分参数,这是我们就可以先比例后微分,意思就是先让微分参数为0,慢慢增大比例参数到能立稳并来回摆动,在慢慢增大微分参数,直到小车在外力推动的情况下比较稳定不抖动,最后我们在根据小车运动现象在刚刚得到的数字附近进行粗调,那么怎么看现象粗调呢?
&&& 要想做到看着现象粗调,我们就必须得了解在直立环中的比例参数和微分参数到底代表着什么,又或者给整个平衡系统带来哪些影响。
比例参数在整个平衡系统中相当于倒立摆的回复力。这个参数需要大于重力加速度所产生的效果才能够使得车模保持直立。随着这个参数逐步增大,车模开始能够保持直立。该参数进一步加大,车模开始出现来回的摆动现象。
微分参数在整个平衡系统中相当于倒立摆中的阻尼力,它可以有效的抑制车模的摆动。当该参数过大时,会引起车模本身的震动。这是由于车模本身不是一个刚体,车体具有一定的共振频率。微分参数过大时会使得车模在电机的驱动下产生车体的共振。
知道了这些,我们就以大致的找到一组不错的参数。
&&& 另外,在直立环中,大家千万不要忽略这行代码:
&&&&&&& Bias=Angle-4;
&&&&这里的减4是我的小车的数字(ps是我放超声波的时候的),可能和你的不一样,又或者你动一下加个东西应为重心改变而不一样,重心一旦改变,需要改的就不仅仅是这里了,整的平衡的PID参数都需要做出相应的调整,不信大家可以看看接下来的视频:
&&& 那怎样找到重心呢,其实很简单,当你小车打开但是不开启平衡,将小车立于平处,能短暂的立者或者随时有可能向两边任何一方倾倒的时候,显示屏上所显示的角度,就是我们所需要减掉的,如果这种方法不好拿捏的话,还有方法二,那就是开启小车自平衡立于平地,这是小车微微来回摆动,oled屏幕上的角度数值会在三个连续的数之间来回的跳变,比如说 3,4,5,这个时候我们就取中间的,也就是4 。
&&& 说完了直立环,也只能是抛砖引玉一下了,具体还要考大家耐心调试了,毕竟速度环与直立环存在耦合,这里提醒一下同学们,在调速度环的时候,要先积分再比例,顺序和之前颠倒过来,并且调大速度环的时候可以等比例稍微调小一些直立环参数。
最后也希望大家能调试好属于自己的PID,真正玩转PID,不一定要调到静止,那很难做到,不仅仅只是让小车处于非欠阻尼状态下这么简单,除非地面阻力够大,像一下这段我拍的视频一样:
&&& 静止了,难道就一点最优吗,大家可以试试,跑起来就真的稳定吗,其实版主发货的时候的一组参数还是算还可以的啦
最后的最后,给大家一点小技巧,大家调试PID的时候频繁的插拔,波动开关,编译,并且很难拔出来,编译时间长,开关拨来拨去麻烦,调试一次一二十秒,你可能会问为什么不用像32战舰板一样一键下载运行呢,也可能吐槽为什么不能蓝牙在线调试PID呢?但是我们目前也只有这样的条件不是吗,因此我们得适应,还得心平气和的调试,所以我建议大家先不要用最新的代码,这样下载完之后不需要波动红色开个可以直接运行了,下次编译时也不需要,将下载软件的下载波特率调到最高,也就是460800,把编程后执行勾选上,下载好了就可以直接运行了,下载的时候不要把下载线插进去,稍微接触就好,这样就不要费很多时间去拔出来了,或许这些真的能在大家调试PID需要大量次数的修改程序时给大家节约点时间呢(ps用s—link的就当我没说)
提前祝大家玩转PID,我说的不对的地方,欢迎大家指正!
& & & & & & & & & & & &实验五&&&编码器控制
& & & &抱歉,这一次的更新让大家等待了这么久,好在我又回来了,今天我们来看看小车的编码器,来仔细分析一下我们是怎么控制电机的运转的,还是那句话,有什么说的不对的或者有好的见解地方希望大家跟帖指正交流。
& & & &首先,既然要来分析编码器,我们得先看看编码器长啥样在哪里不是、、、
& & & &在了解了编码器长啥样了之后,我们接下来看看PWM电机调速的工作原理:
& & & &对于电机的转速调整,我们是采用脉宽调制(PWM)办法,控制电机的时候,电源并非连续地向电机供电,而是在一个特定的频率下以方波脉冲的形式提供电能。不同占空比的方波信号能对电机起到调速作用,这是因为电机实际上是一个大电感,它有阻碍输入电流和电压突变的能力,因此脉冲输入信号被平均分配到作用时间上,这样,改变在输入方波的占空比就能改变加在电机两端的电压大小,从而改变了转速。简单点说就是输入方波占空比越大,小车速度越快,当我们将电源直接接在电机两端时,就相当于100%的占空比时,小车速度也就达到极致了。至于PWM信号打哪儿来的,我们稍后继续讨论!
& & & &当然了,这只是个示意图,看完了编码器上的接线说明,我们继续分析电机的驱动,因为我们这里的直流电机是需要足够的电流才可以驱动,而主控芯片的IO口所提供的电流显然达不到这个要求,所以我们就需要一块电机驱动芯片来对电流进行放大。我们这次的小车用的驱动芯片是TB6612FNG,它的原理图如下:
& & & & & & & & & & & & & & & & & & & & &
& &可能大家看到这个原理图会说,然而这并没有什么卵用,应为大家都知道它的实质其实基于MOSFET的H桥集成电路,其效率高于晶体管H桥驱动器。
& & & & & & &
& &大家都知道,电流经过直流电机的方向不一样是,转动的方向是相反的,那么假设S1和S4闭合时直流电机是正向转动的,那么S3和S2闭合时直流电机显然就是反向转动的。
void Set_Pwm(int moto1,int moto2)
if(moto1&0) AIN2=1, AIN1=0; else AIN2=0, AIN1=1;
PWMA=myabs(moto1);&
if(moto2&0) BIN1=0, BIN2=1; else BIN1=1, BIN2=0;
PWMB=myabs(moto2);&
这段代码就能很好的证明了。
那么接下来我们看看TB6612FNG这款驱动芯片的真值表吧:
& &首先,STBY很明显是使能端信号,高电平有效的。
& &其次,IN1和IN2是控制电流流过的方向,可以控制电机的正反转。
& &最后,PWM信号为低电平时,可以短暂制动,脉冲输入信号被平均分配到作用时间上,因此通过占空比的调节可以调整转速,前面也已经讲过了。
& &好了,前面的基本知识我们也已经讲解的比较仔细了,现在我们来解析一下代码是怎么写出来的。在motor.c这段代码中(ps:7月10号版本),我们可以很看到通过TIM3(定时器3)作为电机的PWM源信号产生,并且都工作早PWM1模式下,而在PWM1模式下,变化的计数器不断与CCRy进行比较。假设计数器采用加计数方式,当计数器的值TIMx_CNT小于TIMx_CCRy时,PWM信号保持高电平。当TIMx_CNT不断增加,直到大于TIMx_CCRy时,PWM变成低电平。TIMx_CNT继续增加,当达到TIMx_ARR预设的值后,复位返回0值。在第15行代码中:
& & & & & &TIM3-&ARR=//设定计数器自动重装值
& &可以看到,TIM3-&ARR决定了PWM信号的周期
& &在encoder.c中,TIM2和TIM4被初始化为编码器接口模式
int Read_Encoder(u8 TIMX)
int Encoder_TIM;
switch(TIMX)
Encoder_TIM= (short)TIM2 -& CNT;
TIM2 -& CNT=0;
Encoder_TIM= (short)TIM3 -& CNT;
TIM3 -& CNT=0;
Encoder_TIM= (short)TIM4 -& CNT;
TIM4 -& CNT=0;
Encoder_TIM=0;
return Encoder_TIM;
& &在这段程序中,很容易看出来,TIM2&-&&CNT决定了PWM信号的占空比,也就是决定了速度,因此,返回值Encoder_TIM如注释所说为速度值。
通过这里的Read_Encoder函数,我们就可以的到左右轮的编码值了,这个可以在最重要的控制文件control.c中的第13行和第17行中看到:
Encoder_Right=Read_Encoder(4);
Encoder_Left =-Read_Encoder(2);
& &得到的左右轮编码值会作为速度PI控制函数的入口参数。
& &在上位机中,我们得到的左右轮编码值空转时都接近600左右,基本保持一致:
& &这次的编码器实验我没能写的太清楚,可能自己理解的还不够透彻,有什么问题希望大家不吝赐教,谢谢。
大家都来这里交流交流啊!
&&&&&&&&&&&&&&&&&& 实验六 2.4G无线通信控制小车
&&&&& 本次实验我们来简要的讨论如何利用nrf24l01+来控制小车的运转,因为后面我们会重点关注体感小车的制作,所以本节主要以程序分析为主,稍后会奉上接受发送端源代码以及运行视频,谢谢大家关注和指正。
首先,我们要准备的工具有两块nrf24l01+芯片,一块stm32的最小系统板,stm32的开发板也行,我用的就是正点原子的战舰stm32mini板。有了这个我们就可以看我们的实验了。因为stm32mini板和小车都带有nrf24l01+的接口,所以这里就不讲芯片原理图和接口配置了,大家的如果不一样的话改改IO口的配置就好了嘛。
我们先看看接收端(也就是小车)&的程序,小车的接收程序显然就在24l01.c里面:
if(mode==0)//RX模式
RX_Mode();
if(NRF24L01_RxPacket(tmp_buf)==0)//一旦接收到信息,则显示出来.
printf("%d\r\n",tmp_buf[1]);
if(tmp_buf[1]&120)Flag_Left=1,Flag_Right=0;
else if(tmp_buf[1]&60)Flag_Left=0,Flag_Right=1; else Flag_Left=0,Flag_Right=0; }else delay_us(100); }; }
&&&&&&& 这就是接收端的控制核心代码了,在nrf24l01.c里面的169到183行。
从程序中我们清楚的看到,控制小车左右的是接收到的tmp_buf[1]的值决定的,当tmp_buf[1]&60时,Flag_Right=1,那么右转。tmp_buf[1]&120时,Flag_Left=1,那么左转。
&&&&&& 我们做如下修改,修改后代码如下:
if(mode==0)//RX模式
RX_Mode();
//while(1)
if(NRF24L01_RxPacket(tmp_buf)==0)//一旦接收到信息,则显示出来.
printf("%d\r\n",tmp_buf[1]);
if(tmp_buf[1]==30)
{Flag_Left=0,Flag_Right=0,Flag_Qian=0,Flag_Hou=0;}
if(tmp_buf[1]==40)
{Flag_Left=0,Flag_Right=0,Flag_Qian=1,Flag_Hou=0;}
if(tmp_buf[1]==50)
{Flag_Left=0,Flag_Right=0,Flag_Qian=0,Flag_Hou=1;}
if(tmp_buf[1]==60)
{Flag_Left=1,Flag_Right=0,Flag_Qian=0,Flag_Hou=0;}
if(tmp_buf[1]==70)
{Flag_Left=0,Flag_Right=1,Flag_Qian=0,Flag_Hou=0;}
delay_us(100);
&&&&&& 在这里,我们注释掉了while(1)语句,不注释的话显示屏就不会显示,然后我还将接受控制函数稍作修改,为后面的体感小车做铺垫。
最后我们移步到Minibalance.c(7.10更新的最新代码)中看看,
首先将第38行的&“NRF24L01_Init();&”NRF24L01模块初始化函数取消注释;
然后将第40行的“NRF24L01_FindMyself();&”NRF24L01自检程序取消注释;
首先将第45行的&“NRF24L01();&”&&&&&NRF24L01模块收发函数取消注释;
好了,接收端函数就先讲这些了,接下来我们看看发送端程序:
&&&&&&& 发送端程序我采用的是在正点原子的stm32系列教程中关于无线通信实验的例程的基础上进行修改的,现在简要说下主要思想:
if(NRF24L01_TxPacket(tmp_buf)==TX_OK)
key=KEY_Scan(0);
if(key==KEY0_PRES)
tmp_buf[1]=70;
else if(key==KEY1_PRES)
tmp_buf[1]=60;
else tmp_buf[1]=30;
LED0=!LED0;
delay_ms(1000);
&&&&&& 这里是两个很简单的if语句就可以实现我们的方向控制了。那么最后就放出整个发送端和接收端的程序打包给大家:
然后还有一段演示视频。
大家有什么好的建议或者疑问可以留言哦,我一定尽我所能给大家答复。
匿名不能发帖!请先 [
Copyright (C) 《电子产品世界》杂志社 版权所有STM32 实验21 红外遥控实验_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
STM32 实验21 红外遥控实验
&&STM32 实验
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
加入VIP
还剩6页未读,
定制HR最喜欢的简历
你可能喜欢21ic官方微信-->
ST MCU Finder
安装免费手机应用,
寻找理想的ST MCU
【连载】STM32开发指南--第十五章 输入捕获实验
高级技术员, 积分 617, 距离下一级还需 383 积分
高级技术员, 积分 617, 距离下一级还需 383 积分
高级技术员, 积分 617, 距离下一级还需 383 积分
高级技术员, 积分 617, 距离下一级还需 383 积分
本帖最后由 正点原子 于
19:04 编辑
第十五章 输入捕获实验上一章,我们介绍了STM32的通用定时器作为PWM输出的使用方法,这一章,我们将向大家介绍通用定时器作为输入捕获的使用。在本章中,我们将用TIM5的通道1(PA0)来做输入捕获,捕获PA0上高电平的脉宽(用WK_UP按键输入高电平),通过串口打印高电平脉宽时间,从本章分为如下几个部分:15.1 输入捕获简介15.2 硬件设计15.3 软件设计15.4 下载验证15.1 输入捕获简介 输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6和TIM7,其他定时器都有输入捕获功能。STM32的输入捕获,简单的说就是通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA等。本章我们用到TIM5_CH1来捕获高电平脉宽,也就是要先设置输入捕获为上升沿检测,记录发生上升沿的时候TIM5_CNT的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的TIM5_CNT值。这样,前后两次TIM5_CNT之差,就是高电平的脉宽,同时TIM5的计数频率我们是知道的,从而可以计算出高电平脉宽的准确时间。接下来,我们介绍我们本章需要用到的一些寄存器配置,需要用到的寄存器有:TIMx_ARR、TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1这些寄存器在前面2章全部都有提到(这里的x=5),我们这里就不再全部罗列了,我们这里针对性的介绍这几个寄存器的配置。首先TIMx_ARR和TIMx_PSC,这两个寄存器用来设自动重装载值和TIMx的时钟分频,用法同前面介绍的,我们这里不再介绍。再来看看捕获/比较模式寄存器1:TIMx_CCMR1,这个寄存器在输入捕获的时候,非常有用,有必要重新介绍,该寄存器的各位描述如图15.1.1所示:
图15.1.1 TIMx_CCMR1寄存器各位描述& && & 当在输入捕获模式下使用的时候,对应图15.1.1的第二行描述,从图中可以看出,TIMx_CCMR1明显是针对2个通道的配置,低八位[7:0]用于捕获/比较通道1的控制,而高八位[15:8]则用于捕获/比较通道2的控制,因为TIMx还有CCMR2这个寄存器,所以可以知道CCMR2是用来控制通道3和通道4(详见《STM32参考手册》290页,14.4.8节)。& && & 这里我们用到的是TIM5的捕获/比较通道1,我们重点介绍TIMx_CMMR1的[7:0]位(其实高8位配置类似),TIMx_CMMR1的[7:0]位详细描述见图15.1.2所示:
图15.1.2 TIMx_CMMR1 [7:0]位详细描述其中CC1S[1:0],这两个位用于CCR1的通道配置,这里我们设置IC1S[1:0]=01,也就是配置IC1映射在TI1上(关于IC1,TI1不明白的,可以看《STM32参考手册》14.2节的图98-通用定时器框图),即CC1对应TIMx_CH1。输入捕获1预分频器IC1PSC[1:0],这个比较好理解。我们是1次边沿就触发1次捕获,所以选择00就是了。输入捕获1滤波器IC1F[3:0],这个用来设置输入采样频率和数字滤波器长度。其中,是定时器的输入频率(TIMxCLK),一般为72Mhz,而则是根据TIMx_CR1的CKD[1:0]的设置来确定的,如果CKD[1:0]设置为00,那么=。N值就是滤波长度,举个简单的例子:假设IC1F[3:0]=0011,并设置IC1映射到通道1上,且为上升沿触发,那么在捕获到上升沿的时候,再以 的频率,连续采样到8次通道1的电平,如果都是高电平,则说明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话)。这样可以滤除那些高电平脉宽低于8个采样周期的脉冲信号,从而达到滤波的效果。这里,我们不做滤波处理,所以设置IC1F[3:0]=0000,只要采集到上升沿,就触发捕获。再来看看捕获/比较使能寄存器:TIMx_CCER,该寄存器的各位描述见图14.1.2(在第14章)。本章我们要用到这个寄存器的最低2位,CC1E和CC1P位。这两个位的描述如图15.1.3所示:
图15.1.3 TIMx_CCER最低2位描述& && & 所以,要使能输入捕获,必须设置CC1E=1,而CC1P则根据自己的需要来配置。接下来我们再看看DMA/中断使能寄存器:TIMx_DIER,该寄存器的各位描述见图13.1.2(在第13章),本章,我们需要用到中断来处理捕获数据,所以必须开启通道1的捕获比较中断,即CC1IE设置为1。控制寄存器:TIMx_CR1,我们只用到了它的最低位,也就是用来使能定时器的,这里前面两章都有介绍,请大家参考前面的章节。最后再来看看捕获/比较寄存器1:TIMx_CCR1,该寄存器用来存储捕获发生时,TIMx_CNT的值,我们从TIMx_CCR1就可以读出通道1捕获发生时刻的TIMx_CNT值,通过两次捕获(一次上升沿捕获,一次下降沿捕获)的差值,就可以计算出高电平脉冲的宽度。至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过输入捕获,来获取TIM5_CH1(PA0)上面的高电平脉冲宽度,并从串口打印捕获结果。下面我们介绍输入捕获的配置步骤:1)开启TIM5时钟,配置PA0为下拉输入。要使用TIM5,我们必须先开启TIM5的时钟(通过APB1ENR设置)。这里我们还要配置PA0为下拉输入,因为我们要捕获TIM5_CH1上面的高电平脉宽,而TIM5_CH1是连接在PA0上面的。 2)设置TIM5的ARR和PSC。在开启了TIM5的时钟之后,我们要设置ARR和PSC两个寄存器的值来设置输入捕获的自动重装载值和计数频率。 3)设置TIM5的CCMR1TIM5_CCMR1寄存器控制着输入捕获1和2的模式,包括映射关系,滤波和分频等。这里我们需要设置通道1为输入模式,且IC1映射到TI1(通道1)上面,并且不使用滤波(提高响应速度)器。 4)设置TIM5的CCER,开启输入捕获,并设置为上升沿捕获。TIM5_CCER寄存器是定时器的开关,并且可以设置输入捕获的边沿。只有TIM5_CCER寄存器使能了输入捕获,我们的外部信号,才能被TIM5捕获到,否则一切白搭。同时要设置好捕获边沿,才能得到正确的结果。 5)设置TIM5的DIER,使能捕获和更新中断,并编写中断服务函数因为我们要捕获的是高电平信号的脉宽,所以,第一次捕获是上升沿,第二次捕获时下降沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就会溢出,对溢出必须做处理,否则结果就不准了。这两件事,我们都在中断里面做,所以必须开启捕获中断和更新中断。设置了中断必须编写中断函数,否则可能导致死机。我们需要在中断函数里面完成数据处理和捕获设置等关键操作,从而实现高电平脉宽统计。6)设置TIM5的CR1,使能定时器 最后,必须打开定时器的计数器开关,通过设置TIM5_CR1的最低位为1,启动TIM5的计数器,开始输入捕获。 通过以上6步设置,定时器5的通道1就可以开始输入捕获了,同时因为还用到了串口输出结果,所以还需要配置一下串口。15.2 硬件设计 本实验用到的硬件资源有:1)&&指示灯DS02)&&WK_UP按键3)&&串口4)&&定时器TIM35)&&定时器TIM5前面4个,在之前的章节均有介绍。本节,我们将捕获TIM5_CH1(PA0)上的高电平脉宽,通过WK_UP按键输入高电平,并从串口打印高电平脉宽。同时我们保留上节的PWM输出,大家也可以通过用杜邦线连接PB5和PA0,来测量PWM输出的高电平脉宽。
本帖子中包含更多资源
才可以下载或查看,没有帐号?
高级技术员, 积分 617, 距离下一级还需 383 积分
高级技术员, 积分 617, 距离下一级还需 383 积分
高级技术员, 积分 617, 距离下一级还需 383 积分
高级技术员, 积分 617, 距离下一级还需 383 积分
15.3 软件设计 本章,我们依旧是在前一章的基础上修改代码,先打开之前的工程,然后我们在上一章的基础上,在timer.c里面加入如下代码: //定时器5通道1输入捕获配置//arr:自动重装值//psc:时钟预分频数void TIM5_Cap_Init(u16 arr,u16 psc){& && && && && && & RCC-&APB1ENR|=1&&3;& && && & //TIM5 时钟使能 & && & RCC-&APB2ENR|=1&&2;& && && &//使能PORTA时钟& && && && & GPIOA-&CRL&=0XFFFFFFF0;& &//PA0 清除之前设置&&& && & GPIOA-&CRL|=0X;& && & //PA0 输入& && && & GPIOA-&ODR|=0&&0;& && && && && &&&//PA0 下拉& && && && &TIM5-&ARR=& && && && && && && & //设定计数器自动重装值& && && & TIM5-&PSC=& && && && && && && &&&//预分频器 & && & TIM5-&CCMR1|=1&&0;& && && && && &//CC1S=01& &&&选择输入端 IC1映射到TI1上& && &TIM5-&CCMR1|=0&&4;& && && && &&&//IC1F=0000 配置输入滤波器 不滤波& && &TIM5-&CCMR1|=0&&10;& && && && & //IC2PS=00& & 配置输入分频,不分频 & && & TIM5-&CCER|=0&&1;& && && && && &&&//CC1P=0& && & 上升沿捕获& && & TIM5-&CCER|=1&&0;& && && && && &&&//CC1E=1& && &允许捕获计数器的值到捕获寄存器中& && & TIM5-&DIER|=1&&1;& && && && && & //允许捕获中断& && && && && && && && &&&& && & TIM5-&DIER|=1&&0;& && && && && & //允许更新中断& &&&& && & TIM5-&CR1|=0x01;& && && && && && &//使能定时器2& && & MY_NVIC_Init(2,0,TIM5_IRQChannel,2);//抢占2,子优先级0,组2& && &}//捕获状态//[7]:0,没有成功的捕获;1,成功捕获到一次.//[6]:0,还没捕获到高电平;1,已经捕获到高电平了.//[5:0]:捕获高电平后溢出的次数u8&&TIM5CH1_CAPTURE_STA=0;& & //输入捕获状态& && && && && && && && && && && && && && &&&u16&&TIM5CH1_CAPTURE_VAL&&;& && &//输入捕获值//定时器5中断服务程序& &&&void TIM5_IRQHandler(void){& && && && && & & && & u16& && & tsr=TIM5-&SR;& && &if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获& &&&& && & {& && && && &&&if(tsr&0X01)//溢出& && && && &&&{& && && && && && && && && && &if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了& && && && && && && &{& && && && && && && && && & if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了& && && && && && && && && & {& && && && && && && && && && && &&&TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次& && && && && && && && && && && &&&TIM5CH1_CAPTURE_VAL=0XFFFF;& && && && && && && && && & }else TIM5CH1_CAPTURE_STA++;& && && && && && && &}& &&&& && && && &&&}& && && && &&&if(tsr&0x02)//捕获1发生捕获事件& && && && &&&{& &&&& && && && && && && &if(TIM5CH1_CAPTURE_STA&0X40)& && &&&//捕获到一个下降沿& && && &&&& && && && && && && &{& && && && && && && && & & && && && && && && && && & TIM5CH1_CAPTURE_STA|=0X80;& &&&//标记成功捕获到一次高电平脉宽& && && && && && && && & TIM5CH1_CAPTURE_VAL=TIM5-&CCR1;//获取当前的捕获值.& && && && && && && && && &TIM5-&CCER&=~(1&&1);& && && && && && &//CC1P=0 设置为上升沿捕获& && && && && && && &}else& && && && && && && && && && && && && && && && && && & //还未开始,第一次捕获上升沿& && && && && && && &{& && && && && && && && && & TIM5CH1_CAPTURE_STA=0;& && && &&&//清空& && && && && && && && && & TIM5CH1_CAPTURE_VAL=0;& && && && && && && && && & TIM5CH1_CAPTURE_STA|=0X40;& &&&//标记捕获到了上升沿& && && && && && && && && &TIM5-&CNT=0;& && && && && && && && && && && &//计数器清空& && && && && && && && && &TIM5-&CCER|=1&&1;& && && && && && && && &//CC1P=1 设置为下降沿捕获& && && && && && && &}& && && && && & & && && && &&&}& && && && && && && && && && && && && && && && && && && && && && && &&&& && &}& && & TIM5-&SR=0;//清除中断标志位& &&&}此部分代码包含2个函数,其中TIM5_Cap_Init函数用于TIM5通道1的输入捕获设置,其设置和我们上面讲的步骤是一样的,这里就不多说,重点来看看第二个函数。TIM5_IRQHandler是TIM5的中断服务函数,该函数用到了两个全局变量,用于辅助实现高电平捕获。其中TIM5CH1_CAPTURE_STA,是用来记录捕获状态,该变量类似我们在usart.c里面自行定义的USART_RX_STA寄存器。TIM5CH1_CAPTURE_STA各位描述如表15.3.1所示:
TIM5CH1_CAPTURE_STA
捕获完成标志
捕获到高电平标志
捕获高电平后定时器溢出的次数表15.3.1 TIM5CH1_CAPTURE_STA各位描述& && & 另外一个变量TIM5CH1_CAPTURE_VAL,则用来记录捕获到下降沿的时候,TIM5_CNT的值。& && & 现在我们来介绍一下,捕获高电平脉宽的思路:首先,设置TIM5_CH1捕获上升沿,这在TIM5_Cap_Init函数执行的时候就设置好了,然后等待上升沿中断到来,当捕获到上升沿中断,此时如果TIM5CH1_CAPTURE_STA的第6位为0,则表示还没有捕获到新的上升沿,就先把TIM5CH1_CAPTURE_STA、TIM5CH1_CAPTURE_VAL和TIM5-&CNT等清零,然后再设置TIM5CH1_CAPTURE_STA的第6位为1,标记捕获到高电平,最后设置为下降沿捕获,等待下降沿到来。如果等待下降沿到来期间,定时器发生了溢出,就在TIM5CH1_CAPTURE_STA里面对溢出次数进行计数,当最大溢出次数来到的时候,就强制标记捕获完成(虽然此时还没有捕获到下降沿)。当下降沿到来的时候,先设置TIM5CH1_CAPTURE_STA的第7位为1,标记成功捕获一次高电平,然后读取此时的定时器值到TIM5CH1_CAPTURE_VAL里面,最后设置为上升沿捕获,回到初始状态。& && & 这样,我们就完成一次高电平捕获了,只要TIM5CH1_CAPTURE_STA的第7位一直为1,那么就不会进行第二次捕获,我们在main函数处理完捕获数据后,将TIM5CH1_CAPTURE_STA置零,就可以开启第二次捕获。 接着我们修改timer.h如下:#ifndef __TIMER_H#define __TIMER_H#include &sys.h& //通过改变TIM3-&CCR2的值来改变占空比,从而控制LED0的亮度#define LED0_PWM_VAL TIM3-&CCR2 void TIM3_Int_Init(u16 arr,u16 psc);void TIM3_PWM_Init(u16 arr,u16 psc);void TIM5_Cap_Init(u16 arr,u16 psc);#endif这里比较简单,就不多说了。接下来,我们修改主程序里面的main函数如下:extern u8&&TIM5CH1_CAPTURE_STA;& &&&//输入捕获状态& && && && && && && && && && && && && && &&&extern u16& && &TIM5CH1_CAPTURE_VAL; //输入捕获值int main(void){& && && && && && & & && & u32 temp=0;& && && && && &Stm32_Clock_Init(9);& && && && && && &//系统时钟设置& && & uart_init(72,9600);& && && && && && &&&//串口初始化为9600& && & delay_init(72);& && && && && && && && && && &&&//延时初始化 & && & LED_Init();& && && && && && && && && && & //初始化与LED连接的硬件接口& && &TIM3_PWM_Init(899,72-1);& && & //不分频。PWM频率=7)=80Khz& && &TIM5_Cap_Init(0XFFFF,72-1);& & //以1Mhz的频率计数 & &&&while(1)& && & {& && && && & delay_ms(10);& && && && &&&LED0_PWM_VAL++;& && && && &&&if(LED0_PWM_VAL==300)LED0_PWM_VAL=0;& && && && && && && && && && & if(TIM5CH1_CAPTURE_STA&0X80)& && && && && &//成功捕获到了一次高电平& && && && &&&{& && && && && && && &temp=TIM5CH1_CAPTURE_STA&0X3F;& && && && && && && &temp*=65536;& && && && && && && && && && && && && && & //溢出时间总和& && && && && && && &temp+=TIM5CH1_CAPTURE_VAL;& && && &&&//得到总的高电平时间& && && && && && && &printf(&HIGH:%d us\r\n&,temp);& && && && && &&&//打印总的高点平时间& && && && && && && &TIM5CH1_CAPTURE_STA=0;& && && && && && &//开启下一次捕获& && && && &&&}& && & }}该main函数是在PWM实验的基础上修改来的,我们保留了PWM输出,同时通过设置TIM5_Cap_Init(0XFFFF,72-1),将TIM5_CH1的捕获计数器设计为1us计数一次,并设置重装载值为最大,所以我们的捕获时间精度为1us。主函数通过TIM5CH1_CAPTURE_STA的第7位,来判断有没有成功捕获到一次高电平,如果成功捕获,则将高电平时间通过串口输出到电脑。至此,我们的软件设计就完成了。15.4 下载验证 在完成软件设计之后,将我们将编译好的文件下载到战舰STM32开发板上,可以看到DS0的状态和上一章差不多,由暗à亮的循环。说明程序已经正常在跑了,我们再打开串口调试助手,选择对应的串口,然后按WK_UP按键,可以看到串口打印的高电平持续时间,如图15.4.1所示:
图15.4.1 PWM控制DS0亮度& && & 从上图可以看出,其中有2次高电平在50us以内的,这种就是按键按下时发生的抖动。这就是为什么我们按键输入的时候,一般都需要做防抖处理,防止类似的情况干扰正常输入。大家还可以用杜邦线连接PA0和PB5,看看上一节中我们设置的PWM输出的高电平是如何变化的。
中级技术员, 积分 283, 距离下一级还需 17 积分
中级技术员, 积分 283, 距离下一级还需 17 积分
中级技术员, 积分 283, 距离下一级还需 17 积分
中级技术员, 积分 283, 距离下一级还需 17 积分
实习生, 积分 3, 距离下一级还需 47 积分
实习生, 积分 3, 距离下一级还需 47 积分
实习生, 积分 3, 距离下一级还需 47 积分
实习生, 积分 3, 距离下一级还需 47 积分
请问这个实验我用模拟调试为什么无法捕获呢?而用JLINK调试就可以呢?
实习生, 积分 6, 距离下一级还需 44 积分
实习生, 积分 6, 距离下一级还需 44 积分
实习生, 积分 6, 距离下一级还需 44 积分
实习生, 积分 6, 距离下一级还需 44 积分
如果要捕获低电平 需要修改哪些东西&&为什么我修改的就是不正确&&急!求帮忙
初级工程师, 积分 2211, 距离下一级还需 789 积分
初级工程师, 积分 2211, 距离下一级还需 789 积分
初级工程师, 积分 2211, 距离下一级还需 789 积分
初级工程师, 积分 2211, 距离下一级还需 789 积分
谢谢 好样的& &继续连载
初级技术员, 积分 78, 距离下一级还需 22 积分
初级技术员, 积分 78, 距离下一级还需 22 积分
初级技术员, 积分 78, 距离下一级还需 22 积分
初级技术员, 积分 78, 距离下一级还需 22 积分
实习生, 积分 15, 距离下一级还需 35 积分
实习生, 积分 15, 距离下一级还需 35 积分
实习生, 积分 15, 距离下一级还需 35 积分
实习生, 积分 15, 距离下一级还需 35 积分
为什么打印不出来呢,,我用的是mini开发板 ...
实习生, 积分 24, 距离下一级还需 26 积分
实习生, 积分 24, 距离下一级还需 26 积分
实习生, 积分 24, 距离下一级还需 26 积分
实习生, 积分 24, 距离下一级还需 26 积分
中级技术员, 积分 177, 距离下一级还需 123 积分
中级技术员, 积分 177, 距离下一级还需 123 积分
中级技术员, 积分 177, 距离下一级还需 123 积分
中级技术员, 积分 177, 距离下一级还需 123 积分
谢谢分享,留个脚印方便访问
资深技术员, 积分 444, 距离下一级还需 56 积分
资深技术员, 积分 444, 距离下一级还需 56 积分
资深技术员, 积分 444, 距离下一级还需 56 积分
资深技术员, 积分 444, 距离下一级还需 56 积分
扫描二维码,随时随地手机跟帖
时间类勋章
技术奇才奖章
人才类勋章
涓涓之细流
发帖类勋章
时间类勋章
技术高手奖章
人才类勋章
晶莹之水滴
发帖类勋章
技术新星奖章
人才类勋章
社区建设奖章
等级类勋章
时间类勋章
时间类勋章
您需要登录后才可以回帖

我要回帖

更多关于 pwm输入捕获 正点原子 的文章

 

随机推荐