跳转至

BT/BLE双模SDK


概述

BT/BLE双模SDK是以蓝牙5.0为基础的一套通用双模蓝牙方案,支持三路BT Classic链接(两路ACL和一路SCO)和一路BLE(从端)链接。SDK集中支持蓝牙音频和蓝牙语音功能,提供便捷的相关开发方式。SDK具备良好的可阅读性,可扩展性和可移植性。SDK以模块化为导向,提供丰富的接口,模块之间层次和逻辑清晰,简单易懂,为用户学习和开发提供了良好的基础。SDK整体设计目标,是让用户在更短的时间之内,通过对SDK进行配置或者二次开发,就可以形成高品质的产品。

功能概述

目前双模SDK支持以下功能:

  • 支持两路BT ACL连接,一主一从(一路耳机,一路手机)
  • 支持一路BT SCO连接
  • 支持一路BLE ACL连接(Center or Peripheral)
  • 支持BT-A2DP(Sink,Source)
  • 支持BT-HFP(HF,AG)
  • 支持BT-PBAP(Client)
  • 支持BT-AVRCP(CT,TG)
  • 支持BT-SPP
  • 支持BT-ATT
  • 支持BT-IAP2
  • 支持BT-Browsing
  • 支持文件系统(Fat32)
  • 支持操作系统(FreeRTOS)
  • 支持界面管理(LVGL)
  • 支持固件升级(Serial,BT-ATT,BT-SPP,BLE-ATT)
  • 支持电池检测和低压保护
  • 支持系统监测(堆栈检测,看门狗)
  • 支持音频管理(Sink,SRC,SCO,Tone,Play)
  • 支持蓝牙管理(Connect,Inquiry,Reconnect)
  • 支持USB设备(UAC,HID,CDC,MSC)
  • 支持MP3解码
  • 支持MSBC编码和解码
  • 支持CVSD编码和解码
  • 支持语音降噪(NS)
  • 支持回音消除(AEC)
  • 支持动态开关日志

工程结构

代码结构

  • _TLKAPP_GENERAL_:编译输出文件,烧录的bin文件就位于该目录下的output子目录中。
  • app:主要放置用户的应用程序。
  • core:主要放置芯片相关的文件,SDK的启动文件也是放置于该目录。
  • tlkalg:主要放置算法接口相关的文件,比如audio,crypt等。
  • tlkapi:主要放置系统为用户提供的通用接口相关的文件,比如memory,flash,fifo等。
  • tlkdev:主要放置系统或用户的设备访问接口相关的文件,比如codec,mic,spk,usb等。
  • tlkdrv:主要放置系统或用户的底层驱动相关的文件,比如audio,时钟,cpu等。
  • tlklib:主要放置系统运行时,所用到的库文件,包括第三方库文件,比如Fatfs等。
  • tlkmdi:主要放置单一功能模块相关的文件,比如play,a2dp source,a2dpsink等。
  • tlkmmi:主要放置集成功能模块相关的文件,比如BT管理,Audio管理,电话管理等。
  • tlkprt:主要放置内部和外部通信协议相关的文件,比如系统对外的串口访问协议等。
  • tlkstk:主要放置蓝牙协议栈相关的文件。

快速上手

硬件环境

硬件环境

详情请参考 Wearables - Telink wiki

软件环境

环境介绍

Telink IDE for TLSR9 Chips

工程导入

点击File选项,点击import,选择Existing Project into Workspace

导入工程文件

选择Browse已经下载好的工程,点击Finish,一个工程导入成功。

浏览工程文件

编译步骤

编译此工程

编译输出文件在src\_TLKAPP_GENERAL_\output目录下。

软件工具

烧录软件

下面是Telink的烧录器,File的选项可以选择要烧录的bin文件,选择完文件之后,使用Download选项开始下载bin文件。烧录完成之后,按Reset按键,重启设备。

下载调试⼯具:Burning and Debugging Tools for all Series

下载bin文件

调试软件

调试软件

USB工具提供了调试、下载和日志追踪的功能。使用USB前,需要开启Telink提供的UDB设备类,并将设备ID与当前工具配置的ID保持一致。

打开Telink提供的USB工具,使能SDK中的USB功能,连接上USB设备。调试有三种方式:

  • 查看变量,通过“MEM”,查看系统变量的值
  • 发送指令,在“CMD”或者下方的编辑栏输入 11 开头的指令,然后按回车键即可;
  • 波形回放,打开UDB中VCD功能,通过点击VCD,此时会记录时序反转电平,用户停止后,即可点击“View”查看运行时序。

该工具提供了两种下载方式:

  • USB下载,需要开通UDB,点击“In File ...”选择带下载的文件,点击“USB_DL”,下载文件,此时工具会自动写入BT的设备名称和设备地址。
  • EVK下载,需要勾选上"EVK",然后连接Telink提供的烧录工具(EVK),点击“Burn_eagle”即可下载固件。

日志追踪,需要勾选“Log”,即可在日志面板上查看系统运行日志;点击“Clear”,会清除显示面板上所有的log。

测试软件

测试软件

为了方便用户开发,Telink提供一套基于Telink私有协议的串口通信软件(DualModeSDKTestTool.exe),该软件随着SDK发布。用户通过该工具,结合Telink提供的SDK和开发版,就可完美体验Telink为用户打造的产品形态。

Telink为双模SDK提供的上位机工具,适用于双模SDK中app/tlkapp_general工程,工具分为五个部分,即设备管理、快捷测试、发送管理、接收管理和日志管理,下面结合上图,简单介绍以下工具的功能特色和使用事项:

设备管理,提供了设备的扫描、打开和关闭选项。用户在第一次打开工具时,系统会默认扫描串口设备,并选择第一个扫描到的设备。如果有新的设备接入,用户可通过扫描设备,然后选择相应的设备打开即可。需要注意的是,目前SDK的通信方式是串口通信,串口波特率为115200。

快捷测试,提供了基于通信协议的封装后的消息发送按钮,用户可以直接通过点击按钮来控制SDK的运行。这里面又有七个子项:

  • Test,测试用特殊消息,该类消息不会参正式的SDK应用。它提供了电话簿压测、USB设备类之间的切换、提示音的测试等快捷操作;
  • Sys,提供了系统消息的访问接口,包括版本号获取,设备重启、心跳控制等;
  • BT,提供了经典蓝牙的管理接口,包括用户关心的蓝牙查询、连接、断开、获取配对列表等;
  • LE,用来提供低功耗蓝牙的管理接口,暂时未定义;
  • Audio,提供了音频的访问和控制接口,包括设置音量、获取音频状态等;
  • File,提供了文件的访问和控制接口,包括文件传输、列表管理等;
  • Phone,电话相关的接口,包括拨打电话、关断电话等。

发送管理,提供发送相关的管理和控制接口,用户可以在此处修改发送指令和参数。

接收管理,对接收包进行显示,方便调试。

日志管理,对日志进行管理,

  • 显示发送,对发送数据包进行显示和解析;
  • 显示CMD,对接收到的CMD数据包进行解析并显示;
  • 显示RSP,对接收到的RSP数据包进行解析并显示;
  • 显示EVT,对接收到的EVT数据包进行解析并显示;
  • 显示DAT,对接收到的DAT数据包进行解析并显示;
  • 暂停/开始,控制日志是否显示,暂停后,新产生的日志就会被丢失;
  • 清空,将现有日志全部清除。

调试方法

死机问题

(1) 问题类别

开发过程中,常见死机问题有:

  • 高优先级中断访问flash中的函数:为了保证重要逻辑的高效执行和避免操作flash对重要时序的影响,系统设计时,会将一些函数放置在RAM中。目前flash操作安全优先为1和0,重要时序的优先级为2或者3,因此如果在高优先级的中断里面调用内置flash的访问函数,就会造成flash时序的错误,从而造成芯片跑飞;
  • 内存越界:常见问题,我们的编译器会对部分数组越界行为,报出警告,因此用户需要尽量避免警告出现;
  • 非法访问:常见问题,不再细述;

(2) 调试方法

变量跟踪调试:如下如所示,可以定义系统变量,逐层定位追踪。使用该方法时,应该优先从高优先级的中断函数入手,因为高优先级中断,随时可能打断低优先级中断。

变量跟踪调试

系统异常指示:打开“IRQ_DEBUG_ENABLE”开关,当出现异常的时候,读取except_handler_b里面的值,在这里面我们首先判断引起失败的原因mcause(cause字段),然后查看当前中断号irq_src,再通过mepc(pc字段)定位到问题出现的具体地方。

系统异常种类

异常代码及描述

(3) 安全策略

为了保证系统在异常情况下,自动检测复位,用户需要打开“TLK_CFG_WDG_ENABLE”,来使能看门狗功能。

堆栈溢出

用户可以打开“TLKAPI_CHIP_STACK_CHECK_ENABLE”开关,同时开启log,这时系统就会每隔一段时间,检测当前的堆栈深度。

  • tlkapi_chip_stackCheck: 栈检测函数,需要在调用Init之后,循环调用该接口;
  • tlkapi_chip_stackDepth: 获取栈深度;
  • tlkapi_chip_stackOverflow: 判断栈是否越界。

测试跟踪

用户可以选择使用USB调试方式和GPIO模拟串口的调试方式,使用系统调试之前,用户需要打开"TLK_CFG_DBG_ENABLE"开关:

  • USB调试方式,用户需要首先打开USB功能和选择UDB设备类,配合Telink的USB上位机软件,勾选“log"。
  • GPIO模拟串口,用户需要关闭USB,使用第三方串口工具和PC软件,将串口工具的RX接上开发板的GPIO_PD5,波特率调整到1000000.

为了更好的方便用户使用,系统将输出调试信息分为了5个类型,警告、信息、追踪、错误、异常,每种类型都有对应的宏开关,用户可以单独打开和关闭一种或多种类型的日志。

配置选项

该配置项文件中包含了sdk几乎所有的重要配置,用户可通过对某些配置项的使能进行版本控制、设备调试、功能裁剪和功能选择。

** 版本控制类 **

sdk应用版本号(5.x.x.x),4byte

#define TLK_APP_VERSION

sdk库版本号(5.x.x.x),4byte

#define TLK_LIB_VERSION

sdk驱动版本号(2.0.0.0),4byte

#define TLK_DRV_VERSION

sdk协议版本号(1.0.0),2byte

#define TLK_PRT_VERSION

** 主体功能类 **

操作系统(RTOS)使能配置项

#define TLK_CFG_OS_ENABLE

电源(功耗)管理使能配置项

#define TLK_CFG_PM_ENABLE

看门狗使能配置项

#define TLK_CFG_WDG_ENABLE

设备(tlkdev)使能配置项

#define TLK_CFG_DEV_ENABLE

算法(tlkalg)使能配置项

#define TLK_CFG_ALG_ENABLE

协议栈(tlkstk)使能配置项

#define TLK_CFG_STK_ENABLE

模块设计接口(tlkmdi)使能配置项

#define TLK_CFG_MDI_ENABLE

人机交互接口(tlkmmi)使能配置项

#define TLK_CFG_MMI_ENABLE

调试接口使能配置项

#define TLK_CFG_DBG_ENABLE

PTS模式使能配置项

#define TLK_CFG_PTS_ENABLE

** USB类 **

Telink USB视图数据传输使能配置项

#define TLK_USB_VCD_ENABLE

标准USB大容量存储使能配置项

#define TLK_USB_MSC_ENABLE

标准USB声卡使能配置项

#define TLK_USB_UAC_ENABLE

Telink USB调试信息传输使能配置项

#define TLK_USB_UDB_ENABLE

标准USB虚拟串口使能配置项

#define TLK_USB_CDC_ENABLE

标准USB HID输入/输出使能配置项

#define TLK_USB_HID_ENABLE

用户USB使能配置项

#define TLK_USB_USR_ENABLE

** 算法类 **

EQ算法使能配置项

#define TLK_ALG_EQ_ENABLE

AGC算法使能配置项

#define TLK_ALG_AGC_ENABLE

AAC算法使能配置项(暂不支持)

#define TLK_ALG_AAC_ENABLE

AEC-NS算法使能配置项

#define TLK_ALG_AEC_ENABLE

** 协议栈类 **

BT使能配置项

#define TLK_STK_BT_ENABLE

BLE使能配置项

#define TLK_STK_LE_ENABLE

BT host使能配置项

#define TLK_STK_BTH_ENABLE

BT profile使能配置项

#define TLK_STK_BTP_ENABLE

BT host ACL支持数量配置项

#define TLK_STK_BTACl_NUMB

BT host SCO支持数量配置项

#define TLK_STK_BTSCO_NUMB

BT host PSM服务支持数量配置项

#define TLK_STK_BTPSM_NUMB

BT host Channel支持说两配置项

#define TLK_STK_BTCHN_NUMB

** MDI类 **

BT-ACL模块使能配置项

#define TLK_MDI_BTACL_ENABLE

BT-Inquiry模块使能配置项

#define TLK_MDI_BTINQ_ENABLE

BT回连模块使能配置项(包含Page/Page Scan/Inquiry Scan)

#define TLK_MDI_BTREC_ENABLE

BT-ATT模块使能配置项

#define TLK_MDI_BTATT_ENABLE

BT-HID模块使能配置项

#define TLK_MDI_BTHID_ENABLE

BT-HFP模块使能配置项

#define TLK_MDI_BTHFP_ENABLE

Audio模块使能配置项

#define TLK_MDI_AUDIO_ENABLE

文件模块使能配置项

#define TLK_MDI_FILE_ENABLE

MP3模块使能配置项

#define TLK_MDI_MP3_ENABLE

按键检测模块使能配置项

#define TLK_MDI_KEY_ENABLE

LED等管理模块使能配置项

#define TLK_MDI_LED_ENABLE

USB模块使能配置项

#define TLK_MDI_USB_ENABLE

文件系统模块使能配置项

#define TLK_MDI_FS_ENABLE

** flash类 **

芯片自带Flash容量大小配置项

#define TLK_CFG_FLASH_CAP

播放列表位置配置项

#define TLK_CFG_FLASH_PLAY_LIST_ADDR
#define TLK_CFG_FLASH_PLAY_LIST_LENS

播放实时记录信息的位置的置项

#define TLK_CFG_FLASH_PLAY_INFO_ADDR

DFU升级的位置的配置项

#define TLK_CFG_FLASH_OTA_PARAM_ADDR

电话簿信息的位置的配置项

#define TLK_CFG_FLASH_PBAP_LIST_ADDR
#define TLK_CFG_FLASH_PBAP_LIST_LENS

工厂设置信息配的位置的配置项

#define TLK_CFG_FLASH_FACTORY_ADDR

音量变化信息记录位置的配置项

#define TLK_CFG_FLASH_VOLUME_ADDR

BLE广播名称的存储位置的配置项

#define TLK_CFG_FLASH_LE_NAME_ADDR
#define TLK_CFG_FLASH_LE_NAME_LENS

BT广播名称的存储位置的配置项

#define TLK_CFG_FLASH_BT_NAME_ADDR
#define TLK_CFG_FLASH_BT_NAME_LENS

BLE设备地址的存储位置的配置项

#define TLK_CFG_FLASH_LE_ADDR_ADDR

BT设备地址的存储位置的配置项

#define TLK_CFG_FLASH_BT_ADDR_ADDR

EQ运行参数的存储位置的配置项

#define TLK_CFG_FLASH_EQ_TEST_ADDR

BT配对信息的存储位置的配置项

#define TLK_CFG_FLASH_BT_PAIR_ADDR0
#define TLK_CFG_FLASH_BT_PAIR_ADDR1
#define TLK_CFG_FLASH_BT_PAIR_NUMB

** 文件系统类 **

文件系统使能配置项

#define TLK_CFG_FS_ENABLE

FAT分区格式使能配置项

#define TLK_FS_FAT_ENABLE

** 设备类 **

使能配置项

#define TLK_DEV_MIC_BUFF_SIZE

使能配置项

#define TLK_DEV_SPK_BUFF_SIZE

使能配置项

#define TLK_DEV_SERIAL_ENABLE

测试体验

测试准备

配套SDK、板子和工具

测试场景

  • BT查询体验

通过点击Inquiry,开始发现周围的蓝牙设备。之后也可以通过获取名称,查询发现过的蓝牙设备的名称。

BT查询

  • BT连接体验

发现过的设备,选择一个设备,点击链接BT设备,进行连接。

BT连接

  • 音乐播放体验

音乐播放是播放本地音乐,不需要链接蓝牙设备。播放之前需要确认本地设备是否存放了音乐,如果存放了音乐,直接操作上述的音乐各个按键,就会控制音乐的播放,暂停,上一首或者下一首等。

播放音乐

  • 耳机音乐体验

耳机音乐,需要先将蓝牙连接完成,将上位机工具切换到Audio。点击播放歌曲,就会进行音乐播放;点击音量的操作,就会出现音量的查询,调整等;点击暂停播放,就会出现音乐播放停止;点击播放下一首,就会切换音乐到下一首播放;点击播放上一首,就会切换音乐到上一首播放;诸如上述各种功能,在此不一一介绍。

耳机音乐

  • 拨打电话体验

耳机在拨打电话之前,需要先进行蓝牙连接。点击打开Phone,输入需要拨打的电话号码。点击拨打电话,会启动电话拨打功能。目前支持末号回拨,接听电话,拒接电话,挂断电话等功能。

拨打电话

架构设计

设计概述

SDK的主旨是打造一套方便用户使用的,稳定、舒适、便捷的解决方案,将用户关心的功能和逻辑进行结构化封装,最大可能地减少用户二次开发的工作量。为此,SDK的设计始终围绕着结构的层次化和功能的模块化两个角度进行架构。

层次化,即代码在逻辑和功能上进行分层设计,层次叠加,实施严格的层次内部的耦合管理和层次之间的调度管控,避免层次内部耦合调用和层次之间的嵌套使用逻辑,从而最大程度上保证代码的可阅读性、可移植性和稳定性。

模块化,即对具有相似逻辑的接口进行封装管理,进而形成诸多模块,层次内部的每个模块之间确保不耦合,从而极大地提高了代码的可扩展性。同时,为了方便用户理解和使用,系统在更高层次上,实现以底层模块为单元的,对各个具有相似功能的模块进行集成,进而实现具有特定功能属性的完整的功能体(功能体在该层中,也是属于该层的一个模块)。

系统框架

软件组织

SDK整体设计层次明确,层级之间不耦合或少耦合。SDK中各个层级按照系统逻辑与设计特色,可以分为三大类,即适配层(Adapter)、平台层(Platform)和用户层(User)。

适配层(Adapter)是Telink为广大用户提供的,方便用户进行二次开发,而提供的便捷稳定的通用型接口或简易模块,可以极大提高用户的开发效率。适配层内部又分为DRV、LIB、DEV、ALG和API五个部分:DRV(Drivers)主要提供芯片的基础驱动,以及复杂设备的底层运行接口;DEV(Devices)主要提供设备的直接访问和控制的接口;LIB(Libaray)主要是系统对有些用户不用关心的算法实体或者模块化程序进行的打包,形成的仅暴露用户可能使用到的接口;ALG(Algorithm)是对算法层进行分块包装,提供用户可视化的接口;API(Application Interface)是系统提供的,简单高效的通用型接口。

平台层(Platform),主要包含协议栈(Stack),是系统的核心,提供蓝牙建立通信和进行数据交互的标准化接口。蓝牙协议栈的设计完全是按照蓝牙联盟(SIG)提供的SPEC中描述的逻辑进行设计,分为controller、host和profile三个层次。Controller是面向硬件设计的,与对端设备进行实时交互的基础控制平台,其上层为通过HCI交互的Host层逻辑;Host层是控制Controller,按照用户行为进行数据交互,同时接收Controller上报事件的接口层;Profile是面向用户特殊化功能而设计的,供各个平台间进行有序、稳定的通信的标准化模块。

应用层(App)是Telink提供的,面向实体产品的应用例程。用户层主要是将某一功能集成一个模块,然后将多个模块组合形成完整的功能体,最后将多个功能体有序组合,进而性能完善的应用例程。应用层按照层级关系,又可分为MDI(Module Interface)、MMI(Man-Machine Interface)和APP(Application):MDI,即模块设计接口,对单一功能进行集成和管理,提供一套更加简洁的交互接口,如BT的连接、查询、回连等;MMI,即人机交互接口,是将属于某一类型的功能或功能模块进行封装和集中管理,而形成的,简单的应用DEMO,并且为APP层提供交互接口;APP,即应用程序,是完整版的应用Demo,具有一定的产品功能和形态,用户可以直接在此基础上扩展或添加符合需求的逻辑,进而形成自己的产品。

架构设计

  • APP: Application(应用程序)
  • MMI: Man-Machine Interface(人机交互接口)
  • MDI: Module Design Interface(模块设计接口)
  • ALG: Algorithm(算法模块)
  • API: Application Interface(应用调用接口)
  • LIB: Library(库调用)
  • DEV: Devices(设备层,指驱动的外部设备)
  • DRV: Drivers(驱动层,指驱动的驱动程序)

层级关系

下图为系统中,各个层级之间的调用关系,配合系统框架结构图,以便用户能更好理解系统运行机制,方便进行二次开发。

层级关系

另外,需要重点说明是层级之间调用的两种关系:一种是向下的逻辑交互关系;另一种是向上的逻辑通信关系。向下的逻辑交互,主要是上一层通过下发命令或直接调用下一层提供的接口,来实现对下一层的直接控制;向上的逻辑通信,主要是下一层通过上一层注册的回调接口,或者以事件的方式,来将自身获取的数据或者状态改变,上报到上一层,从而达到上下层数据和状态的同步。

逻辑关系

通信协议

为了方便用户体验和使用本套系统,系统内含一套私有通信协议,配合专有PC工具,用户可直接使用SDK提供的应用DEMO,即可体验系统的完整功能。

系统内部的通信协议是Telink私有的,具有较高稳定性和较强扩展性的协议。系统采用串口通信方式,所有控制和处理逻辑均在应用层中的APP和MMI层。如想详细了解协议,请参考相关的协议文档。

API设计说明

API概述

API是为系统或者用户提供的一套通用型应用和算法的接口,用户可通过调用API中提供的接口,访问系统资源和快速实现自身功能。如下图所示,API层为用户提供了日志管理,内存管理,缓存访问、文件系统和调度器等相关接口。

API概览

下面针对功能、接口和使用规则等,对API中各个模块的接口进行详细描述:

  • 功能描述,主要是介绍该模块的一些最基本的功能以及逻辑流程。
  • 规则约束,在使用基本功能的过程中存在的限制性条件。
  • 接口说明,模块内部的各个组成部分的详细描述,包括四部分,模块名称,模块基本元素(如:数据结构、全局变量、宏定义、接口函数等),基本元素定义和基本元素详细释义。

Adapt接口设计

功能描述

Adapt是为整个系统运行提供的,面向单线程的,定时调度和工作队列相关的控制接口。目前系统内部所有任务调度,均是采用定时运行与队列执行配合的方式,最大程度保证系统执行效率,达到最大程度地减少系统空转时间的目的。

Adapt是SDK非常核心的接口,它支撑着整个SDK的正常运行。各个模块通过使用定时调度和工作队列,来执行相应的功能或需求。

下图是Timer的执行流程,在应用程序启动之后,每一层会启动自己的timer管理list,该list中会插入每一个执行的action对应的timer。程序在循环执行的过程中会去遍历查询各个timer list, 判断timer list中的各个timer是否Arrive,Arrive到的timer对应的action会得到执行,执行完成退出,否则直接退出,查询下一个timer list中的timer。

Timer的执行流程

下图是work task的list,每一层有各自的task list,它用来处理对时间要求不太严格的事件。当task list被遍历的时候,会将task list中的所有事件全部处理完毕。可以和timer一起使用,也可以单独使用。

Timer work task

规则约束

使用adapt接口,需要注意以下事项:

  • 定时和队列相关的接口,使用前都需要进行初始化处理;
  • 定时和队列调度过程中,有insert或append就必须要有remove,否则会引发系统异常;
  • 定时的最大时间间隔为134s,超过该间隔,则定时无效;
  • 定时和队列回调中,用户如果想重复使用,只需要返回true即可,否则timer会自动停止;
  • 定时采用的是软件timer,会受中断、阻塞或低功耗等因素影响,从而引发定时精度的偏差。

接口说明

Adapt接口设计如下

宏定义:

最大的Timeout时间:

#define TLKAPI_TIMEOUT_MAX      (0x07FFFFFF)

最小的Timeout时间:

#define TLKAPI_TIMEOUT_MIN      100

数据结构:

定义Timer控制的数据结构:

struct tlkapi_timer_s{
        uint32 arrival;
        uint32 timeout;
        void  *pUsrArg;
        TlkApiTimerCB timerCB;
        struct tlkapi_timer_s *pNext;
};
  • arrival: Timer的计数时间
  • timeout: Timer的Timeout时间
  • *pUsrArg: Timer的CB函数的使用参数
  • timerCB: Timer的回调函数
  • *pNext: Timer的数据结构指针

定义Proc控制的数据结构:

struct tlkapi_procs_s{
        void *pUsrArg;
        TlkApiProcsCB procsCB;
        struct tlkapi_procs_s *pNext;
};
  • *pUsrArg: 任务发生之后流程使用参数
  • *pNext: Proc的数据结构指针

定义Adapt的数据结构:

typedef struct{
        tlkapi_procs_t *pProcsList;
        tlkapi_timer_t *pTimerList;
}tlkapi_adapt_t
  • pProcsList:需要处理的任务list
  • pTimerList:需要处理的Timer时间list

类型定义:

定义Timer list的数据结构:

typedef struct tlkapi_timer_s  tlkapi_timer_t

定义Procs list的数据结构:

typedef struct tlkapi_procs_s  tlkapi_procs_t

定义Procs的回调处理函数:

typedef bool(*TlkApiProcsCB)(tlkapi_procs_t *pProcs, void *pUsrArg)

定义Timer的回调处理函数:

typedef bool(*TlkApiTimerCB)(tlkapi_timer_t *pTimer, void *pUsrArg)

接口定义:

初始化procs和Timer的list:

int  tlkapi_adapt_init(tlkapi_adapt_t *pAdapt)

Procs list和Timer list的数据处理函数,通过这个函数,各个插入Proc list和Timer list中的各个数据得到回调函数处理:

void tlkapi_adapt_handler(tlkapi_adapt_t *pAdapt)

Timer数据的填充,通过这个接口,可以将需要处理的TimerCB和参数和TimeOut配置好:

int  tlkapi_adapt_initTimer(tlkapi_timer_t *pTimer, TlkApiTimerCB timerCB, void *pUsrArg, uint32 timeout)

Proc数据的填充,通过这个接口,可以将需要处理的ProcCB和参数配置好:

int  tlkapi_adapt_initProcs(tlkapi_procs_t *pProcs, TlkApiProcsCB procsCB, void *pUsrArg)

将目标Timer从TimerList去掉:

void tlkapi_adapt_deinitTimer(tlkapi_adapt_t *pAdapt, tlkapi_timer_t *pTimer)

将目标Proc从ProcList去掉:

void tlkapi_adapt_deinitProcs(tlkapi_adapt_t *pAdapt, tlkapi_procs_t *pProcs)

判断Timer是否在Timer list:

bool tlkapi_adapt_isHaveTimer(tlkapi_adapt_t *pAdapt, tlkapi_timer_t *pTimer)

判断Proc是否在Proc List:

bool tlkapi_adapt_isHaveProcs(tlkapi_adapt_t *pAdapt, tlkapi_procs_t *pProcs)

将Procs添加到Procs List:

int  tlkapi_adapt_appendProcs(tlkapi_adapt_t *pAdapt, tlkapi_procs_t *pProcs)

将Procs从Proc list中删除:

int  tlkapi_adapt_removeProcs(tlkapi_adapt_t *pAdapt, tlkapi_procs_t *pProcs)

更新timer list中timer的timeout值:

int  tlkapi_adapt_updateTimer(tlkapi_adapt_t *pAdapt, tlkapi_timer_t *pTimer, uint32 timeout)

将新的Timer插入到Timer list中:

int  tlkapi_adapt_insertTimer(tlkapi_adapt_t *pAdapt, tlkapi_timer_t *pTimer)

将Timer从Timer list中删除:

int  tlkapi_adapt_removeTimer(tlkapi_adapt_t *pAdapt, tlkapi_timer_t *pTimer)

查询当前的时间和Timeout之间的时间差:

uint32 tlkapi_adapt_timerInterval(tlkapi_adapt_t *pAdapt)

获取Timer list中的第一个timer:

tlkapi_timer_t *tlkapi_adapt_takeFirstTimer(tlkapi_adapt_t *pAdapt)

打印Timer:

void tlkapi_adapt_printTimer(tlkapi_adapt_t *pAdapt)

检查Timer是否在Timer list中:

void tlkapi_adapt_checkTimer(tlkapi_adapt_t *pAdapt, tlkapi_timer_t *pTimer)

Debug接口设计

功能描述

Debug是为应用调试而准备的接口,具有多层控制和分级管控的特点。

Debug具有三层控制开关:接口层总开关,可以配置宏使能开关,直接关掉所有调试接口;接口层分开关,用户可以在这里打开和关闭相关等级的log;用户层开关,用户只需要输入相应的标志位,就可以动态控制log是否输出。

Debug的另一个特色是进行了分级管控机制,系统将调试接口,按照严重等级由低到高,分为跟踪(TRACE)、信息(INFO)、警告(WARN)、错误(ERROR)、异常(FATAL)等。

另外,系统也为Debug提供了多套输出接口,目前支持的有USB数据、GPIO模拟串口输出两种方式。

规则约束

在高优先级(优先级高于1)的函数中,不要调用debug接口,否则会有死机的风险。

接口说明

Debug接口设计如下

宏定义:

定义log传输走UDB的路径:

#define TLKAPI_DEBUG_METHOD_UDB      1

定义log传输走GPIO的路径:

#define TLKAPI_DEBUG_METHOD_GPIO     2

定义log传输走UART的路径:

#define TLKAPI_DEBUG_METHOD_UART     3

定义GPIO的PIN:

#define TLKAPI_DEBUG_GPIO_PIN      GPIO_PD5

定义串口波特率:

#define TLKAPI_DEBUG_BAUD_RATE     1000000

定义打印log的级别为WARNING的log:

#define TLKAPI_DBG_WARN_FLAG        0x02

定义打印log级别为INFO的log:

#define TLKAPI_DBG_INFO_FLAG        0x04

定义打印log级别为TRACE的log:

#define TLKAPI_DBG_TRACE_FLAG       0x08

定义打印log级别为ERROR的log:

#define TLKAPI_DBG_ERROR_FLAG       0x10

定义打印log级别为FATAL的log:

#define TLKAPI_DBG_FATAL_FLAG       0x20 

定义打印log级别为ARRAY(处理数据较多)的log:

#define TLKAPI_DBG_ARRAY_FLAG       0x40

定义打印log级别为ASSERT的log:

#define TLKAPI_DBG_ASSERT_FLAG      0x80

定义输出所有的log:

#define TLKAPI_DBG_FLAG_ALL         0xFE

定义warning的log打印函数:

#define tlkapi_warn      tlkapi_debug_warn

定义info的log打印函数:

#define tlkapi_info      tlkapi_debug_info

定义trace的log打印函数:

#define tlkapi_trace     tlkapi_debug_trace

定义fatal的log打印函数:

#define tlkapi_fatal     tlkapi_debug_fatal

定义error的log打印函数:

#define tlkapi_error     tlkapi_debug_error

定义array的log打印函数:

#define tlkapi_array     tlkapi_debug_array

定义assert的log打印函数:

#define tlkapi_assert    tlkapi_debug_assert

定义sprint的函数:

#define tlkapi_sprintf   tlkapi_debug_sprintf

定义WARN级别log的头:

#define TLKAPI_WARN_HEAD       "<WARN>"

定义INFO级别log的头:

#define TLKAPI_INFO_HEAD       "<INFO>"

定义TRACE级别log的头:

#define TLKAPI_TRACE_HEAD      "<TRACE>"

定义FATAL级别log的头:

#define TLKAPI_FATAL_HEAD      "<FATAL>"

定义ERROR级别log的头:

#define TLKAPI_ERROR_HEAD      "<ERROR>"

定义ARRAY级别log的头:

#define TLKAPI_ARRAY_HEAD      "<ARRAY>"

定义ASSERT级别log的头:

#define TLKAPI_ASSERT_HEAD     "<ASSERT>"

定义log的输出长度:

#define TLKAPI_DEBUG_ITEM_SIZE       144

定义log的输出个数:

#define TLKAPI_DEBUG_ITEM_NUMB       16

接口定义:

初始化fifo, 使能GPIO:

int  tlkapi_debug_init(void)

重置参数,UDP使用:

void tlkapi_debug_reset(void)

将FIFO里面的数据取出来,发送:

void tlkapi_debug_process(void)

处理log输出多参数的函数接口:

int tlkapi_debug_sprintf(char *pOut, const char *format, ...)

输出Warn级别的log:

void tlkapi_debug_warn(uint flags, char *pSign, const char *format, ...)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

输出Info级别的log:

void tlkapi_debug_info(uint flags, char *pSign, const char *format, ...)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

输出Trace级别的log:

void tlkapi_debug_trace(uint flags, char *pSign, const char *format, ...)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

输出FATAL级别的log:

void tlkapi_debug_fatal(uint flags, char *pSign, const char *format, ...)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

输出ERROR级别的log:

void tlkapi_debug_error(uint flags, char *pSign, const char *format, ...)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

输出ARRAY级别的log,主要参数是指针之类的:

void tlkapi_debug_array(uint flags, char *pSign, char *pInfo, uint08 *pData, uint16 dataLen)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

输出ASSERT级别的log:

void tlkapi_debug_assert(uint flags, bool isAssert, char *pSign, const char *format, ...)
  • flag:log级别
  • pSign: 模块标签,如A2DP等
  • format: 输入log内容

发送log数据:

void tlkapi_debug_sendData(char *pStr, uint08 *pData, uint16 dataLen)

发送字符数据:

void tlkapi_debug_sendU08s(void *pStr, uint08 val0, uint08 val1, uint08 val2, uint08 val3)

发送短整型数据:

void tlkapi_debug_sendU16s(void *pStr, uint16 val0, uint16 val1, uint16 val2, uint16 val3)

发送整型数据:

void tlkapi_debug_sendU32s(void *pStr, uint32 val0, uint32 val1, uint32 val2, uint32 val3)

发送状态:

void tlkapi_debug_sendStatus(uint08 status, uint08 buffNumb, uint08 *pData, uint16 dataLen)

延迟打印:

void tlkapi_debug_delayForPrint(uint32 us)

MEM接口设计

功能描述

MEM接口是系统提供的动态内存管理接口,包含常用的malloc,free,remalloc等接口。

系统提供了两套接口,系统用内存分配管理接口以及通用内存分配管理接口。用户可使用通用内存分配管理接口,来动态定义自身的内存块,防止因内存不足或者内存碎片,而引发导致系统工作不正常的问题。

规则约束

目前定义的系统内存管理接口只能在单线程情况下使用,如果用户想在多线程下正常使用,可在初始化的时候,使能OS,之后系统会在分配和释放过程中,自动添加临界区保护。

接口说明

mem接口设计如下:

类型定义:

定义tlkapi_mem_t的类型为unsigned long

typedef ulong tlkapi_mem_t

数据结构:

定义存储单元:

typedef struct{
        uint32 prev;
        uint32 next;
        uint32 size:31;
        uint32 used:1;
}tlkapi_mem_unit_t
  • prev 指向当前存储单元的前一块地址
  • next 指向当前存储单元后一块地址
  • size 定义size大小
  • used 定义是否被使用

定义memory的控制数据:

typedef struct{
        uint08 isEnOs;
        uint08 reserv0;
        uint16 reserv1;
        uint32 totalSize;
        uint32 unuseSize;
        uint08 *pBaseAddr;
}tlkapi_mem_ctrl_t
  • isEnOs:是否有OS存在
  • totalSize: memory的大小
  • unuseSize:当前仍未使用的memory size
  • *pBaseAddr:memory的基地址

变量定义:

定义内存池的全局控制变量:

volatile tlkapi_mem_t sTlkApiMemGlobal

接口定义:

从内存池中分配一块内存:

void *tlkapi_malloc(uint32 size)

从内存池中分配一块内存并清零:

void *tlkapi_calloc(uint32 size)

从内存池中分配内存调整一下内存大小,返回大小更合适的内存:

void *tlkapi_realloc(void *ptr, uint32 size)

释放内存:

void  tlkapi_free(void *ptr)

打印某块内存块的内容:

void  tlkapi_printMem(void)

初始化内存池:

tlkapi_mem_t tlkapi_mem_init(bool osIsEn, uint08 *pBuffer, uint32 buffLen)

内存池的deinit,做清除内存的作用:

void tlkapi_mem_deinit(tlkapi_mem_t mem)

清除内存池:

void tlkapi_mem_clean(tlkapi_mem_t mem)

执行打印某块内存的功能:

void tlkapi_mem_print(tlkapi_mem_t mem)

从内存池中分配一块size大小的内存:

void *tlkapi_mem_malloc(tlkapi_mem_t mem, uint32 size)

从内存池中分配一块size大小的内存并清零:

void *tlkapi_mem_calloc(tlkapi_mem_t mem, uint32 size)

从内存池中重新分配一块大小合适于size的内存:

void *tlkapi_mem_realloc(tlkapi_mem_t mem, void *ptr, uint32 size)

释放内存池中的某块内存:

int   tlkapi_mem_free(tlkapi_mem_t mem, void *ptr)

OS接口设计

功能描述

os主要是SDK方便用户对任务管理、时间管理、信号量、消息队列、内存管理、记录功能等操作提供的嵌入式实时操作系统,目前仅支持FreeRTOS,用户可以基于os实现复杂的多任务应用场景。

规则约束

TBD

接口说明

(1) 任务管理

os的每个任务都有多种运行状态,系统初始化完成后,创建的任务就具有获取CPU资源的条件,由内核进行调度。任务状态通常分为就绪态、运行态、阻塞态和挂起态。处于就绪态的任务已经具备执行的能力,等待调度器调度,新创建的任务就会初始化为就绪态;处于运行态的任务正在占用CPU资源处理对应的任务;处于挂起态的任务,调度器不用去理会这个任务的任何信息,直到调用恢复任务的API函数;处于阻塞态的任务,系统需要判断阻塞态的任务是否超时,是否可以解除阻塞。 OS状态机

创建一个任务,创建后的任务处于就绪态,当任务调度器启动时,开始执行任务

int tlkos_task_create(tlkos_task_t *pTask, char *pName, uint08 priority,    uint16 stackSize,  TlkOsTaskEnterCB enter, void *pUsrArg);

删除一个任务

int tlkos_task_destory(tlkos_task_t *pTask);

挂起一个指定的任务,被挂起的任务不会得到CPU的使用权,也不参与任务的调度

int tlkos_task_suspend(tlkos_task_t *pTask);

让处于挂起态的任务重新进入就绪态,恢复前的任务会保留挂起前的状态信息,并根据该状态继续运行

int tlkos_task_resume(tlkos_task_t *pTask);

挂起所有的任务,其原理是将任务调度器锁定,等同于挂起任务调度器。此时如果有中断需要进行上下文切换该中断也会被挂起,除非调度器恢复响应

int tlkos_task_suspendAll(void);

恢复调度器(所有被挂起的任务)。特别注意,调用了多少次tlkos_task_suspendAll()就必须调用多少次tlkos_task_resumeAll()

int tlkos_task_resumeAll(void);

获取当前任务的状态

uint tlkos_task_getState(tlkos_task_t *pTask);

获取当前任务的优先级

uint tlkos_task_getPriority(tlkos_task_t *pTask);

设置当前任务的优先级

void tlkos_task_setPriority(tlkos_task_t *pTask, uint priority);

阻塞延时函数,调用该函数后,任务会被剥夺CPU使用权,进入阻塞态直到延时结束,在阻塞的这段时间内CPU可以去执行其他任务

void tlkos_delay(uint value);

开启任务调度器,创建空闲任务,使能任务调度功能

void tlkos_start_run(void);

进入临界段

uint tlkos_enter_critical(void);

退出临界段

void tlkos_leave_critical(uint irqMsk);

关闭中断

uint tlkos_disable_interrupt(void);

恢复中断

void tlkos_restore_interrupt(uint irqMsk);

(2) 消息队列

消息队列是用于任务(或中断)与任务间通讯的一种异步通信方式。消息队列实现了任务接收其他任务(或中断)的不定长消息,并且通过消息队列服务可以将多条消息放入消息队列中,当有多条消息发送到消息队列时,通常(紧急消息除外)会将先进入消息队列的消息先传给任务。当消息队列不再被使用时,应将其删除以释放系统资源,一旦操作完成,消息队列将被永久删除。

消息队列

创建一个消息队列。消息队列的大小等于 [消息队列控制块的大小 +(单个消息空间大小 × 消息队列长度)]

int tlkos_msgq_create(tlkos_msgq_t *pMsgq, uint numb, uint size);

删除一个消息队列,删除之后的消息队列的所有信息都会被系统回收。如果某个消息队列没有被创建则无法被删除

int tlkos_msgq_destory(tlkos_msgq_t *pMsgq);

发送一个队列消息(尾部),该函数不能在中断服务程序中被调用

int tlkos_msgq_send(tlkos_msgq_t *pMsgq, uint08 *pData, uint16 dataLen, uint timeout);

从消息队列中接收消息,并把该消息从队列中删除,该函数不能在中断服务程序中被调用

int tlkos_msgq_read(tlkos_msgq_t *pMsgq, uint08 *pBuff, uint16 buffLen, uint timeout);

重置消息队列

void tlkos_msgq_clear(tlkos_msgq_t *pMsgq);

(3) 信号量

信号量是实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问。信号量是一个非负整数,当该值为0时,所有试图获取它的任务都将处于阻塞状态。常见的信号量有二值信号量、计数信号量、互斥信号量和递归信号量。二值信号量更适合应用于同步功能,互斥量有优先权继承机制,更适合应用于临界资源访问,SDK中使用了计数信号量。

二值信号量运作机制

计数信号量运作机制

创建一个信号量(计数信号量),计数信号量与二值信号量创建的本质相差无几

int tlkos_sem_create(tlkos_sem_t *pSem, uint maxCount, uint iniCount);

删除一个信号量,包括二值信号量、计数信号量等上述所有信号量。如果有任务阻塞在该信号量上,则不要删除该信号量

int tlkos_sem_destory(tlkos_sem_t *pSem);

获取信号量,该函数不能在中断中使用

int tlkos_sem_take(tlkos_sem_t *pSem, uint timeout);

释放信号量,该函数不能在zai中断中使用

int tlkos_sem_post(tlkos_sem_t *pSem);

获取信号量的当前计数值是否为计数信号量的最大值maxCount

bool tlkos_sem_isFull(tlkos_sem_t *pSem);

获取信号量的当前计数值是否为0

bool tlkos_sem_isEmpty(tlkos_sem_t *pSem);

(4) 互斥锁

互斥量又叫互斥锁,是一种特殊的二值信号量。互斥量与信号量的不同之处在于其支持互斥量所有权、递归访问以及防止优先级反转的特性,因此互斥量更多的用于保护资源的互锁,在用于临界资源的保护时一般使用互斥量。 因为互斥量有优先级继承机制,当采用互斥量保护的资源被低优先级(L)的任务1占用时,优先级更高(H)的任务2想要使用该资源就会被阻塞,并且任务1的优先级将被系统临时提升到与高优先级任务2相等。当任务1使用完资源后,释放互斥量,此时任务1的优先级才会变回L,任务2此时可以获得互斥量,然后对共享资源进行访问,当任务2访问了共享资源时,互斥量的状态变为闭锁状态,其他任务无法获取互斥量。

互斥锁运作机制

创建一个互斥量,互斥量本质上是信号量

int tlkos_mutex_create(tlkos_mutex_t *pMutex);

删除一个互斥量

int tlkos_mutex_destory(tlkos_mutex_t *pMutex);

获取互斥量(上锁),当互斥量处于开锁状态时,任务才能成功获取互斥量

int tlkos_mutex_lock(tlkos_mutex_t *pMutex);

释放互斥量(解锁),互斥量的释放只能在任务中进行,中断中不允许释放互斥量

int tlkos_mutex_unlock(tlkos_mutex_t *pMutex);

(5) 软件定时器

软件定时器是os提供的一类系统接口,构建在硬件定时器的基础之上,使硬件定时器能够提供不受硬件资源限制的定时器服务。软件定时器相当于扩充了定时器的数量,允许创建更多的定时业务。os提供的软件定时器支持单词模式和周期模式,单次模式和周期模式的定时时间到之后都会调用软件定时器的回调函数,用户可以在回调函数中加入要执行的工程代码。软件定时器在任务调动启动后才嗯正常运行。

定时器运作机制

创建一个软件定时器,软件定时器在创建成功后处于休眠状态

int tlkos_timer_create(tlkos_timer_t *pTimer, char *pName, uint period, bool autoReload, TlkOsTimerEnterCB enter, void *pUsrArg);

删除一个已经被创建成功的软件定时器,定时器相应的资源被系统回收

int tlkos_timer_destory(tlkos_timer_t *pTimer);

启动一个软件定时器,将休眠状态的定时器激活

int tlkos_timer_start(tlkos_timer_t *pTimer, uint delay);

复位软件定时器,重新计算定时周期到达的时间点

int tlkos_timer_reset(tlkos_timer_t *pTimer);

停止一个已经启动的软件定时器

int tlkos_timer_stop(tlkos_timer_t *pTimer);

改变一个已经创建成功的软件定时器的周期

int tlkos_timer_setPeriod(tlkos_timer_t *pTimer, uint period);

File接口设计

功能描述

文件系统主要是SDK为方便用户存储文件而提供的操作接口,目前SDK中的文件系统是FAT32文件系统,SDK可以根据业务需求将文件通过FAT文件系统来进行管理。

规则约束

使用文件系统时,需要注意以下事项:

  • 文件访问只能在不高于1级的中断或者非中断中访问;
  • 打开的接口一定要关闭掉,否则无法完全删除;
  • 当遇到删除错误的时候,可以先打开文件,再关闭文件,然后再次尝试删除。

接口说明

file接口设计如下

宏定义:

指定读访问对象,从文件读取数据:

#define        TLKAPI_FM_READ                  0x01

指定写访问对象,向文件中写入数据:

#define        TLKAPI_FM_WRITE                 0x02

打开文件,如果文件不存在,则打开失败:

#define        TLKAPI_FM_OPEN_EXISTING         0x00

创建一个新文件,如果文件存在,则创建失败:

#define        TLKAPI_FM_CREATE_NEW             0x04 

创建一个新文件,如果文件已存在,则它将被截断并覆盖:

#define        TLKAPI_FM_CREATE_ALWAYS         0x08

如果文件存在,则打开;否则,创建一个新文件:

#define        TLKAPI_FM_OPEN_ALWAYS           0x10

打开文件向文件追加内容:

#define        TLKAPI_FM_OPEN_APPEND           0x30

定义FCHAR为uint16的类型:

#define FCHAR      uint16

定义在没有文件系统下FCHAR为char类型:

#define FCHAR      char

定义在没有文件系统下FIL为空类型:

#define FIL        void

全局变量:

定义Fat文件系统控制变量:

FATFS gTlkFileFatFs

接口定义:

未定义:

int tlkapi_file_size(FIL *pFile)

获取文件路径的长度:

int tlkapi_file_strlen(FCHAR *pPath)

打开文件、文件路径和文件模式打开文件:

int tlkapi_file_open(FIL *pFile, const FCHAR *pPath, uint08 mode)

关闭某个文件:

int tlkapi_file_close(FIL *pFile)

将文件操作指针从当前位置向后移到ofs的位置:

int tlkapi_file_seek(FIL *pFile, uint32 ofs)

从文件中读取数据:

int tlkapi_file_read(FIL pFile, void buff, uint32 btr, uint32* br)

向文件中写数据:

int tlkapi_file_write(FIL pFile, const void buff, uint32 btw, uint32* bw)

打开目录文件:

int tlkapi_file_opendir(DIR pDir, const FCHAR path)

关闭目录文件:

int tlkapi_file_closedir(DIR *pDir)

读取目录函数:

int tlkapi_file_readdir(DIR pDir, FILINFO fno)

查找目录下的第一个文件:

int tlkapi_file_findfirst(DIR pDir, FILINFO fno, const FCHAR* path, const FCHAR* pattern)

查找目录下当前文件的下一个文件:

int tlkapi_file_findnext(DIR pDir, FILINFO fno)

创建一个以path为名称的文件:

int tlkapi_file_mkdir(const FCHAR* path)

删除path指定的文件:

int tlkapi_file_unlink(const FCHAR* path)

将老的文件名替换为新文件名:

int tlkapi_file_rename(const FCHAR* path_old, const FCHAR* path_new)

加载文件系统:

int tlkapi_file_mount(const FCHAR* path, uint08 opt)

在特定的分区下建立文件系统:

int tlkapi_file_mkfs(const FCHAR* path, const void* opt, void* work, uint32 len)

磁盘操作函数:

int tlkapi_file_fdisk(uint08 pdrv, const uint32 ptbl[], void* work)

Chip接口设计

功能描述

Chip里面主要包含系统时钟切换和程序堆栈深度检测相关的接口。

  • 时钟切换主要是为了满足不同运行场景而提供的动态时钟切换的接口,目前有48M和96M两种;其中48M为系统空闲运行的时钟,96M是系统音频打开的情况下运行(需要跑多种算法)。
  • 堆栈深度检测,主要是用来实时动态检测,当前程序执行所使用的最大堆栈深度。

规则约束

TBD

接口说明

Chip接口设计如下

宏定义:

定义控制栈操作的宏:

#define TLKAPI_CHIP_STACK_CHECK_ENABLE       0

数据结构:

定义系统主时钟, 有96M和48M:

typedef enum{
        TLKAPI_CHIP_CLOCK_96M = 1,
        TLKAPI_CHIP_CLOCK_48M = 2,
}TLKAPI_CHIP_CLOCK_ENUM

接口定义:

配置系统主时钟,协调mainloop的频率:

void tlkapi_chip_switchClock(TLKAPI_CHIP_CLOCK_ENUM clock)

初始化栈的栈顶和栈底:

void tlkapi_chip_stackInit(void)

查询当前栈的使用深度:

uint tlkapi_chip_stackUsed(void)

查询栈的总深度:

uint tlkapi_chip_stackDepth(void)

查询栈是否发生溢出:

bool tlkapi_chip_stackOverflow(void)

FIFO接口设计

功能描述

FIFO是对大内存块做数据有序存取的接口。系统提供了覆盖式写和选择性写的功能配置:

  • 用户可以通过打开可覆盖式读写,达到在fifo数据满的情况下,覆盖旧的数据的效果;
  • 用户可以通过打开部分写功能的,达到当fifo空间不足时,会按照最大能写入的长度写入的效果。

规则约束

用户实际可用buffer比用户实际设置buffer小1。

接口说明

FIFO接口设计如下

宏定义:

定义FIFO最小的size:

#define TLKAPI_FIFO_MIN_SIZE      64

数据结构:

定义FIFO的数据结构:

typedef struct{
        uint08 isCover; 
        uint08 isParty;
        uint16 more75L;
        uint16 more90L;
        uint16 woffset;
        uint16 roffset;
        uint16 buffLen;
        uint08 *pBuffer;
}tlkapi_fifo_t;
  • isCover: 定义fifo满之后是否可以用新写入的数据覆盖之前的数据;
  • isParty:定义fifo当前的空间只剩下一部分,可否只写一部分新数据;
  • more75L: 标记当前fifo的空余空间是否少于25%;
  • more90L: 标记当前fifo的空余空间是否少于10%;
  • Roffset: fifo当前读的位置;
  • Woffset: fifo当前写的位置;
  • buffLen: buffer的长度;
  • *pBuffer:buffer的地址位置。

接口定义:

初始化fifo的控制结构体:

int  tlkapi_fifo_init(tlkapi_fifo_t *pFifo, uint08 isCover, uint08 isPartly, uint08 *pBuffer, uint16 buffLen)
  • pFifo,是FIFO的控制结构体;
  • isCover,是指是被可以被覆盖;
  • isParty,是指在fifo空间不够的情况下,是否允许只写一部分数据到fifo中;
  • pBuffer,是指将那一块buffer作为fifo使用。

检验当前的fifo控制结构体是否被释放,没有释放,就执行释放过程:

void tlkapi_fifo_deinit(tlkapi_fifo_t *pFifo)

将pfifo的控制结构体清空:

void tlkapi_fifo_reset(tlkapi_fifo_t *pFifo)

将fifo的写入数据擦除:

void tlkapi_fifo_clear(tlkapi_fifo_t *pFifo)

判断fifo中的数据是否为空:

bool tlkapi_fifo_isEmpty(tlkapi_fifo_t *pFifo)

查询当前还有多少空闲buffer未使用:

uint16 tlkapi_fifo_idleLen(tlkapi_fifo_t *pFifo)

查询当前fifo中存储数据的长度:

uint16 tlkapi_fifo_dataLen(tlkapi_fifo_t *pFifo)

fifo的长度:

uint16 tlkapi_fifo_buffLen(tlkapi_fifo_t *pFifo)

判断当前fifo的使用率是否超过75%:

bool tlkapi_fifo_isMore75(tlkapi_fifo_t *pFifo, uint16 dataLen)

判断当前fifo的使用率超过90%:

bool tlkapi_fifo_isMore90(tlkapi_fifo_t *pFifo, uint16 dataLen)

从fifo中读取数据:

int tlkapi_fifo_read(tlkapi_fifo_t *pFifo, uint08 *pBuff, uint16 readLen)

向fifo插入数据:

int tlkapi_fifo_write(tlkapi_fifo_t *pFifo, uint08 *pData, uint16 dataLen)

以byte的形式从fifo中读数据:

int tlkapi_fifo_readByte(tlkapi_fifo_t *pFifo, uint08 *pByte)

以byte形式向fifo写数据:

int tlkapi_fifo_writeByte(tlkapi_fifo_t *pFifo, uint08 obyte)

QFIFO接口设计

功能描述

QFIFO为队列式缓存访问接口,提供固定长度、多item的有序访问接口。QFIFO在初始化过程中,需要明确fifo中item的大小和item的个数。系统中,在audio数据存取过程中,大量使用了该类接口。

QFIFO与FIFO的不同之处在于,QFIFO是对一段buffer拆分成多个块的控制(操纵单位为块),而FIFO的操作单位为字节。

规则约束

暂无约束。

接口说明

qfifo接口设计如下

宏定义:

qfifo的大小:

#define tlkapi_qfifo_size(pFifo)               ((pFifo)->size)

qfifo的空间个数:

#define tlkapi_qfifo_count(pFifo)              ((pFifo)->count)

qfifo是否已经填满数据:

#define tlkapi_qfifo_isFull(pFifo)             ((pFifo)->full || (pFifo)->count == 0)

qfifo是否为空:

#define tlkapi_qfifo_isEmpty(pFifo)            (!(pFifo)->full && (pFifo)->wptr == (pFifo)->rptr)

数据结构:

定义qfifo的控制数据结构:

typedef struct{
        uint08 full;
        uint16 numb;
        uint16 size;
        uint16 wptr;
        uint16 rptr;
        uint08 *pBuff;
}tlkapi_qfifo_t;
  • full: qfifo是否为已经填满
  • numb: 当前的个数
  • size: qfifo的大小
  • wptr: qfifo的写位置
  • rptr:qfifo的读位置
  • pBuff: buff的指针

接口定义:

初始化qfifo:

int  tlkapi_qfifo_init(tlkapi_qfifo_t *pFifo, uint16 numb, uint16 size, uint08 *pBuffer, uint32 buffLen);

重置qfifo:

void tlkapi_qfifo_reset(tlkapi_qfifo_t *pFifo)

清空qfifo的数据:

void tlkapi_qfifo_clear(tlkapi_qfifo_t *pFifo)

向qfifo中写数据:

void tlkapi_qfifo_dropBuff(tlkapi_qfifo_t *pFifo)

从qfifo中读取数据:

void tlkapi_qfifo_dropData(tlkapi_qfifo_t *pFifo)

计算qfifo中的使用个数:

uint16 tlkapi_qfifo_usedNum(tlkapi_qfifo_t *pFifo)

计算qfifo中空闲buffer个数:

uint16 tlkapi_qfifo_idleNum(tlkapi_qfifo_t *pFifo)

从qfifo中取出一块Buffer:

uint08 *tlkapi_qfifo_getBuff(tlkapi_qfifo_t *pFifo)

从qfifo中去除一块buffer的数据:

uint08 *tlkapi_qfifo_getData(tlkapi_qfifo_t *pFifo)

获取一块buffer,向其中写数据:

uint08 *tlkapi_qfifo_takeBuff(tlkapi_qfifo_t *pFifo)

从buffer中去除一块数据:

uint08 *tlkapi_qfifo_takeData(tlkapi_qfifo_t *pFifo)

Save接口设计

功能描述

SAVE是SDK为方便用户访问内置flash而准备的接口,用于将用户信息动态保存到flash中。目前SDK提供四种保存算法:

  • 算法一,单item、单扇区、可变长度的保存算法。该算法适合保存可变长度的信息,但是无抗异常掉电特性。
  • 算法二,单item、单扇区、固定长度的保存算法。该算法适合保存固定长度的信息,具有一定的异常掉电的可恢复性,系统用此算法保存音量信息。
  • 算法三,单item、双扇区、固定长度的保存算法。该算法适合保存固定长度的信息,具有异常掉电可恢复的特性。
  • 算法四,多item、双扇区、固定长度的保存算法。该算法适合同时保存多条固定长度的信息,具有异常掉电可恢复的特性。系统用此算法保存配对信息。

规则约束

系统SAVE接口在使用过程中,需要注意以下事项:

  • item的最小长度需要在10以上;
  • 各个算法之间不能混用,否则会有不可预期的问题;
  • 在系统迭代过程中,如果结构或信息有变化,只需要更改版本号即可,这样系统在重新加载的时候,会删除之前的信息。

接口说明

Save接口设计如下:

宏定义:

定义页的存储空间为256B:

#define TLKAPI_FLASH_PAGE_SIZE        256

定义扇区的存储空间为4KB:

#define TLKAPI_FLASH_SECTOR_SIZE      4096

数据结构:

定义正确操作flash:

typedef struct{
        uint08 sign;
        uint08 vers;
        uint16 prev;
        uint16 lens;
        uint16 offs;
        uint32 addr;
        uint32 addr0;
        uint32 addr1;
}tlkapi_save_ctrl_t;
typedef struct{
        uint32 address;
        uint16 dataLen;
        uint16 buffLen;
        uint08 *pBuffer;
}tlkapi_save_item_t;
  • sign: 描述flash的标志
  • vers: 描述flash的版本
  • lens: 描述flash的存储header的长度
  • offs: 记录当前相对于addr的offset
  • addr: 描述flash的起始位置
  • addr0: 描述flash sector0
  • addr1: 描述flash sector1
  • tlkapi_save_item: 描述的操作存取数据的数据结构
  • address:操作数据的地址
  • dataLen:操作数据的长度
  • buffLen:操作数据的buffer长度
  • *pBuffer: 操作数据的Buffer

接口定义:

save1主要用来存储非固定长度的数据,初始化flash的存储数据结构:

int  tlkapi_save1_init(tlkapi_save_ctrl_t *pCtrl, uint08 sign, uint08 version, uint32 address)

从flash中读取长度为bufferLen某块数据到*pBuff中:

int  tlkapi_save1_load(tlkapi_save_ctrl_t *pCtrl, uint08 *pBuff, uint16 buffLen)

将长度为dataLen的*pData指向的某块数据存储到flash中:

int  tlkapi_save1_save(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

清除某块数据:

void tlkapi_save1_clean(tlkapi_save_ctrl_t *pCtrl)

标记某块数据将从当前的存储位置迁移到别的位置:

int  tlkapi_save1_migrate(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

save2主要用来存储固定长度的数据,掉电恢复。初始化save2的控制数据结构:

int  tlkapi_save2_init(tlkapi_save_ctrl_t *pCtrl, uint08 sign, uint08 version, uint16 length, uint32 address)

从flash中读取长度为bufferLen某块数据到*pBuff中:

int  tlkapi_save2_load(tlkapi_save_ctrl_t *pCtrl, uint08 *pBuff, uint16 buffLen)

将长度为dataLen的*pData指向的某块数据存储到flash中:

int  tlkapi_save2_save(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

清除某块数据:

void tlkapi_save2_clean(tlkapi_save_ctrl_t *pCtrl)

根据当前需要存储的数据长度,动态调整已经储存的数据的位置,将需要存储的数据存储:

int  tlkapi_save2_smartSave(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

标记某块数据将从当前的存储位置迁移到别的位置:

int  tlkapi_save2_migrate(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

根据当前需要存储的数据长度,动态调整已经储存的数据的位置,将需要的存储数据存储:

int tlkapi_save1_smartSave(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

save将数据保存到双扇区,初始化存储数据结构:

int  tlkapi_save3_init(tlkapi_save_ctrl_t *pCtrl, uint08 sign, uint08 version, uint16 length, uint32 address0, uint32 address1)

从flash中读取长度为bufferLen某块数据到*pBuff中:

int  tlkapi_save3_load(tlkapi_save_ctrl_t *pCtrl, uint08 *pBuff, uint16 buffLen)

将长度为dataLen的*pData指向的某块数据存储到flash中:

int  tlkapi_save3_save(tlkapi_save_ctrl_t *pCtrl, uint08 *pData, uint16 dataLen)

清除某块数据:

void tlkapi_save3_clean(tlkapi_save_ctrl_t *pCtrl)

save将数据保存到双扇区,初始化存储数据结构:

int  tlkapi_save4_init(tlkapi_save_ctrl_t *pCtrl, uint08 sign, uint08 version, uint16 length, uint32 address0, uint32 address1)

从flash中读取长度为bufferLen某块数据到*pBuff中:

int  tlkapi_save4_load(tlkapi_save_ctrl_t *pCtrl, tlkapi_save_item_t *pItems, uint16 itemCount, uint16 indexOffs)

将长度为dataLen的*pData指向的某块数据存储到flash中:

int  tlkapi_save4_save(tlkapi_save_ctrl_t *pCtrl, tlkapi_save_item_t *pItems, uint16 itemCount)

清除某块数据:

void tlkapi_save4_clean(tlkapi_save_ctrl_t *pCtrl)

擦除数据:

int  tlkapi_save4_remove(tlkapi_save_ctrl_t *pCtrl, tlkapi_save_item_t *pItems, uint16 itemCount)

修改某块数据:

int  tlkapi_save4_modify(tlkapi_save_ctrl_t *pCtrl, tlkapi_save_item_t *pItems, uint16 offset, uint16 length)

将当前的数据进行迁移:

int  tlkapi_save4_migrate(tlkapi_save_ctrl_t *pCtrl, tlkapi_save_item_t *pItems, uint16 itemCount)

String接口设计

功能描述

String的接口设计主要为了满足SDK的工程中字符串数据和各个基本数据类型之间或者进制之间的正确转换,以及字符查找等功能。它提供了字符串转各种整型数据,字符串转字符,字符串转十进制数,字符串转十六进制数等等。以及各整型数据转字符串,或者各进制转字符串等等。

规则约束

TBD

接口说明

String模块设计如下

宏定义:

定义unsigned int08转字符串:

#define tlkapi_uint08ToStr(str,value,isDropZero)     tlkapi_hexToStr(str, value, 2, isDropZero)

定义unsigned int16转字符串:

#define tlkapi_uint16ToStr(str,value,isDropZero)     tlkapi_hexToStr(str, value, 4, isDropZero)

定义unsigned int32转字符串:

#define tlkapi_uint32ToStr(str,value,isDropZero)     tlkapi_hexToStr(str, value, 8, isDropZero)

接口定义:

统计有效的字符个数:

int tlkapi_wcharStrlen(uint08 *pStr, uint16 maxLen)

字符转16进制数:

char tlkapi_ascii_char2val(const  char c)

字符根据base转不同进制的数字:

int tlkapi_ascii_str2val(const char str[], char base)

十进制数转字符串:

int tlkapi_decToStr(char *str, uint32 dec, uint num, bool isDropZero)

16进制数转字符串:

int tlkapi_hexToStr(char *str, uint32 hex, uint num, bool isDropZero)

字符串转int32:

int tlkapi_strToInt32(char *str, int strLen, sint32 *pValue)

字符串转Int08:

int tlkapi_strToInt08(char *str, int strLen, sint08 *pValue)

字符串转Int16:

int tlkapi_strToInt16(char *str, int strLen, sint16 *pValue)

字符串转unsigned Int32:

int tlkapi_strToUint32(char *str, int strLen, uint32 *pValue)

字符串转unsigned Int08:

int tlkapi_strToUint08(char *str, int strLen, uint08 *pValue)

字符串转unsigned Int16:

int tlkapi_strToUint16(char *str, int strLen, uint16 *pValue)

字符串转数组:

int tlkapi_strToArray(char *str, int strLen, uint08 *pHex, int hexLen)

数组转字符串:

int tlkapi_arrayToStr(uint08 *pHex, int hexLen, char *str, int strLen, char split)

字符查找:

char *tlkapi_str_findChar(char *pSrc, uint16 srcLen, char dst, uint16 *pOffset)

字符串查找:

char *tlkapi_str_findStr(char *pSrc, uint16 srcLen, char *pDst, uint16 dstLen, uint16 *pOffset)

MMI设计说明

MMI,人机交互接口层,主要是根据业务逻辑,将MDI层(下面章节会讲解)和STK层的相关功能模块,做进一步的集成处理,形成某一满足多场景下,可自由调用配置的功能体。如下图所示,目前,MMI中有Audio, Phone,BT Manager, BLE Manager等功能体:

  • BTMGR(BT Manager)主要用于管理BT发现,连接和重连相关的操作。
  • LEMGR(BLE Manager)主要用于管理LE的广播和连接相关的操作,目前还待进一步完善。
  • Audio主要是将音频相关的模块进行集成,并形成统一调配接口。目前,音乐播放、提示音、SCO等音频的控制接口,全部封装到该功能体里。
  • Phone主要用于对电话状态和电话簿的管理,并将电话号码与电话簿进行绑定,这样有电话状态改变的时候,系统会自动去电话簿里面加载联系人,并将与电话状态一起,上报给用户。

MMI组织结构

BTMGR设计与实现

BT Manager是面向用户的一个重要模块,主要是整合了BT的发现、连接和回连的功能,应用程序只需要调用这一层的接口就能实现BT的发现、连接、断开和回连等操作。下面从组织结构、状态转换、重要流程和接口设计四个角度,讲解这一部分的逻辑框架。

组织结构

这个模块提供了BT的服务管理逻辑和对外交互访问接口,主要有四个子模块组成,即BTMGR,CTRL,ACL,REC,INQ等。

  • BTMGR这个模块提供各个子模块的初始化接口;
  • CTRL这个模块负责维护BT MGR的控制变量;
  • ACL模块主要负责连接流程管理;
  • REC模块主要负责回连流程管理;
  • INQ模块主要负责发现的流程管理。

BTMGR组织结构

状态转换

下图是BTManager在发现、连接以及回连过程中设计的一些状态跃迁图,从下图可以看出,BT Manager一共有五个状态分别为Initiated状态,Inquired状态,Closed状态,Connected状态和Connecting状态。箭头指向即是上一个状态到下一个状态的转换,状态和状态之间的转换由图示行为所触发,比如:用户可以发起page动作,来达到从Inquired到Connected跃迁。

BTMGR状态转换

重要流程

通过流程图和流程子序列图,我们描述BT Manager模块内和模块间的关系。BT Manager各个模块协作实现蓝牙设备的发现,链接,加密,断开,回连,以及Profile的链接和断开功能。下面提供了初始化流程子序列图,发现流程子序列图和链接流程图和链接流程子序列图等。

(1) 初始化流程

下面的这幅图是BT Manager的初始化序列图,通过它来协助理解BT Manager的初始化流程。序列图主要由以下几部分组成:文件名称或者模块名称;函数以及描述;文件之间的调用关系和反馈关系。比如,App是一个模块,通过调用tlkmmi_btmgrCore的函数tlkmmi_btmgr_coreinit去做初始化处理。tlkmmi_btmgrCore文件调用Btc文件中的bt_ll_set_local_host_support_ssp函数去做ssp相关的配置等。

BTMGR初始化

(2) 发现流程

发现子流程序列图由文件名或者模块名,函数和调用函数和回调函数及部分组成,前面的模块或者文件通过函数调用后面的模块,或者文件内部函数之间调用(self-call)。然后后面的模块通过回调函数返回执行的结果或者状态,例如:tlkmmi_btmgrInq文件通过回调tlkmmi_btmgr_inquiryReportCB函数回调相关的执行结果和状态。发现流程子序列包括两个部分:第一部分是开始发现到发现结果通过回调上报;第二部分是取消发现部分。

BTMGR发现流程

(3) 链接流程

BT链接流程图从整体的角度去包含了三部分,一部分是发现流程,第二部分是链接流程,第三部分是回连流程。在下面的流程图中,链接执行的前提是目标设备必须存在,否则需要发起发现流程去查找对应的设备,当目标设备存在,还需要确认当前是否处于回连状态。然后才能发起链接流程。

BTMGR链接流程

下面是链接流程子序列图。链接的子序列图也是由文件或者模块名,调用关系和反馈关系组成。虚线是表示同一个文件中的函数在不同阶段被调用或者产生了回调。执行流程包含两部分,第一部分是用户发起链接请求,第二部分是用户发起断开链接请求。

BTMGR链接流程子序列图

接口设计

接口设计主要由四列表示,第一列是模块名称,第二列是模块的接口函数,第三列是接口函数定义或者宏定义或者数据结构定义或者全局变量定义等,第四列是第三列内容的具体描述。

(1) 通用接口说明

通用接口模块主要是用来将发现,链接的资源初始化,一般会在应用程序启动的时候,会调用通用接口的初始化流程,将BT Manager的子模块全部做初始化。

BT Manager

接口设计

int tlkmmi_btmgr_init(void)

a) 初始化串口

b) 初始化控制模块

c) 初始化acl链接模块

d) 初始化Inquiry模块

e) 初始化重连模块

f) 设置蓝牙地址

g) 设置本地蓝牙名称

(2) COMM接口说明

这个模块主要是用来管理串口通信,从MDI层的串口获取消息,经过执行产生的结果或者响应等,会通过MMI层的进行封装后发送给MDI层,通过MDI层发送给应用或别的模块。它主要用来处理应用程序过来的发现和链接命令,以及返回他们的结果。

Bt Manager Comm设计

接口定义:

注册串口信号处理的回调函数:

int tlkmmi_btmgr_commInit(void)

将Profile类型转换成Channel类型:

uint08 tlkmmi_btmgr_ptypeToCtype(uint08 ptype, uint08 usrID)

通过串口发送ACL的链接信息:

void tlkmmi_btmgr_sendAclConnectEvt(uint16 handle, uint08 status, uint08 *pBtAddr)

通过串口发送ACL的断开连接信息:

void tlkmmi_btmgr_sendAclDisconnEvt(uint16 handle, uint08 reason, uint08 *pBtAddr)

通过串口发送Profile的链接信息:

void tlkmmi_btmgr_sendProfConnectEvt(uint16 handle, uint08 status, uint08 ptype, uint08 usrID, uint08 *pBtAddr)

通过串口发送Profile的断开链接信息

void tlkmmi_btmgr_sendProfDisconnEvt(uint16 handle, uint08 reason, uint08 ptype, uint08 usrID, uint08 *pBtAddr)

(3) CTRL接口说明

主要用来初始化控制变量和查询蓝牙名称和蓝牙地址,辅助其它模块的正常使用。

BT Manager Ctrl设计

数据结构:

定义BT Ctrl的数据结构:

typedef struct{
        uint08 btaddr[6];
        uint08 btname[TLKMMI_BTMGR_BTNAME_LENS];
}tlkmmi_btmgr_ctrl_t;
  • btaddr:蓝牙地址
  • btname:蓝牙名称

定义gTlkMmiBtmgrCtrl:

tlkmmi_btmgr_ctrl_t gTlkMmiBtmgrCtrl;

接口定义:

初始化gTlkMmiBtmgrCtrl,从flash读取蓝牙地址和名称:

int tlkmmi_btmgr_ctrlInit(void)

获取设备名称:

uint08 *tlkmmi_btmgr_getBtName(void)

获取蓝牙地址:

uint08 *tlkmmi_btmgr_getBtAddr(void)

(4) ACL接口说明

ACL接口定义了数据结构和接口函数,以及数据结构和接口定义的各个基本元素的详细说明。其主要功能有ACL的链接与断开;各个Profile的链接和断开;以及如何在各个使用场下如何使用发现和回连的功能。

BTManager ACL设计

宏定义:

定义调度timer的Timeout的值:

#define TLKMMI_BTMGR_TIMEOUT          200000 

定义链接的timeout值:

#define TLKMMI_BTMGR_TIMEOUT_MS       200

Wait持续的timeout值:

#define TLKMMI_BTMGR_WAIT_TIMEOUT     (5000000/TLKMMI_BTMGR_TIMEOUT)

发现设备的Rssi阈值:

#define TLKMMI_BTMGR_INQ_RSSI         0x9C

数据结构:

typedef enum{
   TLKMMI_BTMGR_BUSY_NONE = 0x00,
   TLKMMI_BTMGR_BUSY_WAIT_REC  = 0x01,
   TLKMMI_BTMGR_BUSY_WAIT_INQ  = 0x02,
   TLKMMI_BTMGR_BUSY_OPEN_INQ  = 0x04                   TLKMMI_BTMGR_BUSY_WAIT_DEV  = 0x08,
   TLKMMI_BTMGR_BUSY_OPEN_CONN = 0x10,
   TLKMMI_BTMGR_BUSY_WAIT_CONN = 0x20,
}TLKMMI_BTMGR_BUSY_ENUM;
  • TLKMMI_BTMGR_BUSY_WAIT_REC 标记当前是否重复等待重连
  • TLKMMI_BTMGR_BUSY_WAIT_INQ 标记当前是否重新等待Inquiry
  • TLKMMI_BTMGR_BUSY_OPEN_INQ 标记当前是否重新打开Inquiry
  • TLKMMI_BTMGR_BUSY_WAIT_DEV 标记当前是否重新等待等待设备
  • TLKMMI_BTMGR_BUSY_OPEN_CONN 标记是否重新发起连接
  • TLKMMI_BTMGR_BUSY_WAIT_CONN 标记当前是否需要重新等待链接
typedef struct{
        uint08 busys;
        uint08 flags;
        uint16 timeout;
        uint32 devClass;
        uint16 connTime;
        uint08 btaddr[6];
        tlkapi_timer_t timer;
}tlkmmi_btmgr_acl_t;
  • busys:存储当前的执行行为是否需要重新发送(pending falg)
  • flags:判断当前收到的Event是否为当前执行动作的反馈(判断过滤条件)
  • timeout:设置当前执行动作的timeout
  • devClass:对方设备的设备类型
  • connTime:链接时间
  • btaddr:对方设备的蓝牙地址
  • timer:动作执行的timer

接口定义:

(1) 初始化BT ACL的数据结构 (2) 初始化timer (3) 注册回调函数

int tlkmmi_btmgr_aclInit(void)

查询当前是否有acl链接:

bool tlkmmi_btmgr_aclIsBusy(void)

注册ACL链接的回调函数:

void tlkmdi_btmgr_regAclConnectCB(TlkMmiBtMgrAclConnectCallback connCB)

注册ACL断开连接的回调函数:

void tlkmdi_btmgr_regAclDisconnCB(TlkMmiBtMgrAclDisconnCallback discCB)

注册Profile链接的回调函数:

void tlkmdi_btmgr_regProfileConnectCB(TlkMmiBtMgrProfileConnectCallback connCB)

注册Profile断开链接的回调函数:

void tlkmdi_btmgr_regProfileDisconnCB(TlkMmiBtMgrProfileDisconnCallback discCB)

发起目标地址的设备链接:

int tlkmmi_btmgr_connect(uint08 btaddr[6], uint16 timeout)

断开handle指定的ACL链接:

int tlkmmi_btmgr_disconn(uint16 handle)

断开蓝牙地址指定的ACL链接:

int tlkmmi_btmgr_disconnByAddr(uint08 btaddr[6])

(5) INQ接口说明

主要用来描述设备发现中的接口设计以及接口设计元素的详细说明。其功能主要是提供给用户在各个不同使用场景下的发现功能。

BT Manager Inquiry设计

接口设计:

Inquiry初始化:

int tlkmmi_btmgr_inqInit(void)

开始Inquiry,接口可以指定:

int tlkmmi_btmgr_startInquery(uint08 inqType, uint08 rssiThd, uint08 maxNumb, uint16 timeout, bool isGetName)
  • inqType:Inquiry类型
  • rssiThd:rssi的阈值
  • maxNmb:最大上报设备数
  • timeout:Inquiry时长
  • isGetName:是否获取设备名称

停止Inquiry

int tlkmmi_btmgr_closeInquery(void)

(6) REC接口说明

主要用来描述设备回连中的接口设计以及接口设计元素的详细说明。其功能主要是提供给用户在各个不同使用场景下的回连功能

BT Manager Reconnect设计

接口设计:

int tlkmmi_btmgr_recInit(void)

a) 初始化重连

b) 设置Inquiry+page模式

c) 设置Scan和Page参数

d) 使能page和scan

e) 注册回调函数

重连开始:

int tlkmdi_btmgr_recStart(uint08 *pDevAddr, uint32 devClass)

a. pDevAddr:对方设备地址

b. 设备类型

关闭重连:

int tlkmdi_btmgr_recClose(void)

查询重连正在执行:

bool tlkmdi_btmgr_recIsBusy(void)

查询是否在执行page:

bool tlkmmi_btmgr_recIsPage(void);

查询是否在执行Scan(page scan或者inquiry scan):

bool tlkmmi_btmgr_recIsScan(void);

查询在执行page的设备地址:

uint08 *tlkmmi_btmgr_recPageAddr(void);

LEMGR设计与实现

TBD

Audio设计与实现

Audio模块主要用来管理应用的各个Audio业务需求,对这些需求的前后执行关系做出合适的策略,并控制各个不同使用场景下的音量和Audio的信息和状态的更新。同时,它能够控制MDI层去执行各个Audio任务,并监控MDI的执行状态。它很好的做到了将策略和执行的分离,有利于应用程序的快速开发和维护。这个模块主要由四个部分组成,第一部分是组织结构;第二部分是状态转换;第三部分是重要流程;第四部分是接口设计。

组织结构

根据Audio的功能需求定义,Audio结构包括INFO,CTRL,MODINF和SCH模块。他们子模块的功能如下所示:

  • INFO:用于加载和保存用户信息,比如音量,播放进度等;
  • CTRL:控制和管理音频启动和停止流程;
  • MODINF:这个模块主要用来将各个Audio操作的行为统一,易于扩展;
  • SCH:管理Audio不同用户行为时的优先级和执行行为。

Audio组织结构

状态转换

Audio状态转换图,由五个状态转成:Initiated状态,Opened状态,Closed状态,Running状态和Paused状态。状态之间的转换路径如下面的单箭头所示,箭头指向的是下一个状态。状态之间的转换由a,b,c,d,Auto的执行动作来完成。其本质是,需要处理当前任务队列中的任务在各状态下的下一步执行行为。比如:本地提示音处于Running状态下(正在响铃),插入了语音通话的任务需要立即执行,但是此时的本地提示音还未执行完成,但是后续又不需要它在通话结束之后继续执行,因此可以将本地提示音暂停之后移除出任务队列,同时开始语音通话任务执行。

Audio状态转换

重要流程

Audio的流程根据Audio的执行分为三个部分,每个部分由控制不同的Audio操作行为,分别是:添加任务流程,移除任务流程和任务执行流程。

(1) 添加任务流程

每当有新的任务需要执行时,需要将新的任务按照优先级来插入Audio的工作队列。如果是立即执行的任务,需要插入任务队列首位,并立即执行;或者是由高优先级的任务插入队列,需要对当前执行任务和要插入任务的优先级比较,找到正确的位置插入。Audio队列的任务会依次执行。

Audio添加任务流程

(2) 移除任务流程

删除任务,是指将执行完的任务或者由于其他原因不能正常执行的任务从任务队列中移除。保证此队列首位的任务顺利执行。

Audio移除任务流程

(3) 任务执行流程

任务执行的流程比较复杂,主要由两个Timer来控制当前的任务执行。第一个Timer运行Audio任务,主要管理任务调度。第二个Timer主要管理任务具体执行时数据执行,根据任务执行的成功与否调整执行任务的时间间隔。

Audio任务执行流程

接口设计

(1) 通用接口说明

主要用来描述Audio中的数据结构设计和接口设计以及其中元素的详细说明。其功能主要给应用提供访问整个Audio模块的入口,包括初始化和销毁Audio的资源,将当前的Audio任务加入到Audio的策略管理数组中,或者将某个任务删除。

Audio Interface设计

宏定义:

定义Audio任务调度的Timeout:

#define TLKMMI_AUDIO_TIMEOUT          100000

定义Audio状态更新的Timeout:

#define TLKMMI_AUDIO_TIMEOUT_MS       100

定义Audio子任务的Timeout:

#define TLKMMI_AUDIO_TIMER_TIMEOUT    (3000000/TLKMMI_AUDIO_TIMEOUT) //Prevent timer termination, which may cause problem

定义Audio信息更新的Timeout:

#define TLKMMI_AUDIO_INFO_TIMEOUT     (5000000/TLKMMI_AUDIO_TIMEOUT) 

定义Audio codec待机状态超时关闭的Timeout:

#define TLKMMI_AUDIO_CODEC_IDLE_TIMEOUT     (300000/TLKMMI_AUDIO_TIMEOUT)

定义Audio模块的DBG开关:

#define TLKMMI_AUDIO_DBG_FLAG         (TLKMMI_AUDIO_DBG_ENABLE | TLKAPI_DBG_FLAG_ALL)

定义Audio模块的DBG标识符:

#define TLKMMI_AUDIO_DBG_SIGN         "[MMI]"

定义Audio模块的进程ID:

#define TLKMMI_AUDIO_PROCID           TLKTSK_PROCID_AUDIO

数据结构:

typedef enum{
  TLKPTI_AUD_OPTYPE_NONE = 0,
  TLKPTI_AUD_OPTYPE_TONE,
  TLKPTI_AUD_OPTYPE_PLAY,
  TLKPTI_AUD_OPTYPE_HF,
  TLKPTI_AUD_OPTYPE_AG,
  TLKPTI_AUD_OPTYPE_SCO, 
  TLKPTI_AUD_OPTYPE_SRC, 
  TLKPTI_AUD_OPTYPE_SNK, 
  TLKPTI_AUD_OPTYPE_UAC,
  TLKPTI_AUD_OPTYPE_MAX,
 } TLKPTI_AUD_OPTYPE_ENUM;
  • TLKPTI_AUD_OPTYPE_TONE:播放本地弹出提示音
  • TLKPTI_AUD_OPTYPE_PLAY:播放本地音乐
  • TLKPTI_AUD_OPTYPE_HF:HF主动发起链接手机
  • TLKPTI_AUD_OPTYPE_AG:AG主动发起链接耳机
  • TLKPTI_AUD_OPTYPE_SCO:控制SCO链路建立的各个状态,主要在语音识别和电话中使用。
  • TLKPTI_AUD_OPTYPE_SRC:链接耳机,并发送音乐数据给耳机
  • TLKPTI_AUD_OPTYPE_SNK:链接手机,并接受手机的音乐数据
  • TLKPTI_AUD_OPTYPE_UAC: USB Audio Class,USB音频设备类,实现对外接音频操作

接口定义:

这个接口主要有以下功能:

int tlkmmi_audio_init(void);
a) codec的初始化,包含对MIC和SPK的初始化

b) Audio task的初始化

c) Audio 用户信息模块的初始化

d) Audio 控制模块的初始化

e) Audio 状态控制初始化

f) Audio Timer的初始化

查询当前是否有Audio的操作属性行为在执行:

bool tlkmmi_audio_isBusy(void);

根据handle断开对应audio的连接

void tlkmmi_audio_disconn(uint16 handle);

开始 Audio操作

void tlkmmi_audio_start(void);

关闭 AUdio操作

void tlkmmi_audio_close(void);

将当前的Audio操作插入到Audio状态管理数组中:

extern int tlkmmi_audio_insertItem(uint16 aclHandle, uint08 optype);

将当前的Audio操作从Audio状态管理数组中删除:

extern int tlkmmi_audio_removeItem(uint16 aclHandle, uint08 optype)

查询当前本地播放状态:

extern bool tlkmmi_audio_isLocalPlay(void);

停止当前本地播放:

extern void tlkmmi_audio_stopLocalPlay(void);

Audio全局变量:

uint16 gTlkMmiAudioCurHandle;
uint08 gTlkMmiAudioCurOptype;
uint08 gTlkMmiAudioTmrState = 0;
uint08 gTlkMmiAudioTmrCount = 0;
tlkapi_timer_t gTlkMmiAudioCurTimer;
tlkapi_timer_t gTlkMmiAudioIrqTimer;
static uint08 sTlkMmiAudioCodecIdleTmrCount = 0;
  • gTlkMmiAudioCurHandle: 当前正在执行的audio操作对应的handle句柄
  • gTlkMmiAudioCurOptype: 当前的执行的Audio行为
  • gTlkMmiAudioTmrState: 标记Timer的状态,0 --表示Idle状态, 2--表示运行状态
  • gTlkMmiAudioTmrCount: 记录Audio Timer的数量
  • gTlkMmiAudioCurTimer: 标记当前的Timer
  • gTlkMmiAudioIrqTimer: 事件处理的Timer
  • sTlkMmiAudioCodecIdleTmrCount: 记录codec的待机时长
void timer0_irq_handler(void)

a. tlkmmi_audio_modinfHandler(gTlkMmiAudioCurOptype) 中断处理函数会去启动当前执行动作的Handler。处理接下来的行为,比如Stream Audio数据等等

b. 重新启动一个Timer0来触发Timer0的中断

(2) 控制接口说明

主要用来描述Audio控制中的数据结构和接口设计以及接口设计元素的详细说明。其定义本地播放,提示音,A2DP Source和A2DP Sink的调用接口和音量音乐状态查询的接口,另外有回调函数来监控底层profile上报的状态等。

Audio Ctrl设计

宏定义:

定义Tone的音量步长:

#define TLKMMI_AUDIO_VOLUME_TONE_STEP         5

定义Music的音量步长:

#define TLKMMI_AUDIO_VOLUME_MUSIC_STEP        3

定义Voice的音量步长:

#define TLKMMI_AUDIO_VOLUME_VOICE_STEP        6

定义Headset的音量步长:

#define TLKMMI_AUDIO_VOLUME_HEADSET_STEP      6

数据结构:

typedef enum{
        TLKMMI_AUDIO_REPORT_BUSY_NONE     = 0x0000,
        TLKMMI_AUDIO_REPORT_BUSY_PROGR100 = 0x0001,
        TLKMMI_AUDIO_REPORT_BUSY_PROGR000 = 0x0002,
        TLKMMI_AUDIO_REPORT_BUSY_PROGRESS = 0x0004,
        TLKMMI_AUDIO_REPORT_BUSY_SONG_CHANGE   = 0x0800,
        TLKMMI_AUDIO_REPORT_BUSY_STATE_CHANGE = 0x0010,
        TLKMMI_AUDIO_REPORT_BUSY_VOLUME_CHANGE = 0x0020,
}TLKMMI_AUDIO_REPORT_BUSYS_ENUM;
  • TLKMMI_AUDIO_REPORT_BUSY_PROGR100: 音乐进度条上报事件
  • TLKMMI_AUDIO_REPORT_BUSY_PROGR000: 音乐进度条上报事件
  • TLKMMI_AUDIO_REPORT_BUSY_PROGRESS: 音乐进度条上报事件
  • TLKMMI_AUDIO_REPORT_BUSY_SONG_CHANGE:用来标记音乐变化的事件
  • TLKMMI_AUDIO_REPORT_BUSY_STATE_CHANGE:标记播放状态变化
  • TLKMMI_AUDIO_REPORT_BUSY_VOLUME_CHANGE:标记播放音量变化的事件

typedef struct{
    uint16 busys;
    uint16 timeout;
    uint16 interval;
    uint16 newIndex;
    uint08 enable;
    uint08 reserve;
    uint08 volType;
    uint08 volValue;
    uint08 statusChn0;
    uint08 statusVal0;
    uint08 statusChn1;
    uint08 statusVal1;
}tlkmmi_audio_report_t;
- busys: 标记report的执行状态,用来设置当前未执行action,和timer配合使用 - timeout:设置的timer的timeout - interval:标记report的执行间隔 - newIndex:新的音乐索引 - enable:是否使能 - volType:音量类型 - volValue:音量值 - statusChn0:通道0的音频状态 - statusVal0: 通道0的状态值 - statusChn1: 通道1的音频状态 - statusVal1: 通道1的状态值

typedef struct{
        uint16 mp3Timeout;
        uint16 mp3Interval;
}tlkmmi_audio_update_t;
  • mp3Timeout:MP3的timeout时间
  • mp3Interval:MP3的时间间隔,该数据结构用来更新上面的timeout和时间间隔
typedef struct{
    tlkapi_timer_t timer;
    tlkapi_queue_t procs;
    tlkmmi_audio_report_t report;
}tlkmmi_audio_ctrl_t;
  • timer:用来控制report的各个事件的执行
  • proces: 用来控制report的工作队列的执行
  • report:需要被执行的report事件,用来控制report的各个事件
tlkmmi_audio_ctrl_t gTlkMmiAudioCtrl;

gTlkMmiAudioCtrl:Audio 控制块

外部接口:

int tlkmmi_audio_ctrlInit(void);

a) 初始化Audio控制块

b) 注册Audio相关的回调函数

c) 初始化Audio控制的相关timer及Queue

d) 对每一个Audio的操作设置初始化音量

e) 初始化report相关的数据

为Audio操作获取音频类型及操作句柄

void tlkmmi_audio_validOptype(uint08 *pOptype, uint16 *pHandle);
bool tlkmmi_audio_startPlay(uint16 fileIndex);

a) 执行A2dp的start流程

b) 设置播放音乐的index

c) 将播放任务添加到Audio任务状态控制数组中

void tlkmmi_audio_closePlay(void);

a) suspend A2dp

b) 将音乐播放从Audio任务控制数组中删除

c) 保留audio 信息

配置当前Audio快进或快退播放:

bool tlkmmi_audio_fastPlay(bool isRewind, bool isStart)

判断是否是本地播放:

bool tlkmmi_audio_isLocalPlay(void);

停止本地播放:

void tlkmmi_audio_stopLocalPlay(void);

播放下一首:

bool tlkmmi_audio_playNext(void);

a) 切换播放下一首的信息

b) 将播放任务添加到Audio任务状态控制中

播放上一首:

bool tlkmmi_audio_playPrev(void);

a) 切换播放上一首得信息

b) 将播放任务添加到Audio任务状态控制中

开始播放提示音:

bool tlkmmi_audio_startTone(uint16 fileIndex, uint16 playCount);

a) 设置tone音相关参数

b) 将播放任务添加到Audio任务状态控制中

停止播放Tone音,将播放任务添加到Audio任务状态控制中:

void tlkmmi_audio_stopTone(void);

调高音量,输入参数是Channel,用来查找当前得Audio执行的Action

bool tlkmmi_audio_volumeInc(uint08 channel);

调低音量,输入参数是Channel,用来查找当前得Audio执行的Action

bool tlkmmi_audio_volumeDec(uint08 channel);

获取指定channel对应的Audio action的音量:

int  tlkmmi_audio_getVolume(uint08 channel, uint08 *pVolume);

设置音量(channel对应的Audio action):

bool tlkmmi_audio_setVolume(uint08 channel, uint08 volume, bool isStep, bool isDec);

使能本地播放音乐信息上报:

int  tlkmdi_audio_setReport(uint08 enable, uint16 interval);

查找当前Audio action的Channel:

int tlkmmi_audio_getCurChannel(uint08 *pChannel);

通过Channel查找Audio的操作类型:

int tlkmmi_audio_channelToOptype(uint08 channel, uint08 *pOptype);

通过Audio的操作类型查找Channel:

int tlkmmi_audio_optypeToChannel(uint08 optype, uint08 *pChannel);

切换新旧Audio任务:

void tlkmmi_audio_optypeChanged(uint08 newOptype, uint16 newHandle, uint08 oldOptype, uint16 oldHandle);

(3) 信息接口说明

主要用来描述Audio Info中的数据结构和接口设计以及接口设计元素的详细说明。用来管理Audio信息的更新上报,管理音量的改变和Audio播放状态。同时还会管理Audio信息的存储和加载。

Audio Info设计

宏定义:

定义Audio音量存储的标志:

#define TLKMDI_AUDIO_VOLUME_SAVE_SIGN        0x3A

定义Audio音量存储的版本:

#define TLKMDI_AUDIO_VOLUME_SAVE_VERS        0x02

定义Audio音量存储的地址:

#define TLKMDI_AUDIO_VOLUME_SAVE_ADDR        TLK_CFG_FLASH_VOLUME_ADDR

定义Audio音量存储的大小:

#define TLKMDI_AUDIO_VOLUME_SAVE_SIZE        12

数据结构:

typedef struct{
    uint08 tone;
    uint08 music;
    uint08 voice;
    uint08 headset;
    uint08 resv001;
    uint08 enReport;
    uint16 interval;
    uint08 resv002[4];
}tlkmmi_audio_infoItem_t;
- tone: 标记提示音的音量 - music: 标记本地音乐的音量 - voice: 标记手机音乐的音量 - headset:标记耳机音乐的音量 - enReport: 使能audio信息上报 - interval: audio信息上报的间隔

typedef struct{
        uint08 isChange;
        uint08 reserved;
        uint16 interval;
        tlkapi_save_ctrl_t save;
        tlkmmi_audio_infoItem_t item;
}tlkmmi_audio_infoCtrl_t;
  • isChange:标记audio info是否已经改变
  • interval:事件上报间隔
  • save:存储的信息头
  • item:信息的集合

Audio信息块,用来存储信息,并表征信息是否需要存储或者更新或者查找和取出:

gTlkMmiAudioInfoCtrl;

用来控制Audio信息

接口定义:

初始化Audio控制信息:

int tlkmmi_audio_infoInit(void)

查询信息是否已经发生变化:

bool tlkmmi_audio_infoIsChange(void)

查询信息是否需要更新:

bool tlkmmi_audio_infoIsUpdate(void)

信息加载:

int tlkmmi_audio_infoLoad(void)

信息存储:

int tlkmmi_audio_infoSave(void)

获取当前Audio执行任务的音量:

int tlkmdi_audio_infoGetVolume(uint08 optype, uint08 *pVolume)

获取上报状态及上报条件:

int tlkmdi_audio_infoGetReport(uint08 *pEnable, uint16 *pInterval)

存储当前Audio任务的音量:

int tlkmdi_audio_infoSetVolume(uint08 optype, uint08 volume)

设置上报的条件:

int tlkmdi_audio_infoSetReport(uint08 enable, uint16 interval)

(4) 模块接口说明

主要用来描述Audio ModInf中的数据结构和接口设计以及设计元素的详细说明,这个模块定义了访问Audio的各个任务的使用接口,方便管理不同的功能调用,包括开始,结束,切换状态,中断处理等等。

Audio Modinf设计

数据结构:

定义了函数接口,这套函数接口包含:

typedef struct{
    int (*Start)(uint16 handle, uint32 param);
    int (*Close)(uint16 handle);
    void(*Timer)(void);
    bool(*FPlay)(bool isRewind, bool isStart); //FastPlay
    bool(*ToNext)(void);
    bool(*ToPrev)(void);
    bool(*Switch)(uint16 handle, uint08 status);
    bool(*IsBusy)(void);
    uint(*Intval)(void); //Interval
    bool(*IrqProc)(void);
}tlkmmi_audio_modinf_t;
  • Start:开始某个动作的接口
  • Close:结束某个动作的接口
  • Timer:对应动作的Timer
  • FPlay:是否是快速播放
  • ToNExt:下一个行为
  • ToPrev:上一个行为
  • Switch:状态转换函数接口
  • isBusy:查询是否有业务是否在执行
  • Intval:持续间隔的函数接口
  • IrqProc:事件处理函数接口

接口定义:

开始当前Audio执行操作:

int tlkmmi_audio_modinfStart(TLKMMI_AUDIO_OPTYPE_ENUM optype, uint16 handle, uint32 param)

结束当前的Audio执行动作:

int tlkmmi_audio_modinfClose(TLKMMI_AUDIO_OPTYPE_ENUM optype, uint16 handle)

Audio任务执行的Timer:

void tlkmmi_audio_modinfTimer(TLKMMI_AUDIO_OPTYPE_ENUM optype)

配置当前Audio快近或快退播放:

bool tlkmmi_audio_modinfFPlay(TLKPTI_AUD_OPTYPE_ENUM optype, bool isRewind, bool isStart)

开始Audio的下一首:

bool tlkmmi_audio_modinfToNext(TLKMMI_AUDIO_OPTYPE_ENUM optype)

开始Audio的上一首:

bool tlkmmi_audio_modinfToPrev(TLKMMI_AUDIO_OPTYPE_ENUM optype)

查询当前Audio操作是否处于Running状态:

bool tlkmmi_audio_modinfIsBusy(TLKMMI_AUDIO_OPTYPE_ENUM optype)

获取当前Audio操作的执行的Interval:

uint tlkmmi_audio_modinfIntval(TLKMMI_AUDIO_OPTYPE_ENUM optype)

Audio状态转换函数:

bool tlkmmi_audio_modinfSwitch(TLKMMI_AUDIO_OPTYPE_ENUM optype, uint16 handle, uint08 status)

Audio执行的处理函数:

Bool tlkmmi_audio_modinfIrqProc(TLKMMI_AUDIO_OPTYPE_ENUM optype)

查询当前的Audio执行行为的属性:

const tlkmmi_audio_modinf_t *tlkmmi_audio_getModinf(TLKMMI_AUDIO_OPTYPE_ENUM optype)

(5) 调度接口说明

主要用来描述Audio Status中的数据结构和接口设计以及接口设计元素的详细说明,这个模块主要是用来设计各个不同Audio任务在任务队列之间的执行策略,比如将当前的Audio任务插入到队列中的什么位置,位置决定了当前任务是否立即执行或者延后执行,或者用什么策略启动任务的执行。

Audio SCH设计

宏定义:

Audio的任务队列的最大数:

#define TLKMMI_AUDIO_STATUS_ITEM_NUMB       4 

定义无效的Audio handle:

#define TLK_INVALID_HANDLE           0xFFFF

数据结构:

typedef enum{
    TLKMMI_AUDIO_PRIORITY_LEVEL0 = 0,
    TLKMMI_AUDIO_PRIORITY_LEVEL1,
    TLKMMI_AUDIO_PRIORITY_LEVEL2,
    TLKMMI_AUDIO_PRIORITY_LEVEL3,
    TLKMMI_AUDIO_PRIORITY_LEVEL4,
    TLKMMI_AUDIO_PRIORITY_LEVEL5,
    TLKMMI_AUDIO_PRIORITY_LEVEL6,
    TLKMMI_AUDIO_PRIORITY_LOWEST = TLKMMI_AUDIO_PRIORITY_LEVEL0,

    TLKMMI_AUDIO_SCO_PRIORITY  = TLKMMI_AUDIO_PRIORITY_LEVEL6,
    TLKMMI_AUDIO_HFP_PRIORITY  = TLKMMI_AUDIO_PRIORITY_LEVEL5,
    TLKMMI_AUDIO_UAC_PRIORITY  = TLKMMI_AUDIO_PRIORITY_LEVEL3,
    TLKMMI_AUDIO_SRC_PRIORITY  = TLKMMI_AUDIO_PRIORITY_LEVEL2,
    TLKMMI_AUDIO_SNK_PRIORITY  = TLKMMI_AUDIO_PRIORITY_LEVEL1,
    TLKMMI_AUDIO_TONE_PRIORITY = TLKMMI_AUDIO_PRIORITY_LEVEL4,
    TLKMMI_AUDIO_PLAY_PRIORITY = TLKMMI_AUDIO_PRIORITY_LEVEL0,
}TLKMMI_AUDIO_PRIORITY_ENUM;

定义事件处理的优先级

typedef struct{
        uint08 optype;
        uint08 priority;
        uint08 isMutex;
        uint08 isFirst;
}tlkmmi_audio_schParam_t;
  • optype:定义Audio任务类型,具体类型参考:TLKMMI_AUDIO_OPTYPE_ENUM
  • priority:Audio任务的优先级,参考TLKMMI_AUDIO_PRIORITY_ENUM
  • isMutex:是否支持Audio任务的抢占:如果为True,当有高优先级的任务插入时,当前的任务将会被挂起,直到高优先级任务执行完,重新启动执行;如果为False,当有高有些高优先级的任务插入时,当前任务将会被停止(从状态表中移除)。
  • isFirst:首次插入是否必须为可执行状态:如果为True,当列表中有高优先级任务时,则插入失败;如果为False,当列表中有高优先级的任务时,则会寻找合适位置插入。
typedef struct{
        uint08 optype;
        uint08 priority;
        uint16 handle;
}tlkmmi_audio_schItem_t;
  • optype:Audio任务类型,参考:TLKMMI_AUDIO_OPTYPE_ENUM
  • priority:Audio任务类型的优先级
  • Handle: Audio任务的handle,主要用来表征一个Audio的任务
typedef struct{
        uint08 nowNumb;
        tlkmmi_audio_schItem_t item[TLKMMI_AUDIO_STATUS_ITEM_NUMB];
}tlkmmi_audio_schCtrl_t;
  • nowNumb:当前Audio任务的数量
  • tlkmmi_audio_schItem_t item[TLKMMI_AUDIO_STATUS_ITEM_NUMB]:Audio任务队列

接口设计:

初始化Audio任务控制全局变量:

int tlkmmi_audio_schInit(void)

查找当前执行的任务类型:

int tlkmmi_audio_itemIndex(uint16 handle, uint08 optype)

根据任务类型查找当前任务在Audio任务队列的位置

int tlkmmi_audio_itemIndexByOptype(uint08 optype);

int tlkmmi_audio_insertItem(uint16 handle, uint08 optype)

a) 查询当前的Audio任务是否已经在队列;

b) 查找当前的Audio任务执行函数;

c) 将当前的任务执行函数按照优先级和可否被抢占和是是否插入队头的判断,将任务插入到任务队列中;

d) 启动Audio任务的状态函数执行;

e) 最终会启动timer执行handler函数。

int tlkmmi_audio_removeItem(uint16 handle, uint08 optype)

a) 将当前执行的任务停止,并从任务队列删除;

b) 将次优先级的任务函数启动,执行状态函数修改状态,并开始执行handler。

验证两个音频任务的优先级:

bool tlkmmi_audio_itemIsSwitch(uint08 dstOptype, uint08 srcOptype);

根据任务类型删除指定的任务队列:

void tlkmmi_audio_removeItemByOptype(uint08 optype)

根据任务的handle删除指定的任务队列:

void tlkmmi_audio_removeItemByHandle(uint16 handle)

打印任务列表中所有激活的任务:

void tlkmmi_audio_printfList(void)

查询当前执行的任务类型:

int  tlkmmi_audio_getCurOptype(void)

查询当前的Audio任务队列是否有指定的任务:

bool tlkmmi_audio_isHaveOptype(uint08 optype)

查找当前执行的任务:

tlkmmi_audio_schItem_t *tlkmmi_audio_getCurItem(void)

查找次高优先级的执行任务:

tlkmmi_audio_schItem_t *tlkmmi_audio_getSndItem(void)

流程示例

下图是各个流程的子序列图

(1) 获取音频音量

查询音频音量

设置音量功能,调节音量功能和上面的流程类似,可以参考上面流程。

(2) 查询音频状态

查询Audio状态

查询进度条功能,查询播放时长功能,查询歌曲名称功能,查询歌手功能,查询播放模式功能,设置播放模式功能,和查询Audio状态功能类似,可以参考。

(3) 上报音频状态

设置上报audio info

取消Audio信息上报功能和上述流程类似,可以参考上面流程。

(4) 播放本地音乐

Audio play start

(5) 暂停本地音乐

Audio play stop

Play next和Play prev流程和上面的Play执行流程是类似的,请参考以上描述。

新增模块

本节将会就新增音频模块的步骤和注意事项做详细讲解:

  1. 步骤一,对新增音频模块进行命名,并添加到音频列表(TLKPTI_AUD_OPTYPE_ENUM)中。注意添加的位置,这与后面的接口调用保持一致。
  2. 步骤二,对照系统已有的接口编写方法和说明,自行编写一套接口,并按照步骤一中的位置偏移,将接口嵌入到音频接口列表(spTlkMmiAudioModinfs)中。
  3. 步骤三,参考系统中对状态机配置项的描述,自行将待添加的音频调度机制,放置于状态机列表(spTlkMMidAudioSchList)中的相应位置(步骤一中的位置)。
  4. 步骤四,在"tlkmmi_audio_optypeToChannel"和"tlkmmi_audio_channelToOptype"的接口中,将步骤一的音频类型与协议类型进行绑定。

Phone设计与实现

Phone模块的设计和实现为应用程序提供了使用电话功能的接口,能够通过这一层接口发起拨打电话,关断电话,接听电话,重播电话等功能。并收到对应的执行状态上报给应用层。根据功能需求,需要从三个方面去描述Phone的设计实现,第一部分为组织结构,第二部分为重要流程,第三部分为Phone模块设计。

组织结构

因为Phone是面向用户的设计,需要接口满足客户的全部需求且要满足易用性,本质上它需要提供。根据设计需求,需要将Phone模块分为三个子模块分别为:Phone模块,Call Status和PBAP,且各模块对应的功能如下所示:

  • Phone:提供应用程序的初始化接口
  • Call Status:这个模块主要监听MDI模块操作电话功能的各个状态
  • PBAP:主要提供电话簿的查询,上报功能

Phone组织结构

重要流程

Phone的流程图

Phone的流程包含了语音拨打电话,重拨电话,接听电话,拒接电话和挂断电话。同时也有电话簿获取的逻辑流程。以及将各个流程对应的状态通过串口模块发送给应用。

Phone的流程图

Phone初始化流程序列图:

Phone初始化

Phone通话相关流程序列图

Phone通话相关流程序列图

重播电话,挂断等功能流程和上边拨打电话类似,不再重复。

Phone模块设计

(1) Phone接口设计

主要用来描述Phone中的接口设计以及接口设计元素的详细说明,提供Phone模块的初始化功能,配置资源等。

Phone接口设计

宏定义:

定义Phone DBG info的开关:

#define TLKMMI_PHONE_DBG_FLAG         (TLKMMI_PHONE_DBG_ENABLE | TLKAPI_DBG_FLAG_ALL)

定义phone DBG info的标识符:

#define TLKMMI_PHONE_DBG_SIGN         "[MMI]"

主要接口:

int tlkmmi_phone_init(void)

a) Phone Ctrl模块初始化

b) Phone Comm模块初始化

c) Phone Book模块初始化

d) Phone Status模块初始化

(2) Phone串口模块设计

主要用来描述Phone串口中的接口设计以及接口设计元素的详细说明。应用通过串口可以发送命令和接收命令响应和事件等,实现Phone得执行动作和执行状态上报。

Phone串口模块设计

主要接口:

注册回调函数:

int tlkmmi_phone_commInit(void)

(3) Phone Book模块设计

主要用来描述PhoneBook中的数据结构和接口设计以及设计元素的详细说明,提供电话本的查询,存储和擦除等接口,以及PhoneBook的结果的上报等。

Phone Book模块设计

宏定义:

定义通讯录姓名的最大长度:

#define TLKMMI_PHONE_NAME_MAX_LEN 32

定义电话簿的包头长度:

#define TLKMMI_PHONE_BOOK_HEADLEN 16

定义电话簿的初始化标识符:

#define TLKMMI_PHONE_BOOK_FLAG_INIT 0xFF

定义电话簿的写标识符:

#define TLKMMI_PHONE_BOOK_FLAG_WRITE 0xFE

定义电话簿的完成标识符:

#define TLKMMI_PHONE_BOOK_FLAG_COMPLATE 0xF0

定义电话簿的存储标识符:

#define TLKMMI_PHONE_BOOK_SIGN 0x50424150 

定义电话簿的存储记录长度:

#define TLKMMI_PHONE_BOOK_ITEM_LENGTH (BTP_PBAP_CALL_NAME_LENGTH2+BTP_PBAP_CELL_NUMB_LENGTHBTP_PBAP_CELL_NUMB_COUNT)

定义电话簿的存储记录的条数:

#define TLKMMI_PHONE_BOOK_MAX_ITEM_NUMB ((TLK_CFG_FLASH_PBAP_LIST_LENS-32)/TLKMMI_PHONE_BOOK_ITEM_LENGTH)

数据结构:

typedef struct{
        uint32 pbapSign;
        uint08 btAddr[6];
        uint08 itemLens;
        uint08 callFlag;
        uint32 callNumb;
}__attribute__ ((__packed__)) tlkmmi_phoneBookHead_t;
  • pbapSign: pbap业务标记
  • btAddr: 蓝牙地址
  • itemLens: pbap内容长度
  • callFlag: 执行状态
  • callNumb: 个数
typedef struct{
        uint08 isBusy;
        uint08 isReady;
        uint16 aclHandle;
        uint08 btAddr[6];
        uint32 bookCount;
        tlkapi_timer_t timer;
}tlkmmi_phoneBookCtrl_t;
  • isBusy: 是否正在执行
  • isReady: 是否Ready
  • aclHandle: Acl的标记
  • btAddr: 蓝牙地址
  • bookCount: pbap的个数
  • timer: 执行timer

接口定义:

下面接口有以下几个功能:

int tlkmmi_phone_bookInit(void)

a) 初始化sTlkMmmiPhoneBookCtrl

b) 注册回调函数

c) 从Flash中读取falsh信息

判断当前Pbap info是否有效:

bool tlkmmi_phone_bookInfoValid(uint08 *pBtAddr)

擦除Pbap信息:

void tlkmmi_phone_bookCleanInfo(void)

同步Pbap信息:

int tlkmmi_phone_startSyncBook(uint16 aclHandle, uint08 *pBtAddr, bool isForce)

关闭pbap同步,并擦除之前的信息:

int tlkmmi_phone_closeBookSync(uint16 aclHandle)

提取pbap信息中的name:

int tlkmmi_phone_bookGetName(uint08 *pNumber, uint08 numbLen, uint08 *pName, uint08 nameLen, uint08 *pGetLen);

(4) Pbap Status模块定义

主要用来描述Phone Status中的接口设计以及设计元素的详细说明,主要提供一些执行动作的状态回调事件,电话开始状态上报,电话响铃状态上报,电话接通状态上报,电话挂断状态上报。

Pbap Status模块定义

接口定义:

注册Call Evt的回调函数:

int tlkmmi_phone_statusInit(void)

MDI设计说明

MDI,模块设计接口,是承接MMI与Platform之间的桥梁,负责将Platform中的功能单元进行封装和综合管理,形成功能模块,为MMI提供简易化的调用接口。

系统模块设计

Comm设计与实现

(1) 模块简介

串口主要用来人机交互或者和客户端的app交互,通过串口交互cmd和Rsp和事件等等,来触发执行不同的流程,让整个SDK执行用户的行为动作,比如查询,链接和播放音乐等。

(2) 接口说明

宏定义:

定义支持的数据通道最大个数:

#define TLKMDI_COMM_DATA_CHANNEL_MAX       8

定义回调函数类型:

定义command回调函数的类型:

typedef void(*tlkmdi_comm_cmdCB)(uint08 msgID, uint08 *pData, uint08 dataLen)

定义data回调函数的类型:

typedef void(*tlkmdi_comm_datCB)(uint16 datID, uint16 number, uint08 *pData, uint08 dataLen)

接口函数:

初始化串口, 注册串口回调函数:

int tlkmdi_comm_init(void)

串口事件handler:

void tlkmdi_comm_handler(void)

注册串口数据回调函数:

int  tlkmdi_comm_regDatCB(uint16 datID, tlkmdi_comm_datCB datCB, bool isForce)

注册系统回调函数:

void tlkmdi_comm_regSysCB(tlkmdi_comm_cmdCB cmdCB)

注册BT回调函数:

void tlkmdi_comm_regBtCB(tlkmdi_comm_cmdCB cmdCB)

注册BLE回调函数:

void tlkmdi_comm_regLeCB(tlkmdi_comm_cmdCB cmdCB)

注册debug回调函数:

void tlkmdi_comm_regDbgCB(tlkmdi_comm_cmdCB cmdCB)

注册文件操作回调函数:

void tlkmdi_comm_regFileCB(tlkmdi_comm_cmdCB cmdCB)

注册电话回调函数:

void tlkmdi_comm_regCallCB(tlkmdi_comm_cmdCB cmdCB)

注册Audio回调函数:

void tlkmdi_comm_regAudioCB(tlkmdi_comm_cmdCB cmdCB)

发送系统命令:

int tlkmdi_comm_sendSysCmd(uint08 cmdID, uint08 *pData, uint08 dataLen)

发送系统response:

int tlkmdi_comm_sendSysRsp(uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送系统事件:

int tlkmdi_comm_sendSysEvt(uint08 evtID, uint08 *pData, uint08 dataLen)

发送BT的cmd:

int tlkmdi_comm_sendBtCmd(uint08 cmdID, uint08 *pData, uint08 dataLen)

发送BT的RSP:

int tlkmdi_comm_sendBtRsp(uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送BT的EVT:

int tlkmdi_comm_sendBtEvt(uint08 evtID, uint08 *pData, uint08 dataLen)

发送BLE的command:

int tlkmdi_comm_sendLeCmd(uint08 cmdID, uint08 *pData, uint08 dataLen)

发送BLE的Resp:

int tlkmdi_comm_sendLeRsp(uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送BLE EVT:

int tlkmdi_comm_sendLeEvt(uint08 evtID, uint08 *pData, uint08 dataLen)

发送File命令:

int tlkmdi_comm_sendFileCmd(uint08 cmdID, uint08 *pData, uint08 dataLen)

发送File Resp:

int tlkmdi_comm_sendFileRsp(uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送File Evt:

int tlkmdi_comm_sendFileEvt(uint08 evtID, uint08 *pData, uint08 dataLen)

发送Call的command:

int tlkmdi_comm_sendCallCmd(uint08 cmdID, uint08 *pData, uint08 dataLen);

发送电话Resp:

int tlkmdi_comm_sendCallRsp(uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送电话的EVT:

int tlkmdi_comm_sendCallEvt(uint08 evtID, uint08 *pData, uint08 dataLen)

发送Audio command:

int tlkmdi_comm_sendAudioCmd(uint08 cmdID, uint08 *pData, uint08 dataLen)

发送Audio Resp:

int tlkmdi_comm_sendAudioRsp(uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送Audio Evt:

int tlkmdi_comm_sendAudioEvt(uint08 evtID, uint08 *pData, uint08 dataLen)

发送命令:

int tlkmdi_comm_sendCmd(uint08 mType, uint08 cmdID, uint08 *pData, uint08 dataLen)

发送resp:

int tlkmdi_comm_sendRsp(uint08 mType, uint08 cmdID, uint08 status, uint08 reason, uint08 *pData, uint08 dataLen)

发送evt:

int tlkmdi_comm_sendEvt(uint08 mType, uint08 msgID, uint08 *pData, uint08 dataLen)

发送数据:

int tlkmdi_comm_sendDat(uint08 datID, uint16 numb, uint08 *pData, uint16 dataLen)

Key设计与实现

(1) 功能描述

key接口设计主要为用户提供了统一的按键接口,方便用户直接使用。按键接口实现了单击、双击、长、短按和组合按键等功能,能满足用户各种使用场景。可以对单个按键插入、删除,具有简单易懂,使用灵活,功能完善等特点。

(2) 规则约束

按键使用过程中,要注意以下事项:

  • 用户根据实际应用修改按键数(TLKMDI_KEY_MAX_NUMB);
  • 按键已经在mdi层被初始化,不需要再次调用初始化函数;
  • keyID、evtMsk参数必须大于0;
  • 按键在调用插入函数时已经被使能,按下可以被正常检测到,当该按键不在使用时删除该按键即可。

(3) 接口说明

宏定义:

定义按键的超时时间(按键扫描周期):

#define TLKMDI_KEY_TIMEOUT0               100000

定义按键的超时时间(按键扫描周期):

#define TLKMDI_KEY_TIMEOUT1               10000

定义按键数量:

#define TLKMDI_KEY_MAX_NUMB              2

定义单击最小窗口时间:

#define TLKMDI_KEY_CLICK_TIME_MIN      50000

定义单击最大窗口时间

#define TLKMDI_KEY_CLICK_TIME_MAX        500000

定义短按最小窗口时间

#define TLKMDI_KEY_SHORT_TIME_MIN        500000 

定义短按最长窗口时间

#define TLKMDI_KEY_SHORT_TIME_MAX        2500000

定义长按最小窗口时间

#define TLKMDI_KEY_LONG_TIME_MIN         2500000

定义长按最大窗口时间

#define TLKMDI_KEY_LONG_TIME_MAX         5000000

定义双击间隔时间

#define TLKMDI_KEY_DCLICK_INTV_MAX       500000

类型定义:

定义按键事件:

typedef enum{
    TLKMDI_KEY_EVTID_NONE    = 0x00,
    TLKMDI_KEY_EVTID_PRESS   = 0x01,
    TLKMDI_KEY_EVTID_RELEASE = 0x02,
    TLKMDI_KEY_EVTID_CLICK   = 0x03,
    TLKMDI_KEY_EVTID_SHORT   = 0x04,
    TLKMDI_KEY_EVTID_LONG    = 0x05,
    TLKMDI_KEY_EVTID_DCLICK  = 0x06,
}TLKMDI_KEY_EVTID_ENUM;
  • 按键没有按下 0x00
  • 按键按下事件 0x01
  • 按键松开事件 0x02
  • 按键单击事件 0x03
  • 按键短按事件 0x04
  • 按键长按事件 0x05
  • 按键双击事件 0x06

定义要捕获的按键类型:

typedef enum{
    TLKMDI_KEY_EVTMSK_NONE    = 0x00,
    TLKMDI_KEY_EVTMSK_PRESS   = 0x01,
    TLKMDI_KEY_EVTMSK_RELEASE = 0x02,
    TLKMDI_KEY_EVTMSK_CLICK   = 0x04,
    TLKMDI_KEY_EVTMSK_SHORT   = 0x08,
    TLKMDI_KEY_EVTMSK_LONG    = 0x10,
    TLKMDI_KEY_EVTMSK_DCLICK  = 0x20,
    TLKMDI_KEY_EVTMSK_DEFAULT = TLKMDI_KEY_EVTMSK_CLICK | TLKMDI_KEY_EVTMSK_SHORT | TLKMDI_KEY_EVTMSK_LONG |TLKMDI_KEY_EVTMSK_DCLICK,
    TLKMDI_KEY_EVTMSK_ALL     = TLKMDI_KEY_EVTMSK_CLICK | TLKMDI_KEY_EVTMSK_SHORT | TLKMDI_KEY_EVTMSK_LONG | TLKMDI_KEY_EVTMSK_DCLICK | TLKMDI_KEY_EVTMSK_PRESS | TLKMDI_KEY_EVTMSK_RELEASE,
}TLKMDI_KEY_EVTMSK_ENUM;
  • 无 0x00
  • 捕获按下事件 0x01
  • 捕获松开事件 0x02
  • 捕获单击事件 0x04
  • 捕获短按事件 0x08
  • 捕获长按事件 0x10
  • 捕获双击事件 0x20
  • 捕获默认事件 0x3c
  • 捕获所有事件 0x3f

定义按键标志位:

typedef enum{
    TLKMDI_KEY_FLAG_NONE = 0x00,
    TLKMDI_KEY_FLAG_PRESS = 0x01,
    TLKMDI_KEY_FLAG_RELEASE = 0x02,
}TLKMDI_KEY_FLAGS_ENUM;
  • 无 0x00
  • 按下 0x01
  • 松开 0x02

数据结构:

定义单个按键按下事件的特征:

typedef struct{
    uint08 keyID;
    uint08 flags;
    uint08 level;
    uint08 evtMsk;
    uint32 ioPort;
    uint32 preTimer;
    uint32 curTimer;
    TlkMdiKeyEventCB evtCB;
}tlkmdi_key_unit_t;
  • keyID 按键编号
  • flags 参照按键标志位
  • level 按键按下时的电平
  • evtMsk 需要捕获的事件
  • ioPort 按键io
  • preTimer 当前按下时间
  • curTimer
  • evtCB 捕获事件触发后的回调函数

定义多个按键:

typedef struct{
        tlkapi_timer_t timer;
        tlkmdi_key_unit_t unit[TLKMDI_KEY_MAX_NUMB];
}tlkmdi_key_ctrl_t;
  • timer 按键对应的timer
  • unit[TLKMDI_KEY_MAX_NUMB] 多个按键的集合

接口定义:

初始化按键:

int tlkmdi_key_init(void);

添加一个按键:

int tlkmdi_key_insert(uint08 keyID, uint08 evtMsk, uint32 ioPort, uint08 level, uint08 upDown, TlkMdiKeyEventCB evtCB);

移除一个按键:

int tlkmdi_key_remove(uint08 keyID, uint08 upDown, bool enInput);

LED设计与实现

(1) 功能描述

led接口设计主要为用户提供统一的led接口,方便用户直接使用。led接口提供了普通IO和PWM两种类型的接口分别实现led的亮、灭、闪烁和呼吸效果。

(2) 规则约束

led使用过程中,要注意以下事项:

  • 用户根据实际应用修改按键数(TLKMDI_LED_NOR_MAX_NUMB 、TLKMDI_LED_PWM_MAX_NUMB )。
  • ledID必须大于0。
  • led已经在mdi层被初始化,不需要再次调用初始化函数。

(3) 接口说明

宏定义:

定义led的超时时间(led定时器周期)

#define TLKMDI_LED_TIMEOUT          20000

普通IO类型led的数量

#define TLKMDI_LED_NOR_MAX_NUMB     3

pwm类型led的数量

#define TLKMDI_LED_PWM_MAX_NUMB     2

pwm类型led的时钟

#define TLKMDI_LED_PWM_PCLK_SPEED   1000000

类型定义:

定义PWM时钟单位使时间

enum{
    TLKMDI_LED_PWM_CLOCK_1S  = TLKMDI_LED_PWM_PCLK_SPEED,
    TLKMDI_LED_PWM_CLOCK_1MS = (TLKMDI_LED_PWM_CLOCK_1S / 1000),
    TLKMDI_LED_PWM_CLOCK_1US = (TLKMDI_LED_PWM_CLOCK_1S / 1000000),
    TLKMDI_LED_PWM_CLOCK_10MS  = (TLKMDI_LED_PWM_CLOCK_1S / 100),
    TLKMDI_LED_PWM_CLOCK_100MS = (TLKMDI_LED_PWM_CLOCK_1S / 10),
    TLKMDI_LED_PWM_CLOCK_10US  = (TLKMDI_LED_PWM_CLOCK_1S / 100000),
    TLKMDI_LED_PWM_CLOCK_100US = (TLKMDI_LED_PWM_CLOCK_1S / 10000),
};
  • 1s 0x00
  • 1ms 0x01
  • 1us 0x02
  • 10ms 0x03
  • 100ms 0x04
  • 10us 0x05
  • 100us 0x06

数据定义:

定义普通IO的led灯点亮控制结构

typedef struct{
    uint08 ledID;
    uint08 level;
    uint16 count;
    uint08 resv0;
    uint08 isCtrl;
    uint08 isOpen;
    uint08 isKeep; //keep on when ctrl is over
    uint32 ioPort;
    uint32 onTimer;
    uint32 offTimer;
    uint32 runTimer;
}tlkmdi_led_nor_unit_t;
  • ledID
  • level 点亮led的有效电平
  • count 闪烁次数
  • resv0 预留位
  • isCtrl 当前led的被控制状态
  • isOpen 当前led的亮灭状态
  • isKeep 闪烁结束后需要保持的状态
  • ioPort ledIO
  • onTimer 亮灯时间
  • offTimer 灭灯时间
  • runTimer 运行的总时间
typedef struct{
    #if (TLKMDI_LED_NOR_MAX_NUMB != 0)
    tlkapi_timer_t norTimer;
    tlkmdi_led_nor_unit_t norUnit[TLKMDI_LED_NOR_MAX_NUMB];
    #endif
    #if (TLKMDI_LED_PWM_MAX_NUMB != 0)
    tlkapi_timer_t pwmTimer;
    tlkmdi_led_pwm_unit_t pwmUnit[TLKMDI_LED_PWM_MAX_NUMB];
    #endif
    #if ((TLKMDI_LED_NOR_MAX_NUMB != 0) && (TLKMDI_LED_PWM_MAX_NUMB != 0))
    uint32 reserve;
    #endif
}tlkmdi_led_ctrl_t;

接口定义:

初始化led

int tlkmdi_led_init(void);

添加一个led

int  tlkmdi_led_insert(uint08 ledID, uint32 ioPort, uint08 upDown, uint08 onLevel);

移除一个led

int  tlkmdi_led_remove(uint08 ledID, uint08 upDown, bool enOutput)int tlkmdi_key_remove(uint08 keyID, uint08 upDown, bool enInput);

获取当前led灯的亮灭状态

bool tlkmdi_led_isOn(uint08 ledID);

获取当前led灯的是否处于自动控制状态

bool tlkmdi_led_isAuto(uint08 ledID);

点亮当前led

bool tlkmdi_led_on(uint08 ledID);

关闭当前led

bool tlkmdi_led_off(uint08 ledID);

自动控制led的闪烁

bool tlkmdi_led_auto(uint08 ledID, uint08 firstOn, uint16 count, uint16 onTimerMs, uint16 offTimerMs, bool isKeepOn);

添加当前pwm类型的led

int tlkmdi_pwmled_insert(uint08 ledID, uint32 ioPort, uint08 pwmID, bool isInvert);

删除当前pwm类型的led

int tlkmdi_pwmled_remove(uint08 ledID, uint08 upDown, bool enOutput);

获取当前led的亮灭状态

bool tlkmdi_pwmled_isOn(uint08 ledID);

获取当前led的是否处于自动控制状态

bool tlkmdi_pwmled_isAuto(uint08 ledID);

点亮一个led

bool tlkmdi_pwmled_on(uint08 ledID, uint16 pwmPeriodUs, uint16 pwmDutyUs);

关闭一个led

bool tlkmdi_pwmled_off(uint08 ledID);

自动控制led的亮灭以及光强

bool tlkmdi_pwmled_auto(uint08 ledID, uint08 firstOn, uint16 count, uint16 onTimerMs, uint16 offTimerMs, uint16 pwmPeriodUs, uint16 pwmDutyUs, uint16 dutyFlushUs, uint08 stepUs, uint08 isKeepOn);

蓝牙模块设计

蓝牙模块是设备上业务执行的前提,它封装了蓝牙协议的发现和链接接口,为MMI提供了蓝牙的发现和链接以及回连等功能。这部分由三个部分组成,第一部分是发现;第二部分是链接;第三部分是回连。

BTINQ设计与实现

(1) 模块简介

这个模块主要用来封装Inquiry功能的接口,方便MMI层的访问,并且易于后续开发。他提供了使用Inquiry的一些基本策略,确保MMI层用户的各种行为得到正确的执行。

(2) 状态转换

发现模块设计了如下的状态机,由Idle、Opening、Closing、Inquiry和GetName这几部分组成。在Idle状态下,设备处于静默状态;Opening状态是当MMI发起Inquiry的请求时,需要向Controller去设置Inquiry流程所需要的参数,此时BTINQ模块的状态会从Idle进入到Opening。如果参数失败或者MMI层取消Inquiry,则会进入到Closing状态;Inquiry状态是Opening执行完,发起了Inquiry的命令后,会进入到这个状态,在该状态下,设备会去发起Inquiry,然后将查询到的设备上报,当发现设备的的个数达到上限或者Inquiry失败或者被取消,会结束Inquiry,就会进入到Closing状态。GetName状态是用来查询设备的名称,相比于Inquiry它是独立的过程,设备名称获取结束之后会进入到Closing状态。Closing状态是结束Opening、Inquiry和GetName时候会进入,然后会进入到Idle状态。

BTINQ状态转换

(3) 运行流程

发现的整个流程由INIT、Inquiry和GetName三部分组成,其流程图如下:

初始化完成后会接着判断GetName(设备名称的获取)流程是否开启,未开启该流程会紧接着判断inquiry(设备查询)流程是否开启,未开启Inquiry流程会重置该参数。Inquiry被触发进入Inquiry状态,在这个状态下,设备会不停的查询周边设备并上报。当Inquiry未结束,会一直执行设备查询。当Inquiry结束或者达到Inquiry的窗口大小则退出Inquiry的状态,并进入GetName状态,获取到设备名字时退出GetName状态再次进入Inquiry流程。

BTINQ流程图

发现的子序列图如下:

BTINQ发现的子序列图

(4) 接口说明

接口由四个部分组成,第一部分的模块名称;第二部分的设计分类,第三部分的设计细节和第四部分的细节释义。

MDI Inquiry设计

宏定义:

定义inquiry timeout:

#define TLKMDI_BTINQ_TIMEOUT        100000

定义inquiry最大次数:

#define TLKMDI_BTINQ_TIMEOUT_MS     (TLKMDI_BTINQ_TIMEOUT/1000)

定义getname执行的timeout:

#define TLKMDI_BTINQ_WAIT_GETNAME_TIMEOUT         (3000000/TLKMDI_BTINQ_TIMEOUT)

定义wait状态下的执行timeout:

#define TLKMDI_BTINQ_WAIT_CANCEL_TIMEOUT         (3000000/TLKMDI_BTINQ_TIMEOUT)

定义发现的设备名称长度:

#define TLKMDI_BTINQ_NAME_LENS      19

定义发现的设备个数:

#define TLKMDI_BTINQ_ITEM_NUMB      15

数据结构:

定义发现的设备类型:

typedef enum{
        TLKMDI_BTINQ_DTYPE_MISC     = 0x00,
        TLKMDI_BTINQ_DTYPE_PC       = 0x01,
        TLKMDI_BTINQ_DTYPE_PHONE    = 0x02,
        //TLKMDI_BTINQ_DTYPE_NET      = 0x03, 
        TLKMDI_BTINQ_DTYPE_HEADSET  = 0x04, 
        //TLKMDI_BTINQ_DTYPE_KEYBOARD = 0x05, 
        //TLKMDI_BTINQ_DTYPE_SPP      = 0x0F, 
        TLKMDI_BTINQ_DTYPE_UNKNOWN  = 0xFF, 
}TLKMDI_BTINQ_DTYPE_ENUM;
  • PC
  • Phone
  • Network_dev
  • Headset
  • Keyboard
  • Spp

Inquiry的各个状态:

typedef enum{        
        TLKMDI_BTINQ_STATE_IDLE = 0,
        TLKMDI_BTINQ_STATE_OPENING,
        TLKMDI_BTINQ_STATE_INQUIRY,
        TLKMDI_BTINQ_STATE_GETNAME,
        TLKMDI_BTINQ_STATE_PENDING, 
        TLKMDI_BTINQ_STATE_CLOSING, 
}TLKMDI_BTINQ_STATE_ENUM;
  • Idle
  • Opening
  • Inquiry
  • GetName
  • For Get Name
  • Closing

定义Inquiry特殊阶段下的action:

typedef enum{
 TLKMDI_BTINQ_STAGE_NONE = 0,
 TLKMDI_BTINQ_OPENING_STAGE_CLOSE_SCAN = 1,
 TLKMDI_BTINQ_OPENING_STAGE_SET_TIMEOUT,
TLKMDI_BTINQ_OPENING_STAGE_START_INQUIRY, 
TLKMDI_BTINQ_OPENING_STAGE_SET_SCHEDULE,
TLKMDI_BTINQ_INQUIRY_STAGE_CANCEL = 1,
TLKMDI_BTINQ_INQUIRY_STAGE_WAIT,
TLKMDI_BTINQ_INQUIRY_STAGE_START
TLKMDI_BTINQ_INQUIRY_STAGE_DOING,
TLKMDI_BTINQ_GETNAME_STAGE_CANCEL = 1
TLKMDI_BTINQ_GETNAME_STAGE_CLOSING,
TLKMDI_BTINQ_GETNAME_STAGE_WAITING,
TLKMDI_BTINQ_GETNAME_STAGE_OPENING,
TLKMDI_BTINQ_CLOSING_STAGE_CANCEL_GETNAME = 1,
 TLKMDI_BTINQ_CLOSING_STAGE_WAIT_GETNAME,
 TLKMDI_BTINQ_CLOSING_STAGE_CANCEL_INQUIRY,
 TLKMDI_BTINQ_CLOSING_STAGE_WAIT_INQUIRY,
TLKMDI_BTINQ_CLOSING_STAGE_INQUIRY_OVER,      
}TLKMDI_BTINQ_STAGE_ENUM;
  • Opening状态下的关闭扫描
  • Opening状态下的set timeout
  • Opening状态下start inquiry
  • Opening状态下设置schedule
  • Inquiry状态下取消
  • Inquiry状态下的Wait
  • Inquiry状态下Start
  • Inquiry状态下Doing
  • GetName状态下Cancel
  • GetName状态下Closing
  • GetName状态Waiting
  • GetName状态Opening
  • Closing状态下cancel getname
  • Closing状态下等待getName
  • Closing状态下取消Inquiry
  • Closing状态等待Inquiry
  • closing状态下inquiry over

定义Inquiry item状态:

typedef enum{
        TLKMDI_BTINQ_ITEM_STATE_NONE = 0,
        TLKMDI_BTINQ_ITEM_STATE_WAIT,
        TLKMDI_BTINQ_ITEM_STATE_OVER,
}TLKMDI_BTINQ_ITEM_STATE_ENUM;
  • Inquiry item状态Wait
  • Inquiry item状态Over

定义以下几种数据结构:

typedef struct{
        uint08 rssi;
        uint08 state; 
        uint08 smode;
        uint08 dtype;
        uint08 nameLen;
        uint16 clkOff;
        uint32 devClass;
        uint08 btaddr[6];
        uint08 btname[TLKMDI_BTINQ_NAME_LENS+1];
}tlkmdi_btinq_item_t;
  • rssi: 信号强度
  • state: inquiry item的状态
  • Smode: scan mode
  • dtype: 设备类型
  • nameLen: 设备名称长度
  • clkOff: 时钟偏移补偿
  • devClass: 设备类型
  • btaddr: 蓝牙设备地址
  • btname: 蓝牙名称

定义以下几种数据结构:

typedef struct{
        uint08 state;
        uint08 busys;
        uint08 stage;
        uint08 inqType; 
        uint08 curNumb;
        uint08 maxNumb;
        uint08 nameIdx;
        uint08 rssiThd;
        uint08 getName;
        uint16 optTimeout;
        uint16 runTimeout;
        uint16 inqTimeout;
        tlkapi_timer_t timer;
        tlkmdi_btinq_item_t item[TLKMDI_BTINQ_ITEM_NUMB];
}tlkmdi_btinq_ctrl_t;
  • state: Inquiry状态
  • busys: 标记当前没有能够正常执行的action
  • stage: 标记不同状态下的action
  • inqType: 查询类型,包括// pc: 1, phone:2, network_dev :3, headset 4, keyboard:5, spp_dev : 0x0f;
  • curNumb:当前的设备个数
  • nameIdx: 设备名称索引
  • rssiThd: 信号强度阈值
  • getName: 查询名称
  • optTimeout: 操作timeout时长
  • runTimeout: 运行timeout
  • inqTimeout: 查询时长
  • timer: Inquiry timer

回调函数类型定义:

查询结果回调函数:

typedef int(*TlkMmiBtInqReportCallBack)(uint32 devClass, uint08 rssi, uint08 nameLen, uint08 *pBtaddr, uint08 pBtName);

查询结束回调函数:

typedef void(TlkMmiBtInqCompleteCallBack)(void);

接口定义:

下面接口有以下几个功能:

int  tlkmdi_btinq_init(void)

a) 初始化Inquiry的控制变量

b) 初始化timer

c) 注册回调函数

查询当前Inquiry的状态:

bool tlkmdi_btinq_isBusy(void)

启动Inquiry:

int  tlkmdi_btinq_start(uint08 inqType, uint08 rssiThd, uint08 maxNumb, uint16 timeout, bool isGetName);

关闭inquiry:

void tlkmdi_btinq_close(void)

关闭timer, 清空数据:

void tlkmdi_btinq_reset(void)

注册设备发现上报的回调函数:

void tlkmdi_btinq_regCallback(TlkMmiBtInqReportCallBack reportCB, TlkMmiBtInqCompleteCallBack completeCB)

将扫描的设备数量清除:

void tlkmdi_btinq_cleanItems(void)

查找当前扫到的设备数量:

int  tlkmdi_btinq_getItemCount(void)

查找pAddr指定的设备索引:

int  tlkmdi_btinq_getItemIndex(uint08 *pAddr)

根据索引查找Inquiry的Item:

tlkmdi_btinq_item_t *tlkmdi_btinq_getItem(uint08 index)

查找未使用的Inquiry Item:

tlkmdi_btinq_item_t *tlkmdi_btinq_getIdleItem(void)

查找根据pAddr正在使用的Inquiry Item:

tlkmdi_btinq_item_t *tlkmdi_btinq_getUsedItem(uint08 *pAddr)

BTACL设计与实现

(1) 模块简介

这个模块有三个职责,职责一是封装BT Host和BT Profile层的调用接口,避免用户直接访问到Host和Profile;职责二是提供了ACL链接的策略,确保用户各种不同触发链接行为能正常执行;职责三是提供了Profile的链接。

(2) 运行流程

链接流程图如下:

当ACL连接超时则直接结束连接流程,否则上报ACL的连接事件紧接着开启加密认证,加密认证失败或者加密超时时结束连接,否则完成加密事件的上报,然后开启SDP流程直到其结束后开始连接各个profile,当profile链接成功,完成profile事件的上报后结束连接流程。

BTACL流程图

链接流程子序列图:

BTACL流程子序列图

(3) 接口说明

接口由四个部分组成,第一部分的模块名称;第二部分的设计分类,第三部分的设计细节和第四部分的细节释义。

MDI ACL设计

宏定义:

定义链接和断开的timeout:

#define TLKMDI_BTACL_TIMEOUT             200000 //us
#define TLKMDI_BTACL_TIMEOUT_MS          200
#define TLKMDI_BTACL_CONN_DEF_TIMEOUT    (20000000/TLKMDI_BTACL_TIMEOUT)
#define TLKMDI_BTACL_CONN_MIN_TIMEOUT    (10000000/TLKMDI_BTACL_TIMEOUT) //6S
#define TLKMDI_BTACL_CONN_MAX_TIMEOUT    (40000000/TLKMDI_BTACL_TIMEOUT) //40S 
#define TLKMDI_BTACL_DISC_DEF_TIMEOUT    (8000000/TLKMDI_BTACL_TIMEOUT)
#define TLKMDI_BTACL_DISC_MIN_TIMEOUT    (5000000/TLKMDI_BTACL_TIMEOUT)
#define TLKMDI_BTACL_DISC_MAX_TIMEOUT    (30000000/TLKMDI_BTACL_TIMEOUT)

#define TLKMDI_BTACL_IDLE_DEF_TIMEOUT    (10000000/TLKMDI_BTACL_TIMEOUT)

#define TLKMDI_BTACL_PROFILE_CONN_TIMEOUT     (15000000/TLKMDI_BTACL_TIMEOUT)
#define TLKMDI_BTACL_PROFILE_DISC_TIMEOUT     (10000000/TLKMDI_BTACL_TIMEOUT)

下面接口有以下几个功能:

#define TLKMDI_BTACL_ITEM_NUMB           TLK_BT_ACL_MAX_NUMB
#define TLKMDI_BTACL_PROF_NUMB           8
  • 定义最多允许链接的ACL个数
  • 定义最多允许Profile的个数

回调函数类型定义:

typedef void(*TlkMdiBtAclConnCallback)(uint16 handle, uint08 status, uint08 *pBtAddr);
typedef void(*TlkMdiBtAclDiscCallback)(uint16 handle, uint08 reason, uint08 *pBtAddr);
typedef void(*TlkMdiBtAclProfConnCallback)(uint16 handle, uint08 status, uint08 ptype, uint08 usrID, uint08 *pBtAddr);
typedef void(*TlkMdiBtAclProfDiscCallback)(uint16 handle, uint08 reason, uint08 ptype, uint08 usrID, uint08 *pBtAddr);
  • ACL链接回调类型定义
  • ACL断开链接回调的类型定义
  • Profile链接回调的类型定义
  • Profile断开链接回调的类型定义

数据结构:

定义以下几种数据结构:

typedef enum{
        TLKMDI_BTACL_ATTR_NONE = 0x00,
        TLKMDI_BTACL_ATTR_REQUEST = 0x01,
        TLKMDI_BTACL_ATTR_CONNECT = 0x02,
        TLKMDI_BTACL_ATTR_ENCRYPT = 0x04,
}TLKMDI_BTACL_ATTRS_ENUM;
  • TLKMDI_BTACL_ATTR_REQUEST 链接请求属性
  • TLKMDI_BTACL_ATTR_CONNECT 链接属性
  • TLKMDI_BTACL_ATTR_ENCRYPT 加密属性

定义以下几种数据结构:

typedef enum{
        TLKMDI_BTACL_BUSY_NONE = 0x00,
        TLKMDI_BTACL_BUSY_CONN_PROF  = 0x02,
        TLKMDI_BTACL_BUSY_DISC_PROF  = 0x04,
        TLKMDI_BTACL_WAIT_DISC_PROF  = 0x08,
        TLKMDI_BTACL_BUSY_DISC_RFC   = 0x01,
        TLKMDI_BTACL_WAIT_DISC_RFC   = 0x02,
        TLKMDI_BTACL_BUSY_DISC_ACL   = 0x80,
        TLKMDI_BTACL_WAIT_DISC_ACL   = 0x40,
}TLKMDI_BTACL_BUSYS_ENUM;
  • TLKMDI_BTACL_BUSY_CONN_PROF 标记Profile链接处于忙碌状态
  • TLKMDI_BTACL_BUSY_DISC_PROF 标记断开profile处于忙碌状态
  • TLKMDI_BTACL_WAIT_DISC_PROF 标记等待断开
  • TLKMDI_BTACL_BUSY_DISC_RFC 标记断开RF处于忙碌状态
  • TLKMDI_BTACL_WAIT_DISC_RFC 标记等待断开
  • TLKMDI_BTACL_BUSY_DISC_ACL 标记断开ACL处于忙碌状态
  • TLKMDI_BTACL_WAIT_DISC_ACL 标记等待ACL断开

ACL的事件:

typedef enum{
        TLKMDI_BTACL_FLAG_NONE = 0x00,            
}TLKMDI_BTACL_FLAGS_ENUM;

定义以下几种数据结构:

typedef struct{
        uint08 state;
        uint08 ptype; 
        uint08 usrID;
        uint08 cwait; 
        uint16 delay; //
}tlkmdi_btacl_prof_t;
  • state: profile的链接状态
  • ptype: profile的类型
  • usrID: 标记Client/Server
  • cWait: 链接等待时间
  • delay: 延迟

定义以下几种数据结构:

typedef struct{
        uint08 state;
        uint08 stage;
        uint08 busys;
        uint08 attrs;
        uint08 flags;
        uint16 handle;
        uint16 timeout;
        uint16 connFlag;
        uint16 idleTime;
        uint32 devClass;
        uint08 active;
        uint08 hfpChannel;
        uint08 sppChannel;
        uint08 pbapChannel;
        uint08 btaddr[6];
        tlkapi_timer_t timer;
        tlkmdi_btacl_prof_t prof[TLKMDI_BTACL_PROF_NUMB];
}tlkmdi_btacl_item_t;
  • state:链接状态
  • stage:标记当前处于某个执行阶段
  • busys:标记执行action是否被阻塞
  • attrs:标记request/connect/encrypt事件
  • flags:标记链接等待事件
  • handle:标记acl handle
  • timeout:标记timeout时间
  • connFlag:标记Connect flag
  • idleTime:标记idle 时长
  • devClass:对端设备类型
  • active:比较链路是正在使用
  • hfpChannel:标记hfp的Channel
  • sppChannel:标记spp Channel
  • PbapChannel:标记pbap Channel
  • btaddr:标记蓝牙地址
  • timer:当前执行action的timer
  • prof:profile数组

acl链路管理数组:

typedef struct{
        tlkmdi_btacl_item_t item[TLKMDI_BTACL_ITEM_NUMB];
}tlkmdi_btacl_ctrl_t;

接口定义:

初始化sTlkMdiBtAclCtrl, 注册各个回调函数:

int tlkmdi_btacl_init(void)

判断handle对应的acl是否处于链接状态:

bool tlkmdi_btacl_isActive(uint16 handle)

判断handle对应的acl链路上是否有rfcomm:

bool tlkmdi_btacl_isHaveRfc(uint16 handle)

判断handle对应的acl链路上是否有hfp:

bool tlkmdi_btacl_isFindHfp(uint16 handle)

判断handle对应的acl链路上是否有spp:

bool tlkmdi_btacl_isFindSpp(uint16 handle)

判断handle对应的acl链路上是否有pbap:

bool tlkmdi_btacl_isFindPbap(uint16 handle)

取消ACL链接:

int tlkmdi_btacl_cancel(uint08 *pBtAddr)

发起acl链接请求:

int tlkmdi_btacl_connect(uint08 *pBtAddr, uint32 devClass, uint32 timeout)

断开链接请求:

int tlkmdi_btacl_disconn(uint16 handle, uint08 reaosn)

添加需要链接的profile:

int tlkmdi_btacl_appendProf(uint16 handle, uint08 ptype, uint08 usrID, uint16 delayMs)

删除链接的profile:

int tlkmdi_btacl_removeProf(uint16 handle, uint08 ptype, uint08 usrID)

获取ACL链路的role:

int tlkmdi_btacl_getRole(uint32 devClass)

注册链接callback函数:

void tlkmdi_btacl_regConnectCB(TlkMdiBtAclConnCallback callback)

注册断开链接的callback函数:

void tlkmdi_btacl_regDisconnCB(TlkMdiBtAclDiscCallback callback)

注册profile链接的callback函数:

void tlkmdi_btacl_regProfileConnectCB(TlkMdiBtAclProfConnCallback callback)

注册断开profile链接的callback函数:

void tlkmdi_btacl_regProfileDisconnCB(TlkMdiBtAclProfDiscCallback callback)

获取空闲的acl控制变量的个数:

uint08 tlkmdi_btacl_getIdleCount(void)

获取正在链接中的acl控制变量的个数:

uint08 tlkmdi_btacl_getUsedCount(void)

获取已经链接的acl控制变量的个数:

uint08 tlkmdi_btacl_getConnCount(void)

清除pItem的数据:

void tlkmdi_btacl_resetItem(tlkmdi_btacl_item_t *pItem)

为即将建立的ACL分配管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_getIdleItem(void)

获取处于OPEN状态的ACL管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_getInitItem(void)

查找处于链接中的ACL管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_getBusyItem(void)

查找handle指定的ACL管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_getUsedItem(uint16 handle)

查找处于handle指定的链接状态管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_getConnItem(uint16 handle)

查询index指定的链接管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_getConnItemByIndex(uint08 index)

查找pBtaddr指定的使用状态下的ACL管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_searchUsedItem(uint08 *pBtAddr)

查找pBtaddr指定的链接状态下的ACL管理资源:

tlkmdi_btacl_item_t *tlkmdi_btacl_searchConnItem(uint08*pBtAddr)

BTREC设计与实现

(1) 模块简介

这个模块提供了BT回连相关的调度策略,能够确保用户的回连行为得到正确的执行,不需要用户单独设计回连的策略,只需要调用相关接口。

(2) 状态转换

回连由五个状态组成,分别是Idle、Page、Scan、Keep、Close。

  • Idle状态下,如果上层请求回连,会进入到Page状态,如果当前设备正在处于回连状态,则进入到Keep状态,等待当前设备的链接;
  • Page状态下,会在page和Scan(page Scan与Inquiry Scan)之间来回切换,所以会有Page状态和Scan状态之间切换,若page出现timeout,则会进入到Keep状态,在Keep状态下,如果page的次数没有超过最大值,还可以通过Close,进入到Init状态,重新进入page状态。
  • Scan状态下,会在查询到设备后,直接开始链接而进入page状态下。或者进入到Keep状态,等待外部设备发现或者链接本设备。
  • Keep状态下,当有新的page命令下来,会重新启动page,进入到Page状态;或者当有Scan请求重新下来时,可以进入到Scan状态;或者结束Keep状态,进入到Close状态。
  • Close状态下,当有停止page的还未执行完,需要进入到Keep状态,等待停止page执行完,重新进入到Close状态,最后切换到Idle状态。当Close状态下,重新发起page,或者Scan,则会重新进入到Page状态或者Scan状态。Close状态会自然切换到Init状态。

BTREC状态转换

(3) 运行流程

当BT处于Init状态时,收到回连请求后,系统会首先对参数进行初始化,然后启动Scan和Page的流程。首先会取消链接,并关闭page scan和inquiry scan。当发起page,并进入到page状态。在这个状态下,开始链接目标设备,如果链接超时,取消链接,并检查是否超过当前的链接次数,当超过链接次数就会进入到Scan状态,打开Inquiry scan和page scan,等待目标设备来链接本地设备。当page状态下的链接次数并没有超过总数,会进入Keep状态,在Keep状态下,会等待取消链接的执行流程结束。在Scan状态下,如果没有开启Scan的请求,则会重新进入到page的状态下,继续执行page流程。或者在Scan状态下,如果对端设备链接本地设备失败或者超时,如果用户设置了page请求,则会重新进入到Page状态重新执行page流程。否则会进入到Keep状态,等待Scan的执行流程退出。

BTREC流程图

流程序列图如下所示:

BTREC流程序列图

(4) 接口说明

这个模块主要用来控制重连的行为,为MMI层提供重连的接口和一些策略

MDI Reconnect设计

宏定义:

100ms的timeout:

#define TLKMDI_BTREC_TIMEOUT          100000
#define TLKMDI_BTREC_TIMEOUT_MS       100

在重连发起之前,等待一段时间:

#define TLKMDI_BTREC_OPEN_WAIT        (200000/TLKMDI_BTREC_TIMEOUT)

定义每次扫描切换之后,按照Step去增加的扫描时间:

#define TLKMDI_BTREC_SCAN_STEP        (500000/TLKMDI_BTREC_TIMEOUT)

Inquiry Scan完成之后,切换到page的等待时间:

#define TLKMDI_BTREC_SCAN_WAIT        (200000/TLKMDI_BTREC_TIMEOUT)

当page完成之后,等待一段时间切换到Inquiry:

#define TLKMDI_BTREC_PAGE_WAIT        (300000/TLKMDI_BTREC_TIMEOUT)

当page完成之后,等待一段时间开始Inquiry:

#define TLKMDI_BTREC_INIT_WAIT        (300000/TLKMDI_BTREC_TIMEOUT)

当需要关闭page的时候,需要保持一段时间等待关闭:

#define TLKMDI_BTREC_KEEP_WAIT        (300000/TLKMDI_BTREC_TIMEOUT)

当关闭Inquiry的时候,需要等待一段时间等待Inquiry关闭:

#define TLKMDI_BTREC_KEEP_WAIT1       (500000/TLKMDI_BTREC_TIMEOUT)

等待取消链接:

#define TLKMDI_BTREC_STOP_WAIT        (200000/TLKMDI_BTREC_TIMEOUT)

Page的次数:

#define TLKMDI_BTREC_PAGE_COUNT_DEF   3

保持等待的timeout:

#define TLKMDI_BTREC_KEEP_TIME_DEF    (60000/TLKMDI_BTREC_TIMEOUT_MS)

定义了page时长的范围:

#define TLKMDI_BTREC_PAGE_TIME_DEF    (10000000/TLKMDI_BTREC_TIMEOUT) 
#define TLKMDI_BTREC_PAGE_TIME_MIN    (5000000/TLKMDI_BTREC_TIMEOUT) 
#define TLKMDI_BTREC_PAGE_TIME_MAX    (60000000/TLKMDI_BTREC_TIMEOUT) 

定义了Inquiry时长的范围:

#define TLKMDI_BTREC_SCAN_TIME_DEF    (3000000/TLKMDI_BTREC_TIMEOUT) 
#define TLKMDI_BTREC_SCAN_TIME_MIN    (500000/TLKMDI_BTREC_TIMEOUT)  
#define TLKMDI_BTREC_SCAN_TIME_MAX    (30000000/TLKMDI_BTREC_TIMEOUT)

定义了Inquiry的step:

#define TLKMDI_BTREC_SCAN_STEP_DEF    (300000/TLKMDI_BTREC_TIMEOUT) 
#define TLKMDI_BTREC_SCAN_STEP_MAX    (5000000/TLKMDI_BTREC_TIMEOUT)

Inquiry的次数:

#define TLKMDI_BTREC_SCAN_COUNT       5

数据结构:

定义扫描的模式:

typedef enum{
 TLKMDI_BTREC_KEEP_MODE_NONE = 0,
        TLKMDI_BTREC_KEEP_MODE_INQUIRY_SCAN,
        TLKMDI_BTREC_KEEP_MODE_PAGE_SCAN,
        TLKMDI_BTREC_KEEP_MODE_BOTH_SCAN,
}TLKMDI_BTREC_KEEP_MODE_ENUM;
  • TLKMDI_BTREC_KEEP_MODE_INQUIRY_SCAN:定义保持inquiry扫描
  • TLKMDI_BTREC_KEEP_MODE_PAGE_SCAN:定义保持page 扫描
  • TLKMDI_BTREC_KEEP_MODE_BOTH_SCAN:保持两种扫描同时存在

定义以下几种数据结构:

typedef enum{
        TLKMDI_BTREC_ACTIVE_MODE_NONE = 0x00,
        TLKMDI_BTREC_ACTIVE_MODE_PAGE = 0x01,
        TLKMDI_BTREC_ACTIVE_MODE_SCAN = 0x02,
        TLKMDI_BTREC_ACTIVE_MODE_BOTH = 0x03,
}TLKMDI_BTREC_ACTIVE_MODE_ENUM;
  • TLKMDI_BTREC_ACTIVE_MODE_PAGE :定义设备page
  • TLKMDI_BTREC_ACTIVE_MODE_SCAN:定义设备Inquiry
  • TLKMDI_BTREC_ACTIVE_MODE_BOTH:定义打开page 和inquiry

定义以下几种数据结构:

typedef enum{
        TLKMDI_BTREC_STATE_IDLE = 0,
        TLKMDI_BTREC_STATE_INIT,
        TLKMDI_BTREC_STATE_PAGE,
        TLKMDI_BTREC_STATE_SCAN,
        TLKMDI_BTREC_STATE_KEEP,
        TLKMDI_BTREC_STATE_STOP,
}TLKMDI_BTREC_STATE_ENUM;
  • TLKMDI_BTREC_STATE_INIT: 回连初始状态
  • TLKMDI_BTREC_STATE_PAGE:回连Page状态
  • TLKMDI_BTREC_STATE_SCAN:回连Inquiry状态
  • TLKMDI_BTREC_STATE_KEEP:保持状态
  • TLKMDI_BTREC_STATE_STOP:结束状态

定义以下几种数据结构:

typedef enum{
        TLKMDI_BTREC_STAGE_NONE = 0,
        //TLKMDI_BTREC_STATE_INIT
        TLKMDI_BTREC_STAGE_INIT_NONE = 0,
        TLKMDI_BTREC_STAGE_INIT_CLOSE_SCAN,
        TLKMDI_BTREC_STAGE_INIT_SET_TIMEOUT,
        TLKMDI_BTREC_STAGE_INIT_SET_SCHEDULE,
        TLKMDI_BTREC_STAGE_INIT_WAIT,
        //TLKMDI_BTREC_STATE_PAGE
        TLKMDI_BTREC_STAGE_PAGE_NONE = 0,
        TLKMDI_BTREC_STAGE_PAGE_START,
        TLKMDI_BTREC_STAGE_PAGE_WAIT0,
        TLKMDI_BTREC_STAGE_PAGE_CLOSE,
        TLKMDI_BTREC_STAGE_PAGE_WAIT1,
        //TLKMDI_BTREC_STATE_SCAN
        TLKMDI_BTREC_STAGE_SCAN_NONE = 0,
        TLKMDI_BTREC_STAGE_SCAN_START,
        TLKMDI_BTREC_STAGE_SCAN_WAIT0,
        TLKMDI_BTREC_STAGE_SCAN_CLOSE,
        TLKMDI_BTREC_STAGE_SCAN_WAIT1,
        //TLKMDI_BTREC_STATE_KEEP
        TLKMDI_BTREC_STAGE_KEEP_NONE= 0,
        TLKMDI_BTREC_STAGE_KEEP_CLOSE_PAGE,
        TLKMDI_BTREC_STAGE_KEEP_CLOSE_SCAN,
        TLKMDI_BTREC_STAGE_KEEP_WAIT,
        TLKMDI_BTREC_STAGE_KEEP_START,
        TLKMDI_BTREC_STAGE_KEEP_RUN,
        //TLKMDI_BTREC_STATE_STOP
        TLKMDI_BTREC_STAGE_STOP_NONE = 0,
        TLKMDI_BTREC_STAGE_STOP_PAGE,
        TLKMDI_BTREC_STAGE_STOP_SCAN,
        TLKMDI_BTREC_STAGE_STOP_WAIT,
}TLKMDI_BTREC_STAGE_ENUM;
  • TLKMDI_BTREC_STAGE_INIT_NONE:初始化阶段的初始状态
  • TLKMDI_BTREC_STAGE_INIT_CLOSE_SCAN:初始化阶段的关闭Inquiry
  • TLKMDI_BTREC_STAGE_INIT_SET_TIMEOUT:初始化阶段设置timeout
  • TLKMDI_BTREC_STAGE_INIT_SET_SCHEDULE:初始化阶段设置schedule
  • TLKMDI_BTREC_STAGE_INIT_WAIT: 初始化阶段等待
  • TLKMDI_BTREC_STAGE_PAGE_NONE:进入page阶段
  • TLKMDI_BTREC_STAGE_PAGE_START:开始page
  • TLKMDI_BTREC_STAGE_PAGE_WAIT0: 保持page
  • TLKMDI_BTREC_STAGE_PAGE_CLOSE: 关闭page
  • TLKMDI_BTREC_STAGE_PAGE_WAIT1: 保持关闭page状态
  • TLKMDI_BTREC_STAGE_SCAN_NONE:进入到Inquiry阶段
  • TLKMDI_BTREC_STAGE_SCAN_START:开始Inquiry
  • TLKMDI_BTREC_STAGE_SCAN_WAIT0:保持inquiry
  • TLKMDI_BTREC_STAGE_SCAN_CLOSE: 关闭inquiry
  • TLKMDI_BTREC_STAGE_SCAN_WAIT1: 保持等待inquiry的状态
  • TLKMDI_BTREC_STAGE_KEEP_NONE:进入保持状态
  • TLKMDI_BTREC_STAGE_KEEP_CLOSE_PAGE:保持关闭page
  • TLKMDI_BTREC_STAGE_KEEP_CLOSE_SCAN:保持关闭Inquiry
  • TLKMDI_BTREC_STAGE_KEEP_WAIT: 保持等待
  • TLKMDI_BTREC_STAGE_KEEP_START:保持start
  • TLKMDI_BTREC_STAGE_KEEP_RUN: 保持运行
  • TLKMDI_BTREC_STAGE_STOP_NONE:进入关闭状态
  • TLKMDI_BTREC_STAGE_STOP_PAGE:停止page
  • TLKMDI_BTREC_STAGE_STOP_SCAN:停止Inquiry
  • TLKMDI_BTREC_STAGE_STOP_WAIT: 停止等待

由于Core和host执行的时间不一致导致产生的

定义以下几种数据结构:

typedef struct{
        uint08 state;
        uint08 stage;
        uint16 timeout;
        uint08 actMode;
        uint08 tempCount;
        uint08 pageCount;
        uint08 scanCount;
        uint08 scanMode;
        uint08 keepMode;
        uint16 keepTime;
        uint16 ptimeout;
        uint16 pageTime;
        uint16 pageWait;
        uint16 scanTime;
        uint16 scanStep;
        uint16 scanWait;
        uint32 devClass;
        uint08 pageAddr[6];
        tlkapi_timer_t timer;
}tlkmdi_btrec_t
  • state: 标记回连状态
  • stage: 标记回连动作执行不同阶段下的各个状态
  • timeout: 执行的动作的时长
  • actMode: 标记不同Mode
  • tempCount: 临时存储不同action执行的次数
  • pageCount: page执行的次数
  • scanCount: Inquiry执行的次数
  • scanMode: 扫描模式
  • keepMode: 保持模式
  • keepTimer: 保持的timer
  • ptimeout: page timout
  • pageTime: page时长
  • pageWait: page等待状态
  • scanTime: inquiry时长
  • scanStep:inquiry的时长的等级
  • scanWait: 等待inquiry
  • devClass: 设备类型
  • pageAddr: 需要链接的设备地址
  • timer: 执行的timer

回调函数类型定义:

定义回连结束回调函数:

typedef void(*TlkMdiBtRecOverCallback)(void);

接口定义:

初始化sTlkMdiBtRecCtrl, 初始化timer:

int tlkmdi_btrec_init(void)

重置sTlkMdiBtRecCtrl的各个状态:

int tlkmdi_btrec_reset(void)

开始回连:

int tlkmdi_btrec_start(uint08 *pPageAddr, uint32 devClass, bool enPage, bool enScan)

关闭回连:

int tlkmdi_btrec_close(void)

设置回调函数:

void tlkmdi_btrec_regCB(TlkMdiBtRecOverCallback overCB)

查询当前状态是否为Idle:

bool tlkmdi_btrec_isInBusy(void)

查询当前是否处于page:

bool tlkmdi_btrec_isInPage(void)

查询当前是否处于Inquiry:

bool tlkmdi_btrec_isInScan(void)

查询当前是否处于Keep:

bool tlkmdi_btrec_isInKeep(void)

查询当前是否处于Stop:

bool tlkmdi_btrec_isInStop(void)

获取回连需要的蓝牙地址:

uint08 *tlkmdi_btrec_getPageAddr(void)

设置保持Keep mode下,和不同Mode下的时长和stage:

int tlkmdi_btrec_setKeepMode(TLKMDI_BTREC_KEEP_MODE_ENUM keepMode, uint16 keepTime)

设置page的参数:

int tlkmdi_btrec_setPageParam(uint16 pageTime, uint08 pageCount)

设置inquiry的参数:

int tlkmdi_btrec_setScanParam(uint16 scanTime, uint16 scanStep, bool enInqScan, bool enPageScan)

音频模块设计

音频模块是SDK MDI层重要的业务,这一块将所有SDK Audio相关的行为都抽象为一个模块,这个模块能够管理整个SDK的各种用户行为下音频,包括语音通话,耳机听音乐,本地播放音乐,本地提示音,通过作为音频源的蓝牙设备向耳机发送音乐数据等等行为,是蓝牙应用的核心。

通用接口

MDI Audio设计

数据结构:

宏定义:

定义Audio音量的扩展值:

#define TLKMDI_AUDIO_VOLUME_EXPAND            10
定义提示音的音量索引:
#define TLKMDI_AUDIO_TONE_INDEX_DEF           9 //60% - scTlkMdiAudioToneRawVolTable[TLKMDI_AUDIO_TONE_INDEX_DEF]
定义手机音乐的音量索引:
#define TLKMDI_AUDIO_VOICE_INDEX_DEF          9 //60% - scTlkMdiAudioVoiceRawVolTable[TLKMDI_AUDIO_VOICE_INDEX_DEF]
定义本地音乐的音量索引:
#define TLKMDI_AUDIO_MUSIC_INDEX_DEF          19 //60% - scTlkMdiAudioMusicRawVolTable[TLKMDI_AUDIO_MUSIC_INDEX_DEF]
定义耳机音乐的音量索引:
#define TLKMDI_AUDIO_HEADSET_INDEX_DEF        9 //60% - scTlkMdiAudioHesdSetRawVolTable[TLKMDI_AUDIO_HEADSET_INDEX_DEF]

接口定义:

int tlkmdi_audio_init(void)

(1)MP3的初始化 (2)Tone的初始化 (3)Play的初始化 (4)hfp的初始化 (5)sco的初始化 (6)src的初始化 (7)snk的初始化 (8)uac的初始化

发送Audio start事件:

int tlkmdi_audio_sendStartEvt(uint08 audChn, uint16 handle)

发送audio close事件:

int tlkmdi_audio_sendCloseEvt(uint08 audChn, uint16 handle)

发送play start事件:

int tlkmdi_audio_sendPlayStartEvt(uint08 audChn, uint16 playIndex)

发送play over事件:

int tlkmdi_audio_sendPlayOverEvt(uint08 audChn, uint16 playIndex)

发送Volume更新事件:

int tlkmdi_audio_sendVolumeChangeEvt(uint08 audChn, uint08 volume)

发送状态更新事件:

int tlkmdi_audio_sendStatusChangeEvt(uint08 audChn, uint08 status)

获取Tone的Raw音量:

uint tlkmdi_audio_getToneRawVolume(void)

获取Tone的Cal音量:

uint tlkmdi_audio_getToneCalVolume(void)

设置Tone的Raw音量:

void tlkmdi_audio_setToneRawVolume(uint08 volume)

获取Tone的音量:

uint tlkmdi_audio_getToneVolume(void)

Tone音量增加并更新上报:

void tlkmdi_audio_toneVolumeInc(void)

Tone音量减少并更新上报:

void tlkmdi_audio_toneVolumeDec(void)

设置Tone音量,并上报更新:

void tlkmdi_audio_setToneVolume(uint08 volume)

获取指定Tone Raw音量的对应Raw音量索引值:

uint tlkmdi_audio_getToneRawVolumeIndex(uint08 rawVol)

获取Music的Btp音量:

uint tlkmdi_audio_getMusicBtpVolume(bool isIos)

获取Music的Raw音量:

uint tlkmdi_audio_getMusicRawVolume(bool isIos)

获取Music的Cal音量:

uint tlkmdi_audio_getMusicCalVolume(bool isIos)

设置Music的Raw音量:

void tlkmdi_audio_setMusicRawVolume(uint08 volume)

设置Music的Btp音量:

void tlkmdi_audio_setMusicBtpVolume(uint08 volume, bool isIos)

获取Music音量:

uint tlkmdi_audio_getMusicVolume(void)

Music增加音量并上报:

void tlkmdi_audio_musicVolumeInc(void)

Music减少音量并上报:

void tlkmdi_audio_musicVolumeDec(void)

设置Music的音量并上报:

void tlkmdi_audio_setMusicVolume(uint08 volume)

获取指定Music Raw音量的对应Raw音量索引值:

uint tlkmdi_audio_getMusicRawVolumeIndex(uint08 rawVol)

获取指定Music Btp音量的对应Raw音量索引值:

uint tlkmdi_audio_getMusicBtpVolumeIndex(uint08 btpVol, bool isIos)

获取Voice的Btp音量:

uint tlkmdi_audio_getVoiceBtpVolume(void)

获取Voice的Raw音量:

uint tlkmdi_audio_getVoiceRawVolume(void)

获取Voice的Cal音量:

uint tlkmdi_audio_getVoiceCalVolume(void)

设置Voice的Raw音量:

void tlkmdi_audio_setVoiceRawVolume(uint08 volume)

设置Voice的Btp音量:

void tlkmdi_audio_setVoiceBtpVolume(uint08 volume)

获取Voice的音量:

uint tlkmdi_audio_getVoiceVolume(void)

Voice调高音量并上报:

void tlkmdi_audio_voiceVolumeInc(void)

Voice调低音量并上报:

void tlkmdi_audio_voiceVolumeDec(void)

设置voice音量并上报:

void tlkmdi_audio_setVoiceVolume(uint08 volume)

获取指定Voice Raw音量的对应Raw音量索引值:

uint tlkmdi_audio_getVoiceRawVolumeIndex(uint08 rawVol)

获取指定Voice Btp音量的对应Raw音量索引值:

uint tlkmdi_audio_getVoiceBtpVolumeIndex(uint08 btpVol)

获取HeadSet的Btp音量:

uint tlkmdi_audio_getHeadsetBtpVolume(void)

获取HeadSet的Raw音量:

uint tlkmdi_audio_getHeadsetRawVolume(void)

获取HeadSet的Cal音量:

uint tlkmdi_audio_getHeadsetCalVolume(void)

设置HeadSet的Raw音量:

void tlkmdi_audio_setHeadsetRawVolume(uint08 volume)

设置HeadSet的Btp音量:

void tlkmdi_audio_setHeadsetBtpVolume(uint08 volume)

获取headset的音量:

uint tlkmdi_audio_getHeadsetVolume(void)

调高耳机音量,并上报:

void tlkmdi_audio_headsetVolumeInc(void)

调低耳机音量,并上报:

void tlkmdi_audio_headsetVolumeDec(void)

设置耳机音量并上报:

void tlkmdi_audio_setHeadsetVolume(uint08 volume)

获取指定HeadSet Raw音量的对应Raw音量索引值:

uint tlkmdi_audio_getHeadsetRawVolumeIndex(uint08 rawVol)

获取指定HeadSet Btp音量的对应Raw音量索引值:

uint tlkmdi_audio_getHeadsetBtpVolumeIndex(uint08 btpVol)

AudSco设计与实现

(1) 模块简介

这个模块主要提供处理语音相关的接口,包括链接过程中的各个状态变化,和SCO语音通路中的SPK和MIC等各个环节中的数据处理,和语音的算法数据处理,例如数据预处理,回声消隐,噪声抑制,自动增益等等。另外提供了SCO的集中编码器和编码器的参数设置,以及编码器如何处理语音数据,和优化处理语音数据(PLC)。

(2) 运行流程

左边的是SCO建立成功之后,设置硬件参数和Audio处理语音数据的流程。中间的流程图是Mic通过中断获取Mic的语音数据,并进行处理。右边是Spk通过中断接收数据并进行处理。

Audio流程图

下图是语音数据流程图,数据通过RF接收,中间经过解码和语音算法的处理,最后到Spk,或者从Mic过来的数据最终通过语音算法处理,经过编码,最后通过RF发送出去。

语音数据流程图

(3) 接口说明

MDI audSCO设计

宏定义:

speaker的encoder buff个数:

#define TLKMDI_SCO_SPK_ENC_BUFF_NUMB       16

speaker的Encoder的buff大小:

#define TLKMDI_SCO_SPK_ENC_BUFF_SIZE          64

mic的encoder的buff个数:

#define TLKMDI_SCO_MIC_ENC_BUFF_NUMB       16

mic的encoder的buff大小:

#define TLKMDI_SCO_MIC_ENC_BUFF_SIZE          64

speaker的pcm buff大小:

#define TLKMDI_SCO_SPK_PCM_BUFF_SIZE       240

mic的pcm buff大小:

#define TLKMDI_SCO_MIC_PCM_BUFF_SIZE       240

噪声抑制算法的buffer:

#define TLKMDI_SCO_SPEEX_NS_SIZE            5888

噪声抑制算法的结尾标志:

#define TLKMDI_SCO_SPEEX_NS_OFFS            0

SCO自动增益算法的buffer size(要采集数据):

#define TLKMDI_SCO_AGC_ST_SIZE              1024

自动增益的数据buffer size:

#define TLKMDI_SCO_AGC_ST_OFFS              (TLKMDI_SCO_SPEEX_NS_OFFS+TLKMDI_SCO_SPEEX_NS_SIZE)

语音处理需要的buffer:

#define TLKMDI_SCO_SPEEX_BUFFER_SIZE        (TLKMDI_SCO_SPEEX_NS_SIZE+TLKMDI_SCO_AGC_ST_SIZE)

回声消除算法的buffer size:

#define TLKMDI_SCO_AECM_ST_SIZE             6912

回声消除算法的标志结尾:

#define TLKMDI_SCO_AECM_ST_OFFS             0

回声消隐数据buffer size:

#define TLKMDI_SCO_AECM_BUFFER_SIZE         (TLKMDI_SCO_AECM_ST_SIZE)
#define TLKMDI_SCO_AEC_NS_SCRATCH_SIZE          4096
#define TLKMDI_SCO_AEC_NS_SCRATCH_OFFS          0
#define TLKMDI_SCO_AEC_NS_SCRATCH_BUFFER_SIZE   (TLKMDI_SCO_AEC_NS_SCRATCH_SIZE)

CVSD的参数设置:

#define TLKMDI_SCO_CVSD_PARAM_SIZE          TLKALG_CVSD_PARAM_SIZE

CVSD的缓存大小:

#define TLKMDI_SCO_CVSD_CACHE_SIZE          TLKALG_CVSD_CACHE_SIZE

CVSD的buffer size:

#define TLKMDI_SCO_CVSD_BUFFER_SIZE         (TLKMDI_SCO_CVSD_PARAM_SIZE+TLKMDI_SCO_CVSD_CACHE_SIZE)

mSBC压缩的size:

#define TLKMDI_SCO_SBC_ENC_BUFF_SIZE     3368

标志结尾:

#define TLKMDI_SCO_SBC_ENC_BUFF_OFFS        0

mSBC解压缩的size:

#define TLKMDI_SCO_SBC_DEC_BUFF_SIZE     1248

标志结尾:

#define TLKMDI_SCO_SBC_DEC_BUFF_OFFS        TLKMDI_SCO_SBC_ENC_BUFF_SIZE

mSBC的buffer size:

#define TLKMDI_SCO_SBC_BUFFER_SIZE          (TLKMDI_SCO_SBC_ENC_BUFF_SIZE+TLKMDI_SCO_SBC_DEC_BUFF_SIZE)

SCO的临时data buffer size:

#define TLKMDI_SCO_TEMP_BUFFER_SIZE      2048

SCO压缩器的buffer size:

#define TLKMDI_SCO_ENCODE_BUFFER_SIZE       (TLKMDI_SCO_SBC_BUFFER_SIZE)

SCO 缓存的buff size:

#define TLKMDI_SCO_CACHE_BUFFER_SIZE (TLKMDI_SCO_SPK_ENC_BUFF_SIZETLKMDI_SCO_SPK_ENC_BUFF_NUMB\
 +TLKMDI_SCO_MIC_ENC_BUFF_SIZETLKMDI_SCO_MIC_ENC_BUFF_NUMB)

语音编解码器CVSD的id:

#define TLKMDI_SCO_CODEC_ID_CVSD          1

语音编解码器MSBC的id:

#define TLKMDI_SCO_CODEC_ID_MSBC          2

音量的step:

#define TLKMDI_SCO_VOLUME_STEP             6

SCO丢包检测:

#define TLKMDI_SCO_PACKET_LOSS_FLAG        1

SCO lost测试:

#define TLKMDI_SCO_SCO_LOSS_TEST           0

关闭PLC:

#define TLKMDI_SCO_PLC_ENABLE              0

使能PLC:

#define TLKMDI_SCO_PLC_ENABLE1             1

语音的单声道模式:

#define CODEC_DAC_MONO_MODE                1

SCO的GRAD的数量:

#define TLKMDI_SCO_GRAD_NUMBER             12

SCO GRAD的音量:

#define TLKMDI_SCO_GRAD_VOLUME             1

数据结构:

定义以下几种数据结构:

typedef struct{
    uint08 *pTempBuffer;
    uint08 *pAecmBuffer;
    uint08 *pSpeexBuffer;
    uint08 *pScratchBuffer;
    uint08 *pCacheBuffer;
    uint08 *pEncodeBuffer;
}tlkmdi_sco_buff_t;
  • pTempBuffer:临时的数据buffer
  • pAecmBuffer:回声消隐的数据buffer
  • pSpeexBuffer:语音预处理buffer
  • pScratchBuffer:
  • pCacheBuffer:缓存buffer
  • pEncodeBuffer:encoder的buffer

定义以下几种数据结构:

typedef struct{
    uint08 numb;
    uint08 enable;
    uint16 handle;
    uint08 dropSpkNumb;
    uint08 dropMicNumb;
    uint16 spkBuffLen;
    uint16 micBuffLen;
    uint16 spkReadOffs;
    uint16 micReadOffs;
    TlkMdiScoDecFunc dec_func;
    TlkMdiScoEncFunc enc_func;
    TlkMdiScoPlcFunc plc_func;
}tlkmdi_sco_ctrl_t;
  • numb: 数量
  • enable: 是否使能
  • handle:Ctrl的标识符
  • dropSpkNumb:
  • dropMicNumb:
  • spkBuffLen:spk的数据长度
  • micBuffLen:mic的数据长度
  • spkReadOffs:spk的读取偏移
  • micReadOffs:mic的读取偏移
  • dec_func:解压缩函数
  • enc_func:压缩函数
  • plc_func:plc函数

全局变量:

加在decoder前面的包头:

const unsigned char sTlkMdiScoMSBCSilencePkt[60]

Agc数据buffer:

static uint08 *sTlkMdiScoAgcBuffer = nullptr;

存储SCO Spk数据的Fifo:

static tlkapi_qfifo_t sTlkMdiScoSpkFifo;

存储SCO Mic数据的Fifo:

static tlkapi_qfifo_t sTlkMdiScoMicFifo;

SCO的控制块:

static tlkmdi_sco_ctrl_t sTlkMdiScoCtrl;

存储SCO数据的buffer:

static tlkmdi_sco_buff_t sTlkMdiScoBuff;

SCO数据长度:

static uint08 sTlkMdiAudScoLength = 0;

SCO数据帧:

static uint08 sTlkMdiAudScoFrame[120];

存储当前使用的SCO codec:

uint08 sTlkMdiScoCodec

SCO的error index:

uint16 sTlkMdiScoErrIndex = 0

SCO的error count:

uint16 sTlkMdiScoErrCount = 0

SCO的 mic mute索引:

uint08 sTlkMdiMicMuteIndex

SCO的连接回调:

static TlkMdiScoConnCB sTlkMdiScoConnCB = nullptr;

回调函数类型定义:

定义SCO的连接回调函数原型:

typedef void(*TlkMdiScoConnCB)(uint16 aclHandle, uint16 scoHandle, bool isConn)

定义Dec的回调函数原型:

typedef int(*TlkMdiScoDecFunc)(uint08 *pSrc, uint16 srcLen, uint08 *pOut)

定义ENC的回调函数原型:

typedef int(*TlkMdiScoEncFunc)(uint08 *pSrc, uint16 srcLen, uint08 *pOut)

定义PLC的回调函数原型:

typedef int(*TlkMdiScoPlcFunc)(uint08 *pSrc, uint16 srcLen, uint08 *pOut)

接口定义:

下面接口有以下几个功能:

int tlkmdi_audsco_init(void)

a) 注册SCO speaker和mic的回调函数

b) 注册SCO的状态回调函数

为SCO注册回调函数:

void tlkmdi_audsco_regCB(TlkMdiScoConnCB connCB)

当前是否有SCO在使用:

bool tlkmdi_audsco_isBusy(void)

获取SCO的interval,用于计算下一个timer的timeout:

uint tlkmdi_audsco_intval(void)

SCO的diable和enable之间的切换:

bool tlkmdi_audsco_switch(uint16 handle, uint08 status)

SCO的mic和speaker的处理函数:

bool tlkmdi_audsco_irqProc(void)

获取Codec id:

uint tlkmdi_sco_getCodec(void)

设置Codec:

void tlkmdi_sco_setCodec(uint08 codec)

AudHfp设计与实现

(1) 模块简介

HFP模块主要提供蓝牙设备控制通话的接口,如接听、挂断、拒接、语音拨号等。

(2) 运行流程

TBD

(3) 接口说明

MDI AudHFP设计

宏定义:

定义AudHfp的超时时间:

#define TLKMDI_AUDHF_TIMEOUT                100000

定义AudHfp的等待超时时间:

#define TLKMDI_AUDHF_CALL_WAIT_TIMEOUT      (3000000/TLKMDI_AUDHF_TIMEOUT)

数据结构:

typedef struct{
    uint08 status;
    uint16 handle;
    uint08 timeout;
    tlkapi_timer_t timer;
}tlkmdi_audhfp_ctrl_t;
  • status:当前hfp的状态
  • handle:连接句柄
  • timeout:hfp的超时时间
  • timer: hfp的运行Timer

接口定义:

AudHfp的初始化:

int tlkmdi_audhfp_init(void)

根据句柄开启对应的AudHfp:

int tlkmdi_audhfp_start(uint16 handle)

根据句柄关闭对应的AudHfp:

int tlkmdi_audhfp_close(uint16 handle);

判断Hfp是否为open状态:

bool tlkmdi_audhfp_isBusy(void)

获取Hfp的运行间隔:

uint tlkmdi_audhfp_intval(void);

HFP的状态改变:

bool tlkmdi_audhfp_switch(uint16 handle, uint08 status)

获取HFP的enable状态:

bool tlkmdi_audhfp_irqProc(void)

AudMp3设计与实现

(1) 模块简介

MP3模块主要包括两个部分,第一部分是提供处理MP3数据的接口,MDI层的Play、Tone、SRC等模块调用Mp3的接口进行音乐播放;第二部分是MP3的使用策略,包括mp3的信息加载,MP3的decoder的配置和MP3文件的加载,以及MP3数据解码成pcm数据,MP3的播放模式的设计和歌曲信息的更新获取等。

(2) 运行流程

只提供接口,无流程。

(3) 接口说明

MDI audMp3设计

宏定义:

定义PCM的size:

#define TLKMDI_MP3_PCM_SIZE         (1152*4)

定义Dir的size:

#define TLKMDI_MP3_DIR_SIZE         20

定义MP3 name长度:

#define TLKMDI_MP3_NAME_SIZE                TLKMDI_CFG_MP3_NAME_SIZE

定义MP3 path的size:

#define TLKMDI_MP3_PATH_SIZE                (TLKMDI_MP3_DIR_SIZE+TLKMDI_MP3_NAME_SIZE)

定义decoder的参数:

#define TLKMDI_MP3_DEC_PARAM_SIZE       6672

定义decoder的缓存0大小:

#define TLKMDI_MP3_DEC_CACHE0_SIZE      16240

定义decoder的缓存1大小:

#define TLKMDI_MP3_DEC_CACHE1_SIZE      4608

定义decoder的缓存的偏移地址:

#define TLKMDI_MP3_DEC_CACHE1_OFFS      TLKMDI_MP3_DEC_CACHE0_SIZE

定义decoder的缓存大小:

#define TLKMDI_MP3_DEC_CACHE_SIZE       (TLKMDI_MP3_DEC_CACHE0_SIZE+TLKMDI_MP3_DEC_CACHE1_SIZE)

定义decoder的PCM的size:

#define TLKMDI_MP3_DEC_PCM_SIZE       (1152*8+4)

定义mp3的performance的size:

#define TLKMDI_MP3_PERFORMER_SIZE      64

定义MP3 default的interval:

#define TLKMDI_MP3_SAVE_INTERVAL             5000000

定义缺省的timer:

#define TLKMDI_MP3_UPDATE_TIMER_DEF     5000000

定义timer的max值:

#define TLKMDI_MP3_UPDATE_TIMER_MAX   20000000

定义timer的min值:

#define TLKMDI_MP3_UPDATE_TIMER_MIN      1000000

定义文件的标识符:

#define TLKMDI_MP3_FILE_INFO_SIGN        0x3A

定义文件的版本:

#define TLKMDI_MP3_FILE_INFO_VERS        0x02

定义文件的地址:

#define TLKMDI_MP3_FILE_INFO_ADDR        TLK_CFG_FLASH_PLAY_INFO_ADDR

定义文件的大小:

#define TLKMDI_MP3_FILE_INFO_SIZE        (4+TLKMDI_MP3_NAME_SIZE)

定义文件list的标识符:

#define TLKMDI_MP3_FILE_LIST_SIGN        0x3C3C

定义文件list的版本:

#define TLKMDI_MP3_FILE_LIST_VERS        0x02

定义文件list的大小:

#define TLKMDI_MP3_FILE_LIST_SIZE        (4+TLKMDI_MP3_NAME_SIZE)

定义文件list的地址:

#define TLKMDI_MP3_FILE_LIST_ADDR        TLK_CFG_FLASH_PLAY_LIST_ADDR

定义文件list的length:

#define TLKMDI_MP3_FILE_LIST_LENS        TLK_CFG_FLASH_PLAY_LIST_LENS

最多的file list:

#define TLKMDI_MP3_FILE_LIST_MAX         ((TLK_CFG_FLASH_PLAY_LIST_LENS-16)/TLKMDI_MP3_FILE_LIST_SIZE)

Mp3标签的head长度:

#define TLKMDI_MP3_LABEL_HLEN          10

mp3文件缓存大小:

#define TLKMDI_MP3_FILE_CACHE_SIZE     2048

mp3文件缓存的1/2,用来做策略判断:

#define TLKMDI_MP3_FILE_CACHE_HALF     1024

mp3文件缓存的1/3,用来做策略判断:

#define TLKMDI_MP3_FILE_CACHE_THIRD    680

MP3 info的缓存size:

#define TLKMDI_MP3_INFO_CACHE_SIZE     (MINIMP3_MAX_SAMPLES_PER_FRAME)

数据结构:

播放模式的定义:

typedef enum{
        TLKMDI_MP3_PALY_MODE_ORDER = 0,
        TLKMDI_MP3_PALY_MODE_LOOP,
        TLKMDI_MP3_PALY_MODE_RANDOM,
        TLKMDI_MP3_PALY_MODE_MAX,
}TLKMDI_MP3_PALY_MODE_ENUM;
  • TLKMDI_MP3_PALY_MODE_ORDER:顺序播放
  • TLKMDI_MP3_PALY_MODE_LOOP:循环播放
  • TLKMDI_MP3_PALY_MODE_RANDOM:随机播放

定义MP3的数据压缩格式:

typedef enum{
        TLKMDI_MP3_MPEG_TYPE_2P5 = 0,
        TLKMDI_MP3_MPEG_TYPE_RESERVE,
        TLKMDI_MP3_MPEG_TYPE_2,
        TLKMDI_MP3_MPEG_TYPE_1,
}TLKMDI_MP3_MPEG_TYPE_ENUM;
  • TLKMDI_MP3_MPEG_TYPE_2P5:MP3的Mpeg 2P5类型
  • TLKMDI_MP3_MPEG_TYPE_2:MP3的Mpeg 2类型
  • TLKMDI_MP3_MPEG_TYPE_1:MP3的Mpeg 1类型

定义mp3的流的主数据格式:

typedef enum{
        TLKMDI_MP3_LAYER_TYPE_RESERVE = 0,
        TLKMDI_MP3_LAYER_TYPE_3,
        TLKMDI_MP3_LAYER_TYPE_2,
        TLKMDI_MP3_LAYER_TYPE_1,
}TLKMDI_MP3_LAYER_TYPE_ENUM;
  • TLKMDI_MP3_LAYER_TYPE_3:当前主流的格式layer 3
  • TLKMDI_MP3_LAYER_TYPE_2: layer2 的流数据格式
  • TLKMDI_MP3_LAYER_TYPE_1:layer1的流数据格式

MP3的状态定义:

typedef enum{
        TLKMDI_MP3_STATUS_IDLE = 0,
        TLKMDI_MP3_STATUS_WAIT,
        TLKMDI_MP3_STATUS_DONE,
        TLKMDI_MP3_STATUS_PLAY,
}TLKMDI_MP3_STATUS_ENUM;
  • TLKMDI_MP3_STATUS_IDLE: 空闲状态
  • TLKMDI_MP3_STATUS_WAIT:等待状态
  • TLKMDI_MP3_STATUS_DONE:完成状态
  • TLKMDI_MP3_STATUS_PLAY:播放状态

定义以下几种数据结构:

typedef struct{
        uint32 playLens;
        uint08 fileName[TLKMDI_MP3_NAME_SIZE];
}tlkmdi_mp3_fsave_t;
  • playLens: 能够播放的长度
  • fileName:文件名数组

定义以下几种数据结构:

typedef struct{
        uint32 count; 
        uint08 vers; 
        uint08 size; 
        uint16 sign; 
}tlkmdi_mp3_flist_t;
  • count: Mp3文件list的长度
  • vers: mp3文件的版本
  • size:单首歌曲的size
  • sign: mp3 flash的标记符

定义以下几种数据结构:

typedef struct{
        uint08 playMode;
        uint08 reserve0;
        uint16 curIndex;
        uint32 playLens;
        uint32 cacheLens;
        uint32 cacheOffs;
}tlkmdi_mp3_fplay_t;
  • playMode:设置播放模式
  • curIndex:当前歌曲的index
  • playLens: 播放总长
  • cacheLens:缓存大小
  • cacheOffs:缓存偏移

定义以下几种数据结构:

typedef struct{
        uint32 fileSize;
        uint32 headLens;
        uint32 duration;
        uint08 channels;
        uint08 reserve0;
        uint16 sampleRate;
        uint08 performer[TLKMDI_MP3_PERFORMER_SIZE];
}tlkmdi_mp3_finfo_t;
  • fileSize:文件size
  • headLens: 文件头长度
  • duration:歌曲的播放时间
  • channels: 歌曲数据的通道
  • sampleRate:歌曲数据采样率
  • performerperformer:歌手信息

定义以下几种数据结构:

typedef struct{
        uint08 playMode;
        uint08 isUpdate;
        uint16 playIndex;
        uint16 fileCount;
        tlkapi_save_ctrl_t save;
        tlkmdi_mp3_fsave_t fsave;
        tlkmdi_mp3_fplay_t fplay;
        tlkmdi_mp3_finfo_t finfo;
}tlkmdi_mp3_ctrl_t;
  • playMode:播放模式
  • isUpdate:歌曲是否已经更新
  • playIndex:播放歌曲的索引
  • fileCount:歌曲个数
  • save:文件控制
  • fsave:文件存储
  • fplay:文件播放
  • finfo:文件信息

全局变量:

存储当前的mp3状态:

uint08 gTlkMdiMp3Status = 0;

用作Decode PCMdata的buff:

sint16 *spTlkMdiMp3DecodeTempBuff = NULL

用作文件信息的临时buff:

uint08 *spTlkMdiMp3FileInfoTempBuff = NULL

用作mp3播放时长的数据的buff:

uint08 *spTlkMdiMp3DurationTempBuff = NULL

接口定义:

下面接口有以下几个功能:

int tlkmdi_mp3_init(void)

a) 判断各个数据buffer是否清空

b) 初始化控制变量

c) 初始化存储器

d) 加载文件

判断当前mp3是否已经使能:

bool tlkmdi_mp3_isEnable(void)

下面接口有以下几个功能:

bool tlkmdi_mp3_enable(bool enable)

a) 重新分配数据Buffer

b) 启动Mp3文件的flash

c) 初始化文件信息

d) 初始化mp3 decoder

获取当前MP3文件的info:

tlkmdi_mp3_finfo_t *tlkmdi_mp3_getInfo(void)

根据索引值播放音乐:

bool tlkmdi_mp3_play(uint16 index)

MP3开始播放:

bool tlkmdi_mp3_start(uint08 *pFilePath, uint32 fileOffset)

关闭当前MP3:

void tlkmdi_mp3_close(void)

查询MP3是否已经结束:

bool tlkmdi_mp3_isOver(void)

查询所有的音乐是否已经播放完成:

bool tlkmdi_mp3_indexIsOver(void)

通过index指定播放某首音乐:

void tlkmdi_mp3_setPlayIndex(uint playIndex)

获取当前音乐的index:

uint tlkmdi_mp3_getPlayIndex(void)

获取音乐有多少首:

uint tlkmdi_mp3_getPlayCount(void)

获取随机播放音乐的index:

uint tlkmdi_mp3_getRandIndex(void)

获取当前音乐的下一首的index:

uint tlkmdi_mp3_getNextIndex(void)

获取当前音乐的上一首的index:

uint tlkmdi_mp3_getPrevIndex(void)

获取音乐的文件size:

uint32 tlkmdi_mp3_getFileSize(void)

获取音乐的长度:

uint32 tlkmdi_mp3_getPlayLens(void)

获取音乐的持续时间:

uint32 tlkmdi_mp3_getDuration(void)

获取音乐的进度条:

uint16 tlkmdi_mp3_getProgress(void)

获取音乐的文件名:

uint08 *tlkmdi_mp3_getFileName(uint08 *pLength)

获取歌曲的歌手信息:

uint08 *tlkmdi_mp3_getSinger(uint08 *pLength)

获取歌曲的播放模式:

uint08 tlkmdi_mp3_getPlayMode(void)

设置歌曲的播放模式:

void tlkmdi_mp3_setPlayMode(uint08 mode)

获取歌曲的采样率:

uint16 tlkmdi_mp3_getSampleRate(void)

获取音乐的Channel:

uint08 tlkmdi_mp3_getChannels(void)

清空pcm数据:

void tlkmdi_mp3_clearPcmData(void)

获取可以用来存储pcm buffer的fifo个数:

int tlkmdi_mp3_getPcmIdleLen(void)

获取pcm数据的长度:

int tlkmdi_mp3_getPcmDataLen(void)

添加pcm数据buffer到fifo中:

void tlkmdi_mp3_addPcmData(uint08 *pData, uint16 dataLen)

从fifo中获取pcm数据buffer:

int tlkmdi_mp3_getPcmData(uint08 *pBuffer, uint16 buffLen)

获取mp3文件信息:

tlkmdi_mp3_finfo_t *tlkmdi_mp3_getInfo(void)

判断当前MP3是否结束:

bool tlkmdi_mp3_isOver(void)

开始播放指定的音乐:

bool tlkmdi_mp3_play(uint16 index)

准备好解码器,清空buffer,为流数据做准备:

bool tlkmdi_mp3_start(uint08 *pFilePath, uint32 fileOffset)

关闭mp3:

void tlkmdi_mp3_close(void)

将flash中的mp3数据解码成pcm数据,并放入fifo中:

int tlkmdi_mp3_decode(void)

加载文件:

int tlkmdi_mp3_loadFile(bool isForce)

判断是否是mp3文件:

bool tlkmid_mp3_isMp3File(uint08 *pPath, uint08 lens)

查找文件list:

int tlkmdi_mp3_scanFileList(void)

查找文件的路径:

int tlkmdi_mp3_findFilePath(uint08 *pBuff, uint16 buffLen, bool addRoot)

查找文件的名称:

int tlkmdi_mp3_findFileName(uint08 *pBuff, uint16 index)

判断mp3歌曲更新:

bool tlkmdi_mp3_isUpdate(void)

判断是否需要更新:

bool tlkmdi_mp3_needUpdate(void)

开始更新:

void tlkmdi_mp3_startUpdate(void)

允许更新:

void tlkmdi_mp3_updateEnable(bool enable)

更新播放歌曲的名称:

void tlkmdi_mp3_updatePlayName(uint08 *pFName, bool isForce)

更新歌曲的长度:

void tlkmdi_mp3_updatePlayLens(uint32 playLen, bool isForce)

AudSrc设计与实现

(1) 模块简介

这个模块提供MMI使用A2DP Source功能接口,通过这个功能模块,将Flash中存储的MP3音频数据获取进行Encode,然后将压缩过的A2DP数据通过蓝牙发送出去。

(2) 运行流程

下图是A2DP SRC的音乐数据流,如下图表示:

分为Host和Controller两个部分,数据流向是从上到下。UL是上层Audio部分,数据从存储部分取出来,经过A2DP的Encoder压缩,通过加各个层级的包头,通过HCI发送到BTC,BTC加上包头之后通过RF发送给A2DP Sink设备。

AudSrc流程图

下图是代码流程图,首先要将A2DP的状态切换到Stream状态,然后开始配置A2DP encoder和Audio path,并配置获取MP3音频文件需要的参数,和各个流程中需要的Buffer,最后将Mp3的数据从Flash中取出来,并解码为PCM数据流,然后将PCM数据流经过A2DP encoder编码,通过蓝牙发送出去。

AudSrc代码流程图

下图是代码流程序列图

AudSrc代码流程序列图

(3) 接口说明

MDI audSrc设计

宏定义:

SRC packet的长度:

#define TLKMDI_SRC_PKT_BUFF_SIZE           680

压缩器的buffer size:

#define TLKMDI_SRC_SBC_ENC_BUFF_SIZE     3368

SRC帧数据个数:

#define TLKMDI_SRC_FRAME_NUMB        7

SRC reconfig的timeout:

#define TLKMDI_SRC_WAIT_RECFG_TIMEOUT      3000000

数据结构:

定义以下几种数据结构:

typedef struct{
        uint08 runing;
        uint08 enable;
        uint16 handle;
        uint32 refTime;
        uint32 lagTime;
        uint08 sndFrame;
        uint08 resv0001;
        uint16 seqNumber;
        uint32 unitTime;
        uint32 timeStamp;
        uint08 mp3State;
        uint08 waitStart;
        uint16 playIndex;
        uint32 waitTimer;
}tlkmdi_audsrc_ctrl_t;
  • runing: 是否在运行
  • enable: 是否使能
  • handle: 标记符
  • refTime: 参考时钟
  • lagTime:数据播放持续时间
  • sndFrame:压缩数据帧的个数
  • seqNumber: 数据帧的编码
  • unitTime: 一次压缩数据的持续播放时间
  • timestamp: 数据的时间戳
  • mp3State: MP3状态
  • waitStart: 标记等待开始
  • playIndex: 播放的index
  • waitTimer: 控制action的timer

数据接口:

初始化sTlkMdiSrcCtrl, 注册回调函数:

int tlkmdi_audsrc_init(void)

a2dp开始音乐数据流:

int tlkmdi_audsrc_start(uint16 handle, uint32 param)

a2dp停止音乐数据流:

int tlkmdi_audsrc_close(uint16 handle)

切换A2dp状态:

bool tlkmdi_audsrc_switch(uint16 handle, uint08 status)

播放下一首:

bool tlkmdi_audsrc_toNext(void)

播放上一首:

bool tlkmdi_audsrc_toPrev(void)

查询当前是否在使用中:

bool tlkmdi_audsrc_isBusy(void)

获取timer:

uint tlkmdi_audsrc_intval(void)

处理mp3数据:

bool tlkmdi_audsrc_handler(void)

AudSnk设计与实现

(1) 模块简介

这个模块为MMI层提供了使用A2DP SNK功能的接口,这些功能包括接收Audio数据,并对数据进行A2DP解码,并将解码的数据发送给Audio进行处理。

(2) 运行流程

下图是A2DP Sink的部分,接收数据的数据流图,数据从RF接收在BTC进行处理后,发送给Host,Host接收数据之后进行解包。最后经过A2DP Decoder解码后发送给Audio部分。

AudSnk流程图

下图是代码流程图,其流程是包含将A2DP的状态通过start流程切换到Stream状态,在Stream状态下开始配置audio path驱动和A2DP的解码器,并配置audio data在各个流程中所需要的buffer,最后开始处理音乐数据,并将处理完的音乐数据发送到Speaker。

AudSnk代码流程图

流程子序列图如下:

AudSnk流程子序列图

(3) 接口说明

MDI audSnk设计

宏定义:

定义解压缩临时buffer的size:

#define TLKMDI_SNK_DEC_TEMP_SIZE         4096

定义Raw数据的buffer size:

#define TLKMDI_SNK_RAW_RCV_BUFF_SIZE   10560

定义解压缩SBC的buffer:

#define TLKMDI_SNK_SBC_DEC_BUFF_SIZE     1248

定义采样率:

#define TLKMDI_SNK_SBC_FREQ_MASK      0xC0
#define TLKMDI_SNK_SBC_FREQ_16000     0x00
#define TLKMDI_SNK_SBC_FREQ_32000     0x40
#define TLKMDI_SNK_SBC_FREQ_44100     0x80
#define TLKMDI_SNK_SBC_FREQ_48000     0xC0

定义block个数:

#define TLKMDI_SNK_SBC_BLOCK_MASK     0x30
#define TLKMDI_SNK_SBC_BLOCK_4        0x00
#define TLKMDI_SNK_SBC_BLOCK_8        0x10
#define TLKMDI_SNK_SBC_BLOCK_12       0x20
#define TLKMDI_SNK_SBC_BLOCK_16       0x30

定义subband的个数:

#define TLKMDI_SNK_SBC_SUBBAND_MASK     0x01
#define TLKMDI_SNK_SBC_SUBBAND_4        0x00
#define TLKMDI_SNK_SBC_SUBBAND_8        0x10

定义音频模式,是单声道还是双声道还是立体环绕等等:

#define TLKMDI_SNK_SBC_TYPE_MASK            0x0C
#define TLKMDI_SNK_SBC_TYPE_MONO            0x00
#define TLKMDI_SNK_SBC_TYPE_DUAL_MODE 0x04
#define TLKMDI_SNK_SBC_TYPE_STEREO       0x08
#define TLKMDI_SNK_SBC_TYPE_JOINT_STEREO  0x0C

解压缩的fifo大小:

#define TLKMDI_SNK_ENC_FIFO_MIN_SIZE      4096

数据结构:

typedef struct{
        uint08 enable;
        uint08 firstInit;
        uint16 handle;
        uint16 frameSize;
        uint16 sampleRate;
        uint16 frameSample;
        uint32 encParma;
        tlkmdi_snk_decFunc decFunc;
}tlkmdi_audsnk_ctrl_t;
  • enable:使能a2dp snk
  • firstInit: 初始化标记
  • handle: 标记audio snk
  • frameSize: 数据帧大小
  • sampleRate: 采样率
  • frameSample: 数据帧采样数
  • encParma: 压缩参数
  • decFunc:解压缩函数

回调函数类型定义:

定义解压缩数据函数原型:

typedef int(*tlkmdi_snk_decFunc)(uint08 *pData, int len, uint08 *pOut)

接口定义:

下面接口有以下几个功能:

int tlkmdi_audsnk_init(void)

a) 初始化FIFO

b) 初始化sTlkMdiSnkCtrl

c) 注册回调函数

开始音乐播放:

int tlkmdi_audsnk_start(uint16 handle, uint32 param)

停止音乐播放:

int tlkmdi_audsnk_close(uint16 handle)

切换A2dp sink状态,在enable和disable之间转换:

bool tlkmdi_audsnk_switch(uint16 handle, uint08 status)

播放下一首:

bool tlkmdi_audsnk_toNext(void)

播放上一首:

bool tlkmdi_audsnk_toPrev(void)

查询当前A2DP sink是否使能:

bool tlkmdi_audsnk_isBusy(void)

获取interval:

uint tlkmdi_audsnk_intval(void)

处理sink端处理过的PCM数据:

bool tlkmdi_audsnk_handler(void)

添加pcm数据到fifo:

void tlkmdi_snk_addEncFrame(uint08 *pData, uint16 dataLen)

设置buffer到fifo:

int   tlkmdi_snk_setBuffer(uint08 *pBuffer, uint32 buffLen)

获取timer value:

uint tlkmdi_snk_getTimer(void)

AudPlay设计与实现

(1) 模块简介

这个模块主要给MMI Audio提供本地音乐播放的接口,和一些逻辑处理,包括如何启动本地音乐的播放,如何调用mp3和配置decoder,Audio相关通路的设置和音乐数据如何处理。

(2) 运行流程

AudPlay流程图

AudPlay序列图

(3) 接口说明

MDI audPlay设计

宏定义:

定义音量的变化的step:

#define TLKMDI_PLAY_VOLUME_STEP       3

数据结构:

typedef struct{
        uint08 runing;
        uint08 status;
        uint08 enable;
        uint08 curState;
        uint16 playIndex;
        uint16 sampleRate;
}tlkmdi_play_ctrl_t;
  • runing:play是否已经执行
  • Status:链接状态
  • enable:使能mp3播放
  • curState:mp3的状态
  • playIndex:音乐播放的索引
  • sampleRate:音频数据的采样率

接口定义:

下面接口有以下几个功能:

int  tlkmdi_audplay_init(void)

a) 初始化Play的控制数据

b) 初始化音乐播放的歌曲索引

开始播放音乐:

int tlkmdi_audplay_start(uint16 handle, uint32 param)

停止音乐播放:

int tlkmdi_audplay_close(uint16 handle)

播放下一首:

bool tlkmdi_audplay_toNext(void)

播放上一首:

bool tlkmdi_audplay_toPrev(void)

下面接口有以下几个功能:

bool tlkmdi_audplay_switch(uint16 handle, uint08 status)

a) 更新音乐的状态

b) 获取mp3的数据更新Play控制数据

c) 初始化codec

查询当前是否在播放:

bool tlkmdi_audplay_isBusy(void)

查询play的interval用来更新timer:

uint tlkmdi_audplay_intval(void)

player的处理函数:

bool tlkmdi_audplay_IrqProc(void)

配置当前状态和播放索引:

void tlkmdi_play_setParam(uint16 fileIndex)

AudTone设计与实现

(1) 模块简介

这个模块主要给MMI Audio提供tone的接口,和一些逻辑处理,包括如何启动Tone音的播放,如何调用mp3和配置decoder,Audio通路的设置和Tone的数据如何处理。

(2) 运行流程

AudTone流程图

AudTone序列图

接口说明

MDI audTone设计

数据结构:

typedef struct{
        uint08 runing;
        uint08 enable;
        uint08 curState;
        uint08 playNumb;
        uint08 playCount;
        uint16 playIndex;
}tlkmdi_tone_ctrl_t;
  • runing:标记是否运行
  • enable: 标记是否使能
  • curState:标记当前状态
  • playNumb: 已经播放的个数
  • playCount: 总播放的个数
  • playIndex: 当前播放的index
typedef enum{
        TLKMDI_TONE_TYPE_CONNECT = 0x0,                
        TLKMDI_TONE_TYPE_DISCONN,
        TLKMDI_TONE_TYPE_CALL_RING,
        TLKMDI_TONE_TYPE_BI,
        TLKMDI_TONE_TYPE_BO,
        TLKMDI_TONE_TYPE_DING, 
        TLKMDI_TONE_TYPE_DING_DING,
        TLKMDI_TONE_TYPE_MAX
}TLKMDI_TONE_TYPE_ENUM;
  • TLKMDI_TONE_TYPE_CONNECT:链接状态
  • TLKMDI_TONE_TYPE_CONNECT:断开连接状态
  • TLKMDI_TONE_TYPE_CALL_RING:标记电话响铃
  • TLKMDI_TONE_TYPE_BI,TLKMDI_TONE_TYPE_BO,TLKMDI_TONE_TYPE_DING, TLKMDI_TONE_TYPE_DING_DING:提示音类型

接口定义:

下面接口有以下几个功能:

int  tlkmdi_audtone_init(void)

a) 初始化sTlkMdiToneCtrl

b) 获取Tone播放的index和总播放的tone个数

配置sTlkMdiToneCtrl的播放数据:

int tlkmdi_audtone_start(uint16 handle, uint32 param)

切换tone的状态到Close:

int tlkmdi_audtone_close(uint16 handle)

查询当前Tone是否正在使用:

bool tlkmdi_audtone_isBusy(void)

根据当前的音乐数据,来返回Timer的timeout,保证音乐数据源源不断:

uint tlkmdi_audtone_intval(void)

下面接口有以下几个功能:

bool tlkmdi_audtone_switch(uint16 handle, uint08 status)

a) 使能mp3

b) 初始化codec并配置codec采样率

c) 配置Tone的控制数据

Tone的流程和数据处理:

bool tlkmdi_audtone_IrqProc(void)

下面接口有以下几个功能:

bool tlkmdi_tone_start(uint16 index)

a) 从flash中取出要播放的文件

b) 调用mp3的start,配置要播放文件的信息,和清空fifo

c) 配置decoder

关闭mp3:

void tlkmdi_tone_close(void)

设置播放的Tone的index和总的tone的个数:

void tlkmdi_tone_setParam(uint16 playIndex, uint08 playCount)

电话模块设计

HFP设计与实现

(1) 模块简介

这个模块主要提供和电话相关的接口,比如拨打、接听、挂断电话等,同时也会提供PBAP的接口。与AudHF模块的功能的区别是,AudHF只负责Audio相关的部分,不负责电话相关的功能。

(2) 状态转换

HF状态转换

(3) 运行流程

这是HFP HF拨打电话流程的原理图,如下:

HF拨打电话原理图

下图为拨打电话的流程图:

拨打电话流程图

下图是来电的原理图:

HF来电原理图

下图是来电接听的流程图

来电接听流程图

下图是HF拨号和挂断电话的代码流程序列图

HF拨号和挂断电话

接口说明

MDI HF设计

宏定义:

HF的TimeOut时长:

#define TLKMDI_AUDHF_TIMEOUT                100000

Call等待timeout时长:

#define TLKMDI_AUDHF_CALL_WAIT_TIMEOUT      (3000000/TLKMDI_AUDHF_TIMEOUT)

Call phone number长度:

#define TLKMDI_HFPHF_NUMBER_MAX_LEN                                        64

数据结构:

定义以下几种数据结构:

typedef enum{
        TLKMDI_HFPHF_EVTID_NONE = 0,
        TLKMDI_HFPHF_EVTID_CALL_CLOSE,
        TLKMDI_HFPHF_EVTID_CALL_START,
        TLKMDI_HFPHF_EVTID_CALL_ALART,
        TLKMDI_HFPHF_EVTID_CALL_ACTIVE,
}TLKMDI_HFPHF_EVTID_ENUM;
  • TLKMDI_HFPHF_EVTID_CALL_CLOSE电话挂断
  • TLKMDI_HFPHF_EVTID_CALL_START开始打电话
  • TLKMDI_HFPHF_EVTID_CALL_ALART电话响铃
  • TLKMDI_HFPHF_EVTID_CALL_ACTIVE电话接通

定义以下几种数据结构:

typedef enum{
        TLKMDI_HFPHF_CALL_DIR_NONE = 0,
        TLKMDI_HFPHF_CALL_DIR_INCOMING,
        TLKMDI_HFPHF_CALL_DIR_OUTGOING,
}TLKMDI_HFPHF_CALL_DIR_ENUM;
  • TLKMDI_HFPHF_CALL_DIR_INCOMING呼入电话
  • TLKMDI_HFPHF_CALL_DIR_OUTGOING呼出电话

定义以下几种数据结构:

typedef enum{
        TLKMDI_HFPHF_CALL_STATUS_NONE = 0,
        TLKMDI_HFPHF_CALL_STATUS_START,
        TLKMDI_HFPHF_CALL_STATUS_ALART,
        TLKMDI_HFPHF_CALL_STATUS_ACTIVE,
}TLKMDI_HFPHF_CALL_STATUS_ENUM;
  • TLKMDI_HFPHF_CALL_STATUS_START 开始打电话
  • TLKMDI_HFPHF_CALL_STATUS_ALART 等待接通
  • TLKMDI_HFPHF_CALL_STATUS_ACTIVE 接通电话

定义以下几种数据结构:

typedef enum{
        TLKMDI_HFPHF_CALL_BUSY_NONE = 0x00,
        TLKMDI_HFPHF_CALL_BUSY_WAIT_NUMBER = 0x01,
}TLKMDI_HFPHF_CALL_BUSYS_ENUM;

TLKMDI_HFPHF_CALL_BUSY_WAIT_NUMBER 标记需要重新执行获取电话号码的action

定义以下几种数据结构:

typedef enum{
        TLKMDI_HFPHF_CALL_FLAG_NONE   = 0x00,
        TLKMDI_HFPHF_CALL_FLAG_CLOSE  = 0x01,
        TLKMDI_HFPHF_CALL_FLAG_START  = 0x02,
        TLKMDI_HFPHF_CALL_FLAG_ALART  = 0x04,
        TLKMDI_HFPHF_CALL_FLAG_ACTIVE = 0x08,
        TLKMDI_HFPHF_CALL_FLAG_NUMBER = 0x10,
        TLKMDI_HFPHF_CALL_FLAG_REPORT_START  = 0x20,
        TLKMDI_HFPHF_CALL_FLAG_REPORT_ACTIVE = 0x40,
        TLKMDI_HFPHF_CALL_FLAG_REPORT_CLOSE  = 0x80,
        TLKMDI_HFPHF_CALL_FLAG_STATUS_MASK = TLKMDI_HFPHF_CALL_FLAG_START | TLKMDI_HFPHF_CALL_FLAG_ALART 
                | TLKMDI_HFPHF_CALL_FLAG_ACTIVE | TLKMDI_HFPHF_CALL_FLAG_CLOSE,
        TLKMDI_HFPHF_CALL_FLAG_REPORT_MASK = TLKMDI_HFPHF_CALL_FLAG_REPORT_START | TLKMDI_HFPHF_CALL_FLAG_REPORT_ACTIVE
                | TLKMDI_HFPHF_CALL_FLAG_REPORT_CLOSE,
}TLKMDI_HFPHF_CALL_FLAGS_ENUM;
  • TLKMDI_HFPHF_CALL_FLAG_CLOSE标记Call Close事件
  • TLKMDI_HFPHF_CALL_FLAG_START标记Call start事件
  • TLKMDI_HFPHF_CALL_FLAG_ALART标记Call Alart事件
  • TLKMDI_HFPHF_CALL_FLAG_ACTIVE标记Active事件
  • TLKMDI_HFPHF_CALL_FLAG_NUMBER标记电话号码事件
  • TLKMDI_HFPHF_CALL_FLAG_REPORT_START 标记是否上报电话已经开始状态
  • TLKMDI_HFPHF_CALL_FLAG_REPORT_CLOSE 标记是否上报电话关闭状态
  • TLKMDI_HFPHF_CALL_FLAG_REPORT_ACTIVE 标记是否上报电话激活状态
  • TLKMDI_HFPHF_CALL_FLAG_STATUS_MASK 状态掩码
  • TLKMDI_HFPHF_CALL_FLAG_REPORT_MASK 标记是否上报状态掩码

定义以下几种数据结构:

typedef struct{
        uint08 status;
        uint16 handle;
        uint08 timeout;
        uint08 numbLen;
        uint08 callDir;
        uint08 callBusy;
        uint08 callFlag;
        uint08 reserved;
        uint16 callHandle;
        tlkapi_timer_t timer;
        uint08 number[TLKMDI_HFPHF_NUMBER_MAX_LEN];
}tlkmdi_audhf_ctrl_t;
  • status:标记电话状态
  • handle:标记控制块
  • timeout:timeout时长
  • numbLen:电话号码长度
  • callDir:电话呼入/呼出
  • callBusy:记录当前未及时执行的action
  • callFlag:记录当前上报的事件
  • callHandle:电话handle
  • timer:控制块timer
  • number:电话号码

定义以下几种数据结构:

typedef struct{
        uint08 codec;
        uint16 handle;
}tlkmdi_audhf_codecEvt_t;
  • Codec: SCO的codec
  • handle:sco 的handle

定义以下几种数据结构:

typedef struct{
        uint16 handle;
        uint08 callDir;
        uint08 numbLen;
}tlkmdi_audhf_statusEvt_t;
  • handle:标记handle
  • callDIr:呼入/呼出
  • numbLen:电话长度

接口定义:

初始化timer,注册回调:

int tlkmdi_audhf_init(void)
int tlkmdi_audhf_start(uint16 handle)
int tlkmdi_audhf_close(uint16 handle)

销毁handle指定的hf资源:

void tlkmdi_audhf_destroy(uint16 aclHandle)

查询当前的状态是否已经打开:

bool tlkmdi_audhf_isBusy(void)

获取HF的interval:

uint tlkmdi_audhf_intval(void)

切换HF的状态,enable/disable:

bool tlkmdi_audhf_switch(uint16 handle, uint08 status)

查询Audio 是否处于OPEN状态:

bool tlkmdi_audhf_handler(void)

获取phone number:

uint08 *tlkmdi_audhf_getCallNumber(void)

PBAP设计与实现

(1) 模块简介

PBAP模块提供获取对方设备电话本的功能接口。其链接流程如下所示,PCE是Client,PSE是Server。PBAP会在ACL建立完成之后被应用程序触发执行。

(2) 运行流程

PBAP链接流程

PBAP流程图

(3) 接口说明

TBD

Audio音量调节流程

目前已经将Audio的各个业务的音量独立操作,相互不会有影响。

Audio音量调节

USB模块设计

Telink USB支持标准的USB1.1协议,传输速度支持低速及全速。采用外部供电,DM与DP引脚与GPIO口复用(DM-PA5,DP-PA6),如果用户不需要使用USB功能,也可以将DM与DP管脚配为普通的GPIO接口。支持的设备类包括UDB、AUD、CDC、HID、USR、MSC,用户可通过 tlk_config.h中的 TLK_USB_xxx_ENABLE宏来改变设备类的可用性。

基础知识

Telink USB共有9个端点,端点0可同时用作输入和输出,其他端口只能用作输入或输出。端点0只能用作控制传输,包含建立阶段,数据阶段和状态阶段。...用户可在接口描述符中使用bNumEndpoints来配置当前接口支持的端点数量。 Telink USB使用8+256Bytes的USB专属RAM来缓存各个端点的数据。端点0只支持8Bytes,其余端点共享256Bytes。具体端点资源分配用户可参考Telink USB手册Basic Knowledge and Application of Telink USB。

Telink USB的运行可以分为两阶段,初始化阶段和循环检测阶段。 1.初始化阶段 使能USB,完成USB的相关配置并初始化USB的UDB、AUD、CDC、HID、USR、MSC模式,Telink USB默认为UDB模式。 2.循环检测阶段 循环检测阶段主要是不断地检测数据接收和数据发送缓冲区是否有数据,然后根据对应设备类定义的接收/发送数据来完成数据的接收与发送过程。

(1) 运行流程

USB运行流程0

设备控制及描述符获取流程(以MSC设备为例):

USB运行流程1

USB运行流程2

(2) 数据发送

只有具备输入(Device -> Host)功能的端点才具备发送数据的功能。首先,用户发送数据时,应先通过reg_usb_ep_ctrl(i)确定对应端口的繁忙状态,若此端口处于忙状态,则无法发送信息,若此端口处于空闲状态,则先reset此端口,随后通过reg_usb_ep_dat(i)往端口数据寄存器写入数据,数据长度由用户自己控制,但是注意不要超过在该端口的端口描述符中设置的数据最大长度。用户填充完数据后,将端口对应状态改为繁忙,随后,硬件会通过一个IN事务将数据传送给Host,并清除对应端口的繁忙状态。

USB数据发送序列图

(3) 数据接收

只有具备输出(Host -> Device)功能的端口才具备接收数据的功能。设备接收到数据时,首先需要通过读取reg_usb_ep_ptr(i)对于寄存器的值获取到设备接收到数据的长度,随后将该寄存器置0。然后循环通过读取reg_usb_ep_dat(i)获取到接收数据,并将数据存入接收数据缓存区,最后,通过操作reg_usb_ep_ctrl(i)将对应端点的ack状态置1。

USB数据发送序列图

(4) 挂载设备

USB挂载设备序列图

通用接口

** 设备描述符 **

typedef struct{
        uint08 bLength;
        uint08 bDescriptorType;
        uint16 bcdUSB; 
        uint08 bDeviceClass; 
        uint08 bDeviceSubClass; 
        uint08 bDeviceProtocol;
        uint08 bMaxPacketSize0;
        uint16 idVendor; 
        uint16 idProduct; 
        uint16 bcdDevice; 
        uint08 iManufacturer; 
        uint08 iProduct; 
        uint08 iSerialNumber;
        uint08 bNumConfigurations; 
}__attribute__((packed)) tlkusb_stdDeviceDesc_t;
  • bLength:设备描述符的字节数长度
  • bDescriptorType:描述符的类型,这里指设备描述符。
  • bcdUSB:支持的USB协议,目前支持USB1.1
  • bDeviceClass:USB设备所属的设备类
  • bDeviceSubClass:USB设备所属的设备子类
  • bDeviceProtocol:USB设备所属的设备类协议
  • bMaxPacketSize0:指明该USB设备的控制端点(端点0)所支持的最大数据包长度,单位为Byte,Telink USB中为8
  • idVendor:厂商编号,VID
  • idProduct:产品编号,PID
  • bcdDevice:USB设备版本号,即产品发布的版本号
  • iManufacturer:描述厂商信息的字符串描述符的索引值
  • iProduct:描述设备信息的字符串描述符的索引值
  • iSerialNumber:描述设备序列号信息的字符串描述符的索引值
  • bNumConfigurations:指明USB设备所支持的配置数,同一时刻只能使用一个配置。

** 配置描述符 **

typedef struct
{
    uint08 bLength;
    uint08 bDescriptorType; 
    uint16 wTotalLength;
    uint08 bNumInterfaces; 
    uint08 bConfigurationValue; 
    uint08 iConfiguration; 
    uint08 bmAttributes; 
    uint08 bMaxPower; 
}__attribute__((packed)) tlkusb_stdConfigureDesc_t;
  • bLength:配置描述符的字节长度
  • bDescriptorType:描述符的类型,这里指的是配置描述符
  • wTotalLength:配置信息的总长度,指此配置所包含的配置描述符,接口描述符及端点描述符的总大小
  • bNumInterfaces:接口数量,最小为1
  • bConfigurationValue:指明该配置的配置值,例如值为1,在标准描述符枚举完成后,主机发送Setconfiguration(x),x=1时就激活该配置
  • iConfiguration:描述该配置信息的字符串描述符的索引值
  • bmAttributes:供电模式选择:bit[4~0]-保留,必须为0,bit[7]-总线供电(通常为1),bit[6]-自供电(0-自供电,1-总线供电),bit[5]-远程唤醒(0-不支持 1-支持)
  • bMaxPower:USB设备需要的最大电流,可通过TLKUSB_CONFIG_POWER(mA)配置,若主机无法提供该电流,则此配置无法使用

** 接口描述符 **

typedef struct{
    uint08 bLength; 
    uint08 bDescriptorType;
    uint08 bInterfaceNumber; 
    uint08 bAlternateSetting; 
    uint08 bNumEndpoints; 
    uint08 bInterfaceClass; 
    uint08 bInterfaceSubClass; 
    uint08 bInterfaceProtocol; 
    uint08 iInterface; 
}__attribute__((packed)) tlkusb_stdInterfaceDesc_t;
  • bLength:接口描述符的字节长度
  • bDescriptorType:描述符类型,此处为接口描述符
  • bInterfaceNumber:接口的编号,接口编号在该配置中不能重复使用,通常描述同一功能的接口编号应该紧邻。
  • bAlternateSetting:指明接口的可替换设置值,用于为上一个字段选择可供替换的位置,即备用的接口描述符标号
  • bNumEndpoints:指明该接口使用的端点总数,端点0除外
  • bInterfaceClass:当前接口所属的接口类
  • bInterfaceSubClass:当前接口所属的接口子类
  • bInterfaceProtocol:该接口的接口类协议
  • iInterface:描述该接口信息的字符串描述符的索引值

** 端点描述符 **

typedef struct{
    uint08 bLength; 
    uint08 bDescriptorType;
    uint08 bEndpointAddress; 
    uint08 bmAttributes;
    uint16 wMaxPacketSize; 
    uint08 bInterval;
}__attribute__((packed)) tlkusb_stdEndpointDesc_t;
  • bLength:端点描述的字节数长度
  • bDescriptorType:设备描述符,此处为端点描述符
  • bEndpointAddress:USB设备的端点地址,bit[7] -传输方向(1-IN输入 0-OUT输出),bit[6~4]-保留,必须为0,bit[3~0]端点编号,端点编号该配置中不能重复
  • bmAttributes:端点属性,bit[0~1]-传输类型(00-控制,01-同步,10-批量,11-中断),bit[2~3]-同步类型,只在同步传输时有效(00-非同步,01-异步,10-自适应,11-同步),bit[4~5]-端点用法类型(同步传输时,00-数据端点,01-反馈端点,10-显式反馈数据端点,11-保留),bit[6~7]-保留(需为0)
  • wMaxPacketSize:该段的接收或发送数据的最大数据包长度,单位为字节(这里适用于全速/低速模式)
  • bInterval:主机轮询该端点的间隔。只在同步模式或中断模式时需要关注。同步传输下必须为1,中断传输下:低速模式为10~255(ms),全速模式为1~255(ms)

** 字符串描述符 **

typedef struct{
    uint08 bLength; 
    uint08 bDescriptorType; 
    uint16 bString[]; 
}__attribute__((packed)) tlkusb_stdStringDesc_t;
  • bLength:字符串描述符的字节数长度
  • bDescriptorType:描述符的类型,此处为字符串描述符
  • bString:字符串数据,使用Unicode编码类型

setup包处理函数

void tlkusb_ctrlTranSetupReqProc(bool isSetupReq);

tlkusb_getxxxLens:获取对应的描述符长度 tlkusb_getxxxDesc:获取对应的描述符

uint16 tlkusb_getLanguageLens(void);
uint16 tlkusb_getProductLens(void);
uint16 tlkusb_getVendorLens(void);
uint16 tlkusb_getSerialLens(void);
uint16 tlkusb_getDeviceLens(void);
uint16 tlkusb_getConfigLens(void);
uint08 *tlkusb_getLanguageDesc(void);
uint08 *tlkusb_getVendorDesc(void);
uint08 *tlkusb_getProductDesc(void);
uint08 *tlkusb_getSerialDesc(void);
uint08 *tlkusb_getDeviceDesc(void);
uint08 *tlkusb_getConfigDesc(void);

UDB设计与实现

(1) 模块简介

Telink USB中UDB设备主要实现了两个接口,分别对应UBG和VCD服务。用于实现USB 输出,UBG服务主要实现log输出,用于开发人员的调试,VCD服务配合上位机工具使用,用于实现log的图形化显示。UDB设备接口描述符中的bInterfaceClass=0x07,即TLKUSB_PRNT_CSCP_PrinterClass。 UBG服务使用端点3作为IN端点,端点5作为OUT端点,均采用批量传输。 VCD服务使用端点8作为IN端点,端点6作为OUT端点,均采用批量传输。

(2) 接口说明

用于udb设备发送数据

int tlkusb_udb_sendData(uint08 *pData, uint08 dataLen);

循环检测数据的接收

void tlkusb_udb_recvHander(void);

处理接收到的数据

bool tlkusb_udb_recvDatDeal(void);

处理接收到的指令

void tlkusb_udb_recvCmdProc(uint08 *pData, uint16 dataLen, bool *pIsDown);

UAC设计与实现

(1) 模块简介

UAC(USB Audio Class)两个主要模块分别为音频控制模块和音频流模块。这两个模块分别对应一系列的相关描述符。其中UAC的音频控制模块就是实现的是音频的控制功能,同时显示音频的拓扑结构。而音频流模块主要实现的是数据传输。Telink usb目前支持UAC1.0,支持两种音频功能:Microphone和Speaker,注意,当前阶段如需使用音频功能,需要将TLKUSB_UAC_SPK_ENABLE与TLKUSB_UAC_MIC_ENABLE同时打开。设备中包含一个AudioControl接口(接口0)和两个音频流接口(接口1:用于SPK;接口2:用于MIC)以及用于音频功能控制和音频数据流传输的相关端点。Mic使用端点7作为OUT端点,Spk使用端点6作为IN端点,都包含对应的输入终端、输出终端及特性单元。 每个音频流接口具有两个转换接口:转换接口0是通电后的默认设置。此转换接口是零带宽接口,用于释放USB上为此设备分配的所有带宽,即UAC非工作态。转换接口1是包含一个同步等时USB端点,用于工作时的数据传输,即UAC工作态。每个音频流接口都需要通过类特定的音频流接口描述符来指明通信过程中使用的音频数据格式。

(2) 接口说明

typedef struct{
        uint08 Length;
        uint08 DescriptorType;
        uint08 DescriptorSubtype;
        uint08 TerminalID;         
        uint16 TerminalType;        
        uint08 AssocTerminal;        
        uint08 TotalChannels;        
        uint16 ChannelConfig;
        uint08 ChannelStrIndex;
        uint08 TerminalStrIndex;      
}__attribute__((packed)) tlkusb_uacInputDesc_t;
- Length:描述符长度 - DescriptorType:描述符类型,此处为0x24,表示类指定的接口描述符 - DescriptorSubtype:描述符的子类型,此处为0x02,表示输入终端 - TerminalID:输入终端的ID,唯一 - TerminalType:输入终端的类型 - AssocTerminal:用于将指定ID的输出终端与该输入终端关联,如果无关联的输出终端,则置0 - TotalChannels:音频输出集的逻辑通道个数 - ChannelConfig:通道的属性配置 - ChannelStrIndex:字符串描述符的索引,用来描述第一个逻辑通道的名称。 - TerminalStrIndex:该输入终端的字符串索引

typedef struct{
        uint08 Length;
        uint08 DescriptorType;                
        uint08 DescriptorSubtype;       
        uint08 TerminalID;                        
        uint16 TerminalType;                
        uint08 AssocTerminal;                
        uint08 SourceID;                        
        uint08 Terminal;                        
}__attribute__((packed)) tlkusb_uacOutputDesc_t;
- Length:描述符长度 - DescriptorType:描述符类型,此处为0x24,表示类指定的接口描述符 - DescriptorSubtype: 描述符的子类型,此处为0x03,表示为输出终端 - TerminalID:输出终端的ID,唯一 - TerminalType:输出终端的类型 - AssocTerminal:关联的终端 - SourceID:该输出终端连接的单元/终端ID - Terminal:输出终端的字符串索引

typedef struct{
        uint08 Length;
        uint08 DescriptorType;
        uint08 EndpointAddress;
        uint08 MAttributes;     
        uint16 MaxPacketSize;
        uint08 Interval;     
        uint08 Refresh;           
        uint08 SynchAddress;
}__attribute__((packed)) tlkusb_uacStdEndpointDesc_t;
- Length:描述符长度 - DescriptorType:描述符类型 - EndpointAddress:端点地址。BIT7=1表示输入端点,BIT7=0表示输出端点。BIT4-6:保留,置为0。BIT0-3:表示端点地址。 - MAttributes:数据同步方式,bit[1-0]:01表示等时传输,bit[3-2]:01-异步,10-自适应,11-同步 - MaxPacketSize:端点的数据传输的最大包长度 - Interval:置1 - Refresh:置0 - SynchAddress:用于传输同步信息的端点地址,不需要则置0

typedef struct{
        uint08 Length;        
        uint08 DescriptorType;        
        uint08 DescriptorSubtype;
        uint08 MAttributes;
        uint08 LockDelayUnits;
        uint08 LockDelay[2]; //uint16 LockDelay;
}__attribute__((packed)) tlkusb_uacSpcEndpointDesc_t;
- Length:描述符大小 - DescriptorType; //描述符类型,CS_ENDPOINT=0x25 - DescriptorSubtype:描述符子类型,EP_GENERAL=0x01 - MAttributes:BIT0代表是否支持采样率调整(Sampling Frequency)。BIT1代表是否支持高音调整(PITCH)。BIT7代表是否只支持wMaxPacketSize的传输(MaxPacketsOnly)。其他BIT保留 - LockDelayUnits:LockDelay的单位,0-Undefined) 1-Millisecond 2-Decoded PCM samples 3..255-Reserved - LockDelay[2]:代表该USB设备在主机设置后需要多久才能达到时钟稳定

typedef struct{
        uint08 Length;        
        uint08 DescriptorType;       
        uint08 DescriptorSubtype;        
        uint08 FormatType;               
        uint08 NrChannels;              
        uint08 SubFrameSize;       
        uint08 BitResolution;       
        uint08 SampleFrequencyType;
        uint08 tSamFreq[3];
}__attribute__((packed)) tlkusb_uacFormatDesc_t;
- Length:描述符长度 - DescriptorType:描述符类型 - DescriptorSubtype:该描述符种类为音频类特有的接口 - FormatType:该接口具体数据格式
- NrChannels:表示该接口支持的物理声道数 - SubFrameSize:每通道数据的字节数,可以为1,2,3,4 - BitResolution: bSubframeSize中的有效位数。 - SampleFrequencyType:表示采样类型。0表示连续采样频率,非0表示支持的离散采样频率个数 - tSamFreq[3]:由SampleFrequencyType决定的采样率的数量

typedef struct
{
    uint08 Length;
        uint08 DescriptorType;
        uint08 DescriptorSubtype;
        uint08 UnitID;
        uint08 SourceID;
        uint08 ControlSize;
        uint08 MAControls[ch+1];
        uint08 FeatureUnitStrIndex;
}__attribute__((packed)) tlkusb_uacXxxxFeatureDesc_t;
- Length:描述符总长度 - DescriptorType:描述符类型 CS_INTERFACE = 0x24 - DescriptorSubtype:描述符子类型,FEATURE_UNIT = 0x06 - UnitID::常数,唯一地识别音频功能中的单元。此值用于所有处理此单元的请求。 - SourceID:此功能单元所连接到的单元或终端的ID。 - ControlSize:MAControls数组中一个元素的字节大小 - MACControl:对应bit设置为1表示逻辑通道ch支持所提到的控制。 - FeatureUnitStrIndex:特性单元描述符的字符串索引

typedef struct{
        uint08 bLength; 
        uint08 bDescriptorType;
        uint08 Subtype;
        uint08 ACSpecification[2];
        uint08 TotalLength[2];        
        uint08 InCollection;       
        uint08 InterfaceNumber;   
}__attribute__((packed)) tlkusb_uacInterfaceAcDesc_t;
- bLength:该接口描述符的字节长度 - bDescriptorType:描述符类型,此处为0x24,表示类指定的接口描述符 - Subtype:描述符的子类型,此处为0x01,表示头描述符子类型 - ACSpecification:UAC协议的版本,此处为1.0 - TotalLength:总长度,包括自身及后续的如单元描述符,终端描述符。 - InCollection:所有的音频流接口数量 - InterfaceNumber:音频流的接口索引,多个依次后续连接。

typedef struct{
        uint08 Length;
        uint08 DescriptorType;
        uint08 DescriptorSubtype;
        uint08 TerminalLink;    
        uint08 Delay;
        uint08 AudioFormat[2];
}__attribute__((packed)) tlkusb_uacInterfaceAsDesc_t;
- Length:描述符的字节长度 - DescriptorType:描述符类型,此处为0x24 - DescriptorSubtype:描述符的子类型,此处为0x01,表示AS_GENERAL - TerminalLink:此接口的端点连接到的终端的终端ID。 - Delay:延迟 - AudioFormat:必须用于与此接口进行通信的音频数据格式。

CDC设计与实现

(1) 模块简介

CDC(Communication Device Class)类是USB通信设备类的简称,其配置描述符一般由两个接口子类:通信接口类(Communication Interface Class)和数据接口类(Data Interface Class)组成。 通信接口主要用于对设备的管理与控制,需要一个控制终端点(Control Endpoint)和一个可选的中断(Interrupt)型终端点。控制端点用于实现USB设备的枚举过程以及虚拟串口的波特率和数据类型(数据位数、停止位和起始位)的配置。 数据接口用于进行数据的传输过程,需要一对输入(IN)端点、输出(OUT)端点来模拟传统串口的RX与TX引脚。OUT端点用于Host向Slave发送数据,IN端点用于Slave向Host发送数据。 Telink USB的CDC设备主要用于提供虚拟串口服务,需要安装串口驱动才可正常使用该功能。支持两路CDC,一路使用端点1作为控制端点,端点3作为IN端点,端点6作为OUT端点;一路使用端点2作为控制端点,端点4作为IN端点,端点5作为OUT端点。

(2) 接口说明

typedef struct{
        tlkusb_cdcFunctionHead_t  cciHead;
        tlkusb_cdcFunctionAcm_t   cdcACM;
        tlkusb_cdcFunctionUnion_t cdcUnion;
        tlkusb_cdcFunctionUnion_t cdcCall;
        tlkusb_stdEndpointDesc_t  cdcNotyEdp;
        tlkusb_stdInterfaceDesc_t dciInf;
        tlkusb_stdEndpointDesc_t  dciOutEdp;
        tlkusb_stdEndpointDesc_t  dciInEdp;
}tlkusb_cdcFunctionsDesc_t;
- cciHead:头函数描述符,标记了接口的函数描述符连接集的开始。 - cdcACM:功能类型描述符 - cdcUnion:联合功能描述符,描述了一组可以被认为是组成一个功能单元的接口之间的关系。 - cdcCall:Call management描述符 - dciInf:标准接口描述符 - dciOutEdp:输出端点描述符 - dciInEdp:输入端点描述符

typedef struct{
        uint08 bFunctionLength;       
        uint08 bDescriptorType;       
        uint08 bDescriptorSubType;       
        uint16 bcdCDC;     
}tlkusb_cdcFunctionHead_t;
- bFunctionLength:描述符字节长度 - bDescriptorType:描述符类型 - bDescriptorSubType:子类型 - bcdCDC:CDC规范版本

typedef struct{
        uint08 bFunctionLength;
        uint08 bDescriptorType;
        uint08 bDescriptorSubType;
        uint08 bmCapabilities;
}tlkusb_cdcFunctionAcm_t;
- bFunctionLength:描述符字节长度 - bDescriptorType:描述符类型 - bDescriptorSubType:子类型 - bmCapabilities:功能类型

typedef struct{
        uint08 bFunctionLength;                
        uint08 bDescriptorType;               
        uint08 bDescriptorSubType;       
        uint08 bMasterInterface;        
        uint08 bSlaveInterface0;        
}tlkusb_cdcFunctionUnion_t;
- bFunctionLength:描述符字节长度 - bDescriptorType:描述符类型 - bDescriptorSubType:子类型 - bMasterInterface:关联接口id - bSlaveInterface0:关联接口id

typedef struct{
        uint08 bLength; 
        uint08 bDescriptorType; 
        uint08 bFirstInterface;
        uint08 bInterfaceCount; 
        uint08 bFunctionClass; 
        uint08 bFunctionSubClass;
        uint08 bFunctionProtocol; 
        uint08 iFunction; 
}__attribute__((packed)) tlkusb_stdAssociateDesc_t;
- bLength:描述符字节长度 - bDescriptorType:描述符类型 - bFirstInterface:第一个关联接口的id - bInterfaceCount:关联接口的数量 - bFunctionClass:接口类 - bFunctionSubClass:接口子类 - bFunctionProtocol:接口协议类型 - iFunction:字符串索引

HID设计与实现

(1) 模块简介

HID(Human Interface Device)即人机接口设备,是USB设备中常用的设备类型,是直接与人交互的USB设备,例如键盘、鼠标等。Telink USB支持HID键鼠设备,通过USB的控制管道(默认管道,即端点0)传输USB描述符、类请求代码以及供查询的消息数据,中断管道(使用端点1作为鼠标输入端点,端点2作为键盘输入端点)与主机进行数据传输。

HID设备除了标准描述符外,有三种特有的描述符:HID描述符、报告描述符、实体描述符。

HID描述符包括两种描述符,分别为物理描述符集合和报告描述符,也叫报表描述符。

  • 物理描述符集合是可选的描述符,提供用于激活设备控件的人体一个或多个部分的信息。
  • 报告描述符用于描述符USB HID设备上报的数据信息格式。
  • USB主机在请求HID设备的配置描述符时,设备需要按照顺序返回下面几种描述符:配置描述符, 接口描述符,HID描述符, 端点描述符。HID描述符里又包含了其附属的描述符的类型和长度(如报告描述符),然后主机再根据HID描述符的信息请求其相关的描述符。

(2) 接口说明

typedef struct{
        uint08 Size;
        uint08 Type;
        uint16 HIDSpec;       
        uint08 CountryCode;
        uint08 TotalReportDescriptors;
        uint08 HIDReportType;
        uint16 HIDReportLength
}tlkusb_HidEndpointDesc_t; 
- Size:描述符字节长度 - Type:描述符类型,此处为0x21,表示为HID描述符 - HIDSpec:该HID设备符合的协议版本号 - CountryCode:硬件设备所在国家的国家代码 - TotalReportDescriptors:类别描述符的数量,至少有一个报表描述符 - HIDReportType:该HID描述符附属的描述符的类型 - HIDReportLength:该HID描述符附属的描述符的字节长度

MSC设计与实现

(1) 模块简介

MSC(Mass Storage Class)即大容量存储,按照USB规范,MSC设备也是通过默认管道(地址0 、端点 0 )进行枚举,枚举后重新分配地址,再次枚举。 枚举后,实际工作中使用批量端点传送数据 / 命令 / 状态。MSC设备的端点描述符中需要包含一对In 和 Out 端点,在进行数据收发的时候一定要从相应的端点进行。

Telink USB中的MSC设备使用端点1作为IN端点,端点5作为OUT端点。设备类型定义在接口描述符中,bDeviceClass、bDeviceSubClass、bDeviceProtocol三个属性在其设备描述符中的均为0,在接口描述符中依次为0x08:表示为MSC设备类、0x06:表示采用SCSI指令集、0x50表示传输层使用BOT协议。标准的USB支持BOT的大容量存储设备的描述符布局如下:

USB-MSC

设备数据传输过程:

a. 主机发送CBW指令包给设备,告诉设备要进行数据传输。通过out端点发送。

b. 设备收到CBW包后进行解析,如果CBW包合法并且有意义的话,不合法的话,设备会中止in管道,直到主机reset。否则设备从bulk-in端点发送一个CSW包给主机,响应主机的要求。

c. 主机收到CSW后同样进行解析,如果CSW不合法或无意义,则主机可能会进行reset recovery。否则便开始传输数据给U盘或从U盘读取数据。

USB-MSC

MSC设备需要完成磁盘的挂载后方可正常使用,因此需要在USB初始化后为MSC设备挂载至少一个磁盘,MSC接口设计也为用户提供了磁盘的挂载及获取接口。

(2) 接口说明

typedef struct{
        uint32 dSignature;
        uint32 dTag;
        uint32 dDataLength;
        uint08 bmFlags;
        uint08 bLUN;
        uint08 bCBLength;
        uint08 CB[16];
}tlkusb_mscScsiCBW_t;
  • dSignature:指明该数据报为 CBW 的信号标记。固定值为 0x43425355 dTag :主机发送的命令块标签。设备应在相关 CBW 的 dCSWTag 字段中将这个字段的内容返回给主机。
  • dDataLength :主机要求在执行 CBW 命令期间,在批量输入或批量输出端点传输数据字节数。如果该字段为 0 ,则设备和主机不应该在 CBW 和相关的 CSW 中间传输数据,设备应该忽略 bmCBWFlags 中方向位的值。
  • bmFlags :bit [7] :方向。 0 - 从主机到设备的 DataOut , 1 - 从设备到主机的 DataIn ;bit [6] :置 0 ;bit[5-0] :保留,置为0 ;
  • bLUN :命令块发送的设备逻辑单元号( LUN )。
  • bCBLength: 命令的长度,范围在0~16
  • CB:设备将执行的命令块.
typedef struct{
  uint32 dSignature;
  uint32 dTag;
  uint32 dDataResidue;
  uint08 bStatus;
}tlkusb_mscScsiCSW_t;
  • dSignature :指明该数据包为 CSW 的信号标记,固定值为 0x53425355 。
  • dTag :设备应将这个字段设置为接收到的相应 CBW的 dTag 字段值。
  • dDataResidue :对于 DataOut ,设备应在这个字段报告 dDataLength 字段规定的要求数量与设备实际处理的数据量之差。对于 DataIn ,设备应在这个字段报告 dCBDataLength 字段规定的要求数量与设备实际发送的数据量之差。 不会超过 dDataLength 发送的值。
  • bStatus :表示命令执行是否成功。 0 = 执行成功,非 0 表示失败。
typedef struct{
        uint08 isReady;
        uint08 hotPlug;        
        uint16 blkSize;       
        uint32 blkCount;   
        char *pVendorStr;     
        char *pProductStr;  
        char *pVersionStr; 
        int(*Init)(void);
        int(*Read)(uint08 *pBuff, uint32 blkOffs, uint16 blkNumb);
        int(*Write)(uint08 *pData, uint32 blkOffs, uint16 blkNumb);
}tlkusb_msc_disk_t;
  • isReady:当前磁盘是否可以使用
  • hotPlug: 1-Enable, 0-Disable
  • blkSize:Block size
  • blkCount:Block count
  • pVendorStr:
  • pProductStr:
  • pVersionStr:
  • Init:磁盘的初始化接口
  • Read:读取磁盘信息
  • Write:往磁盘写入信息

scsi初始化

int  tlkusb_msc_scsiInit(void);

scsi复位重置

void tlkusb_msc_scsiReset(void);

使能/失能sisi

void tlkusb_msb_scsiEnable(bool enable);

scsi命令块的处理句柄

void tlkusb_msc_scsiHandler(void);

为MSC设备挂载新的磁盘

int tlkusb_msc_appendDisk(tlkusb_msc_disk_t *pUnit);

获取MSC设备的磁盘数量

uint08 tlkusb_msc_getDiskCount(void);

获取MSC设备当前使用的磁盘

tlkusb_msc_disk_t *tlkusb_msc_getDisk(uint08 volNum);

DEV设计说明

DEV,设备层接口,这里提供的仅是通用接口供用户层调用,具体实现是进一步调用drv中设备的对应接口。用户只需要了解DEV层的调用逻辑,若想修改设备仅需要在drv层修改对应接口即可。DEV层做到了设备接口与设备驱动的层次分离,方便用户修改或添加设备,实现对sdk的二次开发。用户可操作宏定义TLK_DEV_xxxxx_ENABLE来使能或失能某个设备。

本章节主要从Telink所支持的设备展开介绍,包含其Dev层接口,并cover对应设备的drv层驱动接口,方便用户更好的了解所使用设备。

充电管理设备

TBD

电池管理设备

接口函数:

电池设备的初始化:

int tlkdev_battery_init(void)

获取电池电量值:

int tlkdev_battery_getVoltage(uint16 *pVoltage)

音频管理设备

宏定义:

Mic的buffer大小:

#define TLK_DEV_MIC_BUFF_SIZE        (1024*2)

Spk的buffer大小:

#define TLK_DEV_SPK_BUFF_SIZE        (1024*4)

全局变量:

存储spk数据的buffer:

uint16 gTlkDevCodecSpkBuffer[TLK_DEV_SPK_BUFF_SIZE/2]

存储mic数据的buffer:

uint16 gTlkDevCodecMicBuffer[TLK_DEV_MIC_BUFF_SIZE/2]

codec设备控制块:

static tlkdev_codec_t sTlkDevCodecCtrl

数据结构:

Codec设备类型:

typedef enum{
    TLKDEV_CODEC_SUBDEV_DEF  = 0x00,
    TLKDEV_CODEC_SUBDEV_MIC  = 0x01,
    TLKDEV_CODEC_SUBDEV_SPK  = 0x02,
    TLKDEV_CODEC_SUBDEV_BOTH = 0x03,
}TLKDEV_CODEC_SUBDEV_ENUM;
  • TLKDEV_CODEC_SUBDEV_MIC:仅麦克风设备
  • TLKDEV_CODEC_SUBDEV_SPK:仅扬声器设备
  • TLKDEV_CODEC_SUBDEV_BOTH:扬声器设备与麦克风设备

Codec声道类型:

typedef enum{
    TLKDEV_CODEC_CHANNEL_LEFT   = 0x01,
    TLKDEV_CODEC_CHANNEL_RIGHT  = 0x02,
    TLKDEV_CODEC_CHANNEL_STEREO = 0x03,
}TLKDEV_CODEC_CHANNEL_ENUM;
  • TLKDEV_CODEC_CHANNEL_LEFT:左声道
  • TLKDEV_CODEC_CHANNEL_RIGHT:右声道
  • TLKDEV_CODEC_CHANNEL_STEREO:立体音

Codec位深度类型:

typedef enum{
    TLKDEV_CODEC_BITDEPTH_16 = 16,
    TLKDEV_CODEC_BITDEPTH_20 = 20,
    TLKDEV_CODEC_BITDEPTH_24 = 24,
}TLKDEV_CODEC_BITDEPTH_ENUM;
  • TLKDEV_CODEC_BITDEPTH_16:位深度为16
  • TLKDEV_CODEC_BITDEPTH_20:位深度为20
  • TLKDEV_CODEC_BITDEPTH_24:位深度为24

Codec采样率类型:

typedef enum{
    TLKDEV_CODEC_SAMPLERATE_8000  = 8000,
    TLKDEV_CODEC_SAMPLERATE_16000 = 16000,
    TLKDEV_CODEC_SAMPLERATE_32000 = 32000,
    TLKDEV_CODEC_SAMPLERATE_44100 = 44100,
    TLKDEV_CODEC_SAMPLERATE_48000 = 48000,
}TLKDEV_CODEC_SAMPLERATE_ENUM;
  • TLKDEV_CODEC_SAMPLERATE_8000:采样率为8000
  • TLKDEV_CODEC_SAMPLERATE_16000:采样率为16000
  • TLKDEV_CODEC_SAMPLERATE_32000:采样率为32000
  • TLKDEV_CODEC_SAMPLERATE_44100:采样率为44100
  • TLKDEV_CODEC_SAMPLERATE_48000:采样率为48000
typedef struct{
    uint08 spkIsMute;
    uint08 micIsMute;
    uint08 spkVolume;
    uint08 micVolume;
    uint08 spkChannel;
    uint08 micChannel;
    uint08 selSpkVolume;
    uint08 selMicVolume;
}tlkdev_codec_t;
  • spkIsMute:spk是否为静音
  • micIsMute:mic是否为静音
  • spkVolume:spk的音量
  • micVolume:mic的音量
  • spkChannel:spk的通道
  • micChannel:mic的通道
  • selSpkVolume:spk音量的播放比例
  • selMicVolume:mic音量的播放比例

接口函数: codec设备的初始化:

int tlkdev_codec_init(void)

(1) mic与spk数据buffer的初始化

(2) codec设备的挂载

(3) 设置mic与spk的音量

(4) 设置codec的通道、位深度、采样率

打开Codec子设备,mic/spk:

int tlkdev_codec_open(TLKDEV_CODEC_SUBDEV_ENUM subDev, uint08 channel, uint08 bitDepth, uint32 sampleRate)

关闭Codec设备:

int tlkdev_codec_close(void)

int tlkdev_codec_extOpen(TLKDEV_CODEC_SUBDEV_ENUM subDev, uint08 spkChannel, uint08 spkBitDepth,
    uint32 spkSampleRate, uint08 micChannel, uint08 micBitDepth, uint32 micSampleRate)
(1) 为subDev设置通道、位深度及采样率

(2) 打开codec设备

(3) 打开PA音频功率放大器

查询音频功率放大器是否开启:

bool tlkdev_codec_paIsOpen(void)

打开PA:

void tlkdev_codec_paOpen(void)

关闭PA:

void tlkdev_codec_paClose(void)

静音spk设备:

void tlkdev_codec_muteSpk(void)

设置Codec spk状态:

void tlkdev_codec_setSpkStatus(bool isMute)

设置Codec mic状态:

void tlkdev_codec_setMicStatus(bool isMute)

设置Codec spk音量:

void tlkdev_codec_setSpkVolume(uint volume)

设置Codec mic音量:

void tlkdev_codec_setMicVolume(uint volume)

获取Codec spk音量:

uint tlkdev_codec_getSpkVolume(void)

获取Codec mic音量:

uint tlkdev_codec_getMicVolume(void)

获取spk的偏移:

uint tlkdev_codec_getSpkOffset(void)

获取mic的偏移:

uint tlkdev_codec_getMicOffset(void)

设置spk的偏移量:

void tlkdev_codec_setSpkOffset(uint16 offset)

设置mic的偏移量:

void tlkdev_codec_setMicOffset(uint16 offset)

为spk设置buffer:

void tlkdev_codec_setSpkBuffer(uint08 *pBuffer, uint16 buffLen)

为mic设置buffer:

void tlkdev_codec_setMicBuffer(uint08 *pBuffer, uint16 buffLen)

获取当前spk的buffer长度:

uint tlkdev_codec_getSpkBuffLen(void)

获取当前mic的buffer长度:

uint tlkdev_codec_getMicBuffLen(void)

获取spk buffer的空闲长度:

uint tlkdev_codec_getSpkIdleLen(void)

获取spk 数据的长度:

uint tlkdev_codec_getSpkDataLen(void)

获取mic 数据的长度:

uint tlkdev_codec_getMicDataLen(void)

读取spk的数据:

bool tlkdev_codec_readSpkData(uint08 *pBuffer, uint16 buffLen, uint16 offset)

读取mic的数据:

bool tlkdev_codec_readMicData(uint08 *pBuff, uint16 buffLen, uint16 *pOffset)
void tlkdev_codec_muteSpkBuff(void)
void tlkdev_codec_zeroSpkBuff(uint16 zeroLen, bool isInc)

向spk buffer填充数据:

bool tlkdev_codec_fillSpkBuff(uint08 *pData, uint16 dataLen)
bool tlkdev_codec_backReadSpkData(uint08 *pBuff, uint16 buffLen, uint16 offset, bool isBack)

串口通信设备

定义回调函数类型:

定义Serial recv接收回调函数的类型:

typedef void(*TlkDevSerialRecvCB)(uint08 *pData, uint16 dataLen)

接口函数:

串口初始化:

int tlkdev_serial_init(void)

挂载串口设备:

int tlkdev_serial_mount(uint08 port, uint32 baudRate, uint16 txPin,
    uint16 rxPin, uint08 txDma, uint08 rxDma, uint16 rtsPin, uint16 ctsPin)

卸载串口设备

int tlkdev_serial_unmount(void)

为串口设备设置波特率:

int tlkdev_serial_setBaudrate(uint32 baudRate)

设置串口接收FIFO:

int tlkdev_serial_setRxFifo(uint08 *pBuffer, uint16 buffLen)

设置串口发送的QFIFO:

int tlkdev_serial_setTxQFifo(uint16 fnumb, uint16 fsize, uint08 *pBuffer, uint16 buffLen)

设置串口接收的QFIFO:

int tlkdev_serial_setRxQFifo(uint16 fnumb, uint16 fsize, uint08 *pBuffer, uint16 buffLen)

串口设备打开:

int tlkdev_serial_open(void)

串口设备关闭:

int tlkdev_serial_close(void)

串口发送数据:

int tlkdev_serial_send(uint08 *pData, uint16 dataLen)

从串口读取数据:

int tlkdev_serial_read(uint08 *pBuff, uint16 buffLen)

清空串口的fifo:

void tlkdev_serial_clear(void)

为串口注册回调函数:

void tlkdev_serial_regCB(TlkDevSerialRecvCB cb)

判断串口的发送fifo使用是否超过60%:

bool tlkdev_serial_sfifoIsMore60(uint16 dataLen)

判断串口的发送fifo使用是否超过80%:

bool tlkdev_serial_sfifoIsMore80(uint16 dataLen)

判断串口是否正在使用:

bool tlkdev_serial_isBusy(void)

唤醒串口设备:

void tlkdev_serial_wakeup(void)

串口的发送与接收Handler:

void tlkdev_serial_handler(void)

界面显示设备

数据结构:

颜色参数结构:

typedef struct{
    uint08 r;
    uint08 g;
    uint08 b;
}tlkdev_lcd_color_t;

笔刷参数结构:

typedef struct{
    uint08 r;
    uint08 g;
    uint08 b;
    uint08 a;
    uint08 w;
}tlkdev_lcd_brush_t;
  • a:笔刷的透明度
  • w:笔刷的宽度

区域结构定义:

typedef struct{
    uint16 x;
    uint16 y;
    uint16 w;
    uint16 h;
}tlkdev_lcd_region_t;
  • x:横坐标
  • y:纵坐标
  • w:宽度
  • h:高度

接口函数:

判断LCD设备是否Open:

bool tlkdev_lcd_isOpen(void)

初始化LCD设备:

int tlkdev_lcd_init(void)

打开LCD设备:

int tlkdev_lcd_open(void)

关闭LCD设备:

int tlkdev_lcd_close(void)

重置LCD设备:

int tlkdev_lcd_reset(void)

刷新LCD:

int tlkdev_lcd_flush(void)

LCD设备清屏:

int tlkdev_lcd_clean(void)

获取当前LCD设备的宽度:

int tlkdev_lcd_getWidth(void)

获取当前LCD设备的高度:

int tlkdev_lcd_getHeight(void)

为当前LCD设备设置笔刷:

int tlkdev_lcd_setBrush(tlkdev_lcd_brush_t *pBrush)

为LCD设备设置前景色:

int tlkdev_lcd_setForegroud(uint08 *pBuffer, uint32 buffLen)

为LCD设备设置背景色:

int tlkdev_lcd_setBackgroud(uint08 *pBuffer, uint32 buffLen)

根据给定坐标在LCD设备上画点:

int tlkdev_lcd_paintPoint(uint16 x, uint16 y, bool isFlush)

在LCD设备指定区域显示Image图片:

int tlkdev_lcd_paintImage(tlkdev_lcd_region_t *pRegion, uint08 *pData, uint32 dataLen, bool isFlush)

在LCD设备上画线:

int tlkdev_lcd_paintLine(uint16 x0, uint16 y0, uint16 x1, uint16 y1, bool isFlush)

在LCD指定位置上画指定长度的水平线:

int tlkdev_lcd_paintHLine(uint16 x, uint16 y, uint16 w, bool isFlush)

在LCD指定位置上画指定长度的垂直线:

int tlkdev_lcd_paintVLine(uint16 x, uint16 y, uint16 h, bool isFlush)
int tlkdev_lcd_paintArc(uint16 x0, uint16 y0, uint16 r, uint16 sAngle, uint16 eAngle, bool isFlush)
int tlkdev_lcd_paintRing(uint16 x0, uint16 y0, uint16 sr, uint16 er, bool isFlush)

在LCD设备上画矩形:

int tlkdev_lcd_paintRect(uint16 x, uint16 y, uint16 w, uint16 h, bool isFlush)

在LCD设备上画圆角矩形:

int tlkdev_lcd_paintRoundedRect(uint16 x, uint16 y, uint16 w, uint16 h, uint16 rounded, bool isFlush)
int tlkdev_lcd_paintChamferRect(uint16 x, uint16 y, uint16 w, uint16 h, uint16 chamfer, bool isFlush)
int tlkdev_lcd_paintEllipse(uint16 x0, uint16 y0, uint16 a, uint16 b, bool isFlush)

在LCD上画圆:

int tlkdev_lcd_paintCircle(uint16 x0, uint16 y0, uint16 r, bool isFlush)
int tlkdev_lcd_paintSector(uint16 x0, uint16 y0, uint16 r, uint16 sAngle, uint16 eAngle, bool isFlush)
int tlkdev_lcd_paintFillRing(uint16 x0, uint16 y0, uint16 sr, uint16 er, bool isFlush)
int tlkdev_lcd_paintFillRect(uint16 x, uint16 y, uint16 w, uint16 h, bool isFlush)
int tlkdev_lcd_paintFillRoundedRect(uint16 x, uint16 y, uint16 w, uint16 h, uint16 rounded, bool isFlush)
int tlkdev_lcd_paintFillChamferRect(uint16 x, uint16 y, uint16 w, uint16 h, uint16 chamfer, bool isFlush)
int tlkdev_lcd_paintFillEllipse(uint16 x0, uint16 y0, uint16 a, uint16 b, bool isFlush)
int tlkdev_lcd_paintFillCircle(uint16 x0, uint16 y0, uint16 r, bool isFlush)
int tlkdev_lcd_paintFillSector(uint16 x0, uint16 y0, uint16 r, uint16 sAngle, uint16 eAngle, bool isFlush)

触摸控制设备

数据结构:

触摸屏动作类型定义:

typedef enum{
    TLKDEV_TOUCH_ACTID_NONE = 0,
    TLKDEV_TOUCH_ACTID_UP,
    TLKDEV_TOUCH_ACTID_DOWN,
    TLKDEV_TOUCH_ACTID_MOVE,
}TLKDEV_TOUCH_ACTID_ENUM;
  • TLKDEV_TOUCH_ACTID_UP:移开
  • TLKDEV_TOUCH_ACTID_DOWN:触摸
  • TLKDEV_TOUCH_ACTID_MOVE:移动

触摸屏事件类型定义:

typedef enum{
    TLKDEV_TOUCH_EVTID_NONE = 0,
    TLKDEV_TOUCH_EVTID_PRESS,
    TLKDEV_TOUCH_EVTID_RELEASE,
    TLKDEV_TOUCH_EVTID_CLICK,
    TLKDEV_TOUCH_EVTID_L2R,
    TLKDEV_TOUCH_EVTID_R2L,
    TLKDEV_TOUCH_EVTID_T2B,
    TLKDEV_TOUCH_EVTID_B2T,
}TLKDEV_TOUCH_EVTID_ENUM;
- TLKDEV_TOUCH_EVTID_PRESS:触摸按压事件 - TLKDEV_TOUCH_EVTID_RELEASE:释放移开事件 - TLKDEV_TOUCH_EVTID_CLICK:点击事件 - TLKDEV_TOUCH_EVTID_L2R:Left to right右滑事件 - TLKDEV_TOUCH_EVTID_R2L:Right to left左滑事件 - TLKDEV_TOUCH_EVTID_T2B:Top to bottom下滑事件 - TLKDEV_TOUCH_EVTID_B2T:Bottom to top上滑事件

触摸屏坐标结构定义:

typedef struct{
    uint16 axisX;
    uint16 axisY;
}tlkdev_touch_point_t;

宏定义:

定义当前的触摸屏设备型号:

#define TLKDEV_TOUCH_DEV     TLKDRV_TOUCH_DEV_FT3168

接口函数:

查询触摸屏设备是否打开:

bool tlkdev_touch_isOpen(void)

查询触摸屏设备是否已经准备好可以进入工作状态:

bool tlkdev_touch_isReady(void)

触摸屏设备的初始化:

int tlkdev_touch_init(void)

打开触摸屏设备:

int tlkdev_touch_open(void)
关闭触摸屏设备:

int tlkdev_touch_close(void)

获取触摸点:

int tlkdev_touch_getPoint(uint16 *pEvtID, tlkdev_touch_point_t *pPoint)

获取触摸手势类型:

int tlkdev_touch_getGesture(uint08 *pGstID, uint08 *pNumb, tlkdev_touch_point_t *pPoints, uint08 pointNumb)

触摸屏事件处理:

int tlkdev_touch_handler(uint16 opcode, uint32 param0, uint32 param1)

创建一个触摸屏手势

int tlkdev_touch_calcGesture(uint16 x, uint16 y, TLKDEV_TOUCH_ACTID_ENUM actID)

存储管理设备

SD Nand

宏定义:

定义当前sd nand的设备型号:

#define TLKDEV_STORE_DEV        TLKDRV_STORE_DEV_XTSD01G

接口函数:

获取SD nand设备的打开状态:

bool tlkdev_store_isOpen(void);

SD nand设备的初始化:

int tlkdev_store_init(void);

打开SD nand设备:

int tlkdev_store_open(void);

关闭Nand flash:

int tlkdev_store_close(void);

根据给定位置擦除sd nand的数据:

int tlkdev_store_erase(uint32 addr);

根据给定位置与长度,读取sd nand数据到指定buff:

int tlkdev_store_read(uint32 addr, uint08 *pBuff, uint16 buffLen);

将指定长度的数据写入到sd nand的指定位置:

int tlkdev_store_write(uint32 addr, uint08 *pData, uint16 dataLen);

sd nand设备磁盘的初始化:

int tlkdev_store_sdInit(void);

sd nand设备的格式化:

int tlkdev_store_format(void);

nand 设备块读:

int tlkdev_store_sdread(uint08 *pBuff, uint32 blkOffs, uint16 blkNumb);

nand 设备块写:

int tlkdev_store_sdwrite(uint08 *pData, uint32 blkOffs, uint16 blkNumb);

获取sd nand一页的大小:

int tlkdev_store_getPageSize(void);

获取sd nand设备的页数:

int tlkdev_store_getPageNumb(void);

获取sd nand的扇区大小:

int tlkdev_store_getSectorSize(void);

获取sd nand的扇区数:

int tlkdev_store_getSectorNumb(void);

获取sd nand的块大小:

int tlkdev_store_getSdBlockSize(void);

获取sd nand的块数量:

int tlkdev_store_getSdBlockNumb(void);

sd nand进入睡眠状态:

void tlkdev_store_enterSleep(uint mode);

SRAM

宏定义:

定义当前使用的外部ram的设备型号:

#define TLKDEV_SRAM_DEV        TLKDRV_SRAM_DEV_ASP1604

接口函数:

判断sram设备是否打开:

bool tlkdev_sram_isOpen(void);

设备的初始化:

int tlkdev_sram_init(void);

打开sram设备:

int tlkdev_sram_open(void);

关闭sram设备:

int tlkdev_sram_close(void);

从指定位置读取sram数据:

int tlkdev_sram_read(uint32 addr, uint08 *pBuff, uint16 buffLen);

向sram指定位置写入数据:

int tlkdev_sram_write(TLKDRV_SRAM_DEV_ENUM dev, uint32 addr, uint08 *pData, uint16 dataLen);