最近一段时间由于项目需要,便开始在阅读TI的cc2541的BLE Stack源码,对于蓝牙4.0这块知识基本是小白,所以几乎从0开始。在没接触蓝牙之前,就知道该部分的内容较为复杂(涉及到通信等协议栈),所以本着只有会使用为目的开始了BLE的学习(哎,一直都站在别人的肩膀上),学到点新的知识就积累一点,以免忘记。

看CC2541的开源BLE,一开始还以为51单片机结构会相当的简单,未曾想过TI的大牛们进行了软件架构的封装(OSAL+HAL,一种比较类似于但非操作系统的架构)。的确,对于我们这些小鸟来说,这样的设计对于我们可能很复杂,但是对于TI的开发人员来说却带来了更多的方便,移植简单化,改点配置就又用到别的处理器上。这都有点类似于Linux了,这套框架我还是比较佩服的,下面和大家分享一些OSAL+HAL在BLE中的使用,和大家解析一些函数。

由于这部分内容,网上也可以看到很多,本人也学习很多,喜欢开源的世界,所以就和大家分享自己深入看过的内容。

1.OSAL和HAL的好处

HAL:硬件抽象层,典型的操作系统硬件封装架构,远点的类似于Linux中常见的BSP,Android中的HAL等,都可以见到。相比于之前我所记得的51编程,在HAL里面各种宏定义,将所有可以操作的寄存器都进行封装,连中断都不放过,让搞裸驱的突然会有点不适应。

OSAL:操作系统抽象层,在cc2541的世界里,OSAL主要用于应用程序的开发,API大行其道,看看API手册就能完成编码。

整套类操作系统围绕着:Event任务,Task事件,Message消息来进行。事件发生,由该事件对应的任务来处理,任务有时会将消息做为另一个事件从而去触发别的任务进行消息的处理。

2.OSAL中的时间API函数

因为OSAL的资源比较多,这里和大家讲讲自己看到时间相关的API,主要是osal_run_system中的osalTimeUpdate()函数,该函数会做2件主要的事情:更新系统运行的时间和更新各个软件定时器。

  1. void osalTimeUpdate( void )//得到消逝时间的整数和小数,小数以余数形式存在
  2. {
  3. uint16 tmp;
  4. uint16 ticks625us; //微妙
  5. uint16 elapsedMSec = 0;//毫秒
  6. // Get the free-running count of 625us timer ticks ,一个计数代表625us
  7. tmp = ll_McuPrecisionCount();
  8. if ( tmp != previousLLTimerTick )
  9. {
  10. // Calculate the elapsed ticks of the free-running timer.
  11. ticks625us = tmp - previousLLTimerTick;
  12. // Store the LL Timer tick count for the next time through this function.
  13. previousLLTimerTick = tmp;
  14. /* It is necessary to loop to convert the usecs to msecs in increments so as
  15. * not to overflow the 16-bit variables.
  16. */
  17. while ( ticks625us > MAXCALCTICKS )  //主要是防止数据过大转换为毫秒时发生溢出,MAXCALCTICKS = 13105,13105是就会溢出从0开始重新计数
  18. {
  19. ticks625us -= MAXCALCTICKS;
  20. elapsedMSec += MAXCALCTICKS * 5 / 8;      //滴答数us转换为ms单位8190ms
  21. remUsTicks += MAXCALCTICKS * 5 % 8;    //余数为5,0.625ms
  22. }
  23. // update converted number with remaining ticks from loop and the
  24. // accumulated remainder from loop
  25. tmp = (ticks625us * 5) + remUsTicks;//将余数加上去
  26. // Convert the 625 us ticks into milliseconds and a remainder (  余数)
  27. elapsedMSec += tmp / 8;
  28. remUsTicks = tmp % 8;//计算出整个时间后,进行取整和余数
  29. // Update OSAL Clock and Timers
  30. if ( elapsedMSec )//前后两次调用该函数时已经过去的时间值,该值只是ms位,有余数未计入
  31. {
  32. osalClockUpdate( elapsedMSec );//更新整个系统的时间,UTCTime以秒为单位记录
  33. osalTimerUpdate( elapsedMSec );//传入过去流逝的时间值,进一步更新每个软件定时器,减去一定的时间
  34. }
  35. }
  36. }
[html] view plain copy
  1. void osalTimeUpdate( void )//得到消逝时间的整数和小数,小数以余数形式存在
  2. {
  3. uint16 tmp;
  4. uint16 ticks625us; //微妙
  5. uint16 elapsedMSec = 0;//毫秒
  6. // Get the free-running count of 625us timer ticks ,一个计数代表625us
  7. tmp = ll_McuPrecisionCount();
  8. if ( tmp != previousLLTimerTick )
  9. {
  10. // Calculate the elapsed ticks of the free-running timer.
  11. ticks625us = tmp - previousLLTimerTick;
  12. // Store the LL Timer tick count for the next time through this function.
  13. previousLLTimerTick = tmp;
  14. /* It is necessary to loop to convert the usecs to msecs in increments so as
  15. * not to overflow the 16-bit variables.
  16. */
  17. while ( ticks625us > MAXCALCTICKS )  //主要是防止数据过大转换为毫秒时发生溢出,MAXCALCTICKS = 13105,13105是就会溢出从0开始重新计数
  18. {
  19. ticks625us -= MAXCALCTICKS;
  20. elapsedMSec += MAXCALCTICKS * 5 / 8;      //滴答数us转换为ms单位8190ms
  21. remUsTicks += MAXCALCTICKS * 5 % 8;    //余数为5,0.625ms
  22. }
  23. // update converted number with remaining ticks from loop and the
  24. // accumulated remainder from loop
  25. tmp = (ticks625us * 5) + remUsTicks;//将余数加上去
  26. // Convert the 625 us ticks into milliseconds and a remainder (  余数)
  27. elapsedMSec += tmp / 8;
  28. remUsTicks = tmp % 8;//计算出整个时间后,进行取整和余数
  29. // Update OSAL Clock and Timers
  30. if ( elapsedMSec )//前后两次调用该函数时已经过去的时间值,该值只是ms位,有余数未计入
  31. {
  32. osalClockUpdate( elapsedMSec );//更新整个系统的时间,UTCTime以秒为单位记录
  33. osalTimerUpdate( elapsedMSec );//传入过去流逝的时间值,进一步更新每个软件定时器,减去一定的时间
  34. }
  35. }
  36. }

整个系统的时间都由LL(Link Layer)层的一个16位硬件定时器来维护,没深入去看因为是在BLE栈协议中,源码没有公开。这部分代码在获得当前LL Timer的计数值后进行时间计算,主要包括计算elapsed(逝去的)时间,该定时器,1个tick就代表625us,相对于频率1.6MHz。该us时间转换为ms的理想状况是Tms=Num*5/8,NUM为前后两次的tick之差,但是需要考虑的时,NUM如果很大,使得上面程序的temp一下就溢出,因此需要把握在Num到13015之内,超过部分直接获得其消逝的时间值和余数,当然为了精确这里不抛弃余数部分,在下次轮询到时继续加入进去,保证系统时间的精确,也正是remUsTicks = tmp % 8;出现的原因。

下面两个函数,前者是维护整个系统系统,保存到一个全局变量中,所有时间以s为单位记录。
      osalClockUpdate( elapsedMSec );//更新整个系统的时间,UTCTime以秒为单位记录

  1. void osalTimerUpdate( uint32 updateTime )
  2. {
  3. halIntState_t intState;
  4. osalTimerRec_t *srchTimer;
  5. osalTimerRec_t *prevTimer;
  6. osalTime_t timeUnion;
  7. timeUnion.time32 = updateTime;
  8. HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  9. // Update the system time
  10. osal_systemClock += updateTime;
  11. HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
  12. // Look for open timer slot
  13. if ( timerHead != NULL )
  14. {
  15. // Add it to the end of the timer list
  16. srchTimer = timerHead;
  17. prevTimer = (void *)NULL;
  18. // Look for open timer slot
  19. while ( srchTimer )
  20. {
  21. osalTimerRec_t *freeTimer = NULL;
  22. HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  23. // To minimize time in this critical section, avoid 32-bit math。
  24. //这边的计数算法总体为了节省时间。
  25. if ((timeUnion.time16[1] == 0) && (timeUnion.time8[1] == 0))//消逝的时间数据只有1Byte
  26. {
  27. // If upper 24 bits are zero, check lower 8 bits for roll over
  28. if (srchTimer->timeout.time8[0] >= timeUnion.time8[0])//定时器的时间还没有到0
  29. {
  30. // 8-bit math
  31. srchTimer->timeout.time8[0] -= timeUnion.time8[0];//对软定时器进行减计时,只有一个字节且定时器数要大于消逝的时间
  32. }
  33. else
  34. {
  35. // 32-bit math
  36. if (srchTimer->timeout.time32 > timeUnion.time32)
  37. {
  38. srchTimer->timeout.time32 -= timeUnion.time32;
  39. }
  40. else
  41. {
  42. srchTimer->timeout.time32 = 0;
  43. }
  44. }
  45. }
  46. else //消逝的时间至少2个Byte,16位
  47. {
  48. // 32-bit math
  49. if (srchTimer->timeout.time32 > timeUnion.time32)
  50. {
  51. srchTimer->timeout.time32 -= timeUnion.time32;
  52. }
  53. else
  54. {
  55. srchTimer->timeout.time32 = 0;//一旦定时器时间值小于了消耗的时间值,直接定时器回0
  56. }
  57. }
  58. // Check for reloading
  59. if ( (srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0) &&
  60. (srchTimer->reloadTimeout) && (srchTimer->event_flag) )//是否需要定时器重载
  61. {
  62. // Notify the task of a timeout
  63. osal_set_event( srchTimer->task_id, srchTimer->event_flag );//事件延时事件一刀就立马让进行任务处理
  64. // Reload the timer timeout value
  65. srchTimer->timeout.time32 = srchTimer->reloadTimeout;
  66. }
  67. // When timeout or delete (event_flag == 0)
  68. if ( ((srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0)) ||
  69. (srchTimer->event_flag == 0) )//在定时器事件处理标志位0或者定时器计数完成,就进行下面的操作
  70. {
  71. // Take out of list
  72. if ( prevTimer == NULL )
  73. {
  74. timerHead = srchTimer->next;//出现需要清空的定时器为头链表,则需要进行重定位
  75. }
  76. else
  77. {
  78. prevTimer->next = srchTimer->next;//删除链表节点,做前后的链接
  79. }
  80. // Setup to free memory
  81. freeTimer = srchTimer;//记录当前需要清空的定时器
  82. // Next
  83. srchTimer = srchTimer->next;
  84. }
  85. else//无满足的定时器,就继续收索到下一个定时器节点进行操作。
  86. {
  87. // Get next
  88. prevTimer = srchTimer;
  89. srchTimer = srchTimer->next;
  90. }
  91. HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
  92. if ( freeTimer )//找到需要清除的软件定时器
  93. {
  94. if ( (freeTimer->timeout.time16[0] == 0) && (freeTimer->timeout.time16[1] == 0) )//定时这里意味着有事件发生
  95. {
  96. osal_set_event( freeTimer->task_id, freeTimer->event_flag );//触发事件,启动任务处理
  97. }
  98. osal_mem_free( freeTimer );//清除无用定时器(定时结束或者事件发生标志清空)
  99. }
  100. }
  101. }
  102. }
[html] view plain copy
  1. void osalTimerUpdate( uint32 updateTime )
  2. {
  3. halIntState_t intState;
  4. osalTimerRec_t *srchTimer;
  5. osalTimerRec_t *prevTimer;
  6. osalTime_t timeUnion;
  7. timeUnion.time32 = updateTime;
  8. HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  9. // Update the system time
  10. osal_systemClock += updateTime;
  11. HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
  12. // Look for open timer slot
  13. if ( timerHead != NULL )
  14. {
  15. // Add it to the end of the timer list
  16. srchTimer = timerHead;
  17. prevTimer = (void *)NULL;
  18. // Look for open timer slot
  19. while ( srchTimer )
  20. {
  21. osalTimerRec_t *freeTimer = NULL;
  22. HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.
  23. // To minimize time in this critical section, avoid 32-bit math。
  24. //这边的计数算法总体为了节省时间。
  25. if ((timeUnion.time16[1] == 0) && (timeUnion.time8[1] == 0))//消逝的时间数据只有1Byte
  26. {
  27. // If upper 24 bits are zero, check lower 8 bits for roll over
  28. if (srchTimer->timeout.time8[0] >= timeUnion.time8[0])//定时器的时间还没有到0
  29. {
  30. // 8-bit math
  31. srchTimer->timeout.time8[0] -= timeUnion.time8[0];//对软定时器进行减计时,只有一个字节且定时器数要大于消逝的时间
  32. }
  33. else
  34. {
  35. // 32-bit math
  36. if (srchTimer->timeout.time32 > timeUnion.time32)
  37. {
  38. srchTimer->timeout.time32 -= timeUnion.time32;
  39. }
  40. else
  41. {
  42. srchTimer->timeout.time32 = 0;
  43. }
  44. }
  45. }
  46. else //消逝的时间至少2个Byte,16位
  47. {
  48. // 32-bit math
  49. if (srchTimer->timeout.time32 > timeUnion.time32)
  50. {
  51. srchTimer->timeout.time32 -= timeUnion.time32;
  52. }
  53. else
  54. {
  55. srchTimer->timeout.time32 = 0;//一旦定时器时间值小于了消耗的时间值,直接定时器回0
  56. }
  57. }
  58. // Check for reloading
  59. if ( (srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0) &&
  60. (srchTimer->reloadTimeout) && (srchTimer->event_flag) )//是否需要定时器重载
  61. {
  62. // Notify the task of a timeout
  63. osal_set_event( srchTimer->task_id, srchTimer->event_flag );//事件延时事件一刀就立马让进行任务处理
  64. // Reload the timer timeout value
  65. srchTimer->timeout.time32 = srchTimer->reloadTimeout;
  66. }
  67. // When timeout or delete (event_flag == 0)
  68. if ( ((srchTimer->timeout.time16[0] == 0) && (srchTimer->timeout.time16[1] == 0)) ||
  69. (srchTimer->event_flag == 0) )//在定时器事件处理标志位0或者定时器计数完成,就进行下面的操作
  70. {
  71. // Take out of list
  72. if ( prevTimer == NULL )
  73. {
  74. timerHead = srchTimer->next;//出现需要清空的定时器为头链表,则需要进行重定位
  75. }
  76. else
  77. {
  78. prevTimer->next = srchTimer->next;//删除链表节点,做前后的链接
  79. }
  80. // Setup to free memory
  81. freeTimer = srchTimer;//记录当前需要清空的定时器
  82. // Next
  83. srchTimer = srchTimer->next;
  84. }
  85. else//无满足的定时器,就继续收索到下一个定时器节点进行操作。
  86. {
  87. // Get next
  88. prevTimer = srchTimer;
  89. srchTimer = srchTimer->next;
  90. }
  91. HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.
  92. if ( freeTimer )//找到需要清除的软件定时器
  93. {
  94. if ( (freeTimer->timeout.time16[0] == 0) && (freeTimer->timeout.time16[1] == 0) )//定时这里意味着有事件发生
  95. {
  96. osal_set_event( freeTimer->task_id, freeTimer->event_flag );//触发事件,启动任务处理
  97. }
  98. osal_mem_free( freeTimer );//清除无用定时器(定时结束或者事件发生标志清空)
  99. }
  100. }
  101. }
  102. }

该函数是处理整个软件定时器的核心所在,设计的稍微复杂一点:之所有存在这个软件定时器,其实也是有LL Timer来完成的。它的存在是为了给某些场合提供方便,比如设备启动之后,就会设计成这样 ,延时5s都会触发一次事件的发生,以表示系统在正常运行,类似于心跳包。

osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD ),该函数是OSAL内实现启动一个事件的函数类型与osal_set_event(),只是前者需要有一个延时,延时事件到后触发事件,而这个延时就是由上面的软件定时器来完成的。

OSAL维护着一个定时器全局链表timerHead,每当调用osal_start_timerEx实际再调用osalAddTimer,来创建一个新的软件Timer。

  1. osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint32 timeout )
  2. {
  3. osalTimerRec_t *newTimer;
  4. osalTimerRec_t *srchTimer;
  5. // Look for an existing timer first
  6. newTimer = osalFindTimer( task_id, event_flag );//通过对任务ID和具体的事件标志来查询定时器链表中是否已经存在
  7. if ( newTimer )
  8. {
  9. // Timer is found - update it.
  10. newTimer->timeout.time32 = timeout;//如果定时器事件对应的任务处理定时器已经存在直接更新时间
  11. return ( newTimer );
  12. }
  13. else
  14. {
  15. // New Timer
  16. newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );//新分配一个软定时器内存
  17. if ( newTimer )
  18. {
  19. // Fill in new timer
  20. newTimer->task_id = task_id;
  21. newTimer->event_flag = event_flag;
  22. newTimer->timeout.time32 = timeout;
  23. newTimer->next = (void *)NULL;//该定时器在定时器链表尾
  24. newTimer->reloadTimeout = 0;
  25. // Does the timer list already exist
  26. if ( timerHead == NULL )
  27. {
  28. // Start task list
  29. timerHead = newTimer;
  30. }
  31. else  //定时器链表头非空
  32. {
  33. // Add it to the end of the timer list
  34. srchTimer = timerHead;
  35. // Stop at the last record
  36. while ( srchTimer->next )
  37. srchTimer = srchTimer->next;//找到链表尾
  38. // Add to the list
  39. srchTimer->next = newTimer;//将最新的定时器加入到链表尾中,其next为NULL
  40. }
  41. return ( newTimer );
  42. }
  43. else
  44. {
  45. return ( (osalTimerRec_t *)NULL );
  46. }
  47. }
  48. }
[html] view plain copy
  1. osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint32 timeout )
  2. {
  3. osalTimerRec_t *newTimer;
  4. osalTimerRec_t *srchTimer;
  5. // Look for an existing timer first
  6. newTimer = osalFindTimer( task_id, event_flag );//通过对任务ID和具体的事件标志来查询定时器链表中是否已经存在
  7. if ( newTimer )
  8. {
  9. // Timer is found - update it.
  10. newTimer->timeout.time32 = timeout;//如果定时器事件对应的任务处理定时器已经存在直接更新时间
  11. return ( newTimer );
  12. }
  13. else
  14. {
  15. // New Timer
  16. newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) );//新分配一个软定时器内存
  17. if ( newTimer )
  18. {
  19. // Fill in new timer
  20. newTimer->task_id = task_id;
  21. newTimer->event_flag = event_flag;
  22. newTimer->timeout.time32 = timeout;
  23. newTimer->next = (void *)NULL;//该定时器在定时器链表尾
  24. newTimer->reloadTimeout = 0;
  25. // Does the timer list already exist
  26. if ( timerHead == NULL )
  27. {
  28. // Start task list
  29. timerHead = newTimer;
  30. }
  31. else  //定时器链表头非空
  32. {
  33. // Add it to the end of the timer list
  34. srchTimer = timerHead;
  35. // Stop at the last record
  36. while ( srchTimer->next )
  37. srchTimer = srchTimer->next;//找到链表尾
  38. // Add to the list
  39. srchTimer->next = newTimer;//将最新的定时器加入到链表尾中,其next为NULL
  40. }
  41. return ( newTimer );
  42. }
  43. else
  44. {
  45. return ( (osalTimerRec_t *)NULL );
  46. }
  47. }
  48. }

结合上面两个函数,可以很好的理解软件定时器的工作模式,是一个减计算模式,加入了点小算法,对定时完成后的事件直接进行事件触发操作,以达到延时效果。

3.OSAL中的消息处理相关API解析

消息的出现,比如按键事件触发,引起了硬件task任务处理,处理时会调用向应用层注册的一个回调函数Callback(初始化时完成),在回调函数中将按键的ID打包,通过消息发给应用层APP,应用层再进一步做出处理。

  1. uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
  2. {
  3. keyChange_t *msgPtr;
  4. if ( registeredKeysTaskID != NO_TASK_ID )
  5. {
  6. // Send the address to the task
  7. msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) ); //其实分配的内存大小为osal_msg_hdr_t + keyChange_t
  8. if ( msgPtr )
  9. {
  10. msgPtr->hdr.event = KEY_CHANGE;//事件类型按键变化
  11. msgPtr->state = state;
  12. msgPtr->keys = keys;
  13. osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );//将按键事件产生的消息发送到注册啦按键这一事件的任务
  14. }
  15. return ( SUCCESS );
  16. }
  17. else
  18. return ( FAILURE );
[html] view plain copy
  1. uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
  2. {
  3. keyChange_t *msgPtr;
  4. if ( registeredKeysTaskID != NO_TASK_ID )
  5. {
  6. // Send the address to the task
  7. msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) ); //其实分配的内存大小为osal_msg_hdr_t + keyChange_t
  8. if ( msgPtr )
  9. {
  10. msgPtr->hdr.event = KEY_CHANGE;//事件类型按键变化
  11. msgPtr->state = state;
  12. msgPtr->keys = keys;
  13. osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );//将按键事件产生的消息发送到注册啦按键这一事件的任务
  14. }
  15. return ( SUCCESS );
  16. }
  17. else
  18. return ( FAILURE );

在这里进行消息的发送,OSAL维护一个全局的osal_qHead队列,允许你将新的消息插入队列头或者尾,整个消息内容包括固定的链表头:

  1. typedef struct
  2. {
  3. void   *next;
  4. uint16 len;
  5. uint8  dest_id;
  6. } osal_msg_hdr_t;
[html] view plain copy
  1. typedef struct
  2. {
  3. void   *next;
  4. uint16 len;
  5. uint8  dest_id;
  6. } osal_msg_hdr_t;

再加上相关消息需要携带的内容。在收到消息的地方,遍历链表,找到对应的消息,再会对数据进行提取。

  1. uint8 *osal_msg_receive( uint8 task_id )
  2. {
  3. osal_msg_hdr_t *listHdr;
  4. osal_msg_hdr_t *prevHdr = NULL;
  5. osal_msg_hdr_t *foundHdr = NULL;
  6. halIntState_t   intState;
  7. // Hold off interrupts
  8. HAL_ENTER_CRITICAL_SECTION(intState);
  9. // Point to the top of the queue
  10. listHdr = osal_qHead;
  11. // Look through the queue for a message that belongs to the asking task
  12. while ( listHdr != NULL )
  13. {
  14. if ( (listHdr - 1)->dest_id == task_id )//判断消息处理函数是否就是当前接收的任务处理函数
  15. {
  16. if ( foundHdr == NULL )
  17. {
  18. // Save the first one
  19. foundHdr = listHdr;
  20. }
  21. else
  22. {
  23. // Second msg found, stop looking
  24. break;
  25. }
  26. }
  27. if ( foundHdr == NULL )
  28. {
  29. prevHdr = listHdr;
  30. }
  31. listHdr = OSAL_MSG_NEXT( listHdr );
  32. }
  33. // Is there more than one?
  34. if ( listHdr != NULL )
  35. {
  36. // Yes, Signal the task that a message is waiting
  37. osal_set_event( task_id, SYS_EVENT_MSG );
  38. }
  39. else
  40. {
  41. // No more
  42. osal_clear_event( task_id, SYS_EVENT_MSG );//清楚这个任务事件消息标志
  43. }
  44. // Did we find a message?
  45. if ( foundHdr != NULL )
  46. {
  47. // Take out of the link list
  48. osal_msg_extract( &osal_qHead, foundHdr, prevHdr );//解析出消息内容
  49. }
  50. // Release interrupts
  51. HAL_EXIT_CRITICAL_SECTION(intState);
  52. return ( (uint8*) foundHdr );
  53. }
[html] view plain copy
  1. uint8 *osal_msg_receive( uint8 task_id )
  2. {
  3. osal_msg_hdr_t *listHdr;
  4. osal_msg_hdr_t *prevHdr = NULL;
  5. osal_msg_hdr_t *foundHdr = NULL;
  6. halIntState_t   intState;
  7. // Hold off interrupts
  8. HAL_ENTER_CRITICAL_SECTION(intState);
  9. // Point to the top of the queue
  10. listHdr = osal_qHead;
  11. // Look through the queue for a message that belongs to the asking task
  12. while ( listHdr != NULL )
  13. {
  14. if ( (listHdr - 1)->dest_id == task_id )//判断消息处理函数是否就是当前接收的任务处理函数
  15. {
  16. if ( foundHdr == NULL )
  17. {
  18. // Save the first one
  19. foundHdr = listHdr;
  20. }
  21. else
  22. {
  23. // Second msg found, stop looking
  24. break;
  25. }
  26. }
  27. if ( foundHdr == NULL )
  28. {
  29. prevHdr = listHdr;
  30. }
  31. listHdr = OSAL_MSG_NEXT( listHdr );
  32. }
  33. // Is there more than one?
  34. if ( listHdr != NULL )
  35. {
  36. // Yes, Signal the task that a message is waiting
  37. osal_set_event( task_id, SYS_EVENT_MSG );
  38. }
  39. else
  40. {
  41. // No more
  42. osal_clear_event( task_id, SYS_EVENT_MSG );//清楚这个任务事件消息标志
  43. }
  44. // Did we find a message?
  45. if ( foundHdr != NULL )
  46. {
  47. // Take out of the link list
  48. osal_msg_extract( &osal_qHead, foundHdr, prevHdr );//解析出消息内容
  49. }
  50. // Release interrupts
  51. HAL_EXIT_CRITICAL_SECTION(intState);
  52. return ( (uint8*) foundHdr );
  53. }

以上的过程就是整个事件触发消息,消息再打包发生,由目的任务处理函数进行进一步处理,直到完成后进行清空。

以上是部分OSAL中的内容,下次继续分析BLE中的主从机简单的工作模型。

CC2541 BLE源码阅读知识积累之OSAL小结相关推荐

  1. TransE,知识图谱嵌入(KGE)源码阅读(一)

    TransE,知识图谱嵌入(KGE)源码阅读(一) Paper: Antoine Bordes等人在2013年发表于NIPS上的文章 Paper Understanding:TransE,知识图谱嵌入 ...

  2. 大神手把手教源码阅读的方法、误区以及三种境界

    丁威 中间件兴趣圈 读完需要 1 分钟 速读仅需 1 分钟 在技术职场中普遍存在如下几种现象: 对待工作中所使用的技术不需要阅读源码,只需在开发过程中能够熟练运用就行 看源码太费时间,而且容易忘记,如 ...

  3. redis源码阅读-持久化之aof与aof重写详解

    aof相关配置 aof-rewrite-incremental-fsync yes # aof 开关,默认是关闭的,改为yes表示开启 appendonly no # aof的文件名,默认 appen ...

  4. Framework 源码解析知识梳理(5) startService 源码分析

    一.前言 最近在看关于插件化的知识,遇到了如何实现Service插件化的问题,因此,先学习一下Service内部的实现原理,这里面会涉及到应用进程和ActivityManagerService的通信, ...

  5. 【SeaJS】【3】seajs.data相关的源码阅读

    在SeaJS官网上推荐了源码阅读顺序,本文并没有采用这个顺序,而是按个人习惯以调试官方示例的方式进行源码阅读.早期版本作者玉伯使用了几个闭包形式,本文源码版本为2.1.1,它的编码方式个人认为更加脚本 ...

  6. TiDB 源码阅读系列文章(六)Select 语句概览

    在先前的 TiDB 源码阅读系列文章(四) 中,我们介绍了 Insert 语句,想必大家已经了解了 TiDB 是如何写入数据,本篇文章介绍一下 Select 语句是如何执行.相比 Insert,Sel ...

  7. SpringMVC源码阅读:过滤器

    SpringMVC源码阅读:过滤器 目录 1.前言 2.源码分析 3.自定义过滤器 3.1 自定义过滤器继承OncePerRequestFilter 3.2 自定义过滤器实现Filter接口 4.过滤 ...

  8. spark.mllib源码阅读:GradientBoostedTrees

    Gradient-Boosted Trees(GBT或者GBDT) 和 RandomForests 都属于集成学习的范畴,相比于单个模型有限的表达能力,组合多个base model后表达能力更加丰富. ...

  9. bert模型简介、transformers中bert模型源码阅读、分类任务实战和难点总结

    bert模型简介.transformers中bert模型源码阅读.分类任务实战和难点总结:https://blog.csdn.net/HUSTHY/article/details/105882989 ...

最新文章

  1. 三十九、文件的逻辑结构
  2. 网站推广必备手册:SEO教程:搜索引擎优化入门与进阶(第2版)
  3. mysql delete exists用法_自学MySQL第五天
  4. 2013年东北赛B题(数位DP)
  5. 编译py-faster-rcnn的问题汇总及解决方法
  6. CF39C-Moon Craters【dp】
  7. react学习(69)--置空操作
  8. html5外置样式表,HTML5移动端通用css详解
  9. java控制台打印图片_java——控制台输入打印图形
  10. mysql 查询包含1或者2_Mysql:同一个表上有2个不同的查询,包含count和group by
  11. windows2008开机占用多少内存_如何提升电脑开机速度?
  12. 为什么用clojure作为storm 的主要开发语言
  13. 合并报表编制采用的理论_跟我一起学合并报表之——长期股权投资的抵消处理...
  14. Jquery 中 ajaxSubmit使用笔记
  15. 高恪或者Padavan等品牌路由用N1作为旁路由
  16. nar神经网络_基于神经网络的预测模型
  17. 安卓Push Rejected解决
  18. java float 输出文本框_关于Java中float数输出时显示问题
  19. 关于Mybatis的深入学习(4)之动态SQL二十四道练习
  20. 小米 红米4(标准版)线刷兼救砖_解账户锁_纯净刷机包_教程

热门文章

  1. 学大伟业:2019年物理竞赛学习方法
  2. 电脑ps4,ps4怎么连接电脑
  3. 2019第三届“数维杯”大学生数学建模夏令营
  4. 推荐几个中文在线音乐网站
  5. 采用to_excel保存文件不覆盖原有的sheet
  6. C++反射(Reflection)
  7. 深度学习入门笔记(李沐)(一)
  8. su su- sudo
  9. linux最大的账户,Linux系统账户安全
  10. canvas - 基础知识 - 绘制剪纸图形