本文将从两个方面着手,一方面分析TI官网下载的CC2530 BasicRF源码中的无线发送,另一方面根据自己的板子来修改实现无线发送控制灯的亮灭。
一、CC2530 BasicRF
Basic RF由TI公司提供,它包含了IEEE 802.15.4标准的数据包的收发功能但并没有使用到协议栈,它仅仅是让两个节点进行简单的通信,也就是说Basic RF仅仅是包含着IEEE 802.15.4标准的一小部分而已。其主要特点有:
1、不会自动加入协议、也不会自动扫描其他节点、同时也没有组网指示灯(LED3)
2、没有协议栈里面所说的协调器、路由器或者终端的区分,节点的地位都是相等的
3、没有自动重发的功能
Basic RF layer为双向无线通信提供了一个简单的协议,通过这个协议能够进行数据的发送和接收。Basic RF还提供了安全所使用的CCM-64身份验证和数据加密,它的安全性在工程文件里定义SECURITY_CCM了。在Project->Option->C/C++ Compiler->Preprocess里面就选择,本次实验并不是什么高度机密,所有在SECURITY_CCM前面带X了。
Basic RF的工作过程:启动、发射、接收
工程文件分析
宏:
MCU_IO_DIR_OUTPUT(port, pin);
/***********************************************************************
* MCU_IO_DIR_OUTPUT(port, pin);将端口port对应的pin位设置为输出(高电平)
* ##为连接符,如port=0,pin=1,即P0DIR |= BM(1);
**********************************************************************/
#define MCU_IO_DIR_OUTPUT(port, pin) MCU_IO_DIR_OUTPUT_PREP(port, pin)
#define MCU_IO_DIR_OUTPUT_PREP(port, pin) st( P##port##DIR |= BM(pin); )
#define st(x) do { x } while (__LINE__ == -1)
#define BM(n) (1 << (n))
MCU_IO_DIR_INPUT(port, pin);
/***********************************************************************
* MCU_IO_DIR_INPUT(port, pin);将端口port对应的pin位设置为输入(低电平)
**********************************************************************/
#define MCU_IO_DIR_INPUT(port, pin) MCU_IO_DIR_INPUT_PREP(port, pin)
#define MCU_IO_DIR_INPUT_PREP(port, pin) st( P##port##DIR |= ~BM(pin); )
#define st(x) do { x } while (__LINE__ == -1)
#define BM(n) (1 << (n))
MCU_IO_GET(port, pin);
/***********************************************************************
* MCU_IO_GET(port, pin);获取端口port的对应位pin的电平值
**********************************************************************/
#define MCU_IO_GET(port, pin) MCU_IO_GET_PREP(port, pin)
#define MCU_IO_GET_PREP(port, pin) (P##port## & BM(pin))
#define BM(n) (1 << (n))
MCU_IO_TGL(port, pin);
/***********************************************************************
* MCU_IO_TGL(port, pin);将端口port的对应位pin的电平值设置为相反电平
**********************************************************************/
#define MCU_IO_TGL(port, pin) MCU_IO_TGL_PREP(port, pin)
#define MCU_IO_TGL_PREP(port, pin) st( P##port##_##pin## ^= 1; )
#define st(x) do { x } while (__LINE__ == -1)
MCU_IO_SET_LOW(port, pin);
/***********************************************************************
* MCU_IO_SET_LOW(port, pin);将端口port的对应位pin设置为低电平
**********************************************************************/
#define MCU_IO_SET_LOW(port, pin) MCU_IO_SET_LOW_PREP(port, pin)
#define MCU_IO_SET_LOW_PREP(port, pin) st( P##port##_##pin## = 0; )
#define st(x) do { x } while (__LINE__ == -1)
MCU_IO_SET_HIGH(port, pin);
/***********************************************************************
* MCU_IO_SET_HIGH(port, pin);将端口port的对应位pin设置为高电平
**********************************************************************/
#define MCU_IO_SET_HIGH(port, pin) MCU_IO_SET_HIGH_PREP(port, pin)
#define MCU_IO_SET_HIGH_PREP(port, pin) st( P##port##_##pin## = 1; )
#define st(x) do { x } while (__LINE__ == -1)
MCU_IO_SET(port, pin, val);
/***********************************************************************
* MCU_IO_SET(port, pin, val);将端口port的对应位pin的电平值设置为val
**********************************************************************/
#define MCU_IO_SET(port, pin, val) MCU_IO_SET_PREP(port, pin, val)
#define MCU_IO_SET_PREP(port, pin, val) st( P##port##_##pin## = val; )
#define st(x) do { x } while (__LINE__ == -1)
MCU_IO_OUTPUT(port, pin, val);
/***********************************************************************
* MCU_IO_OUTPUT(port, pin, val);将端口port的对应位pin的电平值设置为val
* 并且将此IO设置为通用IO,以及设置为输出
**********************************************************************/
#define MCU_IO_OUTPUT(port, pin, val) MCU_IO_OUTPUT_PREP(port, pin, val)
#define MCU_IO_OUTPUT_PREP(port, pin, val) st( P##port##SEL &= ~BM(pin); \
P##port##_##pin## = val; \
P##port##DIR |= BM(pin); )
#define st(x) do { x } while (__LINE__ == -1)
MCU_IO_OUTPUT(port, pin, func);
/***********************************************************************
* MCU_IO_INPUT(port, pin, func);将端口port的对应位pin的设置为通用IO
* 并将此IO口设置为func输入模式:上拉、下拉、默认模式(三态)
* func指的是IO口的输入模式:MCU_IO_PULLUP、MCU_IO_PULLDOWN、默认模式
**********************************************************************/
#define MCU_IO_INPUT(port, pin, func) MCU_IO_INPUT_PREP(port, pin, func)
#define MCU_IO_INPUT_PREP(port, pin, func) st( P##port##SEL &= ~BM(pin); \
P##port##DIR &= ~BM(pin); \
switch (func) { \
case MCU_IO_PULLUP: \
P##port##INP &= ~BM(pin); \
P2INP &= ~BM(port + 5); \
break; \
case MCU_IO_PULLDOWN: \
P##port##INP &= ~BM(pin); \
P2INP |= BM(port + 5); \
break; \
default: \
P##port##INP |= BM(pin); \
break; } )
#define st(x) do { x } while (__LINE__ == -1)
MCU_IO_PERIPHERAL(port, pin);
/***********************************************************************
* MCU_IO_PERIPHERAL(port, pin);将端口port的对应位pin的设置为外设功能
**********************************************************************/
#define MCU_IO_PERIPHERAL(port, pin) MCU_IO_PERIPHERAL_PREP(port, pin)
#define MCU_IO_PERIPHERAL_PREP(port, pin) st( P##port##SEL |= BM(pin); )
#define BM(n) (1 << (n))
light_switch.c——main函数
<pre name="code" class="cpp">void main(void)
{
uint8 appMode = LIGHT;
// Config basicRF
basicRfConfig.panId = PAN_ID; //网络地址,0x2007
basicRfConfig.channel = RF_CHANNEL; //通道,25 // 2.4 GHz RF channel
basicRfConfig.ackRequest = TRUE; //是否应当, 1
#ifdef SECURITY_CCM
basicRfConfig.securityKey = key;
#endif
// Initalise board peripherals
/*
* 一些GPIO口初始化,包括以下LEDs和Buttons初始化
* 将LEDs(P0_0、P1_1、P1_4、P0_1)设置为输出、Buttons(P0_1)设置为输入模式且为三态输入模式
*/
halBoardInit();
/*
* 多方向按键初始化,不用管
*/
halJoystickInit();
// Initalise hal_rf 硬件抽象层rf初始化
if(halRfInit()==FAILED) {
HAL_ASSERT(FALSE);
}
// Indicate that device is powered 将按键1设置为高电平,并将状态设置为1
/*
switch (id)
{
case 1: HAL_LED_SET_1(); break;
case 2: HAL_LED_SET_2(); break;
case 3: HAL_LED_SET_3(); break;
case 4: HAL_LED_SET_4(); led4State=1; break;
default: break;
}
#define HAL_LED_SET_1() MCU_IO_SET_HIGH(HAL_BOARD_IO_LED_1_PORT, HAL_BOARD_IO_LED_1_PIN)
#define HAL_LED_SET_2() MCU_IO_SET_HIGH(HAL_BOARD_IO_LED_2_PORT, HAL_BOARD_IO_LED_2_PIN)
#define HAL_LED_SET_3() MCU_IO_SET_HIGH(HAL_BOARD_IO_LED_3_PORT, HAL_BOARD_IO_LED_3_PIN)
#define HAL_LED_SET_4() MCU_IO_SET_HIGH(HAL_BOARD_IO_LED_4_PORT, HAL_BOARD_IO_LED_4_PIN)
#define HAL_BOARD_IO_LED_1_PORT 1 // Green
#define HAL_BOARD_IO_LED_1_PIN 0
#define HAL_BOARD_IO_LED_2_PORT 1 // Red
#define HAL_BOARD_IO_LED_2_PIN 1
#define HAL_BOARD_IO_LED_3_PORT 1 // Yellow
#define HAL_BOARD_IO_LED_3_PIN 4
#define HAL_BOARD_IO_LED_4_PORT 0 // Orange
#define HAL_BOARD_IO_LED_4_PIN 1
*/
halLedSet(1);
// Print Logo and splash screen on LCD
utilPrintLogo("Light Switch"); //有关LCD的函数不管
// Wait for user to press S1 to enter menu
while (halButtonPushed()!=HAL_BUTTON_1);
halMcuWaitMs(350);
halLcdClear(); //lcd清屏,不用管
// Set application role
appMode = appSelectMode(); //模式选择
halLcdClear();
// Transmitter application
if(appMode == SWITCH) {
// No return from here
appSwitch();
}
// Receiver application
else if(appMode == LIGHT) {
// No return from here
appLight();
}
// Role is undefined. This code should not be reached
HAL_ASSERT(FALSE);
}
hal_board.c——halBoardInit()函数实现
void halBoardInit(void)
{
//mcu初始化,设置系统时钟
halMcuInit();
// LEDs
#ifdef SRF05EB_VERSION_1_3
// SmartRF05EB rev 1.3 has only one accessible LED
MCU_IO_DIR_OUTPUT(HAL_BOARD_IO_LED_1_PORT, HAL_BOARD_IO_LED_1_PIN);
HAL_LED_CLR_1();
#else
/*
* LEDs(P0_0、P1_1、P1_4、P0_1)设置为输出
*/
MCU_IO_DIR_OUTPUT(HAL_BOARD_IO_LED_1_PORT, HAL_BOARD_IO_LED_1_PIN);
HAL_LED_CLR_1();
MCU_IO_DIR_OUTPUT(HAL_BOARD_IO_LED_2_PORT, HAL_BOARD_IO_LED_2_PIN);
HAL_LED_CLR_2();
MCU_IO_DIR_OUTPUT(HAL_BOARD_IO_LED_3_PORT, HAL_BOARD_IO_LED_3_PIN);
HAL_LED_CLR_3();
MCU_IO_DIR_OUTPUT(HAL_BOARD_IO_LED_4_PORT, HAL_BOARD_IO_LED_4_PIN);
HAL_LED_CLR_4();
#endif
// Buttons
/*
* Buttons(P0_1)设置为输入模式且为三态输入模式
*/
MCU_IO_INPUT(HAL_BOARD_IO_BTN_1_PORT, HAL_BOARD_IO_BTN_1_PIN, MCU_IO_TRISTATE);
// Joystick push input 多方向按键 三态输入模式
MCU_IO_INPUT(HAL_BOARD_IO_JOY_MOVE_PORT, HAL_BOARD_IO_JOY_MOVE_PIN, \
MCU_IO_TRISTATE);
// Analog input adc模拟,设置对应引脚为外部功能
MCU_IO_PERIPHERAL(HAL_BOARD_IO_JOYSTICK_ADC_PORT, HAL_BOARD_IO_JOYSTICK_ADC_PIN);
//lcd初始化
halLcdSpiInit();
halLcdInit();
/*
* halIntOn:开启全局中断-->HAL_INT_ON-->st( EA = 1; )
*/
halIntOn();
}
hal_rf.c——halRfInit()函数实现
uint8 halRfInit(void)
{
// Enable auto ack and auto crc
/*
* #define FRMCTRL0 XREG( 0x6189 )
* #define XREG(addr) ((unsigned char volatile __xdata *) 0)[addr]
* 即往寄存器FRMCTRL0(0x6189)写数据
* #define AUTO_ACK 0x20
* #define AUTO_CRC 0x40
*/
FRMCTRL0 |= (AUTO_ACK | AUTO_CRC);
// Recommended RX settings
/*
* #define TXFILTCFG XREG( 0x61FA )
* #define AGCCTRL1 XREG( 0x61B2 )
* #define FSCAL1 XREG( 0x61AE )
*/
TXFILTCFG = 0x09; //调整AGC目标值
AGCCTRL1 = 0x15; //设置 TX 抗混叠过滤器以获得合适的贷款
FSCAL1 = 0x00; //和默认设置比较,降低VCO泄露大约3dB。推荐默认设置以获得最佳 EVM
// Enable random generator -> Not implemented yet
// Enable CC2591 with High Gain Mode
halPaLnaInit();//在CC2530中此函数没有做任何操作
// Enable RX interrupt 使能接收中断
/*
* RFIRQM0 |= BV(6);
* IEN2 |= BV(0);
* #define BV(n) (1 << (n))
*/
halRfEnableRxInterrupt();
return SUCCESS;
}
light_switch.c——appSwitch()、appLight()函数实现
static void appSwitch()
{
//lcd有关操作不用管
halLcdWriteLine(HAL_LCD_LINE_1, "Switch");
halLcdWriteLine(HAL_LCD_LINE_2, "Joystick Push");
halLcdWriteLine(HAL_LCD_LINE_3, "Send Command");
#ifdef ASSY_EXP4618_CC2420 //没有定义
halLcdClearLine(1);
halLcdWriteSymbol(HAL_LCD_SYMBOL_TX, 1);
#endif
pTxData[0] = LIGHT_TOGGLE_CMD; // #define LIGHT_TOGGLE_CMD 0 //被传输的开关命令
// Initialize BasicRF
basicRfConfig.myAddr = SWITCH_ADDR; //#define SWITCH_ADDR 0x2520 //开关节点的地址
if(basicRfInit(&basicRfConfig)==FAILED) {
HAL_ASSERT(FALSE);
}
// Keep Receiver off when not needed to save power 关闭接收
basicRfReceiveOff();
// Main loop
while (TRUE) {
if( halJoystickPushed() ) { //判断多方向按键是否按下
basicRfSendPacket(LIGHT_ADDR, pTxData, APP_PAYLOAD_LENGTH);//发送命令
// Put MCU to sleep. It will wake up on joystick interrupt
halIntOff();
halMcuSetLowPowerMode(HAL_MCU_LPM_3); // Will turn on global
// interrupt enable
halIntOn();
}
}
}
static void appLight()
{
halLcdWriteLine(HAL_LCD_LINE_1, "Light");
halLcdWriteLine(HAL_LCD_LINE_2, "Ready");
#ifdef ASSY_EXP4618_CC2420
halLcdClearLine(1);
halLcdWriteSymbol(HAL_LCD_SYMBOL_RX, 1);
#endif
// Initialize BasicRF
basicRfConfig.myAddr = LIGHT_ADDR; //#define LIGHT_ADDR 0xBEEF //电灯节点的地址
if(basicRfInit(&basicRfConfig)==FAILED) {
HAL_ASSERT(FALSE);
}
//使能接收
basicRfReceiveOn();
// Main loop
while (TRUE) {
while(!basicRfPacketIsReady());//检查是否收到一个新数据包
if(basicRfReceive(pRxData, APP_PAYLOAD_LENGTH, NULL)>0) { //收到的数据复制到pRxData中
if(pRxData[0] == LIGHT_TOGGLE_CMD) {
halLedToggle(1); //将其对应的LED1设置相反电平
}
}
}
}
basic_rf.h——basicRfCfg_t结构体定义
typedef struct {
uint16 myAddr; //16位短地址(就是节点地址)
uint16 panId; //节点的PAN(个人局域网) ID
uint8 channel; //RF通道(必须在11-26之间)
uint8 ackRequest; //目标确定就置true
#ifdef SECURITY_CCM //是否加密
uint8* securityKey;
uint8* securityNonce;
#endif
} <strong>basicRfCfg_t</strong>;
basic_rf.c——basicRfPacketIsReady()、basicRfReceive()、basicRfSendPacket()函数实现
uint8 basicRfPacketIsReady(void)
{
/*
* static basicRfRxInfo_t rxi= { 0xFF }; // Make sure sequence numbers are
* 确保数据序列号为0xFF
*/
return rxi.isReady;
}
uint8 basicRfReceive(uint8* pRxData, uint8 len, int16* pRssi)
{
// Accessing shared variables -> this is a critical region
// Critical region start
halIntOff();
memcpy(pRxData, rxi.pPayload, min(rxi.length, len));
if(pRssi != NULL) {
if(rxi.rssi < 128){
*pRssi = rxi.rssi - halRfGetRssiOffset();
}
else{
*pRssi = (rxi.rssi - 256) - halRfGetRssiOffset();
}
}
rxi.isReady = FALSE;
halIntOn();
// Critical region end
return min(rxi.length, len);
}
uint8 basicRfSendPacket(uint16 destAddr, uint8* pPayload, uint8 length)
{
uint8 mpduLength;
uint8 status;
// Turn on receiver if its not on
if(!txState.receiveOn) {
halRfReceiveOn();
}
// Check packet length
length = min(length, BASIC_RF_MAX_PAYLOAD_SIZE);
// Wait until the transceiver is idle
halRfWaitTransceiverReady();
// Turn off RX frame done interrupt to avoid interference on the SPI interface
halRfDisableRxInterrupt();
mpduLength = basicRfBuildMpdu(destAddr, pPayload, length);
#ifdef SECURITY_CCM
halRfWriteTxBufSecure(txMpdu, mpduLength, length, BASIC_RF_LEN_AUTH, BASIC_RF_SECURITY_M);
txState.frameCounter++; // Increment frame counter field
#else
halRfWriteTxBuf(txMpdu, mpduLength);
#endif
// Turn on RX frame done interrupt for ACK reception
halRfEnableRxInterrupt();
// Send frame with CCA. return FAILED if not successful
if(halRfTransmit() != SUCCESS) {
status = FAILED;
}
// Wait for the acknowledge to be received, if any
if (pConfig->ackRequest) {
txState.ackReceived = FALSE;
// We'll enter RX automatically, so just wait until we can be sure that the ack reception should have finished
// The timeout consists of a 12-symbol turnaround time, the ack packet duration, and a small margin
halMcuWaitUs((12 * BASIC_RF_SYMBOL_DURATION) + (BASIC_RF_ACK_DURATION) + (2 * BASIC_RF_SYMBOL_DURATION) + 10);
// If an acknowledgment has been received (by RxFrmDoneIsr), the ackReceived flag should be set
status = txState.ackReceived ? SUCCESS : FAILED;
} else {
status = SUCCESS;
}
// Turn off the receiver if it should not continue to be enabled
if (!txState.receiveOn) {
halRfReceiveOff();
}
if(status == SUCCESS) {
txState.txSeqNumber++;
}
#ifdef SECURITY_CCM
halRfIncNonceTx(); // Increment nonce value
#endif
return status;
}
二、无线控制灯的亮灭
实现:通过按S1键来控制另一个板子上的LED1灯的亮灭
1、电路原理图
2、分析
既然要实现通过按S1键来控制另一个板子上的LED1灯的亮灭,那么首先需要将被控制版上的LED1对应的P1_0设置为输出模式,且当P1_为低电平时LED1等亮,为高电平时LED1等灭。以及将控制板上的S1对应的P0_4设置为输入模式,当检测到P0_4引脚为低电平表示按键S1被按下,反之为高电平时表示按键S1没有被按下。由于CC2530中的无线发送功能是内置的,不是外设,即TI官网下载的CC2530 BasicRF中的无线发送代码仍然适合我的板子,不需要怎么修改。这样,将按键S1端作为无线发送的发送端,而LED1端作为无线发送的接收端。
根据上面的代码分析,来实现自己的无线点灯,代码修改如下:
(1)、首先根据上面的分析,可以知道TI官网的CC2530 BasicRF中有4个LED,其分别是P0_0、P1_1、P1_4、P0_1,而我的板子中有个两个LED灯,分别是LED1(P1_0)、LED2(P1_1)。所有需要修改LED1的引脚定义,修改hal_board.h对应LEDs的引脚定义处。
修改前:
// LEDs
#define HAL_BOARD_IO_LED_1_PORT 0 // Green
#define HAL_BOARD_IO_LED_1_PIN 0
#define HAL_BOARD_IO_LED_2_PORT 1 // Red
#define HAL_BOARD_IO_LED_2_PIN 1
#define HAL_BOARD_IO_LED_3_PORT 1 // Yellow
#define HAL_BOARD_IO_LED_3_PIN 4
#define HAL_BOARD_IO_LED_4_PORT 0 // Orange
#define HAL_BOARD_IO_LED_4_PIN 1
修改后:
// LEDs
#define HAL_BOARD_IO_LED_1_PORT 1 // Green******************LED1
#define HAL_BOARD_IO_LED_1_PIN 0
#define HAL_BOARD_IO_LED_2_PORT 1 // Red********************LED2
#define HAL_BOARD_IO_LED_2_PIN 1
#define HAL_BOARD_IO_LED_3_PORT 1 // Yellow
#define HAL_BOARD_IO_LED_3_PIN 4
#define HAL_BOARD_IO_LED_4_PORT 0 // Orange
#define HAL_BOARD_IO_LED_4_PIN 1
(2)、首先根据上面的分析,可以知道TI官网的CC2530 BasicRF中有1个按键S1,对应的引脚为P0_1,而我板子上的按键S1引脚为P0_4。所有需要修改LED1的引脚定义,修改hal_board.h对应Buttons的引脚定义处。
修改前:
// Buttons
#define HAL_BOARD_IO_BTN_1_PORT 0 // Button S1
#define HAL_BOARD_IO_BTN_1_PIN 1
修改后:
// Buttons
#define HAL_BOARD_IO_BTN_1_PORT 0 // Button S1
#define HAL_BOARD_IO_BTN_1_PIN 4
(3)、将板子的LED1和LED2灯,设置为关闭状态,修改light_switch.c的main函数,在halLedSet(1)上面添加一行代码:halLesSet(2);
halLedSet(2); //关闭LED2
halLedSet(1); //关闭LED1
(4)、根据上面的light_switch.c中的appSwitch()函数分析,在其函数内死循环中,它是去判断多方向按键是否按下,而实际需要判断自己板子的单方向按键是否按下。
修改前:
while (TRUE) {
if( halJoystickPushed() ) { //判断多方向按键是否按下
basicRfSendPacket(LIGHT_ADDR, pTxData, APP_PAYLOAD_LENGTH);//发送命令
// Put MCU to sleep. It will wake up on joystick interrupt
halIntOff();
halMcuSetLowPowerMode(HAL_MCU_LPM_3); // Will turn on global
// interrupt enable
halIntOn();
}
}
修改后:
while (TRUE) {
//if( halJoystickPushed() ) { //判断多方向按键是否按下
if (halButtonPushed()){
basicRfSendPacket(LIGHT_ADDR, pTxData, APP_PAYLOAD_LENGTH);//发送命令
// Put MCU to sleep. It will wake up on joystick interrupt
halIntOff();
halMcuSetLowPowerMode(HAL_MCU_LPM_3); // Will turn on global
// interrupt enable
halIntOn();
}
}
(5)、将
light_switch.c中的main函数中一些有关lcd屏的操作、模式选择、等待用户按键等代码注释掉,注释如下:
#if 0
// Print Logo and splash screen on LCD
utilPrintLogo("Light Switch"); //有关LCD的函数不管
// Wait for user to press S1 to enter menu
while (halButtonPushed()!=HAL_BUTTON_1);
halMcuWaitMs(350);
halLcdClear(); //lcd清屏,不用管
// Set application role
appMode = appSelectMode(); //模式选择
halLcdClear();
// Transmitter application
if(appMode == SWITCH) {
// No return from here
appSwitch();
}
// Receiver application
else if(appMode == LIGHT) {
// No return from here
appLight();
}
#endif
(7)、根据是控制端(按键端)还是终端(LED灯端),在main函数添加不同函数。如果是将程序下载到控制端板子上,在main函数HAL_ASSERT(FLASE)上方添加appSwitch()函数,而如果是将程序下载到终端板子上,在main函数HAL_ASSERT(FLASE)上方添加appLight()函数。
控制端:
appSwitch();
// Role is undefined. This code should not be reached
HAL_ASSERT(FALSE);
终端:
appLight();
// Role is undefined. This code should not be reached
HAL_ASSERT(FALSE);
准备就绪