ble开发第一步,了解osal系统
了解OSAL
Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,它并不是一个传统意义上的操作系统,但是实现了部分类似操作系统的功能。(只要我跑的够快我就是多线程的)本系统由TI开发并提供了大量参考文档,大家可以前往其官方进行查阅 TI蓝牙论坛 虽然目前TI新产品已经不再使用OSAL系统但一些性能较低的蓝牙芯片依然采用相关设计,可以参考CC25XX系列芯片的相关教程。
OSAL系统其实也就相当于一个while(1)循环,里面包括各种判断当任务事件执行标志为1时即执行该任务事件。
OSAL包括任务与事件两种定义,一开始接触可能会有点混乱,接下来我简要阐述何为任务以及何为事件。
OSAL系统使用多个任务构成,每个任务中又包括各种事件。其关系如下图1.1(手绘渣图)
图1.1
我们之前说过OSAL系统相当于一个whlie(1)循环,不断检查是否有事件发生,如果有就执行相关事件,并将其执行标志消除。运行流程如下图1.2所示
图1.2
接下来根据代码具体分析,其中各个任务具体内容无需了解只看流程即可,接下来以按键为例。
int app_main(void) { /* Initialize the operating system */ osal_init_system();//初始化各个模块 osal_pwrmgr_device( PWRMGR_BATTERY ); /* Start OSAL */ osal_start_system(); //开启OSAL系统 return 0; }
OSAL系统启动前首先进行对各个模块初始化,依次执行以下各个初始化函数。各初始化函数只会在设备启动后执行一次不会像是任务一样重复执行。其中taskID即为给各个任务编号并重命名,方便下一步调用。
void osalInitTasks( void ) { uint8 taskID = 0; tasksEvents = (uint16*)osal_mem_alloc( sizeof( uint16 ) * tasksCnt); osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); /* LL Task */ LL_Init( taskID++ ); /* HCI Task */ HCI_Init( taskID++ ); #if defined ( OSAL_CBTIMER_NUM_TASKS ) /* Callback Timer Tasks */ osal_CbTimerInit( taskID ); taskID += OSAL_CBTIMER_NUM_TASKS; #endif /* L2CAP Task */ L2CAP_Init( taskID++ ); /* SM Task */ SM_Init( taskID++ ); /* GAP Task */ GAP_Init( taskID++ ); /* GATT Task */ GATT_Init( taskID++ ); /* Profiles */ GAPRole_Init( taskID++ ); #if(DEF_GAPBOND_MGR_ENABLE==1) GAPBondMgr_Init( taskID++ ); // 2017-11-15 #endif GATTServApp_Init( taskID++ ); /* Application */ SimpleBLEPeripheral_Init( taskID++ ); userKey_Init( taskID++ ); }
在其中除了最后两个模块都是蓝牙相关初始化,我们暂且忽略。看 userKey_Init()函数,他给引脚注册了中断,当引脚值发生变化时边执行中断函数 user_key_handler
void userKey_Init(uint8 task_id) { userKeyTaskID = task_id; hal_gpio_pin_init(P0, GPIO_OUTPUT); hal_gpio_pull_set(P0, PULL_DOWN); hal_gpio_pin_init(P24, GPIO_OUTPUT);//LED hal_gpio_pull_set(P0, PULL_DOWN); hal_gpio_pin_init(PIN_TOUCH_KEY, IE); hal_gpio_pull_set(PIN_TOUCH_KEY, STRONG_PULL_UP); hal_gpioin_enable(PIN_TOUCH_KEY);//使能中断 hal_gpioin_register(PIN_TOUCH_KEY, NULL,user_key_handler);//注册中断处理 #ifdef PIN_TOUCH_KEY1 hal_gpio_pin_init(PIN_TOUCH_KEY1, IE); hal_gpio_pull_set(PIN_TOUCH_KEY1, STRONG_PULL_UP); hal_gpioin_enable(PIN_TOUCH_KEY1); hal_gpioin_register(PIN_TOUCH_KEY1, NULL,user_key_handler); #endif }
中断处理函数如下,其中中断主要目的为开启 USER_KEY_SCAN_EVT 事件,而 USER_KEY_SCAN_EVT 事件在userKeyTaskID也就是userKeyProcessEvent任务之中。
执行事件的函数主要有两个
osal_set_event( uint8 task_id, uint16 event_flag)//用于向一个 Task 发送事件,之后 Task 的事件处理函数会响应该事件。 osal_start_timerEx(uint8 task_id, uint16 event_id, uint32 timeout_value)//开始一个应用 Timer,到达超时时间系统会向指定的 task 发送一个事件,该 timer 完成一次事件之后自动关闭,不再重发。
void __ATTR_SECTION_SRAM__ __attribute__((used)) user_key_handler(gpio_pin_e pin,gpio_polarity_e type) { if(pin == PIN_TOUCH_KEY) { hal_gpioin_register(PIN_TOUCH_KEY, NULL, NULL);//清除中断 key_time_timeout = KEY_TIMEOUT_MAX; //KEY_SCAN_INTERVAL时间过后开启任务号为userKeyTaskID任务的USER_KEY_SCAN_EVT事件 osal_start_timerEx(userKeyTaskID, USER_KEY_SCAN_EVT, KEY_SCAN_INTERVAL); } }
设置完 USER_KEY_SCAN_EVT 事件后,下次循环变回检测到本事件并执行。接下来我们看OSAL系统的while(1)循环。OSAL会不断检测本数组里的各个任务是否有相关事件需要执行,如果有便去执行该事件。我们通过上一步将 userKeyProcessEvent 任务的USER_KEY_SCAN_EVT 事件设置为执行,本次循环变回进入该任务的相关事件,并执行里面的函数。
const pTaskEventHandlerFn tasksArr[] = { LL_ProcessEvent, // task 0 HCI_ProcessEvent, // task 1 #if defined ( OSAL_CBTIMER_NUM_TASKS ) OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3 #endif L2CAP_ProcessEvent, // task 2 SM_ProcessEvent, // task 3 GAP_ProcessEvent, // task 4 GATT_ProcessEvent, // task 5 GAPRole_ProcessEvent, // task 6 #if (DEF_GAPBOND_MGR_ENABLE==1) GAPBondMgr_ProcessEvent, // task , add 2017-11-15 #endif GATTServApp_ProcessEvent, // task 7 SimpleBLEPeripheral_ProcessEvent, // task 8 userKeyProcessEvent };
让我们看看userKeyProcessEvent 任务里有哪些事件,可以看到其中包括 SYS_EVENT_MSG 系统消息处理 与 USER_KEY_SCAN_EVT 按键扫描事件,而本次便是执行按键扫描事件,按键扫描事件中包括 key_scan()函数便执行该函数。该函数为具体如何实现按键扫描在这里就不具体展开。该函数执行完毕后便将USER_KEY_SCAN_EVT 事件清除,然后继续进入循环。
uint16 userKeyProcessEvent( uint8 task_id, uint16 events ) { if(task_id != userKeyTaskID) { return 0; } if ( events & SYS_EVENT_MSG ) { uint8* pMsg; if ( (pMsg = osal_msg_receive( userKeyTaskID )) != NULL ) { VOID osal_msg_deallocate( pMsg ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } if(events & USER_KEY_SCAN_EVT) { key_scan(); return (events ^USER_KEY_SCAN_EVT); } return 0; }
这便是OSAL处理事件的具体代码,当然其中OSAL系统还包括任务信息处理、睡眠处理等等功能,我们也留到之后在讲解。最后再简陋绘制本次执行按键扫描任务的具体流程。如下图1.3所示,看不懂没事,过两天我自己也看不懂了。
图1.3
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)