分类: 嵌入式
2011-07-20 11:37:15
ZSTACK HAL 底层 驱动修改笔记
针对CC2530 zigbee 创新套件 CITE-T-VC
谷良增
2010-8-26
文档修改记录:
2010-9-28
针对 CITE-T-ZA套件,添加SPI 移植部分
目录:
一 LED
二 KEY 轮询方式
三 KEY 中断方式
四 串口
五 SPI
一:LED
硬件连接情况: P0_1-----LED1; P0_2-----LED2; P0_3-----LED3; P2_0-----LED4
相关文件 :hal_board_cfg.h
文件位置:C:\Texas
Instruments\ZStack-CC2530-
工程中的位置:HAL—Target—CC2530EB—Config--- hal_board_cfg.h
注意:hal_led.c 文件只是实现了ZSTACK HAL_LED 接口函数,此文件不用修改。
1. 修改LED 接口 ,例子:LED1---P1_0
/* 1 - Green */
#define LED1_BV BV(0)
#define LED1_SBIT P1_0
#define LED1_DDR P1DIR
#define LED1_POLARITY ACTIVE_HIGH
2添加一个 LED ,如添加 LED4 (P2_0口)
#define LED4_BV BV(0)
#define LED4_SBIT P2_0
#define LED4_DDR P2DIR
#define LED4_POLARITY ACTIVE_LOW
与ZSTACK HAL_LED 接口函数
#define HAL_TURN_OFF_LED4() st( LED4_SBIT = LED4_POLARITY (0); )
#define HAL_TURN_ON_LED4() st( LED4_SBIT = LED4_POLARITY (1); )
#define HAL_TOGGLE_LED4() st( if (LED4_SBIT) { LED4_SBIT = 0; } else { LED4_SBIT = 1;} )
#define HAL_STATE_LED4() (LED4_POLARITY (LED4_SBIT))
3由于有:
/* Set to TRUE enable LED usage, FALSE disable it */
#ifndef HAL_LED
#define HAL_LED TRUE
#endif
#if (!defined BLINK_LEDS) && (HAL_LED == TRUE)
#define BLINK_LEDS
#endif
所以工程OPTIONS 编译选项不需要再去设置HAL_LED,BLINK_LEDS
4. 在该文件 HAL_BOARD_INIT()宏定义中 ,初始化 P0_2,P0_3 不管用。因为在ZSTACK 中这两脚 被用作UART的 TX,RX,所以 LED 控制端口P0_1,P0_2,P0_3,P2_0的初始化工作放在 main()函数中 int_led();
void init_led(void)
{
//ADD BY GU FOR init LED
P0SEL &=0xF1;
P0DIR |= 0x0E; //设置P0.1、P0.2、P0.3为输出
P2SEL &=~(0x1);
P2DIR |=0x01;
P2_0=1;
}
5. hal_board_cfg 中有 按键初始化部分,不方便删除(直接删除会引起编译出错),所以 将其该到了 P0_6,P0_7 上。眼下P0_6,P0_7 悬空估计 不会有其他副作用。
//changed by gu
/* S1 */
#define PUSH1_BV BV(7)
#define PUSH1_SBIT P0_7
/*
#define PUSH2_BV BV(6)
#define PUSH2_SBIT P0_6
#define PUSH2_POLARITY ACTIVE_HIGH
二:按键的处理之 KEY 轮询方式
硬件连接情况: P0_0-----SW2;
相关文件 :hal_key.c
文件位置:C:\Texas
Instruments\ZStack-CC2530-
工程中的位置:HAL—Target—CC2530EB—Drivers--- hal_led.c
1..修改按键端口 定义(P0_0)
/* SW_2 is at P0.0 */
#define HAL_KEY_SW_2_PORT P0
#define HAL_KEY_SW_2_BIT BV(0)
#define HAL_KEY_SW_2_SEL P0SEL
#define HAL_KEY_SW_2_DIR P0DIR
/* SW_2 interrupts */
#define HAL_KEY_SW_2_IEN IEN1 /* CPU interrupt mask register */
#define HAL_KEY_SW_2_IENBIT BV(5) /* Mask bit for all of Port_0 */
#define HAL_KEY_SW_2_ICTL P0IEN /* Port Interrupt Control register */
#define HAL_KEY_SW_2_ICTLBIT BV(0) /* P0IEN - P0.0 enable/disable bit */
#define HAL_KEY_SW_2_PXIFG P0IFG /* Interrupt flag at source */按键端口 初始
化:
2.void HalKeyInit( void ) 函数中
添加:
//add by gu
HAL_KEY_SW_2_SEL &= ~(HAL_KEY_SW_2_BIT); /* Set pin function to GPIO */
HAL_KEY_SW_2_DIR &= ~(HAL_KEY_SW_2_BIT); /* Set pin direction to Input */
3. HalKeyConfig函数中 屏蔽中断
//add by gu
HAL_KEY_SW_2_ICTL &= ~(HAL_KEY_SW_2_ICTLBIT); /* don't generate interrupt */
HAL_KEY_SW_2_IEN &= ~(HAL_KEY_SW_2_IENBIT); /* Clear interrupt enable bit */
我们这里不采用中断方式,使用轮询方式。延伸阅读:
在ZMain.c 文件 main.c 函数中
调用了 // Initialize board I/O
InitBoard( OB_COLD );
在OnBoard.c 中 定义了 InitBoard()函数
中 最后有初始化 KEY 的操作
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
采用了非中断模式
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
4 HalKeyRead 函数中添加
if (!P0_0)
{
keys |= HAL_KEY_SW_2;
}
5 HalKeyPoll 函数中添加
if (!P0_0)
{
keys |= HAL_KEY_SW_2; (此处最关键,决定了ZSTACK 认为这是 SW 几)
}
至此 hal_key.c 修改完毕
6 KEY 的 相应函数,在你的应用文件例如GenericApp.c 中 GenericApp_HandleKeys函数里if ( keys & HAL_KEY_SW_2 ) 处 添加按键响应代码
6.KEY 的修改 其实挺简单,(1)初始化 按键输入端口,可在 HalKeyInit 函数中,也可在main函数中,(2)HalKeyPoll 函数中if (!P0_0)
{
keys |= HAL_KEY_SW_2; (此处最关键,决定了ZSTACK 认为这是 SW 几)
}
报告OS 按键 事件的发生
7. 现在 按键 是电平触发 且无防抖,有时会出现 按下一次 会响应多次。可以考虑使用按键中断处理机制
三 按键的处理之 KEY 中断方式
硬件连接情况: P0_0-----SW2;
在OnBoard.c 中
1. 定义了 InitBoard()函数
中 最后有初始化 KEY 的操作
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
采用了中断模式
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
han_key.c 中的 设置
2.预定义 相关信息,详情参见2530数据手册
/* SW_2 is at P0.0 */
#define HAL_KEY_SW_2_PORT P0
#define HAL_KEY_SW_2_BIT BV(0)
#define HAL_KEY_SW_2_SEL P0SEL
#define HAL_KEY_SW_2_DIR P0DIR
/* edge interrupt */
#define HAL_KEY_SW_2_EDGEBIT BV(0)
#define HAL_KEY_SW_2_EDGE HAL_KEY_FALLING_EDGE
/* SW_2 interrupts */
#define HAL_KEY_SW_2_IEN IEN1 /* CPU interrupt mask register */
#define HAL_KEY_SW_2_IENBIT BV(5) /* Mask bit for all of Port_0 */
#define HAL_KEY_SW_2_ICTL P0IEN /* Port Interrupt Control register */
#define HAL_KEY_SW_2_ICTLBIT BV(0) /* P0IEN - P0.0 enable/disable bit */
#define HAL_KEY_SW_2_PXIFG P0IFG /* Interrupt flag at source */
3. HalKeyInit 函数 初始化P0_0 为 输入脚
HAL_KEY_SW_2_SEL &= ~(HAL_KEY_SW_2_BIT); /* Set pin function to GPIO */
HAL_KEY_SW_2_DIR &= ~(HAL_KEY_SW_2_BIT); /* Set pin direction to Input */
4. HalKeyConfig 函数 设置 中断 打开,及中断方式 下降沿中断
PICTL &= ~(HAL_KEY_SW_2_EDGEBIT); /* Clear the edge bit */
/* For falling edge, the bit must be set. */
PICTL |= HAL_KEY_SW_2_EDGEBIT;
/* Interrupt configuration:
* - Enable interrupt generation at the port
* - Enable CPU interrupt
* - Clear any pending interrupt
*/
HAL_KEY_SW_2_ICTL |= HAL_KEY_SW_2_ICTLBIT;
HAL_KEY_SW_2_IEN |= HAL_KEY_SW_2_IENBIT;
HAL_KEY_SW_2_PXIFG = ~(HAL_KEY_SW_2_BIT);
osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT); 开启 按键事件 定时器
5. 中断函数
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
if (HAL_KEY_SW_2_PXIFG & HAL_KEY_SW_2_BIT)
{
halProcessKeyInterrupt();
}
/*
Clear the CPU interrupt flag for Port_0
PxIFG has to be cleared before PxIF
*/
HAL_KEY_SW_2_PXIFG = 0;
HAL_KEY_CPU_PORT_0_IF = 0;
}
6. 中断处理函数
void halProcessKeyInterrupt (void)
{
bool valid=FALSE;
if (HAL_KEY_SW_2_PXIFG & HAL_KEY_SW_2_BIT) /* Interrupt Flag has been set */
{
HAL_KEY_SW_2_PXIFG = ~(HAL_KEY_SW_2_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
if (valid)
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
}
}
向系统 报告 按键事件
7 用户层的按键处理,在你的应用文件例如GenericApp.c 中 GenericApp_HandleKeys函数里if ( keys & HAL_KEY_SW_2 ) 处 添加按键响应代码
8. 以下分析摘自《zigbee 技术实践教程—基于CC2430/31的无线传感器网络解决方案》一书258页
任务事件处理函数接收到KEY_CHANGE 消息的途径:
(1) 按键被按下,HAL 层检测到按键状态变化(通过H/W 中断或 轮询)
(2) HAL 对应的OSAL 任务检测 到按键的状态变化,触发OSAL 按键变化的回调函数
(3) OSAL 按键变化回调函数发送一OSAL 的系统事件消息给已经注册过的任务
OS内部 更为详细的 调用过程详见《zigbee 技术实践教程—基于CC2430/31的无线传感器网络解决方案》一书345--347页
四 UART
1.首先 确保hal_board_cfg.h定义了
#define HAL_UART TRUE
//add by gu
#define HAL_UART_DMA 0
#define HAL_UART_ISR 1
定义为 串口中断模式,如果为 DMA 模式的话,HalUARTWrite发送数据时 会丢掉 第二个字符
2. 串口初始化
void init_uart(byte task_id)
{
// uint8 *buf="abcdefghigklmn123456789";
halUARTCfg_t uartConfig;
uartConfig.configured = TRUE; // don't care.
uartConfig.baudRate = HAL_UART_BR_115200; // CC2430 only allow 38.4k or 115.2k
uartConfig.flowControl = HAL_UART_FLOW_OFF; // Turn off flow control to fit most serial ports' setting
uartConfig.flowControlThreshold = SERIAL_PORT_THRESH;
uartConfig.rx.maxBufSize = SERIAL_PORT_RX_MAX;
uartConfig.tx.maxBufSize = SERIAL_PORT_TX_MAX;
uartConfig.idleTimeout = SERIAL_PORT_IDLE; // don't care.
uartConfig.intEnable = TRUE; // don't care.
uartConfig.callBackFunc = uartrxCB;
HalUARTOpen (HAL_UART_PORT_0, &uartConfig);
// Set an event to start the application
// osal_set_event(task_id, ZB_ENTRY_EVENT);
// HalUARTWrite ( HAL_UART_PORT_0, buf, strlen(buf) );
}
各参数详解详见《HAL Driver API.pdf》UART 一章
3. 串口接收
/*********************************************************************
* 串口接收回调函数
* 用于串口回显
*/
void
uartrxCB(uint8 port,uint8 event)
{
uint8 *buf;
uint8 len;
if ((event & (HAL_UART_RX_FULL |
HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)))
{
buf = osal_mem_alloc( SERIAL_PORT_RX_CNT );
if ( buf!=NULL )
{
len = HalUARTRead( HAL_UART_PORT_0, buf, SERIAL_PORT_RX_CNT );
if(len>0)
{
sensor_SendSensorMessage(buf);
}
osal_mem_free( buf );
// osal_set_event( sensor_TaskID, SERIAL_PORT_MSG_RCV_EVT );
// 触发 OS 提供的 串口事件
}
}
}
注意:如果没有 if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL |
HAL_UART_RX_TIMEOUT)))
这一句判断,串口接收会出错。
4. 串口发送:
HalUARTWrite ( HAL_UART_PORT_0, buf, len ); //FOR TEST
5. 此处并未使用 OS 提供的 串口事件,实现了 串口数据回显的功能。
事实上可以用OS 提供的 串口事件实现串口功能,具体实现可参考 TI 提供的透明串口实验SerailApp
OS 提供的 串口事件:
HAL_UART_RX_FULL |
Rx Buffer is full |
HAL_UART_RX_ABOUT_FULL
|
Rx Buffer is at maxRxBufSize
- flowControlThreshold |
HAL_UART_RX_TIMEOUT
|
Rx is idle for idleTimout
time |
HAL_UART_TX_FULL |
Tx Buffer is full |
HAL_UART_TX_EMPTY |
Tx Buffer is free
to write more data |
摘自:《HAL Driver API.pdf》UART 一章
方式二:关于 串口事件
也可以自定义事件,在串口接收 回调函数里 触发该事件,在该事件里 进行串口接收处理
具体 事件 处理方式 如下:
1. 定义串口接收事件编号 #define UART_RX_CB_EVT 0x0002
事件编号的定义:必须用左移一位的方式 实现 例如0x0001,0x0002,0x0004,0x0008
2. 在串口 回调函数中 ,接收数据存到全局数组中, 触发 串口接收事件
/* 触发 串口接收事件 UART_RX_CB_EVT
*/
void uartrxCB(uint8 port,uint8 event)
{
if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)))
{
HalUARTRead( HAL_UART_PORT_0, pcmd, CMD_LENGTH );
osal_set_event( GenericApp_TaskID, UART_RX_CB_EVT);
}
}
3. 串口接收事件 中对 串口接收数据 进行再处理
if ( events & UART_RX_CB_EVT )
{
//串口接收事件 串口回显命令(TEST),再通过 无线方式发送给 子节点
HalUARTWrite ( HAL_UART_PORT_0, pcmd , CMD_LENGTH ); // for test
GenericApp_SendCmdMessage(pcmd,CMD_LENGTH);
// return unprocessed events
return (events ^ UART_RX_CB_EVT);
}
串口接收事件 在应用层 事件处理函数GenericApp_ProcessEvent()中,该函数由系统负责调用。GenericApp_ProcessEvent 为用户任务在 OSAL_GenericApp.c 文件
// The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] 中添加
五:
SPI
ZSTACK 移植记录
1. 在HAL ---- Target---------CC2530EB----Drivers 文件夹 添加 Hal_spi.c 和 Hal_spi.h 文件
D:\Texas Instruments\ZStack-CC2530-2.3.0-1.4.0\Components\hal\target\CC2530EB 文件夹下
2. 在HAL------Common 文件夹 hal_drivers.c 中
添加 SPI 初始化和 SPI 中断轮询 SPI 初始化:
void HalDriverInit (void)
{
。。。。。。。。。。。
/* SPI */
#if (defined HAL_SPI) && (HAL_SPI == TRUE)
HalSpiInit();
#endif
。。。。。。。。。。。。。
}
Main.c 中会调用 HalSpiInit()
SPI 中断轮询:
void Hal_ProcessPoll ()
{
。。。。。。。。。。。。。。。。。
/* SPI Poll */
//add by gu
#if (defined HAL_SPI) && (HAL_SPI == TRUE)
// HalSpiPoll();
HalSpiIntPoll();
#endif
。。。。。。。。。。。。。。。。。。。
}
Main.c 中会调用 osal_start_system ,其中不断 会调用 HalSpiIntPoll()
其中HalSpiInit(); HalSpiIntPoll(); 具体定义在Hal_spi.c中定义实现
(其实 原来就有 只需要我们实现一下)
3. 对于 SPI 发送使用 轮询机制:
直接调用 发送函数发送即可
4. 对于 SPI 接收 采用 中断方式; 中断处理 函数 HAL_ISR_FUNCTION( halSpiIsr, URX1_VECTOR )在Hal_spi.c中定义
当接收完 一包数据 并校验通过后,触发 接收事件
osal_set_event( sensor_TaskID, SPI_POLL_EVT );
在应用层 sensor.c
UINT16 sensor_ProcessEvent( byte task_id, UINT16 events )
SPI接收事件SPI_POLL_EVT中会根据 数据类型 处理这一包数据:
if ( events & SPI_POLL_EVT )
{
switch ( SpiRecBuf[1])
{
case SENSOR_EVENT:
。。。。。。。。。。
// return unprocessed events
return (events ^ SPI_POLL_EVT);
}
5. 对于 按键的处理:
当在SPI 接收的按键数据包中分析出按键值后,向当前 任务发送按键 消息,触发按键 处理
向当前 任务发送按键 消息:
case KEY_EVENT:
switch (SpiRecBuf[3])
{
case KEY_1:
OnBoard_SendKeys(HAL_KEY_SW_1,0);
break;
case KEY_2:
OnBoard_SendKeys(HAL_KEY_SW_2,0);
break;
……………………………………..
OnBoard_SendKeys 函数在 OnBoard.c 中定义
最后调用了 osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
触发按键 处理:
UINT16 sensor_ProcessEvent( byte task_id, UINT16 events )
{
系统事件
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( sensor_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
………………………………….
case KEY_CHANGE:
sensor_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
………………………………………
}
用户的按键处理接口:
void sensor_HandleKeys( byte shift, byte keys )
{
if ( shift )
{
if ( keys & HAL_KEY_SW_1 )
{
}
if ( keys & HAL_KEY_SW_2 )
{
}
}
else
{
if ( keys & HAL_KEY_SW_1 )
{
用户的按键处理接口:
}
。。。。。。。。。。。。。。。。。