TICC2530组网演练(BasicRF——无线控制灯的亮灭)
发表时间:2020-10-19
发布人:葵宇科技
浏览次数:106
本文将大年夜两个方面着手,一方面分析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了。
[img]http://img.blog.csdn.net/20150104112822529
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; }[img]http://img.blog.csdn.net/20150105104048353
[img]http://img.blog.csdn.net/20150105104138570
[img]http://img.blog.csdn.net/20150105104239051
[img]http://img.blog.csdn.net/20150105104225437
[img]http://img.blog.csdn.net/20150105104310422
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、电路道理图
[img]http://img.blog.csdn.net/20150105112305651
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);
预备就绪
[img]http://img.blog.csdn.net/20150105130555562
按下S1
[img]http://img.blog.csdn.net/20150105130843583