一、源码结构和开发方式
1.1 源码结构
顶层目录中包含四个文件夹:components、docs、examples、tools
- components:组件,sdk遵循模块化的编程原则,组件提供了ble开发所需的编程模块,如ble协议栈、driver驱动和其他模块(如freertos、ringbufffer、audio、button)
- docs:文档,提供了指导开发者开发的api文档、数据手册,方便在编程中查阅。
- exapmles:示例,sdk提供了常见ble应用的示例,方便开发者迅速开发自己的产品。
- tools:工具,提供了烧录工具、ota工具、keil下载需要的flm下载算法。
1.2 开发方式
sdk分为源码和库,ble协议栈以库的形式提供给开发者,这部分协议栈为开发者提供了回调接口,用于协议栈与用户的通信,当协议栈处理某个流程或接收到数据时,通过回调通知用户进行相应的处理。
1.2.1 用户入口
用户入口在proj_main.c中,不像常规的开发那样有main函数,sdk为用户提供了相应的入口,我理解是被weak修饰的虚函数,协议栈在运行过程中,执行到相应的流程就会调用这些入口函数。下面按照协议栈处理流程给出接口的说明
- 协议栈初始化前:user_entry_before_ble_init,这个函数在协议栈初始化前
- 协议栈初始化后:user_entry_after_ble_init,这个是用户编程的入口,即协议栈初始化后,用户可以执行自己的逻辑。
- 进入睡眠模式前:user_entry_before_sleep_imp
- 退出睡眠模式后:user_entry_after_sleep_imp
1.2.2 协议栈开发
gatt_add_service根据填充好的gatt_service_t结构体初始化一个服务,向协议栈注册服务的属性和属性的操作方法。
simple_profile_svc.p_att_tb = simple_profile_att_table;
simple_profile_svc.att_nb = SP_IDX_NB;
simple_profile_svc.gatt_msg_handler = sp_gatt_msg_handler;
sp_svc_id = gatt_add_service(&simple_profile_svc);
上面的代码创建了一个simple_profile_svc服务,服务的属性在simple_profile_att_table表中定义,当与另一个设备通信时,属性的操作在sp_gatt_msg_handler中处理。
1.2.3 事件和任务
sdk开发中,任务采用事件驱动,不同任务间通过消息os_event_t *进行通信,当任务A产生的事件可以驱动任务B时,任务A通过os_msg_post向任务B发送消息,任务B接收消息,并从消息中获取事件id和数据,根据事件id做相应的处理。
下面的代码中,user_task_func是一个任务,需要被其他任务通过事件驱动,当其他任务调用os_msg_post向user_task_func发送os_event_t时,user_task_func得到执行。os_event_t的param是按键消息,通过按键消息可以获取到某个按键执行了某个操作。
static int user_task_func(os_event_t *param)
{
switch(param->event_id)
{
case USER_EVT_BUTTON:
{
struct button_msg_t *button_msg;
const char *button_type_str[] = {
"BUTTON_PRESSED",
"BUTTON_RELEASED",
"BUTTON_SHORT_PRESSED",
"BUTTON_MULTI_PRESSED",
"BUTTON_LONG_PRESSED",
"BUTTON_LONG_PRESSING",
"BUTTON_LONG_RELEASED",
"BUTTON_LONG_LONG_PRESSED",
"BUTTON_LONG_LONG_RELEASED",
"BUTTON_COMB_PRESSED",
"BUTTON_COMB_RELEASED",
"BUTTON_COMB_SHORT_PRESSED",
"BUTTON_COMB_LONG_PRESSED",
"BUTTON_COMB_LONG_PRESSING",
"BUTTON_COMB_LONG_RELEASED",
"BUTTON_COMB_LONG_LONG_PRESSED",
"BUTTON_COMB_LONG_LONG_RELEASED",
};
button_msg = (struct button_msg_t *)param->param;
co_printf("KEY 0x%08x, TYPE %s.\r\n", button_msg->button_index, button_type_str[button_msg->button_type]);
if(button_msg->button_type == BUTTON_SHORT_PRESSED){//短按
if(button_msg->button_index == GPIO_PD6 ){//Key2
switch (App_Mode)
{
case SPEAKER_FROM_FLASH:
test_speaker_from_flash();//开始播放音频
break;
case PICTURE_UPDATE://刷图片
co_printf("picture_idx = %d",picture_idx);
LCD_DisPIC(picture_idx ++);
if(picture_idx >= 5)
picture_idx = 0;
break;
case CODEC_TEST:
Test_Codec_demo();
break;
}
}else if(button_msg->button_index == GPIO_PC5){//KEY1 工作模式切换
if((App_Mode == SPEAKER_FROM_FLASH)){
test_end_speaker();//停止播放音频
}else if(App_Mode == CODEC_TEST){
Test_codec_demo_stop();
}
App_Mode++;
if(App_Mode >= MODE_MAX){
App_Mode = PICTURE_UPDATE;
picture_idx = 0;
}
lcd_show_logo(lcd_show_workmode[App_Mode]);//刷新,显示当前模式的名称
}
}else if(button_msg->button_type == BUTTON_LONG_PRESSED){//按键长按
if(button_msg->button_index == GPIO_PC5 ){
co_printf("K1 long Pressed\r\n");
//tft_write_pic_data_to_flash();
}
}
}
break;
}
return EVT_CONSUMED;
}
二、参考
https://gitee.com/freqchip
https://www.freqchip.com/