预备知识

  • Zigbee无线通信,需要高频的载波来提供发射效率,Zigbee模块之间要可以正常的收发,接收模块必须把接收频率设置和发射模块的载波频率一致。
  • Zigbee有27个载波可以进行通信,载波叫做信道(无线通信的通道)。这些载波的频率落在某些频率区段,我们把这些区段叫做频段。
    • 2.4G频段 16个信道
    • 915M频段 896M频段 11个信道
    • 但TI的所有支持Zigbee底层协议的芯片只能在2.4G频段的16个信道里进行通信,即11-26信道。
  • 网络地址:在Zigb
  • ee无线局域网里,每一模块都一个在该网络里唯一的2个字节的地址,这个地址叫做网络地址,网络短地址。
  • PANID:这是一个2个字节的编码,用来区别不同的Zigbee无线局域网,个域网ID.

代码分析

这里不在详细介绍,只给出关键配置代码的代码模板

对于数据发送接收的程序来说,基本的无线通信的基础配置是固定的,只需要知道里面需要自己每次更改的部分即可

将两个接收和发送的代码分别烧录到两个开发板中,实验现象是:发送板按下按键发送你设定的数字SENDVAL到接收板,接收板通过数码管显示该数字

发送数据

分析

void halRfInit(void):无线通信初始化函数

void RFSend(char *pstr,char len):无线数据发送函数

__interrupt void RF_IRQ(void):无线通信中断函数

void RevRFProc():无线数据接收函数

无线通信初始化函数

在这个函数中,需要修改的只有FREQCTRLPAN_ID1两项的值

image-20231010205943211

image-20231010210142126

image-20231010210253727

无线数据发送函数

可以直接看注释,这个函数基本不用改动,使用时直接掉这个函数就行,传入发送数组和数组长度即可

image-20231010210834002

中断函数

image-20231010211712457

发送数据包数组

1
char SendPacket[]={0x0c,0x61,0x88,0x00,0x07,0x20,0xEF,0xBE,0x20,0x50,SENDVAL};
  • 第一个字节0x0C含义,这个自己后面还有12个字节要发送,实际上是10(后面跟的)+2(默认自动添加的CRC 码)
  • 第5 6个字节表示的是PANID
  • 第7 8个字节是无线模块目标设备的网络地址 0xBEEF
  • 第9 10就是本地模块的网络地址
  • 11 个字节是我们有用的数据
  • CRC码 12 13个字节 是硬件自动追加

主函数中的注意事项

image-20231010213115722

发送数据模板代码

将两个接收和发送的代码分别烧录到两个开发板中,实验现象是:发送板按下按键发送你设定的数字SENDVAL到接收板,接收板通过数码管显示该数字

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include<iocc2530.h>
#include"74LS164_8LED.h"
#define SENDVAL 5

char SendPacket[]={0x0c,0x61,0x88,0x00,0x07,0x20,0xEF,0xBE,0x20,0x50,SENDVAL};
//第一个字节0x0C含义,这个自己后面还有12个字节要发送
//第5 6个字节表示的是PANID
//第7 8个字节是无线模块目标设备的网络地址 0xBEEF
//第9 10就是本地模块的网络地址
//11 个字节是我们有用的数据
// CRC码 12 13个字节 是硬件自动追加

void Delay()
{
int y,x;

for(y=1000;y>0;y--)
for(x=30;x>0;x--);
}

void Init32M()
{
SLEEPCMD &=0xFB;//1111 1011 开启2个高频时钟源
while(0==(SLEEPSTA & 0x40));// 0100 0000 等待32M稳定
Delay();
CLKCONCMD &=0xF8;//1111 1000 不分频输出
CLKCONCMD &=0XBF;//1011 1111 设置32M作为系统主时钟
while(CLKCONSTA & 0x40); //0100 0000 等待32M成功成为当前系统主时钟
}

void KeysIntCfg()
{//Key3 Key4 Key5

IEN2|=0x10;//开P1IE组中断
P1IEN|=0x02;//开Key3组内中断
PICTL|=0x02;//设置P1_1为下降沿
EA=1; //开总中断
}
void halRfInit(void)
{
EA=0;
FRMCTRL0 |= 0x60;

// Recommended RX settings
TXFILTCFG = 0x09;
AGCCTRL1 = 0x15;
FSCAL1 = 0x00;

// enable RXPKTDONE interrupt
RFIRQM0 |= 0x40;//把射频接收中断打开
// enable general RF interrupts
IEN2 |= 0x01;

//设置信道
FREQCTRL =(11+(25-11)*5);//(MIN_CHANNEL + (channel - MIN_CHANNEL) * CHANNEL_SPACING);
//设置载波为2475M

//设置ID
PAN_ID0=0x07;
PAN_ID1=0x20; //0x2007

RFST = 0xEC;//清接收缓冲器
RFST = 0xE3;//开启接收使能
EA=1;
}

//发送无线数据
void RFSend(char *pstr,char len)
{
char i;
RFST = 0xEC; //确保接收是空的
RFST = 0xE3; //清接收标志位
while (FSMSTAT1 & 0x22);//等待射频发送准备好
RFST = 0xEE;//确保发送队列是空
RFIRQF1 &= ~0x02;//清发送标志位
//为数据发送做好准备工作

for(i=0;i<len;i++)
{
RFD=pstr[i];
} //循环的作用是把我们要发送的数据全部压到发送缓冲区里面

RFST = 0xE9; //这个寄存器一旦被设置为0xE9,发送缓冲区的数据就被发送出去
while(!(RFIRQF1 & 0x02) );//等待发送完成
RFIRQF1 = ~0x02;//清发送完成标志
}


void main()
{
LS164_Cfg();//74LS164控制数码管的初始化
Init32M(); //主时钟晶振工作在32M
KeysIntCfg();

halRfInit();//无线通信的初始化 初始化相关的寄存器,配置工作信道,和PANID

SHORT_ADDR0=0x50;
SHORT_ADDR1=0x20;//设置本模块地址 设置本模块的网络地址0x2050
//大小端模式问题,

LS164_BYTE(1);
while(1);
}

#pragma vector=P1INT_VECTOR
__interrupt void Key3_ISR() //P1_1
{
if(0x02 & P1IFG)
{
Delay();
if(0==P1_1)
{
P1DIR |=0X01;
P1_0 ^=1;
RFSend(SendPacket,11);//发送数据
}
}

P1IFG=0;
P1IF=0;
}


#pragma vector=RF_VECTOR
__interrupt void RF_IRQ(void)
{//这个是射频中断函数,当小灯模块接收到开关模块发送来的数据时,小灯模块的CPU就会进入中断函数执行
EA=0;
if( RFIRQF0 & 0x40 )
{
RFIRQF0&= ~0x40; // Clear RXPKTDONE interrupt
}
S1CON= 0; // Clear general RF interrupt flag
RFST = 0xEC;//清接收缓冲器
RFST = 0xE3;//开启接收使能
EA=1;
}

接收数据

分析

初始化函数和发送数据的初始化函数一样,只需要把发送函数去掉,添加一个接收函数即可

注意

  1. 两相互通信的模块之间,初始化函数中的接收和发送的信道和PANID一定要一样
  2. 接收工程的主函数中设置的SHORT_ADDR网络地址需要和发送的数据包中的第7、8两个字节所生成的网络地址一致

中断函数

image-20231010213214831

无线数据接收函数

image-20231010214105096

接收数据模板代码

将两个接收和发送的代码分别烧录到两个开发板中,实验现象是:发送板按下按键发送你设定的数字SENDVAL到接收板,接收板通过数码管显示该数字

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include<iocc2530.h>
#include"74LS164_8LED.h"
#define SENDVAL 5

char SendPacket[]={0x0c,0x61,0x88,0x00,0x07,0x20,0xEF,0xBE,0x20,0x50,SENDVAL};
//第一个字节0x0C含义,这个自己后面还有12个字节要发送
//第5 6个字节表示的是PANID
//第7 8个字节是无线模块目标设备的网络地址 0xBEEF
//第9 10就是本地模块的网络地址
//11 个字节是我们有用的数据
// CRC码 12 13个字节 是硬件自动追加

void Delay()
{
int y,x;

for(y=1000;y>0;y--)
for(x=30;x>0;x--);
}

void Init32M()
{
SLEEPCMD &=0xFB;//1111 1011 开启2个高频时钟源
while(0==(SLEEPSTA & 0x40));// 0100 0000 等待32M稳定
Delay();
CLKCONCMD &=0xF8;//1111 1000 不分频输出
CLKCONCMD &=0XBF;//1011 1111 设置32M作为系统主时钟
while(CLKCONSTA & 0x40); //0100 0000 等待32M成功成为当前系统主时钟
}

void halRfInit(void)
{
EA=0;
FRMCTRL0 |= 0x60;

// Recommended RX settings
TXFILTCFG = 0x09;
AGCCTRL1 = 0x15;
FSCAL1 = 0x00;

// enable RXPKTDONE interrupt
RFIRQM0 |= 0x40;//把射频接收中断打开
// enable general RF interrupts
IEN2 |= 0x01;

//设置信道
FREQCTRL =(11+(25-11)*5);//(MIN_CHANNEL + (channel - MIN_CHANNEL) * CHANNEL_SPACING);
//设置载波为2475M

//设置ID
PAN_ID0=0x07;
PAN_ID1=0x20; //0x2007

//halRfRxInterruptConfig(basicRfRxFrmDoneIsr);

RFST = 0xEC;//清接收缓冲器
RFST = 0xE3;//开启接收使能
EA=1;
}

//接受无线数据
void RevRFProc()
{
static char len;
static char ch;

len = ch = 0;
RFIRQM0 &= ~0x40;
IEN2 &= ~0x01;
EA = 0;

len = RFD;//读第一个字节判断这一串数据后面有几个字节;

while (len > 0)
{//只要后面还有数据那么就把他都从接受缓冲区取出来
ch=RFD;
if(3 == len)
{//读取倒数第三个字节的数字,通过数码管显示
LS164_BYTE(ch);
}
len--;
}
EA=1;
RFIRQM0 |= 0x40;
// enable general RF interrupts
IEN2 |= 0x01;
}


void main()
{

LS164_Cfg();//74LS164控制数码管的初始化
Init32M(); //主时钟晶振工作在32M
halRfInit();

SHORT_ADDR0=0xEF;
SHORT_ADDR1=0xBE;//设置本模块地址 0xBEEF

LS164_BYTE(2);
while(1);
}

#pragma vector=RF_VECTOR
__interrupt void RF_IRQ(void)
{//这个是射频中断函数,当小灯模块接收到开关模块发送来的数据时,小灯模块的CPU就会进入中断函数执行
EA=0;
if( RFIRQF0 & 0x40 )
{

RevRFProc();

RFIRQF0&= ~0x40; // Clear RXPKTDONE interrupt
}
S1CON= 0; // Clear general RF interrupt flag
RFST = 0xEC;//清接收缓冲器
RFST = 0xE3;//开启接收使能
EA=1;
}