模吧

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

1577查看 | 6回复

【转】不思带你【从零开始】做四轴!!!(强势整理搬运版)

[复制链接]
发表于 2015-10-9 21:22:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖搬运并整理自茶大阿莫论坛发帖,供大家参考学习。{:3_41:}首次发帖,有什么不对的地方,望大家多多指正啊。

     此帖从最开始的硬件焊接调试,到最后的飞行调试,一一给大家介绍, 并为大家解答在硬件焊接/模块驱动/程序调试等方面会遇到各种各样的问题,也为了方便大家少走弯路,争取让大家跟着教程都能做出自己的飞控。特此开贴。

匿名四轴试飞视频:



[media=x,500px,375px]http://player.56.com/v_ODQ0ODczNDk.swf/1030_plager.swf[/media]

一、全套原理图.程序.上位机
毕业以来,实在闲不住,总想做点什么玩,不想搞太复杂的硬件,想到了四轴,于是开始走入模界.
先买了大四轴,mwc,调的差不多了,发现我们这天天有风,实在是郁闷,于是开始计划做微型四轴,这次决定完全diy,成品实在是无味.
用业余时间做一架自己的四轴,是很有意思也是很难,很难有时间静下心来做.自己做板,写程序,写自己的上位机,用自己的协议来遥控.
因为平时上班忙,工作也全与电子无关,所以进度比较慢.
在跟大学好友俩人的共同努力下,我们的小四轴终于完工了.现在已经可以比较稳定的飞了.现在板子也升级到第二版本了,更加集成,遥控接收也做到板子上了.
下面把这段时间来的成果,自己的经验,还有原理图、程序、上位机分享给大家.

方案:
CPU: STM32F103CB
2.4G: NRF24L01
电子罗盘:HMC5883
陀螺仪+加速度计:MPU-6050

【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 5218 【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 7113            
【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 1333
做四轴过程中的一些经验


HMC5883:要我评价就是娇气,大家焊这个片的时候温度一定不要太高!焊第一片的时候,风枪开到350度,焊好后读取ID没问题,但不管怎么动板子,读出的数据都在正负2以内。后来发现网上很多人也碰到这个问题,主要是焊接时温度太高,HMC5883焊坏了。看它的DATASHEET后知道,温度不能超过260度湿度较大时还要120度烘烤24小时。后来我把风枪温度调到250度,总算是OK了

MPU6050:6轴,不错,也不贵,就不过多评论了

电机:7*16  还行  就是感觉动力不足,换7*20的试试.0.7mm的轴也容易弯,下次换1mm的试试.

主角STM32:速度可以,什么都可以,就是iic蛋疼....实在蛋疼,还没AVR好用......不想多说了...

上位机:开始VB6,做好了,开用,后来增加功能,发现没有代码折叠实在不方便,改delphi,直接跳过用的最多的7,上RAD2007,目前用着不错..后来觉得C#也不错,有想法改投C#,,,,都是半吊子水平...哎,,以后要专心啊....

飞控程序四元数、卡尔曼滤波


飞控源码:最新源码下载可前往匿名科创主站

上位机写了好,已经比较好用了,可以显示角度,各个传感器读数,波形显示


原题图: 【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 8532 原理图.pdf (61.15 KB, 下载次数: 1456)


 楼主| 发表于 2015-10-9 21:22:48 | 显示全部楼层
二、上位机介绍
【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 9830 【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 9182

【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 9621 【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 6538



下位机发送自定义数据,格式为:0x88+FUN+LEN+DATA+SUM
FUN可以是 0xA1到0xAA,共10个;LEN为DATA的长度(不包括0x88、FUN、LEN、SUM)。
SUM是0x88一直到DATA最后一字节的和,uint8格式。


飞控显示对应的帧FUN为0xAF,(帧格式:0x88+0xAF+0x1C+ACC DATA+GYRO DATA+MAG DATA+ANGLE DATA+ 0x00 0x00 + 0x00 0x00+SUM,共32字节ACC/GYRO/MAG/ANGLE(roll/pitch/yaw)数据为int16格式,其中ANGLE的roll和pitch数据为实际值乘以100以后得到的整数值,yaw为乘以10以后得到的整数值,上位机在显示时再 除以100和10)。


遥控,电机pwm,电压显示对应的帧FUN为0xAE,(帧格式:0x88+0xAE+0x12+THROT YAW ROLL PITCH +AUX1 2 3 4 5 + PWM:1 2 3 4 + VOTAGE + SUM,共28字节),数据为uint16格式,遥控数据最小在1000左右,最大在2000左右。数据都为uint16格式,其中pwm范围1-100,votage为实际值*100。


最新上位机下载可前往匿名科创主站 在上位机中按F12查看帮助有更详细介绍,此处不再赘述。


上位机+源码的视频讲解:


       上位机的数据处理速度可以1毫秒处理一个几十字节的数据帧(无线数据包为32字节,这里就以32字节为例),也就是说1秒可以处理32000字节的数据. (1ms接收32字节的数据,并分析,显示数据波形)  这里可以说是写上位机时最难的,占据了大量的时间,因为如果做到10ms接收一帧数据并显示,是比较容易的,如果频率快了,比如现在的1ms一帧数据的话,pc的数据处理和显示刷新要处理的很好才行,要不程序就很容易"死掉",因为windows不是实时系统,还有很多事情要做,不能把所有资源都给上位机程序. 目前传感器的采样频率也是1ms一次,这样就可以用上位机直接观察采样值和滤波后的值,很方便,可以实时观察滤波效果,不用再把数据先保存在内存卡或者电脑上,飞完了在调出来看波形了.
   
       而且听取大家意见,很多人没有航模遥控,这里加上了鼠标键盘控制功能,可以用鼠标和键盘控制飞机了,当然,手感肯定没有遥控好了.

 楼主| 发表于 2015-10-9 21:23:25 | 显示全部楼层
3、Timer
串口做好了,再做点什么呢,就Timer吧,这个是以后程序里很重要的一个内容,因为timer是用来计时的,我们以后要用它来触发做很多事情,比如定时去读传感器并控制电机等等.
还是一样,先初始化Timer,以TIMER3为例
  • void TIM3_INIT(void)
  • {
  •         TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  •                                                                                              初始化使用的结构体
  •         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
  •                                                                                              打开timer3的时钟
  •         TIM_DeInit(TIM3);
  •                                                      将timer3寄存器设置为默认
  •         TIM_TimeBaseStructure.TIM_Period=1000;                             这个寄存器我理解是比较值,就是用这个值来和cnt比较,判断是否溢出(大于这个数就溢出)
  •         TIM_TimeBaseStructure.TIM_Prescaler=72-1;                  这个寄存器我理解是分频值,就是timer3的时钟除以这个值+1就是计数器的时钟,这里72分频,就是1M,刚才比较值设置为1000,那么溢出的频率就是1000HZ,也就是每次中断1MS
  •         TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;            采样分频
  •         TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;            向上计数
  •         TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);               时钟初始化
  •         TIM_ClearFlag(TIM3,TIM_FLAG_Update);                清除标志
  •         TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);                开中断
  •         TIM_Cmd(TIM3,ENABLE);                       使能timer3
  • }


[color=rgb(49, 132, 219) !important]复制代码


4、PID计算
有人让我解释一下我程序里面PID计算的过程,这里就插一节吧,就是介绍下我的PID,当然,还很不完善,后期肯定还要经过很多改动才行,这里就先介绍下现在的"初级版"吧.
  • void PID_CAL(void)                 PID计算函数
  • {
  • static float thr=0,rool=0,pitch=0,yaw=0;               控制量
  • static float rool_i=0,pitch_i=0; 积分
  • int16_t Motor1,Motor2,Motor3,Motor4;              四个电机输出
  • IMU_TEST();                          计算姿态
  • GET_EXPRAD();                     获得控制量
  • ////////////////////////////////////////////////////////////////////////////////
  • rool = PID_RP.P * DIF_ANGLE.X;                     roll方向的P计算
  • if(Q_ANGLE.Rool>-0.1 && Q_ANGLE.Rool<0.1)         判断I是否需要清零
  • {
  • rool_i = 0;
  • }
  • rool_i -= PID_RP.I * Q_ANGLE.Rool;         I计算
  • PID_RP.IMAX = DIF_ANGLE.X * 10;          积分限幅
  • if(PID_RP.IMAX<0)
  • {
  • PID_RP.IMAX = (-PID_RP.IMAX) + 100;
  • }
  • else
  • {
  • PID_RP.IMAX += 100;
  • }
  • if(rool_i>PID_RP.IMAX) rool_i = PID_RP.IMAX;
  • if(rool_i<-PID_RP.IMAX) rool_i = -PID_RP.IMAX;
  • rool += rool_i; 积分
  • rool -= PID_RP.D * GYRO_F.X;               D计算
  • ///////////
  • pitch = PID_RP.P * DIF_ANGLE.Y;           同上
  • if(Q_ANGLE.Pitch>-0.1 && Q_ANGLE.Pitch<0.1)
  • {
  • pitch_i = 0;
  • }
  • pitch_i -= PID_RP.I * Q_ANGLE.Pitch;
  • if(PID_RP.IMAX<0)
  • {
  • PID_RP.IMAX = (-PID_RP.IMAX) + 100;
  • }
  • else
  • {
  • PID_RP.IMAX += 100;
  • }
  • if(PID_RP.IMAX<0) PID_RP.IMAX = -PID_RP.IMAX;
  • if(pitch_i>PID_RP.IMAX) pitch_i = PID_RP.IMAX;
  • if(pitch_i<-PID_RP.IMAX) pitch_i = -PID_RP.IMAX;
  • pitch += pitch_i;
  • pitch -= PID_RP.D * GYRO_F.Y;
  • /////////////
  • GYRO_I[0].Z += EXP_ANGLE.Z/3000;                 yaw方向就简单的用了陀螺的积分 PD
  • yaw = -10 * GYRO_I[0].Z;
  • yaw -= 3 * GYRO_F.Z;
  • //
  • thr = RC_DATA.THROTTLE+400;
  • ////////////////////////////////////////////////////////////////////////////////将控制量输出给电机
  • Motor1=(int16_t)(thr + rool - pitch + yaw);
  • Motor2=(int16_t)(thr + rool + pitch - yaw);
  • Motor3=(int16_t)(thr - rool + pitch + yaw);
  • Motor4=(int16_t)(thr - rool - pitch - yaw);
  • if(FLY_ENABLE && (RC_DATA.THROTTLE>-400))                       和解锁有关,未解锁或油门太低电机禁止转动
  • MOTO_PWMRFLASH(Motor1,Motor2,Motor3,Motor4);
  • else
  • MOTO_PWMRFLASH(0,0,0,0);
  • }

[color=rgb(49, 132, 219) !important]复制代码

程序就这么简单,当然还需要很多改进,PID参数也需要慢慢调试才行

 楼主| 发表于 2015-10-9 21:24:05 | 显示全部楼层
四、传感器
1、加速度计、陀螺仪
【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 6714
      做个比喻,加速度计,以下简称加计,大家可以把它想象成一个铁块,这个铁块是个立方体,有前后左右上下六个面,每个面连接有一个弹簧,弹簧另一端假设固定在一个卡车的集装箱里面,这样这个铁块就被这六个弹簧吊在集装箱里面了,由于铁块有重力,所以汽车不动时,上面的弹簧被拉长,下面的弹簧被压缩,这里假设是通过测量弹簧的拉力来输出加速度(实际有可能是电容什么的,这里不做讨论,了解特性就好),六个弹簧,两两一组,正好3个轴,这就是3轴加速度了,静止不动时,只有Z轴也就是上下两根弹簧有读数,其他两对弹簧是平衡的.现在假设汽车在做加速运动,那么不仅仅上下两根弹簧不平衡了,前后两根弹簧也会有变化,前面的弹簧拉长,后面的弹簧压缩,就有了前后方向的加速度.左右也是一个道理.
       知道了加计的大致原理,那么加计有什么特点呢?让我们大家想象一种情况,就是这辆卡车行驶在颠簸的路上,集装箱里面的铁块肯定不会稳稳的吊着了,他会随着汽车左摇右摆,上下颠簸,而且有一点大家注意,铁块的此时的摆动,不是完全和汽车同步的,由于惯性等原因,铁块会在里面"乱动",荡来荡去,此时的加速度输出会是怎么样的呢?肯定也是随着铁块"荡来荡去",所以我们得加计的一大特点,就是对震动很"敏感",如果把飞控板放在桌子上静止不动,可以说随便一个姿态算法的输出都不错,哪怕不滤波.可以当电机一转动起来,震动来了,加计就有了很大的干扰,此时如果处理不好,姿态就乱掉了.


     然后我们再说说陀螺仪,陀螺仪顾名思义,肯定和陀螺有很大关系,没错,特点也和陀螺一样.还是假设在这个车里面,我们放上一个小时候玩的陀螺,不管用了什么方法,让它高速旋转起来,大家都知道,这样陀螺是不会倒的,他会尽量保持当前的姿态,陀螺仪正是利用这个特点.我们看两段视频来了解下.


      通过视频,大家可以看到,陀螺在高速旋转时,是会尽量保持转轴不变的.那么我们就可以想到陀螺仪的特点了,就是对震动是"不敏感"的,因为它会尽量保持自己不被震动改变,但是陀螺会不断累积误差,造成"漂移".
好了,这里我们知道了加计和陀螺仪的特点,再考虑怎么使用,就简单多了,总的来说就是加计短时间不可靠,因为震动,陀螺仪长时间不可靠,因为"漂移".那么对于加计的数据和陀螺仪的数据,我们就应该短时间相信陀螺仪,长时间相信加速度.好了,到了这里,再怎么做也就清晰了,对加速度的数据,我们要滤波,平均值滤波等等,方法很多,对陀螺仪数据,我们积分,短时间内,这个积分得到的角度还是准确的,而过一段时间,就用处理过的加速度数据来矫正陀螺仪积分的角度,抑制"漂移".这样利用两个传感器的特点,取长补短,来达到一个相对稳定的输出.

 楼主| 发表于 2015-10-9 21:25:35 | 显示全部楼层
2、MPU6050 【转】不思带你【从零开始】做四轴!!!(强势整理搬运版) 飞控,电机,DIY,四轴 作者:emptjf 8578
     今天跟大家谈一谈6050的数据吧.
     首先,量程
GYRO: ±250, ±500, ±1000, and ±2000°/sec(dps).    这是什么意思呢,就是陀螺数据在输出32767(也就是int16型数据的最大值)的时候,分别表示250度每秒一直到2000度每秒.
ACC: ±2g, ±4g, ±8g, and ±16g.  表示输出32767时表示2倍重力加速度一直到16倍重力加速度.
量程选的小,数据就准确一些(在量程范围内),但是在剧烈动作时,容易超调.

     知道了量程和数据的意义,怎么把输出的数据转成角度想必就很简单了吧.
     ACC的数据出来后,进行一个移动平均值滤波,作用就是个低通滤波,GYRO的数据换成角度,积分,就可以得到"姿态角",这里直接得到的姿态角是不对的,要对三轴数据进行"解耦"(耦合是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。 解耦就是用数学方法将两种运动分离开来处理问题)---应该是这么叫,这个我就不太懂了,大家可以参考一下mwc的程序或者网上寻找教程.
得到了角度,定期用ACC的角度来矫正陀螺的误差,就可以实现自稳这个功能了.

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

      有个问题,我们什么时候去读传感器呢?我觉得这样看两个情况
一:系统速度够快,比如6050的采样周期是1ms,我的单片机在1ms里面可以完成数据读取、滤波、姿态计算、输出等计算过程,那么,我就可以使用定时器中断来触发读取,然后是依次滤波。。。。。
二:系统不够快,有的飞控速度慢,1ms完成不了那么多任务,很多浮点运算对8位机确实任务有点重,那怎么办?那我们就循环读,什么时候完成计算过程了,什么时候读取传感器数据,并开始下一次计算过程,这样不浪费单片机的时间。

      有点点调试经验,跟大家分享下,如果大家也和我用的单片机一样,都是stm32f103,想必大家在调试IIC的时候也遇到了各种各样的困难,确实,stm32的IIC是个很蛋疼的东西,当然,我是针对的硬件,模拟的还是很好用的。当时调试IIC,那可是尝试了各种方法,中断,dma,官方的cpal什么的,就一个字,不好用。。。。。当时都想外加个单片机负责IIC的读取了。。。。。如果大家要调试硬件的IIC,我有几个小经验,不能用变量观察窗口,并在IIC程序中设置断点,比方说我想看看SR1或者SR2寄存器什么状态呀,不要想了,不能这样,因为stm IIC相关寄存器很多都是读取后清零,大家用Jlink看寄存器,就相当于读取了,寄存器清零,不该清零的时候清零,后果很严重,IIC直接挂掉了。。。。。STM的模拟IIC还是很稳定的,相关程序请参考我的工程。

       现在假设大家已经得到了一下数据:
滤波后的加速度数据  AX,AY,AZ,陀螺仪数据:GX,GY,GZ,怎么用这些数据得到姿态?  
我的程序里面用的四元数,我就简单给大家讲讲用法,具体四元数的原理什么
IMUupdate(GX,GY,GZ,AX,AY,AZ);
      
      这个函数的调用在control。c 里面,大家可以看一下,只要把加速度和陀螺仪的相关数据,传进这个函数就可以了,就这么简单。
这个函数可以根据我们传递进去的传感器数据,计算表示姿态的四元数,计算出了 四元数怎么用呢?请看程序末尾:
  • Q_ANGLE.Pitch  = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch
  • Q_ANGLE.Rool = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll

[color=rgb(49, 132, 219) !important]复制代码

      这两句话就是根据四元数,计算表示飞机姿态的欧拉角。
       有了欧拉角,就可以方便的用于控制了


 楼主| 发表于 2015-10-9 21:26:06 | 显示全部楼层
三、程序讲解
       硬件调试:大家拿到套件后,请根据原理图进行焊接,芯片方向要注意,不清楚的找楼主要高清大图,按照图片焊接也挺简单.完全都是贴片的元件,考验大家的焊工

       焊接好后,就要开始软件调试了,这里才是四轴最"好玩"的地方,当初决定玩四轴也是因为这个,因为四轴硬件简单,主要拼的是软件,不像直机什么的,硬件太复杂了.
软件的调试,建议大家还是一步一步来.虽然我开源了飞控的全部程序,大家烧进去就可以实现基本的飞行了,但是我们自己做飞控最关键的就是程序了,直接copy一个工程过来,想必大家直接消化还是很不容易的.就像学习骑车一样,还不会走路,就学骑自行车,怎么会骑好.所以还是建议大家从基础的开始.

       从哪里开始呢,因为我们使用的是stm32f103,大家就从最简单的stm32的驱动开始吧.
以下都是针对我的飞控板写的,大家如果用的自己的,记得需要把IO和端口都改为自己的
        stm32可以说就是飞控的大脑,mpu6050和hmc5883就是飞控的眼睛,耳朵.飞控的所有功能,都是通过stm32实现的,所以大家清楚了stm32做什么了以后,就要好好的把他驱动好,让他按照我们的想法来运转,这样飞控才能好好的工作.
        
1、点亮LED
        首先下载我们需要的软件环境,就是MDK (KEIL FOR ARM),我用的是4.7版本,有了自动补全功能,挺好用.当然,还要有个JLINK,使用swd方式进行调试.然后就可以开始第一步的程序编写了.按照网上的教程,使用库函数建立一个空白的工程,从最简单的,点亮一个LED开始.
        要点亮一个led,也不是一件简单的事情,首先,系统时钟要配置好(最新库函数在初始化时已经帮我们把时钟初始化好了,不用我们再初始化了,当然,除非你有特殊要求),然后要配置端口的输入输出,还要打开响应端口的外设时钟.
  • void LED_INIT(void)
  • {
  •         GPIO_InitTypeDef GPIO_InitStructure;                                                         这个是端口配置结构体
  •         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);                 打开外设GPIOB的时钟,因为LED是通过GPIOB的9,11,14,15来驱动的
  •         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_9 ;                          选择响应的管脚
  •         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                             设置IO速度
  •         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                              设置IO驱动方式,out为输出,PP为推挽输出
  •         GPIO_Init(GPIOB, &GPIO_InitStructure);                                                      对GPIOB进行初始化
  • }

[color=rgb(49, 132, 219) !important]复制代码


初始化好了,再写几个函数来操作LED,更加方便

  • #define LED1_OFF          GPIO_SetBits(GPIOB, GPIO_Pin_11)
  • #define LED1_ON                 GPIO_ResetBits(GPIOB, GPIO_Pin_11)
  • #define LED2_OFF          GPIO_SetBits(GPIOB, GPIO_Pin_14)
  • #define LED2_ON                 GPIO_ResetBits(GPIOB, GPIO_Pin_14)
  • #define LED3_OFF          GPIO_SetBits(GPIOB, GPIO_Pin_15)
  • #define LED3_ON                 GPIO_ResetBits(GPIOB, GPIO_Pin_15)
  • #define LED4_OFF          GPIO_SetBits(GPIOB, GPIO_Pin_9)
  • #define LED4_ON                 GPIO_ResetBits(GPIOB, GPIO_Pin_9)
  • #define LEDALL_OFF       GPIO_SetBits(GPIOB, GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_9)
  • #define LEDALL_ON         GPIO_ResetBits(GPIOB, GPIO_Pin_11 | GPIO_Pin_14 | GPIO_Pin_15 | GPIO_Pin_9)

[color=rgb(49, 132, 219) !important]复制代码

本小编附上新版F4版LED函数:
  • #define LED5(x)        x ? GPIO_ResetBits(GPIOE,GPIO_Pin_0): GPIO_SetBits(GPIOE,GPIO_Pin_0)
  • #define LED6(x)        x ? GPIO_ResetBits(GPIOE,GPIO_Pin_1): GPIO_SetBits(GPIOE,GPIO_Pin_1)
  • #define LED7(x)        x ? GPIO_ResetBits(GPIOE,GPIO_Pin_2): GPIO_SetBits(GPIOE,GPIO_Pin_2)
  • #define LED8(x)        x ? GPIO_ResetBits(GPIOE,GPIO_Pin_3): GPIO_SetBits(GPIOE,GPIO_Pin_3)
  • #define LED1(x)        x ? GPIO_ResetBits(GPIOE,GPIO_Pin_4): GPIO_SetBits(GPIOE,GPIO_Pin_4)
  • #define LED2(x)        x ? GPIO_ResetBits(GPIOC,GPIO_Pin_13): GPIO_SetBits(GPIOC,GPIO_Pin_13)
  • #define LED3(x)        x ? GPIO_ResetBits(GPIOC,GPIO_Pin_14): GPIO_SetBits(GPIOC,GPIO_Pin_14)
  • #define LED4(x)        x ? GPIO_ResetBits(GPIOC,GPIO_Pin_15): GPIO_SetBits(GPIOC,GPIO_Pin_15)

[color=rgb(49, 132, 219) !important]复制代码

这几个函数功能很简单,就是对IO的输出电平进行操作.当LED可以点亮后,更高级的比如LED的闪烁,就需要大家自己写了,实现的方法很多,可以用循环实现,也可以用定时中断实现.选用哪种方式实现,就要看具体的使用地点和情况了.

2、驱动串口
第一步LED驱动好了,下一步做点什么呢?就来驱动下串口吧,串口还是调试时一个很好用的工具.

串口,在我们这个工程里,其实就是用来调试的,无线控制是通过SPI来进行,其实无线调试好以后,串口也就没有用了,可以使用无线进行调试的,这样更加方便.其实串口是完全可以省略的.
那么怎么使用这个串口呢?串口可以理解为一根网线,通过他,可以将飞机上的数据传输到电脑上,就这么简单.........至于传什么,怎么传,学问就大了....
串口驱动好以后,可以帮助我们分析很多问题,有些甚至JLINK也做不到,观察变量,观察标志位,观察程序运行状态,等等,都可以通过串口来实现,上位机为了配合串口和无线调试,还特意做了一个DEBUG功能,就是为了帮助大家更方便的调试,可以通过串口发送一个自定义的命令,打开上位机的LED,或者改变上位机显示的寄存器数字.这样可以帮助大家知道程序的执行状态,比如中断进入没进入啊,进入的频率啊,变量现在的值是多少啊等等信息.
我这里仅仅是一点点自己的调试经验,至于怎么发送,发送一个什么样的格式,大家个人习惯不同,写出来当然也各不相同,有人喜欢用printf,我就喜欢十六进制,各有长短

串口调试也一样,第一步,当然是初始化.
  • void USART1_INIT(void)
  • {
  •         USART_InitTypeDef USART_InitStructure;
  •         USART_ClockInitTypeDef USART_ClockInitStruct;
  •         GPIO_InitTypeDef GPIO_InitStructure;


[color=rgb(49, 132, 219) !important]复制代码


串口初始化好了,就要测试收发了,将串口用串口线和电脑连接起来,先来简单的发送,用上位机看看能不能收到数据.
对了  !这里还要设置串口中断
      
  • NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  •         NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  •         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
  •         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
  •         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  •         NVIC_Init(&NVIC_InitStructure);
  • 我用的第二组,串口给了优先级3,subfriority3
  • 串口中断函数:
  • void USART1_IRQHandler(void)
  • {
  •          USART1_IRQ();
  • }
  •          void USART1_IRQ(void)
  • {
  •          if(USART_GetITStatus(USART1,USART_IT_TXE)!=RESET)
  •          {
  •                          发送中断
  •           }
  •          if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  •          {
  •                          接收中断
  •          }
  • }

[color=rgb(49, 132, 219) !important]复制代码


中断也写好后,基本的发送接收应该就没问题了.
但是在使用过程中,没有一个缓冲区肯定是不行的,关于这方面的帖子很多,大家在论坛上搜索下吧.我就简单介绍下我用的简单的环形缓冲吧.
我用了一个环形发送缓冲,比如一个长度为100字节的数组,再定义两个变量,比如num与t_num
num表示缓冲区内下一个空字节在第几字节,t_num表示下一个需要发送的字节在缓冲区的多少字节
比如需要发送12345 这5个数字,分别写入数组的01234字节,共5字节,此时num+=5,表示buf[5]是空的.写入数据后,激活发送,发送完一个字节,t_num+1,并判断t_num与num的大小,如果t_num<num,就急需发送,如果t_num=num,说明数据已经发送完了.
大致意思就是这样.


发表于 2015-10-9 22:56:59 | 显示全部楼层
又学习了,帮顶一下。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|关于模吧|APP下载|广告报价|小黑屋|手机版|企业会员|商城入驻|联系我们|模吧 ( 冀公网安备13080502000084号 )

© 2013-2020 Moz8.com 模吧,玩出精彩!