BLE协议栈学习2——OSAL
OSAL简介
BLE 协议栈包含了 BLE 协议所规定的基本功能,这些功能是以函数的形式实现的,为了便于管理
这些函数集,BLE 协议栈内加入了实时操作系统(并非真正意义上的操作系统),称为 OSAL(操作系统抽象层,OperatingSystemAbstractionLayer)。
OSAL 与标准的操作系统还是有一定区别的,OSAL 实现了类似操作系统的某些功能,例如,任务切换、提供了内存管理功能等,但 OSAL 并不能称为真正意义上的操作系统。
OSAL消息队列
在进行消息队列的解释之前,需要区分一下消息和事件的区别。
事件:是驱动任务去执行某些操作的条件,当系统中产生了一个事件,OSAL 将这个事件传递给相应的任务后,任务才能执行一个相应的操作,例如调用具体的某个功能函数。通常某些事件发生时,又伴随着一些附加信息的产生,例如:主机 GATT 接收到数据后,会产生GATT_MSG_EVENT 消息,但是任务的事件处理函数在处理这个事件的时候,还需要得到所收到的数据。
因此消息的作用就是将事件和完成这个任务所需要的数据进行一个打包封装,将消息发送到消息队列,然后在事件处理函数中就可以使用 osal_msg_receive,从消息队列中得到该消息。如下代码可以从消息队列中得到一个消息。pMsg=osal_msg_receive(simpleBLETaskId))
OSAL 维护了一个消息队列,每一个消息都会被放到这个消息队列中去,当任务接收到事件后,可以从消息队列中获取属于自己的消息,然后调用消息处理函数进行相应的处理即可。
OSAL应用编程接口
OSAL 提供了丰富的编程接口 API 函数,我们只需要了解 API 如何调用就可以使用OSAL进行开发。
OSAL一共提供了8个方面的API:- 消息管理
Osal_msg_allocate()为消息分配缓存空间,分配之后,可以填充消息,然后经过 osal_msg_sned()将消息发送出去,然后任务函数中通过 osal_msg_reveive()函数接收属于自己的消息,并处理,最后调用osal_msg_deallocate()函数销毁由 Osal_msg_allocate()分配的内存空间。 - 任务同步
当我们需要立刻启动一个事件时,调用 osal_set_event(),调用后立即产生一个事件,等到 osal 下次循环时,就会判断到这个事件,然后调用相应的任务函数处理该事件。
如果想过一段时间在启动一个事件,或者说,我需要延时 5 秒然后查询一下 gpio 的状态,这时就需要 osal_start_timerEx 来创建一个定时器事件,当设定的时间到达后,就会产生某个事件。加入刚才已经创建了一个定时器事件,但是中途改变主意了,不需要去轮询 gpio 状态,就可以调用osal_stop_timerEx()来停止已经启动的定时器。 - 时间管理
- 中断管理
- 任务管理
- 内存管理
单片机的内存资源非常有限,CC2540的内部只有8K的RAM,因此OSAL提供了一种“静态内存池”的技术。
简单地讲系统运行的时候开辟了一个非常大的数组,然后提供一些接口给用户,当用户需要使用内存的时候,调用分配 api 取得一些内存,等到用户使用完之后,不在需要这个内存的时候,在调用销户内存的 api,释放先前占用的内存,这样最大程度复用了有限的内存空间。
osal_mem_alloc()用来分配内存,用完之后,通过调用 osal_mem_free 释放内存。 - 电源管理
- 非易失闪存管理
我们在实际项目开发中,肯定会有一些数据需要保证掉电后不丢失,对于常规操作而言就是外挂一个EEPROM设备连接到单片机进行参数存储。但是对于CC2540而言,OSAL提供了内部 flash 的存储管理接口。通过调用这些接口,就可以掉电保存参数,我们只需要先对这些参数数据创建一个唯一的 item 标识,用来区分其他的已经保存的数据的 item标识,然后调用 osal_nv_write()函数写入参数,等到需要的时候可以调用 osal_nv_read()来读取保存的值即可。
- 消息管理
OSAL基础实验
在学习BLE协议栈的过程中,OSAL可以称得上能够贯穿始终,后续的BLE操作都是在OSAL的框架下进行的,因此学会使用OSAL至关重要,接下来就是OSAL的几个基础实验,在这些实验中并不包含BLE的部分。
1. OLED显示实验
和第一部分学习CC2540作为单片机使用一样,第一件事就是先拿OLED显示个什么,毕竟后续的用户交互显示屏才是第一选择。
实现步骤:
1.任务初始化函数里调用 SimpleOsal_Init 函数向 lcd 输出信息
void SimpleOsal_Init( uint8 task_id )
{SimpleOsal_TaskID = task_id;// Register for all key events - This app will handle all key eventsRegisterForKeys( SimpleOsal_TaskID );//向lcd输出相关信息//第一行,显示内容:"SimpleOsal"HalLcdWriteString( "SimpleOsal", HAL_LCD_LINE_1 );//第二行,显示内容:"XL OSAL Test"HalLcdWriteString( "XL OSAL Test", HAL_LCD_LINE_2 );// Setup a delayed profile startup//为了方便观察实验现象,延时2s执行后面的操作osal_start_timerEx( SimpleOsal_TaskID, SBP_START_DEVICE_EVT, 2000 );
}
2.调用 osal_start_timerEx 函数延时启动事件,对 count 计数,同时对count进行显示。
if ( events & SBP_START_DEVICE_EVT ){//向LCD第1行显示Count的10进制计数HalLcdWriteStringValue("Count(10):", count, 10, HAL_LCD_LINE_1);//向LCD第2行显示Count的16进制计数HalLcdWriteStringValue("Count(16):", count, 16, HAL_LCD_LINE_2);count++;//为了方便观察现象,我们1秒之后再次启动该任务osal_start_timerEx( SimpleOsal_TaskID, SBP_START_DEVICE_EVT, 1000 );return ( events ^ SBP_START_DEVICE_EVT );}// Discard unknown eventsreturn 0;
实现效果:
编译下载完成后,OLED显示屏会先显示"SimpleOsal"和"XL OSAL Test"后,跳转到计数页面,页面上显示的是count的十进制和十六进制的形式。
2. ADC五向按键实验
在实际项目中,按键和显示屏的重要性如出一辙,在单片机上按键是主要的交互输入设备,本实验所要实现的就是通过可以进行五个方向按动的按键配合ADC实现按动方向绑定,在显示屏上显示当前按键拨动的方向。
实现步骤:
1.任务初始化函数里调用 SimpleOsal_Init 函数向 lcd 输出信息
void SimpleOsal_Init( uint8 task_id )
{SimpleOsal_TaskID = task_id;// Register for all key events - This app will handle all key eventsRegisterForKeys( SimpleOsal_TaskID );//向lcd输出相关信息//第一行,显示内容:"SimpleOsal"HalLcdWriteString( "SimpleOsal", HAL_LCD_LINE_1 );//第二行,显示内容:"XL OSAL Test"HalLcdWriteString( "XL OSAL Test", HAL_LCD_LINE_2 );// Setup a delayed profile startup//为了方便观察实验现象,延时2s执行后面的操作osal_start_timerEx( SimpleOsal_TaskID, SBP_START_DEVICE_EVT, 2000 );
}
2.调用 osal_start_timerEx 函数延时启动事件
由于按键属于系统消息,因此会在标准的 SYS_EVENT_MSG 中触发,然后通过osal_msg_receive 来接收系统消息,如果消息有效,则进入消息处理函数中去进一步判断是何种系统消息。
uint16 SimpleOsal_ProcessEvent( uint8 task_id, uint16 events )
{VOID task_id; // OSAL required parameter that isn't used in this functionif ( events & SYS_EVENT_MSG ){uint8 *pMsg;if ( (pMsg = osal_msg_receive( SimpleOsal_TaskID )) != NULL ){//处理按键信消息SimpleOsal_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );// Release the OSAL messageVOID osal_msg_deallocate( pMsg );}// return unprocessed eventsreturn (events ^ SYS_EVENT_MSG);}if ( events & SBP_START_DEVICE_EVT ){//HalLcdWriteString( "Key test", HAL_LCD_LINE_3 );return ( events ^ SBP_START_DEVICE_EVT );}// Discard unknown eventsreturn 0;
}static void SimpleOsal_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{switch ( pMsg->event ){case KEY_CHANGE:simpleOsal_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );break;default:// do nothingbreak;}
}
3.根据传入的参数 keys 来判断是哪个按键触发
static void simpleOsal_HandleKeys( uint8 shift, uint8 keys )
{if ( keys & HAL_KEY_SW_1 ){HalLcdWriteString("Joys: RIGHT",HAL_LCD_LINE_2);}if ( keys & HAL_KEY_SW_2 ){HalLcdWriteString("Joys: UP",HAL_LCD_LINE_2);}if ( keys & HAL_KEY_SW_3 ){HalLcdWriteString("Joys: DOWN",HAL_LCD_LINE_2);}if ( keys & HAL_KEY_SW_4 ){HalLcdWriteString("Joys: LEFT",HAL_LCD_LINE_2);}if ( keys & HAL_KEY_SW_5 ){HalLcdWriteString("Joys: CENTER",HAL_LCD_LINE_2);}
}
实验效果:
显示不同方向键按下之后的信息
3. UART串口实验
串口在单片机开发过程中的地位也是举足轻重,毕竟不会所有的消息都通过显示屏展示,比如调试信息、数据监测等,所以此时就需要UART串口来进行开发调试的辅助了。
本实验要实现的是通过五向按键更改数值,同时将数值实时通过串口发送到电脑的串口调试助手上显示。
实现步骤:
1.初始化UART配置,并在SimpleOsal_Init中调用
void SerialApp_Init( uint8 taskID )
{//调用uart初始化代码serialAppInitTransport();//记录任务函数的taskID,备用sendMsgTo_TaskID = taskID;
}/*uart初始化代码,配置串口的波特率、流控制等*/
void serialAppInitTransport( )
{halUARTCfg_t uartConfig;// configure UARTuartConfig.configured = TRUE;uartConfig.baudRate = SBP_UART_BR;//波特率uartConfig.flowControl = SBP_UART_FC;//流控制uartConfig.flowControlThreshold = SBP_UART_FC_THRESHOLD;//流控制阈值,当开启flowControl时,该设置有效uartConfig.rx.maxBufSize = SBP_UART_RX_BUF_SIZE;//uart接收缓冲区大小uartConfig.tx.maxBufSize = SBP_UART_TX_BUF_SIZE;//uart发送缓冲区大小uartConfig.idleTimeout = SBP_UART_IDLE_TIMEOUT;uartConfig.intEnable = SBP_UART_INT_ENABLE;//是否开启中断uartConfig.callBackFunc = sbpSerialAppCallback;//uart接收回调函数,在该函数中读取可用uart数据// start UART// Note: Assumes no issue opening UART port.(void)HalUARTOpen( SBP_UART_PORT, &uartConfig );return;
}
2.ADC 采样到的数据通过 UART 输出到 PC
if ( events & SBP_START_DEVICE_EVT ){AdcValue = HalAdcRead(HAL_ADC_CHANNEL_4, HAL_ADC_RESOLUTION_8);HalLcdWriteStringValue("P0_4 ADC:", AdcValue, 10,HAL_LCD_LINE_1);//将采样的adc值通过UART发送出去SerialPrintValue("P0_4 ADC Value:",AdcValue,10);//换行SerialPrintString("\r\n");//500ms之后再次启动,这样实验500ms采样一次osal_start_timerEx( SimpleOsal_TaskID, SBP_START_DEVICE_EVT, 500 );return ( events ^ SBP_START_DEVICE_EVT );}// Discard unknown eventsreturn 0;
static void simpleOsal_HandleKeys( uint8 shift, uint8 keys )
{if ( keys & HAL_KEY_SW_1 ){HalLcdWriteString("Joystick: RIGHT",HAL_LCD_LINE_2);//向UART发送当前按键SerialPrintString("Joystick: RIGHT\r\n");}if ( keys & HAL_KEY_SW_2 ){HalLcdWriteString("Joystick: UP",HAL_LCD_LINE_2);SerialPrintString("Joystick: UP\r\n");}if ( keys & HAL_KEY_SW_3 ){HalLcdWriteString("Joystick: DOWN",HAL_LCD_LINE_2);SerialPrintString("Joystick: DOWN\r\n");}if ( keys & HAL_KEY_SW_4 ){HalLcdWriteString("Joystick: LEFT",HAL_LCD_LINE_2);SerialPrintString("Joystick: LEFT\r\n");}if ( keys & HAL_KEY_SW_5 ){HalLcdWriteString("Joystick: CENTER",HAL_LCD_LINE_2);SerialPrintString("Joystick: CENTER\r\n");}HalLedBlink(HAL_LED_2, 1,20,500 );
}
3.接收到数据后,输出到显示屏
/*uart接收回调函数*/
void sbpSerialAppCallback(uint8 port, uint8 event)
{uint8 pktBuffer[SBP_UART_RX_BUF_SIZE];// unused input parameter; PC-Lint error 715.(void)event;int i=0;for(i=6000;i>0;i--){asm("nop");}//HalLcdWriteString("Data form my UART:", HAL_LCD_LINE_4 );//返回可读的字节if ( (numBytes = Hal_UART_RxBufLen(port)) > 0 ){//读取全部有效的数据,这里可以一个一个读取,以解析特定的命令(void)HalUARTRead (port, pktBuffer, numBytes);//接收到数据后,输出到LCD显示。HalLcdWriteString(pktBuffer,HAL_LCD_LINE_2);}
}
以上代码实验了 UART 功能,但是还需要设置一下 IAR 工程,需要添加 HAL_UART=TRUE 宏定义来使能UART。
实验效果如下:
LCD 会显示一些内容,并且会打印 ADC 通道 4 的值以及按键的状态信息,还会打印串口发送来的数据。
4. SNV 内部 Flash 存储实验
我们开发板使用的 CC2540 内部有 256K 的 flash 空间,他不光可以存储程序,还可以存储用户数据,TI 针对内部 flash 提供了上层轻便的调用接口 SNV,来存储用户数据。
实现步骤:
1.任务初始化函数里调用 SimpleOsal_Init 函数向 lcd 输出信息
void SimpleOsal_Init( uint8 task_id )
{SimpleOsal_TaskID = task_id;// Register for all key events - This app will handle all key eventsRegisterForKeys( SimpleOsal_TaskID );//向lcd输出相关信息//第一行,显示内容:"SimpleOsal"HalLcdWriteString( "SimpleOsal", HAL_LCD_LINE_1 );//第二行,显示内容:"XL OSAL Test"HalLcdWriteString( "XL OSAL Test", HAL_LCD_LINE_2 );// Setup a delayed profile startup//为了方便观察实验现象,延时2s执行后面的操作osal_start_timerEx( SimpleOsal_TaskID, SBP_START_DEVICE_EVT, 2000 );
}
2.通过五向按键的 UP 和 Down 来完成count 计数
static void simpleOsal_HandleKeys( uint8 shift, uint8 keys )
{static uint8 count=0;if ( keys & HAL_KEY_SW_2 )//UP{count++;HalLcdWriteStringValue("NV Write:",count,10,HAL_LCD_LINE_1);osal_snv_write(0xFF,1,&count);}if ( keys & HAL_KEY_SW_3 )//DOWN{osal_snv_read(0xFF,1,&count);HalLcdWriteStringValue("NV Read:",count,10,HAL_LCD_LINE_2);}
}
实验效果:
先多次按 UP,然后再按一次 down,读取已经存入的数据,然后断电,上电后,直接按 down,读取 0xff 处的数据,这样就实现了数据的掉电存储。
BLE协议栈学习2——OSAL相关推荐
- 蓝牙BLE(协议栈、OSAL、蓝牙APP工具)
目录 蓝牙配对和绑定 蓝牙4.0 BLE 信道(RF Channel) BLE协议栈分层 PHY层(Physical layer 物理层) LL层(Link Layer 链路层) HCI层(Host ...
- 物联网|蓝牙4.0BLE协议栈简介|IAR集成开发环境简介|IAR各版本下载链接|物联网之蓝牙4.0 BLE基础-学习笔记(2)
文章目录 3.蓝牙4.0BLE协议栈简介 Tips: BLE协议结构图介绍 Tips IAR各版本下载链接 3.蓝牙4.0BLE协议栈简介 问题: 1.什么是LE协议栈?BLE协识栈与BLE协议的关系 ...
- Android蓝牙协议栈学习
Android蓝牙协议栈当前的名字叫做fluoride(氟化物),同时Google正在开发一个新的蓝牙协议栈叫做Gabeldorsh,其中用到了Rust语言. 学习Android蓝牙协议栈涉及到的内容 ...
- CC2541的BLE协议栈构成
协议定义的是一系列的通信标准, 通信双方需要共同按照这一标准进行正常的数据收发: 协议栈是协议的具体实现形式, 就是用代码实现的函数库, 以便于开发人员调用. BLE 协议栈将各个层定义的协议 ...
- android ble不配对接收广播数据_蓝牙低功耗(BLE)学习笔记_0
BLE的体系结构主要由三部分组成,分别是控制器(controller),主机(Host)和应用程序(Application),如下图所示: BLE体系结构 Application layer顾名思义主 ...
- java 协议栈_深入浅出讲解低功耗蓝牙(BLE)协议栈
详解BLE连接建立过程 https://www.cnblogs.com/iini/p/8972635.html 详解BLE 空中包格式-兼BLE Link layer协议解析 https://www. ...
- [转载]Bluetooth协议栈学习之SDP
原文地址:Bluetooth协议栈学习之SDP作者:BigSam78 作者: Sam (甄峰) sam_code@hotmail.com SDP(service discovery protocol: ...
- 蓝牙:深入浅出低功耗蓝牙(BLE)协议栈
深入浅出低功耗蓝牙(BLE)协议栈 BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 协议栈框架 一般而言,我们把某个协议的实 ...
- CanOpen协议栈学习笔记1-帧格式,SYNC和NMT报文介绍
前面已经记录过can协议,后面开始CanOpen协议栈学习.其实协议栈代码已经看过了,而且已经在开发板上跑过了.这里回过头来,重新看下之前遇到的坑,记录下学习笔记.下面均以标准帧为例 文章目录 1.C ...
最新文章
- 达摩院副院长金榕:中国 AI 将向何处?热潮有回落,但不应沮丧
- MYSQL中的主表和父表_主表,从表,关联表,父表,子表
- centos配置ftp
- 前端学习(3306):函数组件usermemo和usercallback二
- 条形图坐标轴_解密咨询报告中常见的双层条形图的制作方法
- For循环(十分重要)
- Linux笔记-inode基本概念
- 用html编写勾股定理,一种勾股定理演示器的制作方法
- 马云:不能把孩子放在温室里,光给孩子知识是不够的
- Oracle的order by的中文排序问题
- Docker开启和关闭容器自启动
- eclipse工具性能优化方法
- 前端开发——图片标注工具
- 支撑起SNS的六度分隔理论和150法则
- java 虚拟机 xms_JVM虚拟机选项:Xms Xmx PermSize MaxPermSize区别(转)
- FPGA入门实验-基于状态机实现串口回环收发
- 移动IM开源框架对比
- Spark DataFrame的创建
- 有关加速度计,陀螺仪,姿态融合解算的原理
- 总结使用libwebsockets开发接入层