CC430是TI在2008年末发布的MSP430家族的一个新的RF Soc系列,CC430集成了MSP430当时最新5系列的核以及低功耗无线收发器CC1101的核,并且集成了5系列丰富的外设等。
CC430不但延续了其前辈超低功耗、高性能的传统,而且集成了业界领先的1GHz以下频段的CC1101RF收发器,是真正的业界最低功耗的单芯片射频(RF)解决方案。使用CC430平台既可降低系统复杂性、将封装与印刷电路板尺寸缩小50%,又可简化RF设计,从而将包括RF网络、能量采集、工业监控与篡改检测、个人无线网络以及自动抄表基础设施(AMI)等在内的应用推向前所未有的水平。
我最早听说CC430是在2010年的TI MCU DAY上,到现在这个CC430推出也有好几年了,但是网上资料现在还是很少,至少我还没有发现有多少中文的,只有自己慢慢的看着全英文的PDF慢慢理解,慢慢学习啊,虽然很烦。
最近TI的国内代理利尔达的搞一个CC430的推广活动,正好有西安的,于是我就参加了,于是就花了299大洋买了开发套件,开始学习CC430。
活动网址:http://www.lierda.com/topic/cc430.html
(说个题外话:不知道大家对2010年的TI MCU DAY有印象没,如果有去参加的,最后利尔达给每个参会者赠送了一套基于cortex-m3的以太网转串口模块,最后还承诺说给每个人邮寄一套EZ430-RF5137-433学习套件,不过快两年过去了,我还是没收到,去各大论坛看看,各个城市的与会者很少有收到的……)
不过现在EZ430-RF5137-433终于到手了,虽然是自己花钱买的……
下面简单介绍一下:
eZ430- RF5137系列套件号称是全球最小的1G以下无线应用开发系统。该套件由以下几部分组成:两块CC430F5137目标板,一个支持MSP430 SBW(Spy Bi-Wire)在线仿真器,一个电池适配板。
eZ430与CC430F5137目标板可通过六针(SBW+UART)相连,构成了CC430在线仿真调试平台,并可以通过仿真器的USB转UART应用接口,可轻松实现上位机应用程序管理、控制CC430无线收发工作,可作为无线传感器网络的集中器;同时配合CC430F5137与电池座构成的系统,作为无线传感器网络的采集节点。
我还有一个不明白的地方,我这个套件是利尔达出的,我估计利尔达是仿的TI原版的,可是我在网上找资料,原版的资料一点都找不到……最后在青风的网店里看到了这个……
在这张图片上,打的是TI的Logo,关于原版的资料我只找到这一张图片……
不管了,先把利尔达给的资料给大家传上来~
原理图:
利尔达的这个开发套件有几点很恶心的地方:
给的sch原理图虽然是pdf的,但是都是图片格式的,想找个东西都不能用搜索,放大了还模糊……
利尔达的板子上的插针标号只有他们自己知道,比如说P1.0~P1.7,他们板子上标的是P35~P42……
资料很少,除了一个说明书,只有一个Demo,还是抄的TI的,改完程序注释不改,让人很郁闷……
发邮件找FAE,鸟都不鸟你……
(这个只是我的笔记,整理网上的内容加上自己的内容,欢迎指正~)
先理解几个词语,至少有一些是我以前不懂的:
CCA: Clear Channel Assessment;清除信道访问,一般用于判断信道是不是忙。
RSSI: Received Signal Strength Indicator;接受信号强度指示。
FIFO:First Input First Output;先进先出,一般对缓冲区,或者队列数据操作的类型,典型对比,堆栈,先进后出。
WOR:Wake On Radio;电磁波唤醒,一般用于低功耗配置,为了节省电,还没有用过。
PATABLE:Power Amplifier Table;功率放大表,这里是用来设置功率的。
Radio:无线电。
介绍一下CC430
CC430目前有8款产品:
eZ430-RF5137,顾名思义,主芯片是CC430F5137,主要外设跟5系列的430是一样的,唯一不同的就是内部集成了RF模块,具体是型号是Chipcon的Sub 1GHz的CC1101。(eZ430-RF2500用到的是CC2500,是一款2.4GHz的芯片)
分析Demo程序:
从main函数开始:
这个Demo程序主要实现的是无线收发的测试。无主机从机之分,所有的设备程序都是一样的。
在主函数中,设备默认开启接收状态,如果有按键按下,那么红色LED灯亮,同时关闭接收,切换为发送状态,发送TxBuffer中的内容,发完立马再切换回接收状态,同时红色LED灯灭。
如果接收到数据且CRC校验通过,那么蓝色LED闪烁一下。
可以看出通信不是全双工的,设备在同一时刻只能处于发送和接收其中一种状态。
void main( void )
{
//Stop watchdog timer to prevent time out reset
//WDTCTL = WDTPW + WDTHOLD;
WDTCTL = WDT_ARST_250;//设定看门狗定时时间为250ms
// Increase PMMCOREV level to 2 for proper radio operation
SetVCore(2); //提升内核电压 PMM Core Voltage 2 (1.75V)
ResetRadioCore();//复位cc430里面的Radio
InitRadio();//初始化CC430的Radio模块
InitButtonLeds();//初始化按键和LED
ReceiveOn();//开启接收模式
receiving = 1; //接收标志位
_EINT();
while (1)
{
WDTCTL = WDT_ARST_250;//喂狗
//bis_SR_register( LPM3_bits + GIE );
__no_operation();
if(!(BUTTON_IN & (1<<BUTTON_BIT)))//如果有按键按下(用的是查询的方式)
{
_NOP();
delayms(30);//延时消抖
if(!(BUTTON_IN & (1<<BUTTON_BIT)))//确定按键被按下
{
_NOP();
LEDR_ON();//红色LED亮
ReceiveOff();//关闭接收模式
receiving = 0;
Transmit( (unsigned char*)TxBuffer,sizeof TxBuffer);//发送 TxBuffer 内容
transmitting = 1; //正在发送
while(!(BUTTON_IN & (1<<BUTTON_BIT))); //等待按键被弹起
}
}
else if(!transmitting)//如果没有发送,那么打开接收
{
ReceiveOn();
receiving = 1;
}
}
}
首先开启看门狗定时器,WDTCTL = WDT_ARST_250;设定时间250ms,防止程序跑飞,开启看门狗意味着后面要喂狗,否则程序就会不断地复位。喂狗语句在while(1)循环里可以看到,WDTCTL = WDT_ARST_250;具体意思如下:
#define WDT_ARST_250 (WDTPW+WDTCNTCL+WDTSSEL0+WDTIS2+WDTIS0)
下面需要注意一下,跟单独的CC1101不同,上电后CC430里面的Radio是SLEEP状态,而不是IDLE状态。所以需要先复位CC430里面的Radio,具体操作是发送一个SRES和SNOP指令。
SetVCore(2); //提升内核电压 PMM Core Voltage 2 (1.75V)
接着复位Radio:
void ResetRadioCore (void)
{
Strobe(RF_SRES); // Reset the Radio Core
Strobe(RF_SNOP); // Reset Radio Pointer
}
然后就是开始初始化Radio,主要是开启PMM的高能量消耗允许,配置CC430的Radio寄存器,设置发射功率等。(具体内容很多,以后再讲)
void InitRadio(void)
{
// Set the High-Power Mode Request Enable bit so LPM3 can be entered
// with active radio enabled
PMMCTL0_H = 0xA5; //Password
PMMCTL0_L |= PMMHPMRE_L; //开启PMM的高能量消耗允许(PMM Global High Power Module Request Enable)
PMMCTL0_H = 0x00;
WriteRfSettings(&rfSettings);//配置CC430的Radio寄存器
WriteSinglePATable(PATABLE_VAL);//设置发射功率
}
配置Radio寄存器一般用SmartRF Studio取得数据。(SmartRF™ Studio 是一个 Windows 应用程序,用于评估和配置德州仪器 (TI) 的低功耗 RF-IC。该应用程序将帮助射频系统的设计人员在设计过程的早期阶段轻松评估 RF-IC。它对生成配置寄存器值、实际测试射频系统和查找优化的外部组件值尤为有用。SmartRF Studio 可作为单独的应用程序使用,或与应用程序评估板一起随 RF-IC 开发套件提供。)
最后开启接收和发送中断,选择TX还是RX模式,就可以做最基本的通信测试了。(当然这个Demo里还需要对用到的LED和KEY外设作一下初始化)
void InitButtonLeds(void)//初始化按键和LED
{
// Initialize Port J
PJOUT = 0x01;
PJDIR = 0xFF;
// Set up the button as interruptible
BUTTON_DIR&=~(1<<BUTTON_BIT); // 按键设置为输入
BUTTON_REN|=BIT0; // 内部上拉
// Set up LEDs
LED_OUT&=~((1<<LED_RBIT)|(1<<LED_GBIT)); // LED端口输出0
LED_DIR|=(1<<LED_RBIT)|(1<<LED_GBIT); // LED端口方向设置为输出
}
void Transmit(unsigned char *buffer, unsigned char length)//开启发送
{
RF1AIES |= BIT9; // High-to-low transition sets interrupt flag
RF1AIFG &= ~BIT9; // Clear pending interrupts 清除挂起的中断
RF1AIE |= BIT9; // Enable TX end-of-packet interrupt
WriteBurstReg(RF_TXFIFOWR, buffer, length);
Strobe( RF_STX ); // Strobe STX (Enable TX. If in RX state, only enable TX if CCA passes.)
}
void ReceiveOn(void)//开启接收
{
RF1AIES |= BIT9; // Falling edge of RFIFG9 (Radio Core Interrupt Edge Select Register)
RF1AIFG &= ~BIT9; // Clear a pending interrupt
RF1AIE |= BIT9; // Enable the interrupt
// Radio is in IDLE following a TX, so strobe SRX to enter Receive Mode
Strobe( RF_SRX );//Enable RX. Perform calibration if enabled.
}
void ReceiveOff(void)//关闭接收
{
RF1AIE &= ~BIT9; // Disable RX interrupts
RF1AIFG &= ~BIT9; // Clear pending IFG
// It is possible that ReceiveOff is called while radio is receiving a packet.
// Therefore, it is necessary to flush the RX FIFO after issuing IDLE strobe
// such that the RXFIFO is empty prior to receiving a packet.
Strobe( RF_SIDLE );//Exit RX / TX, turn off frequency synthesizer.
Strobe( RF_SFRX );//Flush the RX FIFO buffer.
}
关于发送接收的处理全部在中断中完成:
本例用的是RFIFG9中断,这个中断源不是乱选的,具体见下图:
RFIFG9的下降沿触发中断是End of packet中断。
具体的中断处理如下:
#pragma vector=CC1101_VECTOR
__interrupt void CC1101_ISR(void)
{
switch(__even_in_range(RF1AIV,32)) // Prioritizing Radio Core Interrupt
{
case 0: break; // No RF core interrupt pending
case 2: break; // RFIFG0
case 4: break; // RFIFG1
case 6: break; // RFIFG2
case 8: break; // RFIFG3
case 10: break; // RFIFG4
case 12: break; // RFIFG5
case 14: break; // RFIFG6
case 16: break; // RFIFG7
case 18: break; // RFIFG8
case 20: // RFIFG9
if(receiving) // RX end of packet 如果是接收引发的中断
{
// Read the length byte from the FIFO //check收到的包长度
RxBufferLength = ReadSingleReg( RXBYTES );
//RXBYTES : ?Overflow and number of bytes //读取数据包
ReadBurstReg(RF_RXFIFORD, RxBuffer, RxBufferLength);
// Stop here to see contents of RxBuffer
// 在这里设置断点 看 RxBuffer 收到的内容
__no_operation();
// Check the CRC results
if(RxBuffer[CRC_LQI_IDX] & CRC_OK)
//如果CRC校验通过 闪烁一下蓝色LED
{
LED_OUT |= (1<<LED_GBIT); // Toggle LED1
delayms(10);
LED_OUT &=~(1<<LED_GBIT);
}
}
else if(transmitting) // TX end of packet 如果是发送引发的中断
{
RF1AIE &= ~BIT9; // Disable TX end-of-packet interrupt
LEDR_OFF(); // Turn off LED after Transmit红色LED灭
transmitting = 0; // 发送结束
}
else while(1); // trap
break;
case 22: break; // RFIFG10
case 24: break; // RFIFG11
case 26: break; // RFIFG12
case 28: break; // RFIFG13
case 30: break; // RFIFG14
case 32: break; // RFIFG15
}
__bic_SR_register_on_exit(LPM3_bits);
}