本文将分析一个利用CC2530实现无线串口,文中将会列举部分代码并对CC2530的具体操作进行分析。本文的具体的内容包括以下几个部分:
为了实现无线串口功能,需要准备两套CC2530模块和一个仿真器。如果条件允许可以增加一个仿真器,仿真器可以是CC Debugger也可以是SmartRF04EB,同时也可以准备一套CC2531 USBDongle做为嗅探器,抓取RF发送数据做调试分析。
本文主要实现了无线串口功能,通过串口调试助手发送字节数据。例如通过串口向设备A发送Hello CC2530,设备B可收到Hello CC2530,并把该字符串通过串口调试助手打印至屏幕。设备B发送Hello RF,设备A同样可以收到数据并打印至屏幕。
图1.2.1(a-b) 设备A和设备B串口调试界面
图中中括号包含的数字为RSSI结果,RSSI表示接收信号强度,例如图中的-28。RSSI结果的单位为dBm,dBm为绝对单位且参考的标准为1mW。
RF部分的寄存器较多,需要耐心阅读数据手册和相关工具才可以完成设置。虽然RF部分的寄存器较多,但是还是借助smartRF工具、数据手册和示例代码,依然可以总结出使用CC2530无线部分的一般方法。
初始化部分包括接收数据包帧过滤控制,发射功率控制和信道选择;借助smartRF工具生成若干推荐值;打开接收终端并进入接收状态。
void main(void)
{
P1SEL &= ~0x13; //功能:通用I/O,默认为通用I/O P1.0 P1.1 P1.4设置为通用IO
P1DIR |= 0x13; //方向:输出 P1.0 P1.1 P1.4设置为输出
// P1DIR |= ( 1<< 4) | (1<<1);
TX =0;
RX =1;
EA = 0; // 暂时关闭全局中断
SLEEPCMD &= ~0x04;
while( !(SLEEPSTA & 0x40) );
CLKCONCMD &= ~0x47; // 设置系统时钟为32MHz
SLEEPCMD |= 0x04;
WDCTL = 0x00; //将超时时间设置为1s,在IDLE模式写CLR寄存器不会产生影响,因此直接赋值只会改变INT和MODE。
WDCTL |= 0x08; //将看门狗定时器设置为看门狗模式
uart0_init(); // 38400
timer1_init();
timer3_init(); // 1s
rf_init(); // RF
EA = 1;
LED1 = 1;
uart0_sendbuf("20181025",8);
delay(1000);
uart0_sendbuf(serial_rxbuf,4);
while(1)
{
WDCTL = 0xa0; //在看门狗模式写INT寄存器和MODE模式不会产生影响,因此直接赋值只会改变CLR
WDCTL = 0x50;
rx();
if(Time_1s > 900)//超过十五分钟未接收到数据,重新初始化串口及RF
{
Time_1s = 0;
//uart0_init(); //38400
rf_init();
}
// tx();
// uart0_sendbuf(serial_rxbuf,4);
delay(10);
}
}
初始化IO口,设置为通用IO、输出模式,分别为LED灯、PA、LNA。
初始化看门狗,做产品并非做实验,软件开门狗必须使用,以防死机。
初始化串口,定时器1,定时器3,RF。定时器1用于串口接收,定时器3用于计时。
whilexu循环中实时监测是否接收到串口数据,收到则立即RF发送。以及判断shif是否连续15分钟内都没接收到RF数据,若无则重新初始化RF。(此机制是因为长时间射频收发,会出现各种干扰导致接收不到RF数据,但可正常发射,程序正常运行。15分钟为实际项目需求,根据实际情况选择。)
void rf_send( char *pbuf , int len)
{
int i=0;
RFST = 0xE3; // RF接收使能 ISRXON
// 等待发送状态不活跃 并且 没有接收到SFD
while( FSMSTAT1 & (( 1<<1 ) | ( 1<<5 )));
RFIRQM0 &= ~(1<<6); // 禁止接收数据包中斿
IEN2 &= ~(1<<0); // 清除RF全局中断
RFST = 0xEE; // 清除发送缓冲区 ISFLUSHTX
RFIRQF1 = ~(1<<1); // 清除发送完成标忿
// 填充缓冲匿填充过程需要增势字节,CRC校验自动填充
RFD = len + 2;
for (int i = 0; i < len; i++)
{
RFD = *pbuf++;
}
RFST = 0xE9; // 发送数据包 ISTXON
while (!(RFIRQF1 & (1<<1))) // 等待发送完房 RFIRQF1 = ~(1<<1); // 清除发送完成标志位
{
i++;
if( i > 50 )//增加退出机制,受干扰会死在此循环中
{
i=0;
break;
}
WDCTL = 0xa0; //在看门狗模式写INT寄存器和MODE模式不会产生影响,因此直接赋值只会改变CLR
WDCTL = 0x50;
delay(100);
}
RFIRQM0 |= (1<<6); // RX接收中断
IEN2 |= (1<<0);
}
除主函数中while的死循环,其他任何地方的while死循环都要特别注意,比如此处实际调试中发现会程序卡死在等待发送完成,因此增加了退出机制。然而退出机制的时间判断亦不可随意设置,过短会导致每次传输数据长度收限制。本人因对程序时间概念不重视,导致此bug困惑许久。实测此延时时间至少可发送100个数据。
void rf_receive_isr()
{
int rf_rx_len = 0;
int rssi = 0;
char crc_ok = 0;
rf_rx_len = RFD - 2; //长度去除两字节附加结果
rf_rx_len &= 0x7F;
for (int i = 0; i < rf_rx_len; i++)
{
rf_rx_buf[i] = RFD; //连续读取接收缓冲区内容
}
rssi = RFD - 73; //读取RSSI结果
crc_ok = RFD; //读取CRC校验结果 BIT7
RFST = 0xED; //清除接收缓冲区
if( crc_ok & 0x80 )
{
LED1 ^= 1;
Time_1s = 0;
uart0_sendbuf( rf_rx_buf , rf_rx_len);
comtx(rssi );
}
else
{
uart0_sendbuf("\r\nCRC Error\r\n",12);
}
}
RF接收函数,接收到的数据自带两位硬件CRC校验,即crc_ok。校验通过认为是有效数据,将计时清零,LED灯反转,串口发送出接收到的数据,并发送出rssi。
PA:功率放大器 发射前置高电平,发射结束立即置低电平。
LNA: 低噪声放大器 发射前置低电平,发射结束立即置高电平。
(不可操作失误,否则导致数据接收异常)