蓝牙协议栈(七、移植到其他平台)

在本节中,我们将重点介绍需要针对不同硬件平台进行调整的BTstack组件。

时间抽象层

BTstack需要一种方法来了解传递时间。 btstack_run_loop_embedded.c支持两种不同的模式:系统标记或具有毫秒分辨率的系统时钟。BTstack的时序要求非常低,因为只需要处理第二范围内的蓝牙超时。

刻度硬件抽象

如果您的平台不需要系统时钟或者您已经有系统时钟(因为它是ARM Cortex设备上CMSIS的默认设置),您可以使用它在include / btstack / hal_tick.h中实现BTstack的时间抽象 >。

为此,您需要在btstack_config.h中定义HAVE_EMBEDDED_TICK:

#define HAVE_EMBEDDED_TICK

然后,您需要实现hal_tick_init和 hal_tick_set_handler函数,这些函数将在运行循环的初始化期间调用。

void hal_tick_init(void);

void hal_tick_set_handler(void (*tick_handler)(void));

int hal_tick_get_tick_period_in_ms(void);

BTstack来电之后hal_tick_init()和 hal_tick_set_handler(tick_handler) ,它预计, tick_handler被调用每 hal_tick_get_tick_period_in_ms()毫秒。

时间MS硬件抽象

如果您的平台已经有系统时钟或者提供这样的时钟更方便,您可以在include / btstack / hal_time_ms.h中使用Time MS Hardware Abstraction 。

为此,您需要在btstack_config.h中定义HAVE_EMBEDDED_TIME_MS:

#define HAVE_EMBEDDED_TIME_MS

然后,您需要实现函数hal_time_ms(),该函数将从BTstack的运行循环和为将来设置计时器时调用。它必须以毫秒为单位返回时间。

uint32_t hal_time_ms(void);

蓝牙硬件控制API

蓝牙硬件控制API可以为HCI层提供自定义初始化脚本,特定于供应商的波特率更改命令和系统电源通知。它还用于控制蓝牙模块的电源模式,即打开/关闭和进入睡眠模式。此外,它还提供了一个错误处理程序hw_error,在蓝牙模块报告硬件错误时调用该错误处理程序。回调允许持久记录或发出此故障的信号。

总的来说,struct btstack_control_t封装了蓝牙规范未涵盖的常用功能。例如,btstack_chipset_cc256x_in-stance函数返回一个指向适合CC256x芯片组的控制结构的指针。

人机交互实施

在嵌入式系统上,可以通过USB或UART端口连接蓝牙模块。BTstack实现了三种基于UART的协议,用于在主机和蓝牙模块之间传输HCI命令,事件和数据:HCI UART传输层(H4),支持eHCILL的H4,德州仪器的轻量级低功耗变体,以及三线制UART传输层(H5)。

HCI UART传输层(H4)

大多数嵌入式UART接口在字节级操作,并在接收到字节时生成处理器中断。在中断处理程序中,公共UART驱动程序然后将接收到的数据放入环形缓冲区并设置标志以进行进一步处理或通知更高级别的代码,即在我们的情况下是蓝牙堆栈。

蓝牙通信是基于数据包的,单个数据包最多可包含1021个字节。为每个字节调用蓝牙堆栈的数据接收处理程序会产生不必要的开销。为了避免这种情况,可以将蓝牙数据包读取为多个块,其中预先知道要读取的字节量。如果可以的话,更好的是使用片上DMA模块进行这些块读取。

BTstack UART硬件抽象层API反映了这种设计方法,底层UART驱动程序必须实现以下API:

void hal_uart_dma_init(void);

void hal_uart_dma_set_block_received(void (*block_handler)(void));

void hal_uart_dma_set_block_sent(void (*block_handler)(void));

int hal_uart_dma_set_baud(uint32_t baud);

void hal_uart_dma_send_block(const uint8_t *buffer, uint16_t len);

void hal_uart_dma_receive_block(uint8_t *buffer, uint16_t len);

嵌入式系统的主要HCI H4实现是 hci_h4_transport-_dma函数。此函数调用以下序列:hal_uart_dma_init,hal_uart_dma_set_block_received 和hal_uart_dma_set_block_sent函数。在此序列中,HCI层将通过调用hal_uart-_dma_receive_block函数来启动数据包处理 。HAL实现负责读取请求的字节数,在收到请求的数据量时通过RTS线停止传入数据,并且必须调用处理程序。这样,HAL实现可以保持通用,而每个HCI数据包只需要三次回调。

H4支持eHCILL

使用标准H4协议接口,主机和基带控制器都无法进入睡眠模式。除了官方的H5协议,各种芯片供应商都提出了专有的解决方案。德州仪器(TI)的eHCILL支持允许主机和基带控制器独立进入睡眠模式,而不会失去与HCI H4传输层的同步。除了IRQ驱动的块式RX和TX之外,eHCILL还需要回调CTS中断。

void hal_uart_dma_set_cts_irq_handler(void(*cts_irq_handler)(void));

void hal_uart_dma_set_sleep(uint8_t sleep);

H5

H5利用SLIP协议传输数据包,并通过重传处理数据包丢失和比特错误。由于它可以从数据包丢失中恢复,因此任何一方都可以进入睡眠模式而不会失去同步。

在H5中使用硬件流控制是可选的,但是,由于BTstack使用硬件流控制来避免数据包缓冲,因此建议仅将H5与RTS / CTS一起使用。

对于移植,实现遵循上述常规H4协议。

持久存储API

在嵌入式系统上,没有通用的方法来保存链接密钥或远程设备名称等数据,因为每种类型的设备都有自己的功能,特性和限制。持久存储API提供了一个接口,用于为特定系统实现具体的驱动程序。

链接密钥DB

作为示例并且出于测试目的,BTstack提供仅存储器的实现btstack_link_key_db_memory。实现必须符合下面的清单的接口。

typedef struct {

// management

void (*open)();

void (*close)();

// link key

int (*get_link_key)(bd_addr_t bd_addr, link_key_t link_key);

void (*put_link_key)(bd_addr_t bd_addr, link_key_t key);

void (*delete_link_key)(bd_addr_t bd_addr);

} btstack_link_key_db_t;

发布了105 篇原创文章 · 获赞 30 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/happygrilclh/article/details/100743632