树莓派与Arduino间的通信实践


最近需要在Arduino之间,以及Arduino和上位机(树莓派)之间传输数据,
原有APC220设备虽然可用,使用也方便,但成本太高,不容易批量,遂寻求其他方案。

一、方案选择

根据搜索的结果和前人经验,有如下几种可行方案:

  • nRF24L01+(RF)

  • ESP8266(WIFI)

  • XBee (ZigBee)

  • ENC28J60(LAN)

  • W5100,W5500(LAN)

其中,Xbee为最优选,但成本太高。
LAN方案不方便,WIFI方案功耗高,蓝牙方案传输距离短,
故考虑RF方案,成本和效果平衡较好。
nRF24L01+价格便宜(5块左右,做工好带天线的15左右),编程简单,
且存在能同时支持树莓派、Arduino、Linux的RF24库。
Git连接为:https://github.com/TMRh20/RF24.git

便宜的(做工一般的)nRF24L01开发板:

二、接线

1、nRF24L01+引脚图

– 1:地
– 2:3.3V(切不可接5V,烧片)
– 3:CE(RF读写控制引脚)
– 4:CSN(选片引脚)
– 5:SCK(SPI时钟)
– 6:MOSI(SPI主出从入)
– 7:MISO(SPI主入从出)
– 8:IRQ(外部中断)

2、接线方法

编号 nRF24L01 Arduino Mega Arduino UNO Rpi(物理管脚)
1 GND 9
2 VCC 1
3 CE 7(可自定义) 7(可自定义) 15(GPIO22)
4 CSN 8(可自定义) 8(可自定义) 24(SPI0CS1)
5 SCK 52 13 23
6 MOSI 51 11 19
7 MISO 53 12 21
8 IRQ

接线示意图:
Arduino UNO

Arduino Mega

RaspberryPi3

三、代码 & 运行

RF24库中自带的GettingStarted例子非常方便,其代码包含发送端和接收端两种类型,
默认为接受模式,输入T时切换为发送,输入R则切为接受模式,并有简单的超时判断。
为了易于理解,可简单修正代码,让接收端返回一自增数字。

1、 Arduino
1)RF24库安装
https://github.com/TMRh20/RF24.git下载RF24后,
将其复制到Arduino安装目录下的libraries目录下,启动ArduinoIDE后,从例子中选择RF24->GettingStarted。

2)代码修改
发送端不必修改,直接编译上传即可。(注意UNO和Mega的选择和串口选择)
接收端将代码中的radioNumber从默认的0修改为1。如下:

但是针对某一款具体的单片机,IAR都有一个单独的安装包,所以,名义上IAR支持的单片机种类最多,但是实际上,它也是一款单片机一个配套软件,只不过对于所有的单片机来说,IAR的“长相”基本类似,所以只要知道了一种单片机在IAR下的使用方法,那么再用IAR开发另一种单片机的时候,按图索骥就能知道大致的使用方法,例如关于芯片选择,堆栈配置、仿真设置这些选项,基本上都是在某个具体选项卡下面,很容易就能上手。(当然,IAR开发单片机和ARM的时候,项目配置的差别还是很大的。)

1
2
3
bool
radioNumber = 0; (自身为2Node,发送给1Node)
  
->
bool
radioNumber = 1;(自身为1Node,发送给2Node)

简言之,1Node为接收端,2Node为发送端。
建议:原代码中的got_time不易观察理解,
可将接收端中的got_time发送前赋值为一静态可增计数值。

3)运行
发送端启动后,输入T,使其进入发送模式。
接受端启动即可,无需输入R。(默认为R接收模式)
如上述配置接线正常,可在Serial Monitor中看到发送方和接收方的输出,大致如下:
发送方图(静态自增变量):

2、 树莓派(RaspberryPi)
本文中使用的树莓派为 16年新发布的RPi3 B型,其管脚如下:

1)RF库安装
将RF24库复制到树莓派(或通过git直接获取)。进入RF24目录后执行如下命令,进行编译和安装(选择SPI方式)

1
2
.
/configure
--driver=SPIDEV
sudo
make
install
-B

2) 修改系统配置

1
2
3
4
5
修改/etc/modprobe.d/raspi-blacklist.conf,如果其中存在
blacklist spi-bcm2708,将其注释。
 
修改/etc/modules文件,在其中追加一行,开启SPI。
spidev

reboot重启树莓派后,/dev下会新增spidev0.0和spidev0.1两个设备文件。

3)代码修改
修改RF24/example_linux/GettingStarted.cpp文件,
同上面的Arduino一样,发送端不必修改,
接收端将radioNumber从默认的0修改为1,并建议吧回送的时戳数据改为自增数字。
在当前目录下执行make后,生成GettingStarted的二进制文件。

4)运行
使用sudo ./ GettingStarted执行,并输入0进入接收模式。
如Arduino的发送端配置、运行正常,则正常发送回应包。大致如下(自增变量版):

四、注意&体会

便宜版本的nRF24L01效果一般,很容易受到干扰。带天线的会好些,真做项目不可图便宜。
接线要准确,SPI要理解下原理。CE、CSN其实是可以任意指定的,只是要修改下RF24的初始化代码。

五、RH24例子代码简单说明

以下是RH24(TMRh20)自带的Arduino例子,简单说明一下,
树莓派上为C语言实现的版本,变量、语法略有区别,但逻辑是基本一致的。

  • 变量定义

1
2
3
4
5
6
7
8
9
10
bool
radioNumber = 1;  
// RF节点名称决定Flag
 
/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
// 指定CE用GPIO7,CSN用GPIO8,需要和接线一致
// 如接线不采用7,8,代码这里需要修改。
RF24 radio(7, 8);
 
byte addresses[][6] = {
"1Node"

"2Node"
}; 
// 两个节点名
 
bool
role = 0; 
// 发送&接收模式Flag
  • 初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void
setup() {
  
Serial.begin(115200);
  
Serial.println(F(
"RF24/examples/GettingStarted"
));
  
Serial.println(F(
"*** PRESS 'T' to begin transmitting to the other node"
));
 
  
radio.begin();
  
radio.setPALevel(RF24_PA_LOW);
 
  
// Open a writing and reading pipe on each radio, with opposite addresses
  
if
(radioNumber){
    
radio.openWritingPipe(addresses[1]);
    
radio.openReadingPipe(1,addresses[0]);
  
}
else
{
    
radio.openWritingPipe(addresses[0]);
    
radio.openReadingPipe(1,addresses[1]);
  
}
 
  
// 默认为监听模式,开始监听
  
radio.startListening();
}
  • 执行逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
void
loop() {
 
/****************** Ping Out Role ***************************/ 
  
if
(role == 1)  { 
// 发送模式
 
    
radio.stopListening();  
// 发送数据前要停止监听
    
Serial.println(F(
"Now sending"
));
 
    
unsigned 
long
start_time = micros(); 
// 待发送的时戳
 
    
// RF24内部会自动处理payload和发送数据不等长的问题
    
if
(!radio.write( &start_time, 
sizeof
(unsigned 
long
) )){ 
// 发送数据
       
Serial.println(F(
"failed"
));
     
}
 
    
radio.startListening(); 
// 数据发送完,需要监听回应数据的到来
 
    
unsigned 
long
started_waiting_at = micros();
    
boolean timeout = 
false
;
 
    
while
( ! radio.available() ){  
// 超时判断
      
if
(micros() - started_waiting_at > 200000 ){
          
timeout = 
true
;
          
break
;
      
}     
    
}
 
    
if
( timeout ){ 
        
Serial.println(F(
"Failed, response timed out."
));
 
    
}
else

// 读数据并显示数据和间隔时间
        
unsigned 
long
got_time; 
        
radio.read( &got_time, 
sizeof
(unsigned 
long
) );
        
unsigned 
long
end_time = micros();
 
        
// Spew it
        
Serial.print(F(
"Sent "
));
        
Serial.print(start_time);
        
Serial.print(F(
", Got response "
));
        
Serial.print(got_time);
        
Serial.print(F(
", Round-trip delay "
));
        
Serial.print(end_time-start_time);
        
Serial.println(F(
" microseconds"
));
    
}
 
    
// Try again 1s later
    
delay(1000);
  
}
 
/****************** Pong Back Role ***************************/
if
( role == 0 )  { 
// 接收模式
    
static
long
count = 1;  
// 自增计数
    
unsigned 
long
got_time = 0;
 
    
if
( radio.available()){
 
      
while
(radio.available()) { 
// 读数据
        
radio.read( &got_time, 
sizeof
(unsigned 
long
) );
      
}
 
      
got_time = count++;  
// 为便于理解,回送自增计数值
      
radio.stopListening();
      
radio.write( &got_time, 
sizeof
(unsigned 
long
) ); 
// 写回应
      
radio.startListening();
      
Serial.print(F(
"Sent response "
));
      
Serial.println(got_time); 
   
}
 
}
 
/****************** Change Roles via Serial Commands ***************************/
if
( Serial.available() )  { 
// 发送&接收模式通过串口决定
    
char
c = 
toupper
(Serial.read());
    
if
( c == 
'T'
&& role == 0 ){  
// 发送模式
      
Serial.println(F(
"*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"
));
      
role = 1; 
 
   
}
else
    
if
( c == 
'R'
&& role == 1 ){  
//接受模式
      
Serial.println(F(
"*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"
));     
       
role = 0;
       
radio.startListening();   
    
}
  
}
 

// Loop

五、参考URL

详细参考(英语):
http://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo

其他人的成功经验:
http://www.cnblogs.com/hangxin1940/archive/2013/05/01/3053467.html
http://www.cnblogs.com/hangxin1940/archive/2013/05/01/3048315.html
注意:其所使用的RF24库并不相同

转载自 http://blog.csdn.net/ydogg/article/details/53307365

本文来自:树莓派实验室
链接地址:http://shumeipai.nxez.com/2017/03/20/communication-between-arduino-and-raspberry-pi.html

> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >

从我的理解出发,单片机是相当“底层”和“硬件”的东西,没有太多高深的数据结构,也没有太多需要技巧的编程算法,只要我们从单片机执行的角度,去理解和编程它们的动作情况就行了。

生成海报
点赞 0

thePro

我还没有学会写个人说明!

相关推荐

数码管轮播显示

硬件: 数码管10个引脚,8个控制二极管亮、灭,2个引脚为负极。1k欧电阻链接负极,接gnd。 将8个二极管引脚依次链接0-7号引脚。 软件: /*共阳极单数码管显示&