仿百科荣创小车通信,使用nRF24L01实现多块单片机通信
开发平台51单片机、艾姆克EasySTC8_V2.0系统板(可实现不同型号单片机无线通信)
以下为具体内容
1. (半双工)测试搭建使用的平台
主端:艾克姆EsaySTC8开发板+LCD12864+nRF24L01p
副端:STC15最小系统板+nRF24L01p
2. 实际运用
启动时,副端监听,主端发送数据,当副端接收到命令,执行后续程序,此时主端可在发送完数据执行监听,当副端执行完任务,可发送数据给主端,主段继续执行接下来的任务。与百科荣创嵌入式智能小车实际ZigBee通信类似,它是两辆编程车与一个控制任务平台使用。
3. 原理
驱动代码:基于艾姆克EasySTC8_V2.0系统板初始nRFL01p测试的驱动代码,添加LCD12864驱动代码,无需添加其他驱动代码。
添加的函数:修改nRF24L01p发送地址函数,LCD12864显示字符函数(非指针),发送数据函数和接收数据函数。
关键的变量:无线接收数组和无线发送数组,无线接收和无线发射缓存的首位缓存,nRF24L01p的发送和接收地址两个地址数组(很重要)。
关键的算法:8位数据校验位算法。
发送原理: (注释二次的是预留测试程序)
void TX_ADDRESS_Configure(uint8 x,uint8 y,uint8 z,uint8 c,uint8 v){
TX_ADDRESS[0]=x;
TX_ADDRESS[1]=y;
TX_ADDRESS[2]=z;
TX_ADDRESS[3]=c;
TX_ADDRESS[4]=v;
Set_TxMode_MA();//nRF24L01p写入发送地址,转为发送模式
}
void TXnRF(uint8 x,uint8 y,uint8 z,uint8 c,uint8 v){
uint8 i;
switch(x){//注意接收地址要先去nRF24l01p.c里配置,相当于单片机的手机号,不配置就收不到
case 1:
TX_ADDRESS_Configure(0xE1,0xE2,0xE3,0xE4,0xE5);//对应第一块单片机的接收地址
break;
case 2:
TX_ADDRESS_Configure(0xE1,0xE2,0xE3,0xE4,0xE5);//对应第二块单片机的接收地址
break;
case 3:
TX_ADDRESS_Configure(0xE1,0xE2,0xE3,0xE4,0xE5);//对应第三块单片机的接收地址
break;
default:
break;
}//自行更改,这里只是示范
delay_ms(50);//可删除
TxData[1]=x;
TxData[2]=y;
TxData[3]=z;
TxData[4]=c;
TxData[5]=v;//这里懒得写了,直接了当的替换
TxData[6] = (TxData[2] + TxData[3] + TxData[4] + TxData[5]) % 256;//校验位算法
for(i=0;i<8;i++){
TxPayload[0]=TxData[i];//写入无线发送缓存
if(NRF24L01_MA_TxPacket(TxPayload) == MAX_TX) //如果发送成功
{
//// Disp2(2,i,1,TxData[i]+0x30);//显示数字,但有间隔
}
delay_ms(50);//延时可调整,根据环境最小27ms,建议50ms
}//自行更改,这里只是示范
led=~led;//NRF24L01模块状态指示灯翻转
delay_ms(20);//等待NRF24L01p硬件响应,自行调整
}
int main() //主函数
{
Init_NRF24L01_MA(); //初始化
Ini_Lcd(); //液晶初始化子程序
Disp(1,0,16,"LCD12864检测成功");
// Disp2(2,0,2,5+0x30-1);//覆盖显示,这种方式不改变驱动代码的情况下,可以做到增加显示位数,
//每位是原位数加1,详情看后面第二种显示方法
while(1)
{
TXnRF(1,1,2,3,4);//添加发送或接收数据函数或者需要执行的命令
//RXnRF();//添加发送或接收数据函数或者需要执行的命令
}
}
接收原理:
void RXnRF(){
uint8 i,j=0,k=0;
RxPayload[0]=0x00;
Set_RxMode_MA();//配置nRF24L01为接收模式
Disp2(2,1,1,5+0x30);
//delay_ms(20);
while(1){
if(NRF24L01_MA_RxPacket(RxPayload) == RX_OK) //如果接收成功
{
if(RxPayload[0]==0xBB){//收到的是包尾,关闭k标志位,j计数复位
j=0;
k=0;
led2=1;
led=0;
//// Disp2(3,0,1,RxData[0]-0x25);//显示数字,但有间隔
//// Disp2(3,6,1,RxData[6]+0x2F);//显示数字,但有间隔
//// Disp2(3,7,1,RxData[7]-0x88);//显示数字,但有间隔
//// for(i=1;i<6;i++){
//// Disp2(4,i,1,RxData[i]+0x30);//显示数字,但有间隔
//// }
if(RxData[6]==((RxData[2] + RxData[3] + RxData[4] + RxData[5]) % 256)){
//校验位算法
for(i=1;i<6;i++){
Disp2(2,i,1,RxData[i]+0x30);//显示数字,但有间隔
}
// {//第二种,覆盖显示,充分利用LCD12864
// Disp2(3,0,5,RxData[4]+0x30-4);
// Disp2(3,0,4,RxData[3]+0x30-3);
// Disp2(3,0,3,RxData[2]+0x30-2);
// Disp2(3,0,2,RxData[1]+0x30-1);
// Disp2(3,0,1,RxData[0]+0x30);
// }
}
//这块显示也可改成单片机动作,比如处理数据、显示数据在数码管上、开关蜂鸣器、开关LED、验证密钥等。
break;
}
if(RxPayload[0]==0x55){//收到的是包头,开启k标志位,j计数复位
led2=0;
led=1;
k=1;
j=0;
//Disp(2,0,16," ");//第二行清空
//led=~led;//NRF24L01模块状态指示灯翻转
}
if(k==1){
RxData[j]=RxPayload[0];//保存接收到的数据
//// Disp2(2,j,1,RxPayload[0]+0x30);//显示数字,但有间隔
j++;
}
RxPayload[0] = 0; //清除
}//自行更改,这里只是示范
}
////delay_ms(500);
////Disp(3,0,16," ");
////Disp(4,0,16," ");
}
//这里复制时对齐有问题,我也不知道为什么。
int main() //主函数
{
Init_NRF24L01_MA(); //初始化
Ini_Lcd(); //液晶初始化子程序
Disp(1,0,16,"LCD12864检测成功");
// Disp2(2,0,2,5+0x30-1);//这种方式不改变驱动代码的情况下,可以做到增加显示位数,每位是原位数加1,详情看后面第二种显示方法
while(1)//循环监听
{
//TXnRF(1,1,2,3,4);//添加发送或接收数据函数或者需要执行的命令
RXnRF();//添加发送或接收数据函数或者需要执行的命令
}
}
4. 存在的BUG:
副端平台采用杜邦线连接,始终无法接收到数据,用初始程序也无法收到,但互换程序主端可成功接收到数据,采用其他单片机平台建议替换 头文件.h 并用初始测试工程测试。
4. 查阅资料:
ASCII.html
2019智能嵌入式实训系统通信协议V4.2.pdf
CSDN:Vcc海波
2020年5月29日首发,使用/二创请注明