zigbee zstack 串口,按键,消息,定时器
来源:互联网 发布:解放战争 国民党 知乎 编辑:程序博客网 时间:2024/05/16 06:45
协议栈中的串口接收流程
串口在底层使用的是中断,轮询还是DMA呢??答案从下面这个文件定义的宏可以得知
在C:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Components\hal\target\CC2530EB\hal_board_cfg.h
- //默认开启DMA
- #ifndef HAL_DMA
- #define HAL_DMA TRUE
- #endif
- //只要定义了以下4个宏,就相当于需要使用串口
- #ifndef HAL_UART
- #if (defined ZAPP_P1) || (defined ZAPP_P2) || (defined ZTOOL_P1) || (defined ZTOOL_P2)
- #define HAL_UART TRUE
- #else
- #define HAL_UART FALSE
- #endif
- #endif
- // HAL_UART和HAL_DMA都已经定义过了,所以协议栈里的串口默认使用的是DMA,而不是中断
- #if HAL_UART
- // Always prefer to use DMA over ISR.
- #if HAL_DMA
- #ifndef HAL_UART_DMA
- #if (defined ZAPP_P1) || (defined ZTOOL_P1)
- #define HAL_UART_DMA 1
- #elif (defined ZAPP_P2) || (defined ZTOOL_P2)
- #define HAL_UART_DMA 2
- #else
- #define HAL_UART_DMA 1
- #endif
- #endif
- #define HAL_UART_ISR 0
- #else
- #ifndef HAL_UART_ISR
- #if (defined ZAPP_P1) || (defined ZTOOL_P1)
- #define HAL_UART_ISR 1
- #elif (defined ZAPP_P2) || (defined ZTOOL_P2)
- #define HAL_UART_ISR 2
- #else
- #define HAL_UART_ISR 1
- #endif
- #endif
- #define HAL_UART_DMA 0
- #endif
- // Used to set P2 priority - USART0 over USART1 if both are defined.
- #if ((HAL_UART_DMA == 1) || (HAL_UART_ISR == 1))
- #define HAL_UART_PRIPO 0x00
- #else
- #define HAL_UART_PRIPO 0x40
- #endif
- #else//否则
- #define HAL_UART_DMA 0
- #define HAL_UART_ISR 0
- #endif
在某个应用的初始化函数中顺序执行下面两个函数,比如在SampleApp_Init函数中,
MT_UartInit();
MT_UartRegisterTaskID(task_id);
MT_UartRegisterTaskID//用于给SampleApp注册串口,这样当串口在MT_UartProcessZToolData(mt_uart.c)发数据到上层时,会发到SampleApp。如果多个app都执行了MT_UartRegisterTaskID,则最后一个注册的有效,因为前面的被覆盖掉了。
void MT_UartRegisterTaskID( byte taskID )
{
App_TaskID = taskID;
}
比如,在osalInitTasks函数中
- void osalInitTasks( void )
- {
- uint8 taskID = 0;
- tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
- osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
- macTaskInit( taskID++ );
- nwk_init( taskID++ );
- Hal_Init( taskID++ );
- #if defined( MT_TASK )
- MT_TaskInit( taskID++ );//会执行MT_UartRegisterTaskID函数
- #endif
- APS_Init( taskID++ );
- #if defined ( ZIGBEE_FRAGMENTATION )
- APSF_Init( taskID++ );
- #endif
- ZDApp_Init( taskID++ );
- #if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
- ZDNwkMgr_Init( taskID++ );
- #endif
- SampleApp_Init( taskID );//会执行MT_UartRegisterTaskID函数,把MT_TaskInit函数注册的串口覆盖掉了。串口有数据发给上层时,SampleApp会收到消息。
- }
MT_UartInit将会指定串口有数据到来时,调用哪个callback函数来处理。如果在option的preprocessor中定义了ZTOOL_P1即串口0(cc2530有2个串口,ZTOOL_P2或ZAPP_P2代表串口1),则调用的是MT_UartProcessZToolData,见下面
- /***************************************************************************************************
- * @fn MT_UartInit
- *
- * @brief Initialize MT with UART support
- *
- * @param None
- *
- * @return None
- ***************************************************************************************************/
- void MT_UartInit ()
- {
- halUARTCfg_t uartConfig;
- /* Initialize APP ID */
- App_TaskID = 0;
- /* UART Configuration */
- uartConfig.configured = TRUE;
- uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;
- uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW;
- uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;
- uartConfig.rx.maxBufSize = MT_UART_DEFAULT_MAX_RX_BUFF;
- uartConfig.tx.maxBufSize = MT_UART_DEFAULT_MAX_TX_BUFF;
- uartConfig.idleTimeout = MT_UART_DEFAULT_IDLE_TIMEOUT;
- uartConfig.intEnable = TRUE;
- #if defined (ZTOOL_P1) || defined (ZTOOL_P2)
- uartConfig.callBackFunc = MT_UartProcessZToolData;
- #elif defined (ZAPP_P1) || defined (ZAPP_P2)
- uartConfig.callBackFunc = MT_UartProcessZAppData;
- #else
- uartConfig.callBackFunc = NULL;
- #endif
- /* Start UART */
- #if defined (MT_UART_DEFAULT_PORT)//默认串口,如果定义了ZTOOL_P1则是串口0,ZTOOL_P2则是串口1
- HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig);
- #else
- /* Silence IAR compiler warning */
- (void)uartConfig;
- #endif
- /* Initialize for ZApp */
- #if defined (ZAPP_P1) || defined (ZAPP_P2)
- /* Default max bytes that ZAPP can take */
- MT_UartMaxZAppBufLen = 1;
- MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY;
- #endif
- }
MT_UartProcessZToolData用于处理串口接收缓冲区的数据,pMsg->msg的结构体的数据如下
- /***************************************************************************************************
- * @fn MT_UartProcessZToolData
- *
- * @brief | SOP | Data Length | CMD | Data | FCS |
- * | 1 | 1 | 2 | 0-Len | 1 |
- *
- * Parses the data and determine either is SPI or just simply serial data
- * then send the data to correct place (MT or APP)
- *
- * @param port - UART port
- * event - Event that causes the callback
- *
- *
- * @return None
- ***************************************************************************************************/
- void MT_UartProcessZToolData ( uint8 port, uint8 event )
- {
- uint8 ch;
- uint8 bytesInRxBuffer;
- (void)event; // Intentionally unreferenced parameter
- while (Hal_UART_RxBufLen(port))
- {
- HalUARTRead (port, &ch, 1);//每次读一个字节
- switch (state)
- {
- case SOP_STATE://0
- if (ch == MT_UART_SOF)// 如果读到的字符时Start-of-frame即0xFE,则进入LEN_STAT状态,进行读LEN
- state = LEN_STATE;
- break;
- case LEN_STATE://1
- LEN_Token = ch;//消息的长度
- tempDataLen = 0;
- /* Allocate memory for the data */
- pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
- MT_RPC_FRAME_HDR_SZ + LEN_Token );
- if (pMsg)
- {
- /* Fill up what we can */
- pMsg->hdr.event = CMD_SERIAL_MSG;
- pMsg->msg = (uint8*)(pMsg+1);
- pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;//pMsg->msg[0]=len
- state = CMD_STATE1;//读完长度,就进入CMD_STATE1状态,进行读命令
- }
- else
- {
- state = SOP_STATE;
- return;
- }
- break;
- case CMD_STATE1:
- pMsg->msg[MT_RPC_POS_CMD0] = ch;//pMsg->msg[1]=命令低字节
- state = CMD_STATE2;
- break;
- case CMD_STATE2:
- pMsg->msg[MT_RPC_POS_CMD1] = ch;//pMsg->msg[2]=命令高字节
- /* If there is no data, skip to FCS state */
- if (LEN_Token)
- {
- state = DATA_STATE;//读完命令,如果数据长度不为0,就进入DATA_STATE状态,进行读数据
- }
- else
- {
- state = FCS_STATE;//否则进入FCS_STATE状态,进行校验
- }
- break;
- case DATA_STATE:
- /* Fill in the buffer the first byte of the data */
- pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;
- /* Check number of bytes left in the Rx buffer */
- bytesInRxBuffer = Hal_UART_RxBufLen(port);
- /* If the remain of the data is there, read them all, otherwise, just read enough,读数量较小的数据 */
- if (bytesInRxBuffer <= LEN_Token - tempDataLen)//如果剩余数据<=len-已读长度,全部读出
- {
- HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);
- tempDataLen += bytesInRxBuffer;
- }
- else//否则只读出(len-已读长度)的数据
- {
- HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);
- tempDataLen += (LEN_Token - tempDataLen);
- }
- /* If number of bytes read is equal to data length, time to move on to FCS */
- if ( tempDataLen == LEN_Token )
- state = FCS_STATE;
- break;
- case FCS_STATE:
- FSC_Token = ch;
- /* Make sure it's correct */
- if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))//计算fcs
- {
- osal_msg_send( App_TaskID, (byte *)pMsg );
- }
- else
- {
- /* deallocate the msg */
- osal_msg_deallocate ( (uint8 *)pMsg );
- }
- /* Reset the state, send or discard the buffers at this point */
- state = SOP_STATE;
- break;
- default:
- break;
- }
- }
- }
所以如果PC发数据给zigbee串口,需要先发送FE...或者修改这个MT_UartProcessZToolData函数以适应自己的应用
协议栈中的串口发送流程
在某个app_init()中初始化和注册串口(如果此app不需要接收数据,可以不用注册)
直接使用如下代码发送数据即可
HalUARTWrite(0,"Hello World\n",12);
如果在opention中定义了MT_TASK,则在发送实际的数据之前会发送一个11字节的数据头(下行红色数据),在PC接收到如下形式的数据:
FE 06 41 80 01 02 00 02 03 00 C5 48 65 6C 6C 6F 20 57 6F 72 6C 64 0A
后面黑色字体的12个字节才是真正的数据
将MT_TASK取消定义则直接发送真实的数据
关于串口buffer的和应用层的串口帧大小的关系:
cc2530的串口buffer是1字节,可以看出串口帧的大小与串口buffer没啥关系,因为帧大小一般总是大于1字节的。
U0DBUF (0xC1) – USART 0 Receive/Transmit Data Buffer
这也说明串口在最低层发送的时候,是把上层给的数据一个字节一个字节的塞进buffer里面发出去的)
但是在协议栈中只要发送的串口帧大于128字节,就发不出去,这是因为单片机内存分给帧最大长度是128字节造成的。位于OnBoard.h。可修改之。
#define MT_UART_TX_BUFF_MAX 128
#define MT_UART_RX_BUFF_MAX 128
如果你想要发送大于128字节的帧而不愿意修改上面的宏,可以在while里面用HalUARTWrite一个字节一个字节的发,每发出一个字节执行一次HalUARTPoll。(相当于flush)。
协议栈中的按键流程
以其为例C:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Projects\zstack\Samples\SampleApp
1.在main函数中会执行InitBoard(),用于初始化led和按键
OnBoard.c
- void InitBoard( uint8 level )
- {
- if ( level == OB_COLD )
- {
- // Interrupts off
- osal_int_disable( INTS_ALL );
- // Turn all LEDs off
- HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
- // Check for Brown-Out reset
- ChkReset();
- }
- else // !OB_COLD
- {
- /* Initialize Key stuff */
- OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;//此处需要使能按键中断
- HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);//配置按键终端产生之后,触发OnBoard_KeyCallback函数
- }
- }
配置函数HalKeyConfig,用于配置哪个按键(哪个端口)
回调函数OnBoard_KeyCallback,此函数执行OnBoard_SendKeys,将按键事件发给上层注册按键的应用
RegisterForKeys( SampleApp_TaskID );
如果按键已被注册,则后来注册的就会失败。
uint8 RegisterForKeys( uint8 task_id )
{
// Allow only the first task
if ( registeredKeysTaskID == NO_TASK_ID )
{
registeredKeysTaskID = task_id;
return ( true );
}
else
return ( false );
}
类似于注册串口,但串口刚好相反。
不管有没有使用中断,对于button1和joystick的按键,都会响应
如果按键没有启用中断,则会每隔100ms轮询一次按键状态,见下
如果注册了中断,则在按键中断发生时,会触发hal_key.c中的中断处理函数,注意kal_key.c中只注册了p2和p0口的中断处理函数,见下
**************************************************************************************************/
HAL_ISR_FUNCTION( halKeyPort2Isr, P2INT_VECTOR )
{
if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)
{
halProcessKeyInterrupt();
}
/*
Clear the CPU interrupt flag for Port_2
PxIFG has to be cleared before PxIF
Notes: P2_1 and P2_2 are debug lines.
*/
HAL_KEY_JOY_MOVE_PXIFG = 0;
HAL_KEY_CPU_PORT_2_IF = 0;
}
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
{
halProcessKeyInterrupt();
}
/*
Clear the CPU interrupt flag for Port_0
PxIFG has to be cleared before PxIF
*/
HAL_KEY_SW_6_PXIFG = 0;
HAL_KEY_CPU_PORT_0_IF = 0;
}
而halProcessKeyInterrupt 只是中断标志。没有对中断做具体处理,同时延时25ms设置一个HAL_KEY_EVENT事件,让Hal_TaskID去处理。延时的目的是去抖动。
void halProcessKeyInterrupt (void)
{
bool valid=FALSE;
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) /* Interrupt Flag has been set */
{
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT) /* Interrupt Flag has been set */
{
HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
if (valid)
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//
}
}
在循环任务中有一个Hal_ProcessEvent (hal_driver.c) ,
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/* Check for keys */
HalKeyPoll();//读取时按个按键
/* if interrupt disabled, do next polling */
if (!Hal_KeyIntEnable)//如果没有使用中断,就重新延时100ms设置事件HAL_KEY_EVENT。从此处可以看出,如果按键没有启用中断,则会每隔100ms轮询一次按键状态
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
其调用了HalKeyPoll(hal_key.c),在这个函数中
**************************************************************************************************/
void HalKeyPoll (void)
{
uint8 keys = 0;
//读取joystick的状态
if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */
{
keys = halGetJoyKeyInput();//返回joystick的哪个按键按下
}
/* If interrupts are not enabled, previous key status and current key status
* are compared to find out if a key has changed status.
*/
if (!Hal_KeyIntEnable)//如果没有启动中断
{
if (keys == halKeySavedKeys)
{
/* Exit - since no keys have changed */
return;
}
/* Store the current keys for comparation next time */
halKeySavedKeys = keys;
}
else
{
/* Key interrupt handled here */
}
//读取button1状态,
if (HAL_PUSH_BUTTON1())//返回button1是否按下
{
keys |= HAL_KEY_SW_6;
}
//如果按键按下设置了回调函数,就调用回调函数,如下
/* Invoke Callback if new keys were depressed */
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
}
onboard.c中定义了回调函数
*********************************************************************/
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
uint8 shift;
(void)state;
/* Get shift key status */
shift = ((keys & HAL_KEY_SW_6) ? true : false);
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//如果上传消息失败(即应用层没有注册按键事件),就就地处理。
{
// Process SW1 here
if ( keys & HAL_KEY_SW_1 ) // Switch 1
{
}
// Process SW2 here
if ( keys & HAL_KEY_SW_2 ) // Switch 2
{
}
// Process SW3 here
if ( keys & HAL_KEY_SW_3 ) // Switch 3
{
}
// Process SW4 here
if ( keys & HAL_KEY_SW_4 ) // Switch 4
{
}
// Process SW5 here
if ( keys & HAL_KEY_SW_5 ) // Switch 5
{
}
// Process SW6 here
if ( keys & HAL_KEY_SW_6 ) // Switch 6
{
}
}
}
由上可知,
/* Switches (keys) */
#define HAL_KEY_SW_1 0x01 // Joystick up
#define HAL_KEY_SW_2 0x02 // Joystick right
#define HAL_KEY_SW_5 0x04 // Joystick center
#define HAL_KEY_SW_4 0x08 // Joystick left
#define HAL_KEY_SW_3 0x10 // Joystick down
#define HAL_KEY_SW_6 0x20 // Button S1 if available
协议栈中只对这6个按键由响应。而HalKeyConfig (hal_key.c) 函数中也只是对这6个按键进行了配置。
协议栈中的消息流程
3个不得不知道的结构体
- typedef struct
- {
- void *next;
- uint16 len;
- uint8 dest_id;
- } osal_msg_hdr_t;
- typedef struct
- {
- uint8 event;
- uint8 status;
- } osal_event_hdr_t;
- typedef void * osal_msg_q_t;
- //无线收到数据向上层发送的消息
- typedef struct
- {
- osal_event_hdr_t hdr; /* OSAL Message header */
- uint16 groupId; /* Message's group ID - 0 if not set */
- uint16 clusterId; /* Message's cluster ID */
- afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,
- it's an InterPAN message */
- uint16 macDestAddr; /* MAC header destination short address */
- uint8 endPoint; /* destination endpoint */
- uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */
- uint8 LinkQuality; /* The link quality of the received data frame */
- uint8 correlation; /* The raw correlation value of the received data frame */
- int8 rssi; /* The received RF power in units dBm */
- uint8 SecurityUse; /* deprecated */
- uint32 timestamp; /* receipt timestamp from MAC */
- afMSGCommandFormat_t cmd; /* Application Data */
- } afIncomingMSGPacket_t;
- //按键按下时向上层发送的消息格式
- typedef struct
- {
- osal_event_hdr_t hdr;
- uint8 state; // shift
- uint8 keys; // keys
- } keyChange_t;
- //串口接到数据向上层发送的消息
- typedef struct
- {
- osal_event_hdr_t hdr;
- uint8 *msg;
- } mtOSALSerialData_t;
定时器
cc2530-ZStack-CC2530-2.3.0-1.4.0
Hal_timer.c
NOTE: The following mapping is done between the logical timer
names defined in HAL_TIMER.H and the physical HW timer.
HAL_TIMER_0 --> HW Timer 3 (8-bits)
HAL_TIMER_2 --> HW Timer 4 (8-bits)
HAL_TIMER_3 --> HW Timer 1 (16-bits)
NOTE: The timer code assumes only one channel, CHANNEL 0, is used
for each timer. There is currently no support for other
channels.
HAL_ISR_FUNCTION( halTimer1Isr, T1_VECTOR )
{
halProcessTimer1 ();//是真实的硬件定时器1处理函数,默认没有开启
}
HAL_ISR_FUNCTION( halTimer3Isr, T3_VECTOR )
{
halProcessTimer3 ();//是真实的硬件定时器3处理函数,默认没有开启
}
HAL_ISR_FUNCTION( halTimer4Isr, T4_VECTOR )
{
halProcessTimer4 ();//是真实的硬件定时器4处理函数,默认没有开启
}
还有一个硬件定时器2,也是mac timer,已被协议栈占用
mac_mcu.c
/**************************************************************************************************
* @fn macMcuTimer2Isr
*
* @brief Interrupt service routine for timer2, the MAC timer.
*
* @param none
*
* @return none
**************************************************************************************************
*/
HAL_ISR_FUNCTION( macMcuTimer2Isr, T2_VECTOR )
osal_start_timerEx使用的是mac timer
但是osal_start_timerEx最快可以实现1ms的定时,而我测在mac timer的中断处理函数中使用P1_1=!P1_1测得的中断周期却大于100ms.?????
是使用的mac tiemr吗???
如下是说是使用的MAC backoff timer,这个timer又是啥玩意?
/*********************************************************************
* @fn osalTimeUpdate
*
* @brief Uses the free running rollover count of the MAC backoff timer;
* this timer runs freely with a constant 320 usec interval. The
* count of 320-usec ticks is converted to msecs and used to update
* the OSAL clock and Timers by invoking osalClockUpdate() and
* osalTimerUpdate(). This function is intended to be invoked
* from the background, not interrupt level.
*
* @param None.
*
* @return None.
*/
void osalTimeUpdate( void )
- zigbee zstack 串口,按键,消息,定时器
- ZigBee Home Automation --- zstack串口应用总结
- Zigbee网关 上电时Zstack串口乱码
- ZigBee,ZStack
- ZigBee TI ZStack CC2530 3.15 按键驱动02-中断模式
- ZigBee TI ZStack CC2530 3.18 串口01-发送
- ZigBee TI ZStack CC2530 3.20 串口03-printf()函数移植
- ZigBee TI ZStack CC2530 3.19 串口02-接收
- ZIGBEE-ZSTACK事件向任务发送消息…
- ZSTACK HAL 硬件修改部分(端口,按键,串口SPI)
- zstack串口
- Zigbee学习Zstack【转】
- zigbee zstack的关系
- zigbee zstack ----- zmain_vdd_check();
- ZigBee TI ZStack CC2530 3.14 按键驱动01-轮询模式
- Zigbee网关 Zstack增加串口功能1-修改底层驱动MT/MT_UART.c
- Zigbee网关 Zstack增加串口功能2-修改应用层APP/ZigbeeGateWay.c
- zigbee按键
- android之自定义dialog
- zigbee 初识zigbee,ieee802,论坛
- 【COCOS2DX-LUA 脚本开发之十二】Hybrid模式-利用AssetsManager实现在线更新脚本文件lua、js、图片等资源(免去平台审核周期)
- IO:使用FileWriter,输出字符串内容
- C++中堆和栈的完全解析(转)
- zigbee zstack 串口,按键,消息,定时器
- 第二个C++程序,认识各种数据格式
- Android SDK Manager更新不了的解决办法
- JAVA反射机制
- linux C内存分配
- C语言两种方法实现字符串反转
- zigbee 编译source_zigbee_sensor_monitor_v1_2_1 vs2008 qt-win-opensource-src-4.4.3
- 梯度下降法
- poj1463 Strategic game