测试环境
协议栈版本:BLE-CC254x-1.4.0
开发环境IAR版本:IAR 8.20
硬件设备:CC2540/CC2541开发板
示例测试Demo工程:simpleBLEPeripheral工程
OSAL应用程序接口API介绍
OSAL提供的API(应用编程接口,Application Programming Interface),这些API允许协议栈以独立于特定操作系统、内核或任务环境的方式编写。下面的内容主要是参照TI协议栈文档中“OSAL API.pdf”(该文档位于协议栈的“Documents\osal”目录下)翻译整理的,并进行了部分的拓展说明。
总体而言,大致可以分为10个方面:
1.消息管理
2.任务同步
3.时间管理
4.中断管理
5.任务管理
6.内存管理
7.电源管理
8.非易失性闪存管理
9.时钟管理
10.其他常用
消息管理API
消息管理有关的API主要用于处理任务间消息的交换,主要包括为任务分配消息缓存、释放消息缓存、接收消息和发送消息等API函数。这类API主要位于“Components\osal\common”目录下的“Osal.c”文件中。
1.osal_msg_allocate()
函数原型:
uint8 * osal_msg_allocate( uint16 len )
功能描述:为消息分配缓存空间。
2.osal_msg_deallocate()
函数原型:
uint8 osal_msg_deallocate( uint8 *msg_ptr )
功能描述:释放消息的缓存空间。
函数原型:
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
功能描述:一个任务发送消息到消息队列。
函数原型:
uint8 *osal_msg_receive( uint8 task_id )
功能描述:一个任务从消息队列接收属于自己的消息。
函数原型:
osal_event_hdr_t *osal_msg_find(uint8 task_id, uint8 event)
功能描述:搜索匹配task_id任务和event事件的已经存在的消息并返回。
任务同步API
任务同步API主要用于任务间的同步,允许一个任务等待某个事件的发生。这类API主要位于“Components\osal\common”目录下的“Osal.c”文件中。
1.osal_set_event()
函数原型:
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
功能描述:运行一个任务设置某一事件。
函数原型:
uint8 osal_clear_event( uint8 task_id, uint16 event_flag )
功能描述:清除一个任务的某一事件。
时间管理API
时间管理API用于开启和关闭定时器,定时时间一般为毫秒级定时,使用该API,大家不需要关注底层如何实现,只需要调用即可。在蓝牙4.0BLE协议栈物理层已经完成了定时器的初始化。这类API主要位于“Components\osal\common”目录下的“OSAL_Timers.c”文件中。
函数原型:
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint32 timeout_value )
功能描述:设置一个定时时间,定时时间到后,相应任务的相应事件被设置。
函数原型:
uint8 osal_stop_timerEx( uint8 task_id, uint16 event_id )
功能描述:停止已经启动的定时器。
函数原型:
uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint32 timeout_value )
功能描述:设置一个定时时间,定时时间到后,相应任务的相应事件被设置,这一点跟“osal_start_timerEx()”功能一样,但是本接口还多了一个功能:就是定时时间到后相应事件被执行,并重新加载定时器,也就是又重新设置了定时器,继续进行定时工作,除非调用“osal_stop_timerEx()”接口,否则一直循环定时操作。
4.osal_GetSystemClock()
函数原型:
uint32 osal_GetSystemClock( void )
功能描述:读取系统时钟,单位ms。
函数原型:
void osalTimerUpdate( uint32 updateTime )
功能描述:更新软件定时器。
中断管理API
中断管理API主要用于控制中断的开启与关闭,这类API主要位于“Components\osal\common”目录下的“Osal.c”文件中。
1.osal_int_enable()
函数原型:
uint8 osal_int_enable( uint8 interrupt_id )
功能描述:当传入的interrupt_id等于0xFF时,打开所有中断,否则返回状态INVALID_INTERRUPT_ID。
函数原型:
uint8 osal_int_disable( uint8 interrupt_id )
功能描述:当传入的interrupt_id等于0xFF时,关闭所有中断。否则返回状态INVALID_INTERRUPT_ID。
下面我们顺便把硬件抽象层封装的中断相关函数也罗列一下,该部分在“Components\hal\target\CC2540EB”目录下的“hal_mcu.h”文件中:
1.HAL_ENABLE_INTERRUPTS()
函数原型:
#define HAL_ENABLE_INTERRUPTS() st( EA = 1; )
功能描述:实现所有中断的打开。
函数原型:
#define HAL_DISABLE_INTERRUPTS() st( EA = 0; )
功能描述:实现所有中断的关闭。
函数原型:
#define HAL_ENTER_CRITICAL_SECTION(x) st( x = EA; HAL_DISABLE_INTERRUPTS(); )
功能描述:将中断寄存器EA的值保存到x中,然后关闭所有中断。
函数原型:
#define HAL_EXIT_CRITICAL_SECTION(x) st( EA = x; )
功能描述:与HAL_ENTER_CRITICAL_SECTION(x)对应,将它关闭的中断打开,注意并不是打开所有中断。
函数原型:
#define HAL_CRITICAL_STATEMENT(x) st( halIntState_t _s; HAL_ENTER_CRITICAL_SECTION(_s); x; HAL_EXIT_CRITICAL_SECTION(_s); )
功能描述:声明用于保存EA值的变量_s,然后将EA值赋值给_s保存,关闭所有中断,执行临界代码x,然后将保存的_s值赋回给EA,打开原来的中断。
函数原型:
#define HAL_ENTER_ISR() { halIntState_t _isrIntState = EA; HAL_ENABLE_INTERRUPTS();
功能描述:从上面的宏定义中,我们看到了HAL_ENTER_ISR()的实现中带了半个“{”,所以很明显它无法单独使用,肯定是与另一部分配合使用。
函数原型:
#define HAL_EXIT_ISR() EA = _isrIntState; }
功能描述:它定义了与HAL_ENTER_ISR()对应的另一半,并且它的定义中携带了另一半“}”。
对于HAL_ENTER_ISR()和HAL_EXIT_ISR()的用法,我们看个简单的例子:
{
HAL_ENTER_ISR()
x = 1;
HAL_EXIT_ISR()
}
上述代码展开如下:
{
{
halIntState_t _isrIntState = EA;
HAL_ENABLE_INTERRUPTS();
x = 1;
EA = _isrIntState;
}
}
这样展开之后就很明显了,定义变量_isrIntState保存EA的值,然后关闭所有中断,执行临界代码块“ x=1;”然后将保存的_isrIntState值赋给EA,打开之前关闭的中断。
注意:有的人会觉得HAL_ENTER_ISR()、HAL_EXIT_ISR()和HAL_ENTER_CRITICAL_SECTION(x)、HAL_EXIT_CRITICAL_SECTION(x)的功能类似,没什么区别。这种想法是错误的,其实大家仔细看是能看出来的,前面两个方法定义的时候分别定义了“{ }”的两半,也就是说前面两个方法必须在同一个函数的同一级上,不能一个在方法A里面,另一个在方法A调用的方法B里面,这样会出错的;但是后面两个方法将 EA 的值取出来了,定义的时候也不涉及大括号的问题,所以后面两个方法不一定非得在函数的同一级里面,只要在调用HAL_EXIT_CRITICAL_SECTION(x)的时候,将调用HAL_ENTER_CRITICAL_SECTION(x)时保存的 EA 的值传过去即可。大家一定注意,如果看着感觉定义很乱,就把它展开,那样就很明显了。
任务管理API
任务管理API主要功能是对OSAL进行初始化和启动,这类API主要位于“Components\osal\common”目录下的“Osal.c”文件中。
函数原型:
uint8 osal_init_system( void )
功能描述:初始化OSAL,该函数是第一个被调用的OSAL函数。
函数原型:
void osal_start_system( void )
功能描述:该函数包含一个无限循环,反复调用执行osal_run_system()函数。
函数原型:
void osal_run_system( void )
功能描述:查询所有的任务事件,如果有事件发生,则调用相应的事件处理函数,处理完该事件后将没有处理的事件返回,到主循环中继续检测是否有事件发生,如果开启了休眠模式,则没有事件发生时,该函数将使处理器进入休眠模式,以降低系统功耗。
函数原型:
uint8 osal_self( void )
功能描述:返回当前正在执行事件处理的任务的ID,如果没有,则返回TASK_NO_TASK。
内存管理API
内存管理API用于在堆上分配缓冲区,这里需要注意以下两个API函数一定要成对使用,防止产生内存泄露。这类API主要位于“Components\osal\common”目录下的“OSAL_Memory.c”文件里。
函数原型:
void *osal_mem_alloc( uint16 size )
功能描述:在堆上分配指定大小的缓冲区。
函数原型:
void osal_mem_free( void *ptr )
功能描述:释放使用osal_mem_alloc()分配的缓冲区。
电源管理API
电源管理API主要是用于电池供电的蓝牙4.0BLE设备节点。这类API主要位于“Components\osal\common”目录下的“OSAL_PwrMgr.c”文件中。这部分接口的调用主要跟系统是否进入休眠模式进而更加省电相关。
1.osal_pwrmgr_init()
函数原型:
void osal_pwrmgr_init( void )
功能描述:电源管理初始化接口,初始化时默认的“pwrmgr_device”是“PWRMGR_ALWAYS_ON”,也就是持续供电,不会进入休眠状态,如果想进入休眠状态,就要在应用部分调用下面的“osal_pwrmgr_device()”方法进行相应设置。
2.osal_pwrmgr_device()
函数原型:
void osal_pwrmgr_device( uint8 pwrmgr_device )
功能描述:设置电源管理的特性方式,主要有两种方式:“PWRMGR_ALWAYS_ON”和“PWRMGR_BATTERY”。如果是“PWRMGR_ALWAYS_ON”方式,电源持续供电,不会进入休眠模式;如果是“PWRMGR_BATTERY”方式,系统将在没有事件要处理的时候进入休眠模式,降低功耗。
3.osal_pwrmgr_task_state()
函数原型:
uint8 osal_pwrmgr_task_state( uint8 task_id, uint8 state )
功能描述:用来标识task_id任务是否允许休眠。state可以取两个值:“PWRMGR_CONSERVE”和“PWRMGR_HOLD”:
PWRMGR_CONSERVE:允许进入休眠;
PWRMGR_HOLD:不允许进入休眠。
4.osal_pwrmgr_powerconserve()
函数原型:
void osal_pwrmgr_powerconserve( void )
功能描述:这个方法在“osal_run_system()”方法中被调用,前提是定义“POWER_SAVING”宏,并且系统没有事件要处理。功能是用来查询系统状态,如果“pwrmgr_device”被设置了“PWRMGR_BATTERY”,并且“pwrmgr_task_state”等于0(也就是系统的每个任务都允许休眠,这里要注意,只要有一个任务不允许,那系统就无法进入休眠状态)时,系统将进入休眠状态。
当出现IO中断或者复位时会退出休眠,或者在休眠定时器中断时候也将会退出休眠。如果是IO中断或者休眠定时器中断退出之后将回到进入休眠的地方继续向下执行,复位退出的话进入程序的初始部分执行。
非易失性闪存管理API
非易失性闪存(Non-Volatile Memory,简称NV)管理API主要是添加了对非易失性闪存的管理函数,一般这里的非易失性闪存指的是系统的Flash存储器(也可以是E2PROM),每个NV条目分配唯一的ID号。这类API主要位于“Components\osal\mcu\cc2540”目录下的“osal_snv.c”文件中。
1.osal_snv_init()
函数原型:
uint8 osal_snv_init( void )
功能描述:初始化NV条目。
2.osal_snv_read()
函数原型:
uint8 osal_snv_read( osalSnvId_t id, osalSnvLen_t len, void *pBuf )
功能描述:从NV条目中读取某个ID的 len 长度的数据,并存到pBuf指针地址上。
3.osal_snv_write()
函数原型:
uint8 osal_snv_write( osalSnvId_t id, osalSnvLen_t len, void *pBuf )
功能描述:将pBuf指针地址上的len长度的数据写到NV条目的某个ID中。
关于非易失性闪存管理,后面会详细深入的讲解,此处先了解常用的API。
时钟管理API
该部分API主要处理系统时钟,对于系统时钟采用UTC格式,该部分的API主要位于“Components\osal\common”目录下的“OSAL_ClockBLE.c”文件。
关于UTC类型的定义在“Components\osal\include”目录下的“OSAL_Clock.h”文件中,如下:
// number of seconds since 0 hrs, 0 minutes, 0 seconds, on the
// 1st of January 2000 UTC
typedef uint32 UTCTime;
// To be used with
typedef struct
{
uint8 seconds; // 0-59
uint8 minutes; // 0-59
uint8 hour; // 0-23
uint8 day; // 0-30
uint8 month; // 0-11
uint16 year; // 2000+
} UTCTimeStruct;</span>
1.osalTimeUpdate()
函数原型:
void osalTimeUpdate( void )
功能描述:更新系统时钟。
2.osal_setClock()
函数原型:
void osal_setClock( UTCTime newTime )
功能描述:设置新的系统时钟,注意传入的是UTCTime类型的值,不是UTCTimeStruct类型,单位s。
3.osal_getClock()
函数原型:
UTCTime osal_getClock( void )
功能描述:获取当前的系统时钟,注意返回的是UTCTime类型的值,不是UTCTimeStruct类型,单位s。
4.osal_ConvertUTCTime()
函数原型:
void osal_ConvertUTCTime( UTCTimeStruct *tm, UTCTime secTime )
功能描述:将UTCTime类型的时钟转换成UTCTimeStruct类型的。
5.osal_ConvertUTCSecs()
函数原型:
UTCTime osal_ConvertUTCSecs( UTCTimeStruct *tm )
功能描述:将UTCTimeStruct 类型的时钟转换成UTCTime类型的。
其他常用API
主要位于“Components\osal\common”目录下的“Osal.c”文件中。
1.osal_rand()
函数原型:
uint16 osal_rand( void )
功能描述:生成一个uint16类型的随机数,uint16类型定义:
typedef unsigned short uint16;
2.osal_memcmp()
函数原型:
uint8 osal_memcmp( const void GENERIC *src1, const void GENERIC *src2, unsigned int len )
功能描述:比较内存中两个指定地址上的特定长度的数据,如果相同返回TRUE;如果不相等,返回FALSE。
3.osal_memset()
函数原型:
void *osal_memset( void *dest, uint8 value, int len )
功能描述:把内存中指定地址上的特定长度上的数据设置为value值。
4.osal_memcpy()
函数原型:
void *osal_memcpy( void *dst, const void GENERIC *src, unsigned int len )
功能描述:将源地址src上指定长度len的数据复制到目的地址dst上。这里有一点要注意的,该函数的返回值跟标准库中memcpy的返回值不一样,该函数的返回值是目的地址上复制完最后一个字节的地址,而标准memcpy返回的是目的地址起始位置的地址。
5.osal_revmemcpy()
函数原型:
void *osal_revmemcpy( void *dst, const void GENERIC *src, unsigned int len )
功能描述:该方法与上述的“osal_memcpy()”方法类似,唯一的区别就是将源地址src上指定长度len的数据以什么样的顺序复制到目的地址dst上。本函数是以倒序的形式,而“osal_memcpy()”函数是以顺序的形式复制。
6.osal_memdup()
函数原型:
void *osal_memdup( const void GENERIC *src, unsigned int len )
功能描述:在内存中动态申请len长度的区域dst,将源地址src上len长度的数据复制到新申请的区域上,并返回新申请的区域dst的地址。如果申请动态内存失败,则返回NULL。