查看: 28559|回复: 33

RemoTI原版按键程序分析

[复制链接]
xingqing 发表于 2010-10-8 22:48:41 | 显示全部楼层 |阅读模式
本帖最后由 xingqing 于 2010-10-15 12:36 编辑

看了outman的关于RemoTI的教程,受益匪浅,然后找了下以前学习所做的笔记,共享出来,大家共同学习,如果有哪里理解错误请大家给我指出来:
   RemoTI遥控板子上按键的发射程序,整个流程及其相对应的程序我都贴上了,好了,首先上个图:
   
    很不幸的是,从刚开始分析我就没留意这个图,造成我花了很长时间来想这个按键的流程,看他的流程,已经很明白了:开始(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 here

  return 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 GPIO
  HAL_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 columns
  }
  else//如果是上拉,进来设置所有的引脚为低电平
  {
    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 GPIO
  HAL_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));//全部初始化为0

  macTaskInit( taskID++ ); //添加mac层的任务  被封掉了
  RCN_Init( taskID++ );    //添加RCN的任务 RCN:RF4CE network layer  被封掉了
  RTI_Init( taskID++ );    //添加RTI的任务 RTI:RemoTI application framework
  RSA_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 */
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
outman 发表于 2010-10-15 08:06:57 | 显示全部楼层
论坛数据库出了点问题,现在终于搞定了,xinqing加油~
 楼主| xingqing 发表于 2010-10-15 12:33:58 | 显示全部楼层
接我上面的:
上面这个函数是对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
   * isn't 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 (tasksEvents[idx])  // Task is highest priority that is ready.
      {
        break;
      }
    } while (++idx < tasksCnt);

    //判断是否有事件产生,如果有进来if
    if (idx < tasksCnt)
    {
      uint16 events;
      halIntState_t intState;//中断位状态

      HAL_ENTER_CRITICAL_SECTION(intState);//保护中断现场
      events = tasksEvents[idx];           //对应相应的发生的事件的数组
      tasksEvents[idx] = 0;  // Clear the Events for this task.清除数组中的事件
      HAL_EXIT_CRITICAL_SECTION(intState);    //恢复总中断

      //调用相应的处理函数,返回的是未处理的事件
      events = (tasksArr[idx])( idx, events );
      //保护中断现场
      HAL_ENTER_CRITICAL_SECTION(intState);
      //添加未处理的事件到任务事件数组中
      tasksEvents[idx] |= 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
  }
}

如果没有任何事件发生的时候,在for中一直死循环。
看到for这个死循环中的第一个函数osalTimeUpdate();,在上一步中我们讲到了定时器溢出,
溢出了再怎么办呢?osalTimeUpdate这个函数告诉你溢出之后该怎么办了,好了贴代码:
 楼主| xingqing 发表于 2010-10-15 12:34:35 | 显示全部楼层
void osalTimeUpdate( void )
{
  uint16 tmp;
  uint16 ticks320us;
  uint16 elapsedMSec = 0;

  // Get the free-running count of 320us timer ticks
  tmp = macMcuPrecisionCount();
  
  if ( tmp != previousMacTimerTick )
  {
    // Calculate the elapsed ticks of the free-running timer.
    ticks320us = tmp - previousMacTimerTick;
  
    // Store the MAC Timer tick count for the next time through this function.
    previousMacTimerTick = tmp;
  
    /* It is necessary to loop to convert the usecs to msecs in increments so as
     * not to overflow the 16-bit variables.
     */
    while ( ticks320us > 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 time
  osal_systemClock += updateTime;
  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

  // Look for open timer slot
  if ( 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 <= updateTime)
      {
        srchTimer->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

    //这里是将相应层的事件标志位设置为有效
tasksEvents[task_id] |= event_flag;  // Stuff the event bit(s)

    HAL_EXIT_CRITICAL_SECTION(intState);     // Release interrupts
    return ( SUCCESS );
  }
   else
  {
    return ( INVALID_TASK );
  }
}

看上面那个蓝色的程序,tasksEvents[task_id] |= event_flag; 这里是将这个任务设置为有效,注意我们再启动定时器的时候的task_id和这里的task_id是一个值,这个你再回去看看就知道了,然后你再看系统启动函数中的do-while这里
do {
      if (tasksEvents[idx])  // Task is highest priority that is ready.
      {
        break;
      }
} while (++idx < tasksCnt);
当(tasksEvents[idx]有效的时候,那么break跳出来,继续往下执行,然后我们紧接着再往下看系统启动函数中的程序
if (idx < tasksCnt)
    {
      uint16 events;
      halIntState_t intState;//中断位状态

      HAL_ENTER_CRITICAL_SECTION(intState);//保护中断现场
      events = tasksEvents[idx];           //对应相应的发生的事件的数组
      tasksEvents[idx] = 0;  // Clear the Events for this task.清除数组中的事件
      HAL_EXIT_CRITICAL_SECTION(intState);    //恢复总中断

      //调用相应的处理函数,返回的是未处理的事件
      events = (tasksArr[idx])( idx, events );
      //保护中断现场
      HAL_ENTER_CRITICAL_SECTION(intState);
      //添加未处理的事件到任务事件数组中
      tasksEvents[idx] |= 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));//全部初始化为0

  macTaskInit( taskID++ ); //添加mac层的任务  被封掉了
  RCN_Init( taskID++ );    //添加RCN的任务 RCN:RF4CE network layer  被封掉了
  RTI_Init( taskID++ );    //添加RTI的任务 RTI:RemoTI application framework
  RSA_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_SAVING
  if ( events & HAL_SLEEP_TIMER_EVENT )
  {
    halRestoreSleepLevel();
    return events ^ HAL_SLEEP_TIMER_EVENT;
  }
#endif

  /* Nothing interested, discard the message */
  return 0;

}

很明显我们又到了,key轮询这一步了
 楼主| xingqing 发表于 2010-10-15 12:34:55 | 显示全部楼层
第六步  key扫描

看看这里面的程序在做什么

void HalKeyPoll (void)
{
#if (HAL_KEY == TRUE)

  uint8 keys = 0;

  /*
  *  If interrupts are enabled, get the status of the interrupt-driven keys from 'halSaveIntKey'
  *  which is updated by the key ISR.  If Polling, read these keys directly.
  */
  keys = HalKeyRead();//读取键值

  /* Exit if polling and no keys have changed */
  if (!Hal_KeyIntEnable)//如果中断没有使能进来
  {
    if (keys == halKeySavedKeys)//如果这次键值和上次一样直接返回
    {
      return;
    }
    //保存当前键值
    halKeySavedKeys = keys;     /* Store the current keys for comparation next time */
  }

  
/* Invoke Callback if new keys were depressed */
//调用回调函数,按键命令发送
  if ((keys != HAL_KEY_CODE_NOKEY || Hal_KeyIntEnable) &&
      (pHalKeyProcessFunction))
  {
    // When interrupt is enabled, send HAL_KEY_CODE_NOKEY as well so that
    // application would know the previous key is no longer depressed.
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }
  
  if (Hal_KeyIntEnable)//如果是使用的中断方式进来
  {
    if (keys != HAL_KEY_CODE_NOKEY)
    {
      // In order to trigger callback again as far as the key is depressed,
      // timer is called here
//这里是连发的作用,50ms一次
      osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, 50);
    }
    else
    {
      halKeyTimerRunning = FALSE;//否则的话定时器处于未运行状态
    }
  }
#endif /* HAL_KEY */

}


下面分析下这个程序,首先是keys = HalKeyRead();     是读取按键的值,然后到下一个蓝色的部分就是那个回调函数,还记得这个回调函数在哪里注册的吗,就是在启动系统函数osal_start_system的上面HalKeyConfig(RSA_KEY_INT_ENABLED, RSA_KeyCback);如果你还不知道的话,那就再去看看HalKeyConfig这里面的函数,或者直接去前面看看我写过的,这个回调函数是做什么的?就是调用相关底层函数然后无线发送按键命令。

还有一点就是连发那块的,如果你一直按住这个键不动,那肯定没有中断触发了,只是在轮询了,这个osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, 50);这句就是启动了连发的定时器,如果你一直按键不放他每50ms,标志位被置位一次。

好了,到此整个按键的流程搞定了,哈哈。。。
heihcx 发表于 2010-10-15 20:52:12 | 显示全部楼层
顶顶顶   学习学习
wuxiujiang 发表于 2010-10-15 22:08:32 | 显示全部楼层
学习啊!!!!!
ZIGBEE 发表于 2010-10-19 21:49:13 | 显示全部楼层
这么长哦。
suyoujiang 发表于 2010-11-5 10:51:05 | 显示全部楼层
学习了  谢谢楼主!!!
CE_Aloysha 发表于 2010-11-10 10:40:01 | 显示全部楼层
现在正准备弄这个,正在无从下手的时候楼主送来了这篇大作,正在膜拜ing! 谢谢楼主!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表