CH592 蓝牙透传模块
设备架构
串口透传协议说明
模块通过串口和用户MCU相连,建立用户MCU 和 BLE 设备之间的双向通讯。
用户可以通过串口,使用指定的AT指令对串口波特率、BLE连接间隔,以及不同的发包间隔,模块将会有不同的数据吞吐能力。
串口默认配置为 115200bps。
模块的串口Rx一次最大可输入3K字节。
模块会根据蓝牙协议协商情况进行分包或者发送完整包。
BLE设备之间的通信,必须在对应的服务通道进行。当模块收到主机或从机发来的无线包后,会先从INT口输出低电平信号再从模块串口Tx端输出数据。
以“TTM:”开头且以“\r\n\0”结尾的字符串会被当成AT指令进行解析并执行,并返回执行结果(“TTM:OK\r\n\0”或者 “TTM:REP\r\n\0”等).不符合AT 指令规则的串口数据包,将被视为透传数据。
透传数据通道
透传数据通道【服务 UUID: 0xFFF0】
特征值UUID | 可执行操作 | 字节数: | 备注 |
---|---|---|---|
FFF1 | Notify | 跟随MTU长度 | 从模块串口Rx输入的数据将会在此通道产生通知发给主机 |
FFF2 | Write | 跟随MTU长度 | 写入的数据将会从串口的 Tx输出。 |
BLE 数据转串口输出,主机端通过 “FFF2” 通道写操作后,数据将会从串口Tx 输出。串口输入转BLE数据输出,如果打开了“FFF1”通道的通知使能开关,串口Rx收到透传的数据
将会通过notify事件发送到
如何验证串口透传协议成功
- 蓝牙模块默认为从机模式
- 等待设备启动完成,使用AT指令 TTM:ADV-START 启动从机的广播功能。
- 在手机上连接蓝牙模块。
- 通过串口 发送 透传数据, 手机上面的 0xFFF1 通道能接收到 串口透传的数据
- 通过手机上 0xFFF2 通道 write 消息,在串口上能够打印出来。
- 使用 TTM:ADV-OFF 关闭广播功能, 手机上将不会扫描到 该蓝牙的信息。
ble_uart 说明文档
CH592 简单的串口透传:
特性:
1, 使用两个128bit uuid,
2, 两个uuid 分别是write without respone,和 notify 方式,分别对应串口收和发,可以在工程文件ble_uart_service/ble_uart_service.c中修改
3, 可以兼容 N* 家的 ble uart 的工程,
4, 支持MTU在20-247 中任意设置,自适适应当前的mtu
5, 默认在CH592上调试,串口使用的UART3,TXD3@PA5,RXD3@PA4,其他的串口需要修改代码
透传UART3口
6, ble 名称为"chCH592le_uart" 已经修改成 qf_ble_uart
7, 默认开启串口notify 成功回写,不需要需要可以去掉代码,在ble service 的回掉函数,BLE_UART_EVT_BLE_DATA_RECIEVED 事件中 屏蔽即可
8, 默认开启串口调试,使用串口1,PA9_TXD 115200.
DEBUG打印UART1口
一些参数修改:
见工程的config.h文件
1 修改mtu 长度,最大为251此时对应mtu是247,但是实际mtu是多少,要看central端连接时候协商的值
2 修改每个连接 最多传输多少个包数量
3,全局宏定义建议在mounriver stdio工程的properties>C/C++ General> Path and Symbols 的Symbols 标签下设置
代码分析
问题
extern const unit_8 VER_LIB[];应该在库里面
TMOS 常见的接口
bool tmos_memcmp(const void *src1, const void *src2, unit32_t len);
void tmos_memset( void * pDst, uint8_t Value, uint32_t len )
注册事件回调函数
tmosTasklD TMOS_ProcessEventRegister( pTaskEventHandIerFn eventCb )
厂商提供的BLE库(参考)
常见的数据结构
typedef struct tag_ble_clock_config {...} bleClockConfig_t; /*ble clock control config struct */
typedef struct tag_ble_pa_control_config {...} blePaControlConfig_t;
typedef struct {...} tmos_event_hdr_t;
#define VER_FILE "CH59x_BLE_LIB_V1.2" // 版本号
struct tag_ble_config {
uint32_t MEMAddr; // library memory start address BLE蓝牙库的开始地址
// MEM_BUF[BLE_MEMHEAP_SIZE / 4]
// -- MEM_BUF[1536]
uint16_t MEMLen; // library memory size 库的内存大小
// -- 1024 * 6
uint32_t SNVAddr; // SNV flash start address( if NULL,bonding information will not be saved ) ? SNV flash 开始地址
uint16_t SNVBlock; // SNV flash block size ( default 256 ) ??
uint8_t SNVNum; // SNV flash block number ( default 1 ) ??
uint8_t BufNumber; // Maximum number of sent and received packages cached by the controller( default 5 ) controller 缓存的 最大的发送和接收包的数量 默认为 5
// -- 5
// Must be greater than the number of connections.
uint16_t BufMaxLen; // Maximum length (in octets) of the data portion of each HCI data packet( default 27 ) 8进制格式的 HCI data 的 数据部分的 最大长度 默认 27
// -- 27
// SC enable,must be greater than 69 SC使能时,必须大于69
// ATT_MTU = BufMaxLen-4,Range[23,ATT_MAX_MTU_SIZE]
// ATT_MTU =
uint8_t TxNumEvent; // Maximum number of TX data in a connection event ( default 1 ) -- 1
uint8_t RxNumEvent; // Maximum number of RX data in a connection event ( default equal to BufNumber )
uint8_t TxPower; // Transmit power level( default LL_TX_POWEER_0_DBM(0dBm) )
uint8_t ConnectNumber; // Connect number,lower two bits are peripheral number,followed by central number
uint8_t WindowWidening; // Wait rf start window(us)
uint8_t WaitWindow; // Wait event arrive window in one system clock
uint8_t MacAddr[6]; // MAC address,little-endian
pfnSrandCB srandCB; // Register a program that generate a random seed
pfnIdleCB idleCB; // Register a program that set idle
pfnTempSampleCB tsCB; // Register a program that read the current temperature,determine whether calibration is need
pfnLSICalibrationCB rcCB; // Register a program that LSI clock calibration
pfnLibStatusErrorCB staCB; // Register a program that library status callback
pfnFlashReadCB readFlashCB; // Register a program that read flash
pfnFlashWriteCB writeFlashCB; // Register a program that write flash
} bleConfig_t
TX POWER 定义
ERR_LIB_INIT lib初始化错误定义
#define ERR_LLE_IRQ_HANDLE 0x01 // 中断错误?
#define ERR_MEM_ALLOCATE_SIZE 0x02 // 内存分配的大小错误
#define ERR_SET_MAC_ADDR 0x03 // MAC地址设置错误
#define ERR_GAP_ROLE_CONFIG 0x04 // GAP_ROLE 配置错误
#define ERR_CONNECT_NUMBER_CONFIG 0x05 // 连接数量错误
#define ERR_SNV_ADDR_CONFIG 0x06 // SNV 地址错误
#define ERR_CLOCK_SELECT_CONFIG 0x07 // 时钟选择错误
BLE_STATUS_VALUES BLE 默认的状态值
#define bleInvalidTaskID INVALID_TASK //!< Task ID isn't setup properly
#define bleEecKeyRequestRejected 0x06 //!< key missing
#define bleNotReady 0x10 //!< Not ready to perform task
#define bleAlreadyInRequestedMode 0x11 //!< Already performing that task
#define bleIncorrectMode 0x12 //!< Not setup properly to perform that task
#define bleMemAllocError 0x13 //!< Memory allocation error occurred
#define bleNotConnected 0x14 //!< Can't perform function when not in a connection
#define bleNoResources 0x15 //!< There are no resource available
#define blePending 0x16 //!< Waiting
#define bleTimeout 0x17 //!< Timed out performing function
#define bleInvalidRange 0x18 //!< A parameter is out of range
#define bleLinkEncrypted 0x19 //!< The link is already encrypted // link被加密了
#define bleProcedureComplete 0x1A //!< The Procedure is completed
#define bleInvalidMtuSize 0x1B //!< SDU size is larger than peer MTU. SDU size的大小比 MTU大
LINKDB
// Special case connection handles
#define INVALID_CONNHANDLE 0xFFFF // Invalid connection handle, used for no connection handle
#define LOOPBACK_CONNHANDLE 0xFFFE // Loopback connection handle, used to loopback a message
// Link state flags
#define LINK_NOT_CONNECTED 0x00 // Link isn't connected
#define LINK_CONNECTED 0x01 // Link is connected
#define LINK_AUTHENTICATED 0x02 // Link is authenticated
#define LINK_BOUND 0x04 // Link is bonded
#define LINK_ENCRYPTED 0x10 // Link is encrypted
// Link Database Status callback changeTypes
#define LINKDB_STATUS_UPDATE_NEW 0 // New connection created
#define LINKDB_STATUS_UPDATE_REMOVED 1 // Connection was removed
#define LINKDB_STATUS_UPDATE_STATEFLAGS 2 // Connection state flag changed
GATT UUID
定义了很多功能的UUID 详细参考 CH59xBLE_LIB.h
MESSAGE ID
ATT MTU SIZE
/************************************ATT***************************************/
#define ATT_MTU_SIZE 23 //!< Minimum ATT MTU size
#define ATT_MAX_MTU_SIZE 512 //!< Maximum ATT MTU size
ATT Methods
ATT Error Codes
PDU
GATT Attribute Access Permission Bit Fields
GATT Charateristic Properties Bit Fields
GAP ROLE 主从角色的定义
GAPBOND 匹配码的定义
在嵌入式编程中,节省代码空间和内存空间非常重要,特别是在资源受限的设备上。以下是一些常见的方法和技巧,可以帮助你优化代码,以节省代码空间和内存空间:
1. 使用高效的数据类型
选择适当的数据类型可以显著减少内存使用。
-
选择合适的数据类型:例如,在可能的情况下使用
uint8_t
而不是int
,因为uint8_t
只占用1个字节,而int
通常占用4个字节。 -
使用位字段:在结构体中使用位字段来节省空间。例如:
struct Flags { uint8_t flag1 : 1; uint8_t flag2 : 1; uint8_t flag3 : 1; uint8_t flag4 : 1; };
2. 减少函数调用的开销
函数调用会带来一些开销,特别是对于小函数。
-
内联函数:使用
inline
关键字提示编译器在调用点插入函数代码,而不是进行常规的函数调用。inline void myFunction() { // Function code }
3. 优化代码结构
通过优化代码结构,可以减少代码的重复和冗余。
-
使用宏定义:用宏定义替换重复的代码段。
#define MAX(a, b) ((a) > (b) ? (a) : (b))
-
代码复用:将常用的代码提取为函数或宏,以减少代码重复。
4. 静态分配内存
在嵌入式系统中,动态内存分配(例如使用 malloc
和 free
)可能会导致内存碎片和不必要的开销。
-
使用静态分配:在编译时确定所有需要的内存,避免动态分配。
int buffer[100]; // 静态分配
5. 优化编译器选项
使用编译器提供的优化选项来减少代码大小和内存使用。
-
启用编译器优化:使用
-Os
(针对代码大小优化)或-O2
(较高的优化级别,平衡代码大小和性能)。gcc -Os -o myProgram myProgram.c
6. 减少全局变量的使用
全局变量会占用内存,并且增加了代码的复杂性。
- 使用局部变量:在可能的情况下,尽量使用局部变量。
7. 代码和数据存储在合适的存储器中
将代码和数据存储在合适的存储器中,以优化存储器使用。
-
使用
const
关键字:将只读数据放入程序存储器(如 Flash),而不是 RAM。const char message[] = "Hello, World!";
8. 使用高效的算法和数据结构
选择合适的算法和数据结构,可以显著减少内存使用。
- 简化算法:选择在空间和时间复杂度上更优的算法。
- 压缩数据结构:例如,使用压缩树或图来存储稀疏数据。
9. 代码段和数据段合并
在某些情况下,可以将代码段和数据段合并,以减少内存浪费。
- 合并数据段:将相似的常量数据合并到一个数据段中。
10. 使用链接器脚本优化内存布局
通过链接器脚本,可以精确控制程序和数据在内存中的布局。
- 自定义链接器脚本:确保重要的代码和数据段放置在最合适的位置。
SECTIONS {
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
11. 使用外部存储器
如果设备支持,可以使用外部存储器来存放较大或次要的数据。
- 外部 EEPROM/Flash:将不经常访问的大数据存储到外部存储器中。
12. 避免使用大型库
使用大型库会显著增加代码大小。
- 选择小型库或自己实现:避免使用功能冗余的大型库,选择精简的库或者自己实现需要的功能。
通过结合以上这些方法,你可以显著减少嵌入式程序的代码空间和内存空间占用,确保在资源受限的设备上高效运行。