美文网首页arduinoardunio
Arduino菜鸟通俗版解读系列(5)NRF24L01遥控串口通

Arduino菜鸟通俗版解读系列(5)NRF24L01遥控串口通

作者: 要啥用户名 | 来源:发表于2018-08-15 21:14 被阅读193次

            大家好,好多天没有更新文章了。前段时间一直在捣鼓四轴,各种焊接和调试,所以没有顾得上写文章。这一讲我们来讲一讲NRF24L01这个模块。随着这个系列讲解的深入,会涉及到越来越多的专业知识,有的我本人也无法讲解的非常清楚,因为都是基于实际项目而思考产生的一些个人理解,通过本人口语化的讲解希望能给读者带来不一样的感悟。但在系列的撰写过程中我也发现有的问题真要讲清楚,绝不是一两句口语能讲清楚的,因为其中涉及到一些专业概念,而这些专业概念如果采用非专业术语去讲解的话,可能会花费大量的篇幅,这也是为什么前面几片文章会有这么多字数的一个原因。所以我也在思考一个问题:是不是所有问题都必须讲彻底,讲细致?尤其是到了这一篇---NRF24L01,我发现有很多细节我自己也不是特别清楚,但是这并不影响我的使用,所以在这一篇中我打算采用一个核心宗旨:以实用为基础,讲解那些实用的知识点,对于不影响使用的知识只进行定性的讲解。

            首先我们来看一下NRF24L01的样子,如图1。图1中红色框选的部分是8个针脚,这8个针脚是我们需要连接到Arduino上的。图2是这8个针脚的名称,它们按照图1中的对应位置排列,其中图1中带有白色框的引脚就是图2中的GND引脚。

    图1 图2

            这一章我们就以一个遥控无人机电机转速为例,来说明NRF24L01怎么使用。通过NRF24L01我们可以实现遥控无人机电机转速,从而实现无人机的起飞降落,当然,真正的无人机控制还需要加入PID控制,否则一阵风过来可能无人机就会失去平衡。但是电机的转速遥控是最基本的功能。

    图3 图4

    图3是一块我手焊的Arduino扩展版,这块扩展版下面插着一块Arduino。图3中间的芯片是作为接收端的NRF24L01模块,图4中的是遥控器端的NRF24L01模块。我们要做的事情是通过图4中的遥控器,把遥控杆(其实就是一个滑动变阻器)的控制数值通过遥控端的NRF24L01模块发送出去,然后在图3中接收端的NRF24L01接收这个数值,最后用这个数值来控制无人机上的四个无刷电机的转速。

    下面是NRF24L01的接线方式,无论接收端还是发射端都这样接线,这些接线方式是基于文章后面下载的“库”而定的,是“库”里面默认好的设定,所以如果你采用其他版本的“库”文件,那么里面定义的接线方式可能是不一样的:

    Arduino uno    ---    nRF24L01

    3.3V                  ---      VCC:模块供电引脚

    GND                  ---      GND:模块接地引脚

    7 号引脚                        ---      CSN:接收端选择引脚

    8 号引脚                      ---        CE:发射/接受状态选择引脚

    11号引脚                    ---      MOSI :控制端输出,接收端输入引脚

    13号引脚                      ---        SCK:时钟信号

    12号引脚                      ---        MISO:控制端输入,接收端输出引脚

    *IRQ引脚在本例中不需要接

            在硬件的连接上,严格按照上面的连接方式将NRF24L01和Arduino连接起来即可,千万别接错了。对于以上引脚的含义,可以做一个简单的说明,不过即使你不十分清楚也没关系,因为你只需要明白这样接上以后,遥控器发射端的NRF24L01就可以发射信号了,而接收端的NRF24L01就可以接受信号了。而对于发送什么数据,接收什么数据,接收到的数据怎么用来控制电机,这些都是软件程序上的问题,后面我们会说到。所以你只要明白硬件的接法并且大致了解引脚含义即可,真正的数据处理要在软件程序中进行理解。不过在这里我们还是对NRF24L01的引脚做一个简单说明,你了解大概用处就可以,如果一定想要搞清楚原理建议上网找视频看看。

    VCCGND大家都明白,就跳过不讲;

    CSN是一个“片选择引脚”,即Chip Select N(N代表低电平有效),假如你想通过1个NRF24L01同多个NRF24L01进行通信的话,就需这个引脚了。一旦将想要通信的那个NRF24L01模块的CSN引脚置0,就代表你之后所有的信息传输对象针对的是这个NRF24L01模块。这就好比你是一个管理员,然后你要管理一群聋子,咋办呢?你喊他们名字肯定是不行的,于是你想出一个好办法,给每一个聋子身上挂一个双色牌,一面红色一面绿色,假如某个聋子身上的牌子翻成红色代表他处于被通信状态,假如是绿色就是不通信状态,那么好,这一群聋子中你只需要把任意一个人的牌子翻成红色,其他人翻成绿色,然后这群聋子就可以知道你到底想指挥的是哪一个人了,CSN就相当于NRF24L01的双色牌。

    CE是“模式选择引脚”,NRF24L01作为无线传输模块,必然有“接收”和“发送”两种状态,那么在决定这块NRF24L01模块是“接收”还是“发送”时,就是通过CE引脚来定义的。

    MOSIMISO这两个引脚在前一篇讲过了,MOSI和MISO是串口通信中信息传输的通道,前者用于发送端传送信息给接收端的线,后者用于接收端反馈信息给接收端的线;通常在控制过程中MOSI用的多,但是有时候我们需要接收端反馈是否接收到信息的话也会需要MISO来传送反馈信息。

    SCK是“时钟引脚”,这个东西存在的意思是什么呢?我们知道NRF24L01传送数据的时候是一帧一帧的是传输的,那么好,在传输的时候我们总得有个节奏吧,发送端不能一会儿快一会儿慢的,否则接收端怎么接收呢?所以我们需要设定一个打节奏的装置,就是这个SCK时钟。SCK会打一个固定的节奏,然后传送端和接收端就可以按照这个固定的节奏有条不紊地收发信息了,否则接收端和发送端的节奏没有一个统一步调的话很容易出现信息丢失或者干脆接收不到。

    IRQ是“中断标志位”,说实话我也没深入取研究,因为没用过。

            以上是关于硬件方面的介绍和准备,下面来讲解程序方面的问题。关于本例中的程序我们同样要用到一个“库”,记住这不是从底层自己去写NRF24L01的控制,而是运用别人写好的“库”,所以你只需要知道“怎么用”和“怎么改”就可以,很多设置参数都是提前设置好的。

            在讲解程序之前,首先我们要对整个控制构想有一个了解,然后我们才能有目的地去写程序。所谓控制的构想指的是我们想要怎么样去控制电机,设置哪些开关和控制杆等等。在这个例子中的遥控装置上,我设计了一个遥控杆和一个安全按钮(见图4)。遥控杆用来控制电机的转速;安全按钮是一个总开关(图4中黄色按钮),一旦按下安全按钮遥控杆才能够起效果,否则遥控杆处于无效状态并且电机转速自动设置为0。之所以要设置安全按钮是因为无人机的螺旋桨转速极快因此非常危险,我们必须有一个安全按钮来保证我们不会误启动电机,导致我们在调试或者触摸无人机的时候发生危险。所以控制器端有一个安全按钮,一个操控杆,当安全按钮按下时可以通过操控杆调整电机转速,当安全按钮松开时整个遥控器处于锁定状态并且电机转速设定为0。 安全按钮的硬件连接如图5所示。当安全按钮断开时,pin2引脚通过下拉电阻接地,此时pin2输入的是低电平;当安全按钮闭合时,pin2引脚和5V端接通,此时pin2输入的是高电平。下拉电阻的存在是防止在安全按钮接通时5V与接地端短路。

    图5

    接下来设计接收端,接收端比较简单,就是接收到发送端的数据后,根据这个数据来设定电机的转速,注意有4个电机,所以我们需要在Arduino上分配4个接口用来控制电机。接收端在接收到数据后,根据这个数据,通过Arduino上分配的4个接口来控制4个电机。在有了整个系统运作的思路后我们就可以正式开始编写程序了。

    发送端程序见图6。

    图6

    图6为发送端程序,可以看到分为4大块内容:头文件,参数定义,setup主函数,loop主函数。基本上所有程序的架构都是这个样子的。下面我们分别来讲解一下:

    头文件---头文件就是“库”文件,这个“库”的概念我在前面第三篇专门用一整篇讲过了,不明白的朋友可以看一下,我就不再累述了。不过这里要说的是,既然是“库”文件,按照第三篇的说法就是别人二次开发的程序包,所以是需要我们自己上网下载源代码的,如果不下载源代码的话,那么我们是无法调用头文件的,这意味着我们在图5中那些#include ***的语句是不会产生作用的。本示例用到库的下载地址:https://github.com/aaronds/arduino-nrf24l01。把下载的Mirf文件夹解压到Arduino 安装文件夹里的 “libraries”路径里即可。这里的下载地址就是GitHub网站,一个开源的代码集散平台,上面是各路大神开发的“库”代码。在下载完上面链接里的“库”,并将其放入Arduino下面的“libraries"路径中后,我们就可以使用这些“库”了。

    定义相关参数---在图6的例子中,我定义了三个参数:value[3],val,sw,其中val和sw是整型变量int,value[3]是整型数组。不过需要说一句的是value[3]其实在本例中并没有用到,因为本例中只是单纯控制电机的转速,并不需要让四个电机转速有所区别,所以只需要用到一个参数即可,所以我们在本例中只需要val这个参数来控制电机转速。但是事实上我们真正在控制无人机的时候是不可能用一个参数去控制全部四个电机转速的,因为还涉及到前进,后退,左行,右行,上升,下降等等。这些不同的状态要求四个电机之间的转速有一定差别,比如前进状态,要求前面两个电机转速稍慢,后面两个电机转速稍快,这样才能够让无人机产生一个向前的倾斜角,然后在达到倾斜平衡后,四个电机的转速需要保持恒定来保持这个倾斜状态,进而使无人机能够向前飞行。由于在真实的无人机控制中,有这么多不同的电机状态,所以我们需要多个控制变量来控制电机转速,在这个时候我们就需要通过数组来传送数据了。不过在本例中我们不会用到value[3], 我们只用到val和sw。那么好,val是直接控制电机转速的参数,所以我们在发送端发送的也就是val,接收端接收的参数也是val。sw是什么呢?正如前面我们讲到,我们需要一个安全按钮,当安全按钮按下去的时候,我们才能够控制电机转速,当安全按钮松开时,我们认为整个系统是锁死状态,此时电机转速为0;而这个sw其实就是用来监测安全按钮状态的参数。具体的逻辑在下面会讲到。

    setup主函数---这个函数内部写的是一些NRF24L01的设置参数,比如我们在通信的时候接收端和发送端之间总得有一些协议吧,这个很好理解。比如你作为发送端在讲中文,而接收端的人是个老外只懂英语,那你们俩就不是在一个频道上,没法沟通的。所以我们在通讯之前先要商量好,咱们通讯统一采用中文或者英文,这个就可以理解为频道的设定。另外还有地址的设定,这个就好比你在那儿嚷嚷,但是你是冲谁嚷嚷呢?世界那么大你总得有个对象,你得告诉对方我在和你说话呢,你听着点啊。对不对?我们在日常交流中也是一样的,你在沟通之前总得先问个好或者打个招呼,引起对方注意,然后大家开始交流,你不能直接站那儿说:“这事儿怎么办的?”那对方肯定会问一句:“你和我说话吗?”你看其实程序在很多方面和我们人类是一样的。所以在setup主程序中你必须设定这些参数,告诉发送端它对应的接收端是谁,它们之间用哪种频率通话等等。当然这些设置其实在图5中都已经设置好了,你大可不必去修改,而你需要添加或者修改的位置是我标蓝的部分。首先你要确认通信速率,图中是9600比特/s,这个数值你可以任意选(当然是IDE面板中有的数值,你可以通过下拉条来选择不同数值,IDE面板的构成在第一篇中就讲过了)但是你需要保证发送端和接收端的通信速率是一样的,也就是发送端你用了9600,那么在接收端的程序中也会有这一项并且你也要设置为9600。

    loop主函数---和setup主函数一样,里面默认的设定你都不需要更改,你需要做的就是在里面增加一些你需要的操作。图6中loop主函数内我增加了一段判断程序,这个判断程序的逻辑是这样的:首先我读取2号引脚的值(这个2号引脚就是图5中的pin2,它的值按照图5所示,取决于安全开关的开闭状态),并把这个值赋值给sw,当2号引脚输出低电平时,sw的值就会是0,当2号引脚输出为高电平时,sw的值就会是1。好,接下来我判断,当sw=0的时候,我把val强行定为0,当sw!=0的时候,我才允许对val进行赋值,然后这个赋值的内容来自于A5引脚的读数。这样我们就完成了安全按钮和控制杆的程序编写,在运行过程中是这样运行的。当我按下安全按钮的时候,2号引脚输出高电平,sw赋值为1,这个时候程序会运行else if后面的语句,Arduino会读取A5引脚的数值,并将其赋值给val,而这个A5引脚是一个模拟输入引脚,它采集的是遥控杆的信号;当我们松开安全按钮的时候,2号引脚输出低电平,sw赋值为0,此时我们将val的值设定为0。这样一来我们就将遥控杆的信号,安全按钮的信号和val联系上了,当安全按钮激活后,通过拨动遥控杆val的数值会不断变化,然后val会被发送端发送给接收端,接收端再根据val值的大小对电机转速进行控制。

    以上就是发送端的程序,下面来讲接收端的程序。

    图7

    图7为接收端程序,接收端程序的整体结构和发送端一致,也都有:头文件,参数定义,setup主函数和loop主函数。所以就不再累述整体结构了,只讲一些与发送端不同的部分,大概分这两点:1.加入了自定义函数,2.伺服电机的控制。下面我们一一讲解:

    1.自定义函数---在接收端加入自定义函数的目的是为了让程序可读性更高。在图6的发送端程序中,我们可以看到有一大部分语句是在定义频道和地址这一类信息,这些东西都是重复性的信息并且也是我们不怎么会去修改和触碰的。在接收端也一样,同样存在这些默认的语句,那么为了让我们程序可读性更高,我们可以把这些默认的语句打包放到别的窗口中,在主窗口中只留下我们自己写的语句,这样就可以让我们的程序看起来干净很多。如图7中所示,我在主窗口之外又另开了两个窗口:一个取名为RevReading,一个取名为RevSetting,主窗口取名为NRF24L01 Receiving。RevReading和RevSetting是用来存放两个自定义函数的,那么这两个自定义函数是怎么构成的呢?其实很简单我们只需要在新窗口中给自定义函数命个名,例如:void RevReading() {...},然后把我们前面讲的那些默认语句和设定语句拷贝过来,放到{...}里面就可以了,是不是很简单?当然你在命名的时候可以取任何名字,没必要把函数名和窗口名用一样的名字,比如RevReading这个窗口中我采用了同样的名字来充当函数名,但是你也可以用a,b或者c等等来作为函数名void a() {...}或者void b() {...}诸如此类等。好了,自定义函数就是这么一回事,目的就是让程序更加简洁,明了。

    2.伺服电机控制---在这个例子中,我们所用到的电机是无刷直流电机,这种电机动力足转速快一般用在中等或者比较大的无人机上。前面几篇文章中讲到的空心杯电机是一种有刷直流电机,动力和转速都比较小,通常用在迷你无人机上。在讲解图7中关于电机的语句之前,我们先简单介绍一下什么是无刷直流电机。见图8。

    图8

    图8中表示的是无刷直流电机接线方式。整个无刷电机的驱动分成四大部分:电机的本体,电调,Arduino控制版,电机电源。各部分的功能讲解如下:

    无刷直流电机本体:这种电机区别于传统有刷直流电机的点在于:它没有机械换向器,传统的有刷电机如果你拆开看的话,会发现在转轴上有两个小铁刷,这两个小铁刷是用来变换电流方向的,以保证电机可以连续地向着同一个方向旋转;无刷电机没有这两个小铁刷,而是采用电子换向的方法来周期性地改变电流方向,这样的好处在于更安静更耐磨,因此转速也可以设计的更快。

    电调:上面讲了无刷电机采用电子换向,那么这个电子换向是什么呢?就是这个电调,具体原理在这里就不讲了,反正记住一点:无刷电机电调的角色就相当于有刷电机里面的电刷。从图8中可以看到,无刷电机和电调之间有三根线连接起来,这三根线可以和电调任意搭配连接,如果想要改变电机转动方向,则将其中任意两根线对调即可。

    Arduino控制板:Arduino控制板和电调之间由三根线连接,一根5V,一个接地,一根PWM信号线。因为电调里面是有芯片的,所以需要5V和接地这两根线来给芯片供电,然后剩下的那根PWM信号线则是输出PWM信号给电调,控制电机转速的。我们在第一篇中讲到过PWM控制,PWM采用占空比来控制输出,而占空比就是在一个信号周期中高电平的时间比例,在一个具有特定周期的PWM信号中我们通过改变高电平的输出时间从而得到不同的占空比。在Arduino控制的无刷电机中,一个周期内高电平占据时间达到1000毫秒时电机转速为0,一个周期内高电平占据时间为2000毫秒时电机转速达到最高转速,在1000毫秒到2000毫秒之间电机转速由小到大,注意这个不是正比上升的关系,也就是说1500毫秒的占空比并不意味着中间速度,可能是比中间速度更快一点的速度,具体你可以通过自己尝试来找到这个关系。

    电机电源:这里我特地强调是电机的电源,为的就是和Arduino控制版上那个5V和接地线区分开来。由于无刷电机功率大所以无法用Arduino来驱动,因此需要单独给电机提供一个动力电源,而Arduino仅仅提供控制信号及控制信号的电源。

            好的,上面讲完了无刷电机的一些知识,下面就进入无刷电机的控制程序了。其实无刷电机的控制和舵机原理是一样的,舵机的控制我们在第三篇文章中有所提及,在第三篇文章中,我们在介绍采用“舵机库”控制舵机的时候说到:可以直接输入旋转角度,舵机就会自动转动相应角度;也可以通过设定PWM占空比,通过不同的占空比对应不同的转角来控制舵机。其实无刷电机原理和舵机一样,你可以把无刷电机当作舵机,然后可以用“舵机库”来控制无刷电机,唯一的区别是:舵机中占空比对应的是旋转角度,无刷电机中的占空比对应的是转速。

            在图7中打红框的部分就是与无刷电机控制相关的语句,打蓝框的部分是NRF24L01的参数设置和信号接收部分语句。如前所述,NRF24L01的参数设置和信号接收部分的语句我打包成两个独立函数来调用了,所以我们只需要关注红色的电机控制部分内容即可,是不是清晰很多。好,接下来就是控制电机了,有这么几个步骤:

    1.首先就是把类给对象化,也就是形如Servo servo1的这几条语句,关于“什么是类的对象化”,“为什么要对象化“,我在第三篇文章中做过详细的讲解,这里就不累述了。

    2.在完成对象化以后servo1,servo2,servo3,servo4这四个对象就对应了我们的四个电机,然后我们需要给它们指派相应的控制引脚,分别为10,9,6,5这四个引脚,这四个引脚将会产生PWM信号给电调,进而控制电机转速,注意必须是Arduino上带有”~“标志的引脚才能够输出PWM信号。

    3.把接收到的val映射成PWM中高电平的时间量。什么意思呢?我们结合图7中的程序来讲,if(val>=0 and val<=510) {val=map(val,0,510,1000,1200}这句话的意思是如果接收到的val的数值处于0~510之间的话,那么我们将0~510这个范围映射为1000~1200这个范围。然后下一句else if(val>510 and val<=1023) {val=map(val,511,1023,1201,2000)意味着如果接收到的val数值处于511~1023之间的话,则映射到1201~2000的范围内。1000,2000?是不是有点熟悉?对的,就是前面讲的PWM中高电平时间,也就是说在映射以后获得的val值将直接作为高电平的时间传送给电调来控制电机转速。另外还有一个问题,有的同学可能会问:“你为什么要分两段映射呢?”这个因为如果我直接将0~1023映射到1000~2000上,那么控制杆在平衡位置时A5引脚的输出数值510对应为1500,用1500来控制电机转速过快了,我希望在控制杆处于平衡位置时电机仅仅拥有一个较低的基础转速,所以我希望控制杆的平衡位置对应大约1200左右即可。

    4.监控val的数值。Serial.print(Got Data:); Serial.println(val);这两句语句的作用是将val显示在电脑屏幕上。这一步可有可无,不过最好有,因为可以实时监控你的val值是否正常。正常来讲当val为0的时候电机转速为0, 当val为2000时电机转速为最大转速。

    5.将val的值赋给电机,让电机转起来。servo1.writeMicroseconds(val),这句语句的意思就是把val赋给电机servo1,servo1是电机的名字,就是程序开头“对象化”时候定义的。writeMicroseconds的意思是以毫秒为单位,意味着括号中val的单位是毫秒。那么当val为1000的时候,意思就是把1000毫秒赋给电机servo1,这个1000毫秒将会成为PWM信号中高电平的持续时间。

    好了,至此无人机的电机遥控就讲完了,这只是最基础的部分,要想让无人机平稳地飞行还需要加入PID控制算法,这个在后续文章中再进行讲解。

    相关文章

      网友评论

      本文标题:Arduino菜鸟通俗版解读系列(5)NRF24L01遥控串口通

      本文链接:https://www.haomeiwen.com/subject/vypubftx.html