zstack按键程序分析.doc_第1页
zstack按键程序分析.doc_第2页
zstack按键程序分析.doc_第3页
zstack按键程序分析.doc_第4页
zstack按键程序分析.doc_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

本文来自飞比论坛,作者:xingqing帖子地址:/bbs/viewthread.php?tid=393膜拜大作,虚心学习。看了outman的关于RemoTI的教程,受益匪浅,然后找了下以前学习所做的笔记,共享出来,大家共同学习,如果有哪里理解错误请大家给我指出来: RemoTI遥控板子上按键的发射程序,整个流程及其相对应的程序我都贴上了,好了,首先上个图: 下载 (25 KB)2010-10-8 22:42 很不幸的是,从刚开始分析我就没留意这个图,造成我花了很长时间来想这个按键的流程,看他的流程,已经很明白了:开始(start),所有的列配置为输出,所有的行配置为中断使能输入,这里很简单就是个矩阵键盘,51中是用查询法来检测按键,这里只不过用的是中断方式来触发而已,然后调用消除抖动定时器(start key de-bounce timer),当定时器溢出的时候,开始扫描按键,如果没有检测到按键,那么有返回到开始之下,重新来,如果有按键被检测到,这时候启动轮询定时器,启动这个定时器的原因是为了能够连发,如果你按下这个键一直不放那么就是靠这个定时器,来实现连发的,连发的时间间隔这个值我们是可以改动的,如果这个时候又检测不到按键了,那么又回到开始之下,当你再次按键的时候是靠中断来触发的。有很多人觉得读取按键还有无线发送按键命令是在端口0的中断处理函数中进行的,其实不是这样的,中断中只是给这个任务添加了一个定时器而已这里先插一句:分析程序的时候我们要忽略次要的,看主要的。以上是整个按键的流程了,下面分析下具体的程序:凡是要找个开始,不用说肯定先看主函数,:int main(void)/* Initialize hardware 初始化硬件,选择时钟,还有灯的方向选择*/HAL_BOARD_INIT();/* Initialze the HAL driver 初始化板上的硬件*/HalDriverInit();/* Initialize NV system初始化nv系统?*/osal_nv_init(NULL);/* Initialize MAC被封掉了 */MAC_InitRf4ce();/* Initialize the operating system初始化操作系统*/osal_init_system();/* Enable interrupts使能总中断*/HAL_ENABLE_INTERRUPTS();/* Setup Keyboard callback 这里是选择了按键的检测方式*/最重要的是指向了按键回调函数HalKeyConfig(RSA_KEY_INT_ENABLED, RSA_KeyCback);/* Start OSAL 进入操作系统,不能返回 */osal_start_system(); / No Return from herereturn 0;以上我已经做了注释了,这个主函数很简单,下面看看具体跟按键相关的程序:函数HalDriverInit中,有这么一句/* KEY */#if (defined HAL_KEY) & (HAL_KEY = TRUE)HalKeyInit();#endif我们再看看这个HalKeyInit();函数到底做了什么,void HalKeyInit( void )#if (HAL_KEY = TRUE)/* Initialize previous key to 0 初始化先前的键值为0*/halKeySavedKeys = HAL_KEY_CODE_NOKEY;/列端口1除了P1_1没有使用之外其他都用到了,设置为普通的IO,方向输出HAL_KEY_COL_SEL &= (uint8) HAL_KEY_COL_BITS; / set pin function to GPIOHAL_KEY_COL_DIR |= HAL_KEY_COL_BITS; / set pin direction to output/以下几句是在选择他的触发方式,上升沿触发还是下降沿触发,如果学过单片机的同学/这个应该不是问题if (HAL_KEY_ROW_PULLDOWN)/如果是下拉的话,进来设置所有的引脚为高电平 HAL_KEY_COL_PORT |= HAL_KEY_COL_BITS; / output high on all columnselse/如果是上拉,进来设置所有的引脚为低电平 HAL_KEY_COL_PORT &= (uint8) HAL_KEY_COL_BITS; / output low on all columns/行端口0设置为普通的IO(所有端口都用到了),方向输入HAL_KEY_ROW_SEL &= (HAL_KEY_ROW_BITS); / set pin function to GPIOHAL_KEY_ROW_DIR &= (HAL_KEY_ROW_BITS); / set pin direction to input/ pull down setup if necessary 如果有必要的话 设置为下拉if (HAL_KEY_ROW_PULLDOWN) HAL_KEY_ROW_INP |= HAL_KEY_ROW_PDUPBIT;/设置端口0为下拉/以下三句程序现在不用管他是做什么用的,后面我们会看到他的作用,/* Initialize callback function这个要好好关注下,开始的时候这个回调时空的 */pHalKeyProcessFunction= NULL;/这里初始化回调函数是空的/* Start with key is not configured */HalKeyConfigured = FALSE;/起始的时候键值没有被配置halKeyTimerRunning = FALSE;#endif /* HAL_KEY */上面这个函数只是对按键的初始化,注意这里初始化只是将按键配置成了:普通的IO口,列为输出,行为输入,并没有让行的中断使能,再就是对回调函数的清空,这个回调是重点,要注意!在osal_init_system这个函数中,osalInitTasks这个函数,具体程序如下:void osalInitTasks( void )uint8 taskID = 0;/注意这里任务ID是从0开始的/*这里返回的是分配内存空间的首地址,注意这个首地址的上一个地址就是他的内存控制头,关于他内存的分配可以看看我的下一教程*/tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);/分配了足够的空间osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt);/全部初始化为0macTaskInit( taskID+ ); /添加mac层的任务被封掉了RCN_Init( taskID+ ); /添加RCN的任务 RCN:RF4CE network layer被封掉了RTI_Init( taskID+ ); /添加RTI的任务 RTI:RemoTI application frameworkRSA_Init( taskID+ ); /添加RSA的任务Hal_Init( taskID ); /添加硬件抽象层的任务,这里就是对按键处理任务的添加还有一个数组要十分注意的是:/ The order in this table must be identical to the task initialization calls below in osalInitTask./这里的顺序很重要,要和osalInitTasks这里添加任务的顺序一样const pTaskEventHandlerFn tasksArr =macEventLoop,RCN_ProcessEvent,RTI_ProcessEvent,RSA_ProcessEvent,Hal_ProcessEvent;这个数组的顺序和上面的taskID要对应上,也就是说他给每个处理函数排了个队,并且给他们编了个号,而且有个标志位,这个标志位很重要,标志位是靠下层触发的(这句话现在看有点不明白,看到最后的时候你会明白),究竟怎么触发的我们先不用管,还是举个例子吧,比如无线接收到数据了,你要从下层传到上层,最后就是靠下层触发这些标志位,这样上层才知道有事情发生了,通过taskID找到你相应的函数,这样比较简单,系统在轮询事件的时候是按照这个顺序来走了,你的taskID和相应的程序对不上号那不就乱了吗,简单说下,以后详再说。以上两个程序,准确的来说是在添加任务,以前的协议栈中说的比较明白,新的刚出来的时候都不知道怎么添加任务的,呵呵,也怪我太笨了,为什说他这个是在添加任务,这个以后在说,现在记住就可以了,一定记住。然后最关键的函数来了,HalKeyConfig(RSA_KEY_INT_ENABLED, RSA_KeyCback);这个函数可以说是按键程序最厉害的了,贴出来程序先:void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)#if (HAL_KEY = TRUE)/* Enable/Disable Interrupt or */Hal_KeyIntEnable = interruptEnable;/* Register the callback fucntion 注册按键回调函数*/pHalKeyProcessFunction = cback;/注册回调函数,注册的是RSA_KeyCback这个函数/* Determine if interrupt is enable or not */if (Hal_KeyIntEnable)/判断中断是否使能,这里我们是使能的RSA_KEY_INT_ENABLED /将行设置中断使能,前面初始化的时候只是将端口设置为一般的IO,输入 PICTL &= (HAL_KEY_ROW_EDGEBIT); / set rising or falling edge设置上升沿触发还是下降沿触发 if (HAL_KEY_ROW_EDGE = HAL_KEY_FALLING_EDGE) PICTL |= HAL_KEY_ROW_EDGEBIT;/注意这里中断使能的时候有两个地方要使能 HAL_KEY_ROW_ICTL |= HAL_KEY_ROW_ICTLBITS; / Set interrupt enable 引脚的使能 HAL_KEY_ROW_IEN |= HAL_KEY_ROW_IENBIT; / enable interrupt 端口的使能/* Do this only after the hal_key is configured - to work with sleep stuff */ if (HalKeyConfigured = TRUE)/这个参数其实我们已经见过,也初始化过,在前面的/HalKeyInit这个函数中 /这里是取消定时器,因为在查询法中使用定时器来查询按键的状态,但是在中断方/式中不需要定时器 osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT);/* Cancel polling if active */ else /* Interrupts NOT enabled */ / disable interrupt禁用中断HAL_KEY_ROW_ICTL &= (HAL_KEY_ROW_BITS); HAL_KEY_ROW_IEN &= (HAL_KEY_ROW_IENBIT); /启动定时器轮询检测按键100ms一次,不使用中断函数那只能用查询法了,这个就是给/定时的,每多长时间查询一次,我想对于学过单片机的同学应该不是很难理解吧 osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); /* Kick off polling */* Key now is configured */HalKeyConfigured = TRUE;/这个配置的值设置为1了#endif /* HAL_KEY */接我上面的:上面这个函数是对key的配置函数,使能行的外部引脚中断,指定回调函数,使用中断方式检测按键,关于那两个启动和消除定时器的函数,也要注意他的作用。上个程序中有个关键的回调函数,这个我们要分析下,注册回调函数是在main函数的倒数第二句程序,HalKeyConfig(RSA_KEY_INT_ENABLED, RSA_KeyCback);看后面这个RSA_KeyCback,这可不是个一般的参数,他是个函数,他的程序就不贴出来了,他的功能就是检测按键,检测到之后,然后调用:RTI_SendDataReq( rsaDestIndex, profileId, vendorId, txOptions, len, pData);从这个函数的表面意思就知道,他是个发送数据的函数,注意开头是RTI,现在只是发到RTI这层了,还要发到网络层,(不过这里我看他的结构框架,RemoTI的应用框架和网络层是写在一起的),然后发送到物理层,然后通过物理层向空中发射出去,这里的代码没办法贴了,因为他都封掉了,比较郁闷的一件事情,所以还是开源的好啊,因此非常支持outman现在搞开源的zigbee,题外话了。写了这么多,看了是不是有很多问题,比如中断函数在哪里,他在里面做了什么,按键发送那个函数又是怎么调用的?等等一系列问题,好的 下面我们再从最上面的图开始整理思路,同时将没有涉及到的程序贴出来,第一步:开始第二步:按键的配置第三步:产生中断,这里有程序没讲,贴出来,在hal_key,c这个文件中有个中断函数,HAL_ISR_FUNCTION,具体程序如下:HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )halProcessKeyInterrupt();/这里主要的作用是消抖#if HAL_KEY/* Make sure that we clear all enabled, but unused P0IFG bits. * For P0 we can only enable or disable high or low nibble, not bit by * bit. For P1 and P2 enabling of single bits are possible, therefore * will not any unused pins generate interrupts on P1 or P2. * We could have checked for low and high nibble in P0, but this * isnt necessary as long as we only clear unused pin interrupts. */P0IFG = (uint8) (HAL_KEY_P0INT_LOW_USED | HAL_KEY_POINT_HIGH_USED);P0IF = 0;CLEAR_SLEEP_MODE();#endif这里这是调用了一个函数halProcessKeyInterrupt,下面的程序不用管先,再贴这个函数的程序,看看他在做什么,void halProcessKeyInterrupt (void)#if (HAL_KEY = TRUE)/首先通过中断标志位来看看是否真的有键按下if (HAL_KEY_ROW_PXIFG & HAL_KEY_ROW_BITS)/如果有键按下进来 / Disable interrup禁用行所有行端口的位中断 HAL_KEY_ROW_ICTL &= (uint8) HAL_KEY_ROW_ICTLBITS; / interrupt flag has been set清除中断标志位HAL_KEY_ROW_PXIFG = (uint8) (HAL_KEY_ROW_BITS); / clear interrupt flag/注意我们再key初始化函数中也有定义这个halKeyTimerRunning参数哦,为false/也就是开始时没有处在运行状态的 if (!halKeyTimerRunning)/如果定时器没有处在运行状态,那么让定时器运行起来 halKeyTimerRunning = TRUE; osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);/启动定时器,为了消抖 / Enable interrupt 使能中断 HAL_KEY_ROW_ICTL |= HAL_KEY_ROW_ICTLBITS;#endif /* HAL_KEY */我想当你看到这个osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);这句话的时候,就到了图中的第四步第四步:启动消抖定时器第五步:定时器溢出他所做的事情,在定时器溢出之后,他会将相应的标志位置位(注意不是中断标志位),首先贴出osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE)的代码:uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )halIntState_t intState;osalTimerRec_t *newTimer;HAL_ENTER_CRITICAL_SECTION( intState );/ Hold off interrupts./ Add timer这个是我们要关注的地方,添加一个定时器newTimer = osalAddTimer( taskID, event_id, timeout_value );HAL_EXIT_CRITICAL_SECTION( intState ); / Re-enable interrupts.return ( (newTimer != NULL) ? SUCCESS : NO_TIMER_AVAIL );这个函数中我们关键是看osalAddTimer,到此我们就增加了一个key的定时器了,再往下的代码,就不贴了,定时器已经开始计时了,过了消抖的时间之后,下一步要怎么办呢,这个是最关键的了,这时我们看到了启动系统的函数:void osal_start_system( void )#if !defined ( ZBIT ) & !defined ( UBIT )for(;)/ Forever Loop#endif uint8 idx = 0; osalTimeUpdate(); /这里是在扫描哪个事件被触发了,然后置相应的标志位 Hal_ProcessPoll();/ This replaces MT_SerialPoll() and osal_check_timer(). /*通过这个tasksEvents可以知道是哪个层的事件发生了,接着就是调用相应的层处理 函数,在各层出来函数中还有小的事件,这就需要用events再进一步判断了*/ do if (tasksEventsidx)/ Task is highest priority that is ready. break; while (+idx tasksCnt); /判断是否有事件产生,如果有进来if if (idx MAXCALCTICKS ) ticks320us -= MAXCALCTICKS; elapsedMSec += MAXCALCTICKS * 8 / 25; remUsTicks += MAXCALCTICKS * 8 % 25; / update converted number with remaining ticks from loop and the / accumulated remainder from loop tmp = (ticks320us * 8) + remUsTicks; / Convert the 320 us ticks into milliseconds and a remainder elapsedMSec += tmp / 25; remUsTicks = tmp % 25; / Update OSAL Clock and Timers if ( elapsedMSec )/以下是我们要关心的地方 osalClockUpdate( elapsedMSec );/对时钟的更新 osalTimerUpdate( elapsedMSec );/这个函数关键了,下面有分析 前面那些程序我就不分析了,忽略次要我们捡主要的分析,看到最后一个函数osalTimerUpdate( elapsedMSec );先说下他就是定时器溢出之后,置位标志位(再次强调不是中断标志位)的函数,代码如下:void osalTimerUpdate( uint16 updateTime )halIntState_t intState;osalTimerRec_t *srchTimer;osalTimerRec_t *prevTimer;HAL_ENTER_CRITICAL_SECTION( intState );/ Hold off interrupts./ Update the system timeosal_systemClock += updateTime;HAL_EXIT_CRITICAL_SECTION( intState ); / Re-enable interrupts./ Look for open timer slotif ( timerHead != NULL ) / Add it to the end of the timer list srchTimer = timerHead;/这个应该是个链表结构从开始索引 prevTimer = (void *)NULL; / Look for open timer slot 开始轮询,看看哪个事件到时间了,如果到时间了会设置相应的标志位 while ( srchTimer ) osalTimerRec_t *freeTimer = NULL; HAL_ENTER_CRITICAL_SECTION( intState );/ Hold off interrupts. if (srchTimer-timeout timeout = 0; else srchTimer-timeout = srchTimer-timeout - updateTime; / When timeout or delete (event_flag = 0) /当时间溢出或者取消的时候(后面这个取消我想可以从配对来理解,我们可以取消配对) if ( srchTimer-timeout = 0 | srchTimer-event_flag = 0 ) / Take out of list 提出任务链表 if ( prevTimer = NULL ) timerHead = srchTimer-next; else prevTimer-next = srchTimer-next; / Setup to free memory freeTimer = srchTimer;/这个链表是我们将要处理的链表,提取出来 / Next srchTimer = srchTimer-next;/然后再指向下一个链表 else / Get next如果定时器没有溢出或者取消,那么直接指向下一个链表 prevTimer = srchTimer; srchTimer = srchTimer-next; HAL_EXIT_CRITICAL_SECTION( intState ); / Re-enable interrupts. if ( freeTimer )/这是我们提取出来的链表 if ( freeTimer-timeout = 0 )/如果溢出 osal_set_event( freeTimer-task_id, freeTimer-event_flag );/设置相应的事件 osal_mem_free( freeTimer ); 首先我得补充下,定时器是向下计数的,也就是从大到小来的,到0的时候溢出,看到这句freeTimer-timeout = 0,这个时候定时器溢出了,至于定时器具体是怎么操作的,那些程序被TI 封了,我们根本看不到,比较郁闷。再看看下一句是在做什么,看到osal_set_event 这个函数,他就是置位相应标志位的函数,刚才我们的定时器溢出了,然后在这里将相应的标志位设置为有效,然后进来看看他在这里做了什么,uint8 osal_set_event( uint8 task_id, uint16 event_flag )if ( task_id tasksCnt ) halIntState_t intState;HAL_ENTER_CRITICAL_SECTION(intState); / Hold off interrupts /这里是将相应层的事件标志位设置为有效tasksEventstask_id |= event_flag;/ Stuff the event bit(s) HAL_EXIT_CRITICAL_SECTION(intState); / Release interrupts return ( SUCCESS ); else return ( INVALID_TASK );看上面那个蓝色的程序,tasksEventstask_id |= event_flag; 这里是将这个任务设置为有效,注意我们再启动定时器的时候的task_id和这里的task_id是一个值,这个你再回去看看就知道了,然后你再看系统启动函数中的do-while这里do if (tasksEventsidx)/ Task is highest priority that is ready. break; while (+idx tasksCnt);当(tasksEventsidx有效的时候,那么break跳出来,继续往下执行,然后我们紧接着再往下看系统启动函数中的程序if (idx tasksCnt) uint16 events; halIntState_t intState;/中断位状态 HAL_ENTER_CRITICAL_SECTION(intState);/保护中断现场 events = tasksEventsidx; /对应相应的发生的事件的数组 tasksEventsidx = 0;/ Clear the Events for this task.清除数组中的事件 HAL_EXIT_CRITICAL_SECTION(intState); /恢复总中断 /调用相应的处理函数,返回的是未处理的事件 events = (tasksArridx)( idx, events ); /保护中断现场 HAL_ENTER_CRITICAL_SECTION(intState); /添加未处理的事件到任务事件数组中 tasksEventsidx |= events;/ Add back unprocessed events to the current task. HAL_EXIT_CRITICAL_SECTION(intState);/恢复中断现场 #if defined( POWER_SAVING )/节电模式 else/ Complete pass through all task events with no activity? osal_pwrmgr_powerconserve();/ Put the processor/system into sleep #endif看蓝色部分的程序,我们去那里看看他在做什么const pTaskEventHandlerFn tasksArr =macEventLoop,RCN_ProcessEvent,RTI_ProcessEvent,RSA_ProcessEvent,Hal_ProcessEvent;咋一看是个数组,但这不是一般的数组,其实它里面每个参数都是个函数,我们用到的是哪个呢?对了最后一个,Hal_ProcessEvent,为什么是最后一个,很简单就是上面讲到的这个顺序问题,在添加任务的时候,要跟数组的顺序是相同的,我再把添加任务的函数贴出来,void osalInitTasks( void )uint8 taskID = 0;/注意这里任务ID是从0开始的/*这里返回的是分配内存空间的首地址,注意这个首地址的上一个地址就是他的内存控制头*/tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);/分配了足够的空间osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt);/全部初始化为0macTaskInit( taskID+ ); /添加mac层的任务被封掉了RCN_Init( taskID+ ); /添加RCN的任务 RCN:RF4CE network layer被封掉了RTI_Init( taskID+ ); /添加RTI的任务 RTI:RemoTI application frameworkRSA_Init( taskID+ ); /添加RSA的任务Hal_Init( taskID ); /添加硬件抽象层的任务你可以对下看看是不是一层对应一层,好了,我们该去Hal_ProcessEvent里面看看了,记住先忽略次要看主要,看蓝色部分的程序:uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )uint8 *msgPtr;(void)task_id;/ Intentionally unreferenced parameter/首先判断大事件,是不是内部任务事件if ( events & SYS_EVENT_MSG ) msgPtr = osal_msg_receive(Hal_TaskID);/接收本应用层的消息,并返回接收到的消息 while (msgPtr) /* Do something here - for now, just deallocate the msg and move on */ /* De-allocate */ osal_msg_deallocate( msgPtr ); /* Next */ msgPtr = osal_msg_receive( Hal_TaskID ); return events SYS_EVENT_MSG;if ( events & HAL_LED_BLINK_EVENT )#if (defined (BLINK_LEDS) & (HAL_LED = TRUE) HalLedUpdate();#endif /* BLINK_LEDS & HAL_LED */ return events HAL_LED_BLINK_EVENT;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) osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); #endif / HAL_KEY return events HAL_KEY_EVENT;#ifdef POWER_SAVINGif ( events & HAL_SLEEP_TIMER_EVENT ) halRestoreSleepLevel(); return events HAL_SLEEP_TIMER_EVENT;#endif/* Nothing interested, discard the message */return

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论