# doc
**Repository Path**: zuoc1/doc
## Basic Information
- **Project Name**: doc
- **Description**: 学习笔记,记录点滴。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-11-08
- **Last Updated**: 2025-05-30
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
chengzuod@isoftstone.com
ubuntu: 10.181.81.63 chengzuod/chengzuod
Zuche@19283755
1qaz@4321
isoftstone.com
svn的用户名是chengzuod
# 0、公司
有邮寄需求的小伙伴 在邮寄邮件里确认下成本中心:武汉企业与园区业务实施部 89930
之前写的不对的小伙伴注意更改一下
补签到要么是工作举证(最好),要么是外网电脑工作记录或者日志记录(注意是在应用程序和服务日志->Microsoft->OneApp_IGCC,其他什么系统日志不作数(即使不使用电脑也会自动运转)),以能提供的举证时间与补签到电子流一致,不能提供部分,按请假处理!
请大家一定注意
三菱电机项目负责人:张旻(部长)
张部长电话:15000852833
张部长邮箱:mzhang@saec.com.cn
其他技术人员邮箱:rjzhang@saec.com.cn
jiezheng@saec.com.cn

# 1、Linux命令
```bash
# 递归强制删除文件
rm -rf /path/to/directory
# 解压
tar -xzvf 文件名.tar.gz
# 文件复制
cp file.txt /home/user/Documents/
cp -r dir1 dir2
# 按文件名查找
find /path -name "file.txt" # 精确匹配文件名:ml-citation{ref="1,5" data="citationList"}
find ~ -iname "*.jpg" # 忽略大小写查找 JPEG 文件:ml-citation{ref="3,5" data="citationList"}
# 全量编译
python3 build.py -c ws63-liteos-app
# 增量编译
python3 build.py ws63-liteos-app
# 图形化界面
python3 build.py ws63-liteos-app menuconfig
```
Memory region Used Size Region Size %age Used
ROM: 0 GB 268 KB 0.00%
ITCM: 13256 B 16 KB 80.91%
DTCM: 14844 B 16 KB 90.60%
SRAM: 182288 B 548608 B(535K) 33.23%
PRESERVE_SHARE_MEM(保留共享内存): 252 B 256 B 98.44%
PROGRAM_STARTUP(程序启动): 472 B 1 KB 46.09%
PROGRAM(程序): 1264772 B 2357504 B 53.65%
# 2. IOT
## 1. WS63
python3 build.py -c ws63-liteos-app-iot
python3 build.py ws63-liteos-app-iot menuconfig
// 编译指定模块
python3 build.py -c ws63-liteos-app-iot -component=saec_iot
// 切换为蓝牙辅助配网(不需要)
AT+NVWRITE=0x2150,0,1,01
// HILINK重新配网
AT+HILINKREST
### 1. 分区大小
["0x03", "0x00000000", "0x00000800"],
["0x00", "0x00002000", "0x00006000"],
["0x08", "0x00008000", "0x00004000"],
["0x09", "0x0000C000", "0x00004000"],
["0x02", "0x00010000", "0x00010000"],
["0x01", "0x00020000", "0x00010000"],
["0x20", "0x00030000", "0x00240000"], 270000
["0x21", "0x00270000", "0x00183000"], 183000
["0x30", "0x003F3000", "0x00008000"],
["0x11", "0x003FB000", "0x00001000"],
["0x10", "0x003FC000", "0x00004000"],
imageA区域有2.25M,fota data区域有1.5M
### 2. UTC
| 服务商 | 服务器地址 | 特点 |
| ------------ | ------------------------------ | --------------------------- |
| 中国科学院 | ntp.ntsc.ac.cn (202.120.2.101) | 国家级授时中心,精度±0.01秒 |
| 阿里云 | ntp.aliyun.com | 高可用集群,支持百万级并发 |
| 腾讯云 | ntp.tencent.com | 专有网络优化,延迟低于10ms |
| 上海交通大学 | ntp.sjtu.edu.cn | 教育网优化,延迟低于5ms |
RTC最大只支持36.4小时,而且该RTC没有日历功能,只有count值。
通过osal_gettimeofday获取当前时间,但是不能设置
使用原始liteos接口:settimeofday gettimeofday
自己通过udp连接也可以获取到UTC,使用HEX发送
VN 版本号:3
1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
VN 版本号:4
23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
返回:
1C 02 00 E7 00 00 05 A3 00 00
00 43 64 6B 19 72 EB B5 7E 27
B6 4D EB 96 00 00 00 00 00 00
00 00 EB B5 7E 42 19 8F 3B 6A
EB B5 7E 42 19 90 F2 59
#### 1、NTP消息格式技术解析
------
##### 一、报文头部结构
NTP报文由固定48字节头部和可变扩展字段构成,核心字段定义如下13:
1. **基础控制字段**
- **LI(Leap Indicator)**:2比特,指示闰秒状态
`00`无告警、`01`末分钟61秒、`10`末分钟59秒、`11`时钟失步1
- **VN(Version Number)**:3比特,协议版本号(默认值为3)1
- **Mode**:3比特,工作模式标识
`1`主动对等体、`2`被动对等体、`3`客户端、`4`服务器1
2. **同步参数**
- **Stratum**:8比特,时钟层级(1为原子钟直接同步源,15为不可信时钟)7
- **Poll**:8比特,轮询间隔(以log₂秒为单位,默认最小值6对应64秒,最大值10对应1024秒)4
- **Precision**:8比特,本地时钟精度(log₂秒)
------
##### 二、时间戳字段(核心同步参数)
报文包含4个64位时间戳(共32字节),用于精确计算网络延迟与时钟偏差2:
| 字段名 | 作用描述 | 生成角色 |
| ------ | -------------------------- | ---------- |
| **T1** | 客户端发送请求报文的时间戳 | 客户端记录 |
| **T2** | 服务端接收请求报文的时间戳 | 服务端记录 |
| **T3** | 服务端发送响应报文的时间戳 | 服务端生成 |
| **T4** | 客户端接收响应报文的时间戳 | 客户端记录 |
同步算法通过公式计算时间偏差(θ)与网络延迟(δ):
```
textCopy Codeθ = [(T2 - T1) + (T3 - T4)] / 2
δ = (T4 - T1) - (T3 - T2)
```
------
##### 三、其他关键字段
1. **Root Delay**:32位有符号整数,表示主参考时钟到当前服务器的累计延时(秒)3
2. **Root Dispersion**:32位无符号整数,主参考时钟的最大误差范围3
3. **Reference Identifier**:32位,标识参考时钟源(如GPS、原子钟编码)3
------
##### 四、示例报文格式
```
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+---------------+---------------+-------------------------------+
|LI |VN |Mode |Stratum |Poll |Precision| Root Delay |
+---------------+---------------+-------------------------------+
| Root Dispersion | Reference Identifier |
+-----------------------------+-------------------------------+
| |
| Reference Timestamp (64) |
| |
+---------------------------------------------------------------+
| |
| Origin Timestamp (T1, 64) |
| |
+---------------------------------------------------------------+
| |
| Receive Timestamp (T2, 64) |
| |
+---------------------------------------------------------------+
| |
| Transmit Timestamp (T3, 64) |
| |
+---------------------------------------------------------------+
| |
| Destination Timestamp (T4, 64) |
| |
+---------------------------------------------------------------+
```
### 3. 串口
**波特率测试:**
试了两个命令,智慧生活从下发到收到上报的时间:
+PUTCHAR=strong,{"on":1} 命令花费的时间为365ms 364ms 341ms
+PUTCHAR=freshAirSwitch,{"on":1} 命令花费的时间为461ms 460ms 458ms
和按照波特率计算的时间大致接近:2400波特率发送一个字节时间为4.166ms,发送34个字符141ms,往返时间*2;发送20个字符83.32ms, 往返时间166.64ms。
| 函数 | 说明 |
| --------------------------------------- | ------------------------------------------------------------ |
| uapi_uart_write | 直接写,可设置超时 |
| uapi_uart_write_int | 中断写,中断中通过回调通知写是否完成 |
| uapi_uart_write_by_dma | 通过DMA写,CONFIG_UART_SUPPORT_DMA需要打开,是否阻塞式? |
| uapi_uart_read | 直接读,可设置超时。注意:需要打开CONFIG_SUPPORT_UART_POLL_TIMEOUT,否则一直读取不到数据会死循环 |
| uapi_uart_read_by_dma | 通过DMA读,是否阻塞式? |
| uapi_uart_register_rx_callback | 接收回调 |
| uapi_uart_register_read_by_dma_callback | 注册中断触发DMA搬运的回调,CONFIG_UART_SUPPORT_INT_TRIGGER_DMA需要打开,没看到具体实现 |
uapi_uart_write和uapi_uart_write_int经过实际测试,耗时如下:
| 耗时(us) | uapi_uart_write | uapi_uart_write_int |
| -------- | --------------- | ------------------- |
| 1 | 47 | 43 |
| 2 | 51 | 39 |
| 3 | 67 | 43 |
| 4 | 21 | 37 |
| 5 | 52 | 39 |
| 6 | 38 | 42 |
**结论:**在2400波特率下,uapi_uart_write和uapi_uart_write_int耗时都很短,原因:UART模块有收发FIFO,填入FIFO即可,发送由FIFO后续完成。
### 4. 宏定义
// 配置是否支持输入使能,默认不支持
CONFIG_PINCTRL_SUPPORT_IE
// 配置GPIO是否支持外部中断,默认不支持
CONFIG_PINCTRL_SUPPORT_ST
// 配置GPIO是否支持挂起(目前不知道作用),默认不支持
CONFIG_PINCTRL_SUPPORT_LPM
// menuconfig配置串口是否写互斥的宏
CONFIG_UART0_SUPPORT_WRITE_MUTEX
CONFIG_UART1_SUPPORT_WRITE_MUTEX
CONFIG_UART2_SUPPORT_WRITE_MUTEX
// 支持动态串口绑定,功能对应的串口号通过NV读取。app-iot默认不开启。uart_port_save_bus_id可以设置功能对应的串口NV保存(重启生效),AT命令SETUART调用它
// NV_ID_DBG_UART_BUS_ID、NV_ID_AT_UART_BUS_ID、NV_ID_HSO_UART_BUS_ID
// SW_DEBUG_UART_BUS(CONFIG_DEBUG_UART 0)、AT_UART_BUS(CONFIG_AT_UART 0)、LOG_UART_BUS(CONFIG_LOG_UART 1)
CONFIG_DYNAMIC_UART_ID_BINDDING
默认menuconfig中只有CONFIG_LOG_UART使用了串口1,LOG_UART_BUS等价。log_uart_init初始化了日志串口,LOG_SUPPORT在config中是打开的
## 2. HF
// 设置串口波特率
AT+UART=2400,8,1,NONE,NFC,0\r\n
AT+UART=115200,8,1,NONE,NFC,1\r\n
// 开启日志
AT+NDBGL=2,1
python3 build.py -c standard-bs20-n1200
python3 build.py standard-bs20-n1200 menuconfig
## 3. 注意的点
1. 爱联使用debug版hilink无法正常连接
2. 设备要找厂商添加白名单
3. uint64_t应该使用%lld打印而不是%d
4. **必须按照添加服务、添加特征、添加描述、开启服务的顺序执行,最后同一开启服务会导致部分服务添加不进去。**
5. **HID服务添加了就能自动回连,服务下不需要加特征,不需要开启HID广播**
# 3. 知识点
## 1. ITCM和DTCM
ITCM(Instruction Tightly Coupled Memory)和DTCM(Data Tightly Coupled Memory)是嵌入式系统中常见的高性能、低延迟内存模块,主要用于实时性要求高的场景(如ARM Cortex-M系列处理器)。它们的核心区别在于**用途**和**访问优化**方向不同。
------
**ITCM 和 DTCM 的核心区别**
| **特性** | **ITCM** | **DTCM** |
| ------------ | ---------------------------------------------------------- | ----------------------------------------------------- |
| **用途** | 存储需要快速执行的**指令**(如中断处理函数、关键算法代码) | 存储频繁访问的**数据**(如堆栈、全局变量、DMA缓冲区) |
| **访问方式** | 处理器通过指令总线直接访问 | 处理器通过数据总线直接访问 |
| **优化目标** | 减少取指延迟,提升代码执行效率 | 降低数据访问延迟,避免缓存抖动 |
| **典型应用** | 实时控制代码、中断服务程序(ISR) | 高频访问的传感器数据、实时通信缓冲区 |
| **配置方式** | 需通过链接脚本(`.ld`文件)手动分配 | 需通过链接脚本或编译器属性指定内存区域 |
## 3. hilink
### 3.1 蓝牙靠近发现对原子化服务(FA)与半模态的支持机制
**原子化服务**入口多样性:支持物理标签(NFC/二维码)、服务中心推荐、语音助手(小艺)主动推送等方式触发。
**智慧生活APP**固定入口:需用户主动打开应用,通过主界面管理已绑定设备、自定义场景规则(如自动化触发条件)
原子化服务以 **轻量化、场景化** 为核心,解决“即用即走”的瞬时需求;智慧生活APP则以 **系统化、深度控制** 为优势,承担长期设备管理与复杂场景搭建。两者在HarmonyOS生态中形成互补,共同提升多设备协同体验。
#### 一、触发条件与形态区分
1. **首次配对广播**
- 当未注册设备与手机靠近(距离<20cm),且设备通过开机/上电/双击等操作触发时,发送首次配对广播,手机会弹出连接弹窗(FA或半模态),请求设备代理注册13。
- **适用场景**:新设备初次连接或用户主动触发配对。
2. **回连广播**
- 已注册设备靠近手机时,自动发送回连广播,直接建立连接1。
- **适用场景**:设备已绑定,需快速恢复连接。
------
#### 二、广播包结构与数据格式差异
1. **拉起原子化服务(FA)**
- **Business字段**:取值为 `0x010D`,表示需拉起完整应用界面1。
- **数据格式**:采用 **TLV格式**(Type+Length+Value),其中 `ProtocolD` 的 `type` 固定为 `0x17`1。
- **典型场景**:设备需展示完整功能交互(如智能家居控制)。
2. **拉起半模态界面**
- **Business字段**:取值为 `0x0107`,触发轻量化交互界面1。
- **数据格式**:采用 **TV格式**(Type+Value,不含Length),`ProtocolD` 的 `type` 为 `0x00`1。
- **典型场景**:快速完成设备连接或简单操作(如耳机配对)。
------
#### 三、开发配置与调试要点
1. **服务入口一致性**
- 需确保原子化服务配置的 **产品型号** 与设备 `ProductID` 完全匹配,避免因信息不一致导致服务启动失败4。
- **操作路径**:登录华为快服务平台,在原子化服务编辑界面核对 `HarmonyOS标签` 中的产品型号4。
2. **广播码流准确性**
- 需严格遵循广播包结构规范,验证 `ProtocolD`、`SN` 等字段的编码逻辑,确保手机能正确解析并触发目标服务14。
------
#### 四、典型应用案例
- **蓝牙耳机快速回连**:双击耳机仓按键触发回连广播,手机自动建立连接并展示半模态界面(如电量状态)3。
- **智能家居设备绑定**:新设备靠近时通过首次配对广播拉起FA,引导用户完成注册与功能配置
### 3.2 配网方式
快速配网和SoftAP配网都调用hilink_wifi_main(HILINK_NETCONFIG_OTHER)
极简配网:打开星闪总开关后使能预扫描,后调用hilink_ble_main(HILINK_NETCONFIG_OTHER)
蓝牙辅助配网:hilink_ble_main(HILINK_NETCONFIG_WIFI)
1. **快速配网(如SmartConfig/AirKiss)**
- **原理**:通过组播/广播方式,将Wi-Fi信息编码到IEEE 802.11底层通讯帧中,设备监听并解码后连接指定网络57。
- **流程**:用户通过手机APP输入Wi-Fi信息→设备监听广播信号→自动解码并连接网络78。
2. **SoftAP配网**
- **原理**:设备创建临时Wi-Fi热点(Soft Access Point),用户手动连接该热点后,通过APP或网页将目标Wi-Fi信息传输给设备34。
-
流程
:
- 设备进入配网模式→开启SoftAP热点4。
- 用户手动连接设备热点→通过APP发送目标Wi-Fi信息→设备断开热点并连接家庭网络38。
### 3.3 函数
wifi:
HILINK_PutCharState 设置设备数据,HILINK_ControlCharState为批处理版本
HILINK_GetCharState 从设备获取数据
HILINK_ReportCharState 通过hilink上报数据
HILINK_NotifyDevStatus hilink调用此函数通知设备状态?还是hilink通知APP设备状态?
HILINK_ProcessBeforeRestart hilink触发重启前的回调,执行重启前操作
ble:
BleRcvCustomData调用BleHandleCustomData,通过入参json执行不同的设置和上报动作。BLE_CfgNetInit注册BleRcvCustomData函数到HILINK
BLE_SendCustomData 上报数据
ReporSwitchStatus调用了BLE_SendCustomData,可以作为实现参考
HILINK_SetSdkAttr:设置sdk属性,如线程栈大小,硬重启和软重启回调等
HILINK_SetNetConfigMode:设置配网模式
HILINK_RegisterErrnoCallback:单独升级使用,注册获取错误的回调
hal_reboot_chip:使设备软重启
HILINK_EnableQuickNetCfg:设置支持快速配网
HILINK_Main:HILINK入口函数
StartQuickNetCfg:启动快速配网进程
HILINK_SetProtType:设备端云通道协议类型,极简配网是17,其它是1
BLE_SetAdvNameMpp:设置蓝牙广播的MPP,设置广播类型标志及心跳间隔
HILINK_BT_SetSdkEventCallback:设置蓝牙SDK事件处理函数
BLE_SetAdvType(BLE_ADV_NEARBY_V0); // 设置蓝牙广播格式,包括靠近发现、碰一碰等,下一次发送广播生效
BLE_CfgNetAdvCtrl : 设置蓝牙广播时间,启动广播
BLE_CfgNetInit:蓝牙配网初始化,配置回调函数,没有看到如何获取WIFI名称和密码(通过适配BleGattsRegisterCallbacks等接口让hilink具备蓝牙能力)
BLE_CfgNetAdvUpdate:更新广播参数
HILINK_BT_GetDevSurfacePower:hilink用于获取ble发送信号强度
HILINK_GetCustomInfo:hilink获取ble自定义广播信息(BLE_SetAdvType设置为自定义时)
HILINK_GetManuId:hilink获取ble厂商ID(BLE_SetAdvType设置为自定义时)
HILINK_GetDeviceSn:hilink获取sn
HILINK_BT_GetDevInfo、HILINK_GetDevInfo
```c++
typedef struct {
unsigned int (*getWifiRecoveryType)(void); // 获取网络优化功能类型
int (*scanAP)(const HILINK_APScanParam *param); // 根据参数扫描周围AP信息
int (*getAPScanResult)(HILINK_APList *scanList); // SDK调用scanAP后, 调用此函数获取扫描周围AP信息的结果
int (*restartWiFi)(void); // 重新启动WiFi模块
int (*connectWiFiByBssid)(int securityType, const unsigned char *bssid, unsigned int bssidLen); // 连接指定bssid的与配网ssid同名的Wi-Fi
int (*lastConnResult)(int *result); // 获取上一次连接WiFi失败原因
} WiFiRecoveryApi; // 通过注册这些回调,HILINK就有了WIFI的能力
typedef struct {
StartWifiPromisCallback startPromis; // WiFi 混杂模式(Promiscuous Mode)启动结果
StopWifiPromisCallback stopPromis;
GetWifiChannelCallback getChannel;
SetWifiChannelCallback setChannel; /* provider no need */
SetWifiModeCallback setWifiMode; /* provider no need */
SendWifiFrameCallback sendFrame;
ScanfWifiCallback scanWifi;
} QuickCfgWifiLoader; // 批量快速配网需要注册的回调
/* BLE配网回调函数 */
typedef struct {
BLE_GetDevPinCode getDevPinCodeCb; // PIN码
BLE_GetDeviceInfo getDeviceInfoCb; // 获取设备信息
BLE_SetCfgNetInfo setCfgNetInfoCb; // 设置配网信息函数类型
BLE_RcvCustomData rcvCustomDataCb; // 接收用户数据函数类型,调用BleHandleCustomData
BLE_CfgNetProcess cfgNetProcessCb; // 配网过程状态处理函数类型
} BLE_CfgNetCb;
```
> WiFi 混杂模式应用:智能设备配网
> SmartConfig 协议:手机发送加密的广播包,设备通过混杂模式解析 SSID 和密码。
> 实现逻辑:混杂模式下过滤特定长度或 MAC 地址的广播帧,提取配网参数。
| **特性** | **快速配网(SmartConfig)** | **SoftAP 配网** |
| ------------ | ----------------------------------------------------------- | ------------------------------------------------------------ |
| **核心原理** | 设备进入混杂模式监听 UDP 广播包,手机发送编码后的 SSID/密码 | 设备创建独立 Wi-Fi 热点,手机连接后通过 TCP/UDP 传输 SSID/密码 |
| **操作步骤** | 手机连接路由器后发送广播包 → 设备解析并连接网络 | 手机手动连接设备热点 → 通过网页/App 输入目标路由信息35 |
| **信道切换** | 设备需轮询监听多个信道,手机与路由器需同频段 | 设备固定信道,手机直接连接热点无需信道匹 |
**极简配网流程**
1. **设备发现**:
- 设备通电后自动广播 **星闪 Beacon 信号**,手机/网关通过星闪协议识别设备38。
- 支持 **无屏设备一键触发**(如双击按键),进入待配网状态。
2. **身份认证**:
- 采用 **动态密钥协商机制**,手机与设备通过星闪信道完成加密握手,无需手动输入密码38。
- 部分场景支持 **扫码配网**(如智能电视),直接关联家庭网络。
3. **网络配置**:
- 网关通过星闪协议将 **Wi-Fi SSID/密码** 加密传输至设备,设备自动切换至目标网络48。
- 支持 **双频漫游**,设备根据信号强度自动切换 2.4GHz/5GHz 频段。
适配:
HILINK_CreateTask:创建任务,底层使用OS接口
## 4. WIFI
WIFI_IFNAME_MAX_SIZE为WIFI网卡名称
WIFI_MAX_SSID_LEN是WIFI广播名称
wifi_csi_data_cb:CSI信道状态信息回调函数,csi_config_stru包含csi配置,指定采集策略,如指定MAC和帧类型等
wifi_promis_cb:混杂模式收包回调
wifi_rx_mgmt_cb:管理帧收包回调接口
wifi_psd_cb:当设备启用频谱扫描或信道诊断功能时,底层驱动会周期性采集 PSD 数据,并通过回调函数 `wifi_psd_cb` 上报至应用层
**WiFi 功率谱密度(Power Spectral Density, PSD)数据**
ext_psd_option_param结构体包含采样时长、间隔、开关
wifi_pmf_option_enum:是用于配置 **保护管理帧(Protected Management Frames, PMF)** 的枚举类型,主要应用于 WiFi 安全协议(如 WPA3)中,以增强管理帧的加密和完整性保护
- WIFI_PMF_DISABLED:禁用 PMF 功能,管理帧不加密(不推荐,存在安全风险)。
- WIFI_PMF_OPTIONAL:启用 PMF 但非强制,设备支持与不支持 PMF 的 AP 均可连接。
- WIFI_PMF_REQUIRED:强制启用 PMF,仅允许与支持 PMF 的 AP 建立连接(适用于 WPA3 等高安全场景)
wifi_security_enum:wifi安全枚举,WPA2等
```
typedef enum {
IFTYPE_STA, /*!< 客户端模式(Station),设备作为终端连接至热点 */
IFTYPE_AP, /*!< 热点模式(Access Point),设备作为热点供其他设备连接 */
IFTYPE_P2P_CLIENT, /*!< P2P 客户端模式,用于点对点直连通信的客户端角色 */
IFTYPE_P2P_GO, /*!< P2P 组所有者模式(Group Owner),作为 P2P 网络的中心节点 */
IFTYPE_P2P_DEVICE, /*!< P2P 设备模式,表示设备支持 P2P 发现与协商 */
IFTYPES_BUTT /*!< 枚举结束标记,无实际用途 */
} wifi_if_type_enum;
wifi_iftype_t也是枚举
wifi_dev_t wifi设备信息
ip_config_stru ip掩码和网关
ipv6_config_stru ip6的地址和DNS
wifi_sta_config_stru 要连接哪个WIFI的相关信息 wifi_fast_connect_stru 快速连接结构体
wifi_scan_type_enum 扫描类型
wifi_scan_params_stru 扫描参数
wifi_scan_strategy_stru 扫描策略:信道停留时间、信道数、扫描帧的发送次数
wifi_scan_info_stru 扫描结果信息
wifi_raw_scan 不经过wpa的原始扫描,wifi_scan_no_save_cb回调用于获取扫描结果
protocol_mode_enum 协议模式枚举,11b等等
wifi_ptype_filter_stru 混杂模式报文接收过滤设置
linkloss_paras_stru 包含linkloss阈值时间和次数
wifi_event_stru 配置状态回调函数
wifi_sta_info_stru 热点中,有设备连接进来,设备信息:MAC 信号强度 最佳速率
wifi_linked_info_stru sta连上热点时获取的AP信息
wifi_init WIFI初始化 wifi_deinit wifi_is_wifi_inited
wifi_sta_enable 开启sta wifi_sta_disable wifi_is_sta_enabled
wifi_get_dev 获取使能状态,STA和AP等
wifi_sta_set_protocol_mode 设置协议模式,11b等等 wifi_sta_get_protocol_mode
wifi_sta_scan 扫描 wifi_sta_scan_advance 带参数扫描 wifi_sta_set_scan_policy 设置扫描参数 wifi_raw_scan 原始扫描,带回调 wifi_sta_scan_stop停止扫描 wifi_sta_get_scan_info 获取扫描结果 wifi_sta_scan_result_clear 清空扫描结果
wifi_set_channel 切换信道 wifi_get_channel 获取信道
wifi_sta_connect sta连接 wifi_sta_disconnect wifi_sta_get_ap_info wifi_sta_set_reconnect_policy设置重连参数 wifi_sta_get_connect_status_code 获取连接状态 wifi_sta_fast_connect 快速连接
wifi_register_event_cb 注册时间回调 wifi_unregister_event_cb
wifi_sta_wps_pbc wps_pbc连接 wifi_sta_wps_pin wps_pin连接 wifi_sta_get_wps_pin
wifi_set_app_ie 在管理帧中添加用户IE字段 wifi_del_app_ie
wifi_set_promis_mode 设置混杂模式,包含过滤列表 wifi_set_promis_rx_pkt_cb 混杂模式回调
wifi_sta_set_pmf_mode 保护管理帧 wifi_sta_get_pmf_mode wifi_set_mgmt_frame_rx_cb 管理帧回调
wifi_sta_wnm_bss_query 发送bss query报文 wifi_sta_wnm_notify 发送wnm notify query报文
是 Wi-Fi STA(工作站)模式下用于 请求 BSS(基本服务集)网络状态信 的接口,基于 IEEE 802.11 WNM(Wireless Network Management)协议实现。获取当前或邻近 BSS 的实时状态信息(如信道负载、连接设备数、AP 负载等)。支持动态网络切换(如快速漫游至负载更低的 AP)。
wifi_set_wow_pattern 配置 Wi-Fi 唤醒模式匹配规则的接口,主要用于设备低功耗场景下通过特定数据包触发唤醒 wifi_set_wow_sleep_mode STA下WOW休眠是否使能
wifi_csi_start CSI信道状态开启上报 wifi_csi_stop wifi_set_csi_config wifi_register_csi_report_cb
wifi_send_custom_pkt 发送用户定制报文,报文须按照802.11协议格式封装
wifi_set_pkt_retry_policy 设置数据帧和管理帧的最大软件重传次数
wifi_reset_mac_phy 复位MAC和PHY接口,解决MAC,PHY挂死问题
wifi_set_linkloss_config 设置保活参数
wifi_set_base_mac_addr 设置基本mac地址 wifi_get_base_mac_addr wifi_softap_set_mac_addr wifi_softap_get_mac_addr
wifi_set_mac_derivation_ptr 设置mac派生回调:sta MAC与基础MAC一致,将基础 MAC倒数第二个bit加2生成softap MAC;将基础MAC最后一个bit加1生成BLE广播MAC
wifi_set_low_current_boot_mode 设置低启动电流 wifi_sta_set_pm sta设置低功耗
wifi_get_country_code 获取国家码 wifi_set_country_code
wifi_set_sdp_mode ????? wifi_set_sdp_subscribe
wifi_set_psd_mode 设置频谱扫描模式 wifi_set_psd_cb psd回调
```
```
wifi_set_fixed_tx_rate 设置发送速率:自动或设置好的
wifi_get_negotiated_rate 获取最优速率
wifi_set_tpc_mode 配置无线发射功率控制(Transmit Power Control, TPC)模,0-关闭动态调整,1-功率提升模式,2-自动调整模式
wifi_set_rts_mode用于配置RTS(Request to Send)阈值,解决无线网络中的隐藏节点问题,优化数据传输可靠性。
RTS/CTS 机制:
- 发送数据前通过 RTS/CTS 帧协商信道占用权,避免多设备冲突。
- 阈值设置:当数据帧长度超过阈值时触发 RTS/CTS 流程。
典型参数:`rts_threshold`:取值范围 `0~2347`,默认 `2347`(禁用 RTS);设置为 `500` 表示数据帧≥500字节时触发 RTS。
应用场景:
- 高密度网络:如会议室、机场等用户密集场景,减少数据冲突。
- 长距离传输:降低因信号衰减导致的数据包丢失率
wifi_set_cca_threshold 用于设置CCA(Clear Channel Assessment)检测阈值,控制无线设备判断信道空闲或繁忙的灵敏度,优化信道竞争效率
功能定义:
CCA 机制:802.11 协议要求设备在发送数据前检测信道是否空闲。
当接收信号强度(RSSI)超过阈值时,判定信道为“忙”,延迟发送。
阈值范围:
典型值为 -82dBm(默认值)至 -60dBm,单位 dBm。
阈值越低,设备对信道占用越敏感(减少冲突),但可能导致信道利用率降低。
典型应用场景:
高干扰环境:降低阈值(如 -90dBm),减少因信号干扰导致的冲突。
低功耗设备:提高阈值(如 -70dBm),缩短信道检测时间以节省功耗
wifi_set_intrf_mode 设置抗干扰模式
```
```
softap_config_stru AP配置,WIFI名称和密码
wifi_softap_enable AP使能,会直接开启 wifi_softap_disable wifi_is_softap_enabled wifi_get_softap_config
typedef struct {
uint32_t beacon_interval; // beacon灯塔/广播间隔
uint32_t dtim_period; // DTIM(Delivery Traffic Indication Message)间隔是无线路由器中用于控制组播和广播数据包发送频率的参数。它决定了AP(接入点)在发送Beacon帧时,包含DTIM信息的频率
uint32_t group_rekey; // 定期更新组播/广播数据的加密密钥,提升网络安全性。范围30s~86400s, 默认86400s(1天),0表示未配置
uint32_t hidden_ssid_flag; // 不隐藏:1,隐藏:2。默认配置为不隐藏,0表示未配置。
uint32_t gi; // 配置无线信号的保护间隔(Guard Interval),影响数据传输速率和抗干扰能力。400ns和800ns。GI是两个符号传输间的一段时期,用于清除前次传输的反射。
protocol_mode_enum protocol_mode; // 协议模式,默认按照芯片最大协议能力进行配置,0表示未配置
} softap_config_advance_stru; // 广播配置
wifi_set_softap_config_advance 设置广播配置 wifi_get_softap_config_advance
wifi_softap_get_sta_list 获取sta列表
wifi_softap_deauth_sta 断开指定MAC
```
```
p2p_device_stru 扫描结果 wifi_p2p_find 开启扫描多少秒 wifi_p2p_stop_find 停止扫描 wifi_p2p_get_peers_info 获取扫描结果
p2p_config_stru 待连接的p2p设备信息 wifi_p2p_connect主动连接 wifi_p2p_connect_accept是否接收连接 wifi_p2p_connect_cancel取消连接请求 wifi_p2p_disconnect 断开已有连接
p2p_status_info_stru p2p连接状态信息 wifi_p2p_get_connect_info
p2p_client_info_stru p2p go获取已连接的client的信息 wifi_p2p_go_get_gc_info
p2p_device_config_stru 本设备的配置信息 wifi_p2p_set_device_config wifi_p2p_get_device_config
wifi_p2p_enable p2p使能,会直接开启 wifi_p2p_disable wifi_p2p_is_enabled
wifi_p2p_listen 设置监听的周期和间隔
```
## 5. BLE
#### 1. API
| 地址类型 | 子类 | 说明 |
| -------- | ---------------- | ----------------------------------------------------------- |
| 公有地址 | | 需要申请,分固定不分和内部分配部分 |
| 随机地址 | 静态地址 | 上电时随机,上电后不变 |
| 随机地址 | 可解析私有地址 | 24位随机+24位IRK与随机的哈希,默认15min更新,可解析真实身份 |
| 随机地址 | 不可解析私有地址 | 不可解析真实身份,也会周期变化 |
| 广播方式 | 说明 |
| ------------------------------------- | ------------------------------------------------------------ |
| 可连接的非定向广播(ADV_IND) | 可扫描可连接 |
| 可连接的定向广播(ADV_DIRECT_IND) | 定向广播类型是为了尽可能快的连接,**俗称回连包**,这种报文包含两个地址:广播者的地址和发起者的地址。发起者收到发给自己的定向广播报文之后,可以立即发送连接请求作为回应。当使用定向广播时,设备不能被主动扫描。此外,定向广播报文的净荷中也不能带有其他附加数据。**该净荷只能包含两个必须的地址。** |
| 不可连接的非定向广播(ADV_NONCONN_IND) | 仅仅发送广播数据,而不想被扫描或者连接 |
| 可扫描的非定向广播(ADV_SCAN_IND) | 可扫描不可连接 |
| **PHY类型** | **1M PHY** | **2M PHY** | **Coded PHY (S=8)** |
| ----------- | ---------- | ---------- | ------------------- |
| 数据速率 | 1 Mbps | 2 Mbps | 125/500 Kbps |
| 通信距离 | 中 | 短 | 长(4 倍于 1M) |
| 抗干扰能力 | 一般 | 一般 | 强(FEC 编码) |
**传统广播**:只使用了 37,38,39 这三个通道(**传统广播数据的长度上限是31个字节** )。只能通过扫描响应包传递更多数据:设备名称、服务UUID、制造商信息。
**扩展广播:**使用了两组广播通道**(扩展广播数据长度上限是255 个字节)**:
主要广播通道也使用了 37,38,39 这三个通道,
次要广播通道则使用剩余的 37(0~36) 个通道。
```
gap_ble_appearance_type_t 蓝牙外观类型
gap_ble_adv_filter_allow_scan_t 广播过滤参数,只处理指定扫描和连接请求
gap_ble_adv_type_t 广播类型,与上表有差异
gap_ble_scan_result_evt_type_t 扫描结果广播类型,分传统和扩展
gap_ble_scan_type_t 扫描类型,主动还是被动,主动会发送SCAN_REQ数据包获取额外信息(需要设备地址)
gap_ble_scan_filter_policy_t 扫描过滤策略
gap_ble_scan_result_data_status_t 扫描结果数据完整性
gap_ble_phy_type_t PHY类型
adv_status_t 广播状态,正在和停止
gap_ble_config_adv_data_t 包含广播数据和响应数据
gap_ble_sec_mode_t 设备安全能力:配对和加密使用技术
gap_ble_io_ability_t 设备输入输出能力:展示和键盘等
gap_ble_filter_duplicates_t 是否上报重复的广播包
gap_ble_pair_state_t 蓝牙配对状态,配对后下次连接更快,两个设备绑定
gap_ble_disc_reason_t 断连原因
gap_ble_conn_state_t 连接状态,配对只是用PIN码做设备认证,连接了才能做蓝牙相关操作(先连接再配对)
gap_ble_connect_param_update_callback 连接参数更新回调 gap_ble_conn_param_update_t
gap_ble_auth_complete_callback 认证回调 ble_auth_info_evt_t ltk,用于后续数据加密传输和连接
enable_ble disable_ble 蓝牙使能去使能
gap_ble_set_local_addr 设置本地设备地址 gap_ble_get_local_addr
gap_ble_set_local_appearance 设置外观
gap_ble_set_local_name 设置本地名字 gap_ble_get_local_name
gap_ble_set_sec_param 设置安全参数 gap_ble_sec_params_t 绑定、安全配对等
gap_ble_connect_param_update ble连接参数更新 gap_conn_param_update_t
gap_ble_set_phy 设置BLE PHY参数 gap_le_set_phy_t
gap_ble_set_data_length 设置BLE发包参数 gap_le_set_data_length_t 最大字节数、最大发送时间
gap_ble_register_callbacks 注册回调
bth_ota_init 初始化bth ota通道。作为蓝牙 OTA 的入口函数,需完成协议栈配置、资源分配及异常状态恢复等核心任务,其设计直接影响升级的可靠性和效率。实际开发中需结合硬件特性(如 Flash 布局)和协议规范(如 GATT 服务定义)进行优化,确保兼容不同升级模式(SLB/Single Bank)及异常场景的健壮性
ble_customize_max_pwr 配置定制化信息:蓝牙和星闪的最大功率
gap_ble_set_adv_data 设置广播数据和扫描响应数据
gap_ble_set_adv_data_filter 使用KEY对广播数据过滤 gap_ble_adv_data_filter_t gap_ble_clean_adv_data_filter 清除过滤
gap_ble_set_adv_param 设置广播参数 gap_ble_adv_params_t
gap_ble_start_adv 开始广播 gap_ble_stop_adv
gap_ble_set_scan_parameters 设置扫描参数 gap_ble_scan_params_t
gap_ble_set_scan_extern_parameters 额外扫描参数 gap_ble_extern_scan_params_t 包含重复广播包是否上报
gap_ble_start_scan 开始扫描 gap_ble_stop_scan
gap_ble_scan_result_callback 扫描结果回调 gap_scan_result_data_t
gap_ble_pair_remote_device 配对 gap_ble_remove_pair 删除配对 gap_ble_remove_all_pairs 删除所有配对
gap_ble_get_paired_devices_num 已配对数量 gap_ble_get_paired_devices 配对设备信息
gap_ble_get_pair_state 指定设备配对状态
gap_ble_get_bonded_devices 绑定设备信息
gap_ble_connect_remote_device 连接 gap_ble_disconnect_remote_device
gap_ble_read_remote_device_rssi 读对端信号强度,可用于测距和定位
```
```
gatt_characteristic_property_t 特征特性
gatt_attribute_permission_t attribute权限
gattc_discovery_service_result_t 发现服务结果(包含服务在属性表的范围) gattc_discovery_service_callback 发现回调 gattc_discovery_service_complete_callback 发现完成 gattc_discovery_service 执行发现
gattc_discovery_character_result_t 发现特征结果 gattc_discovery_character_callback 发现回调 gattc_discovery_character_complete_callback 发现完成 gattc_discovery_character 执行发现(gattc_discovery_character_param_t参数)
gattc_discovery_descriptor_result_t 发现特征描述符结果 gattc_discovery_descriptor_callback 发现回调gattc_discovery_descriptor_complete_callback 发现完成 gattc_discovery_descriptor执行发现
gattc_handle_value_t 读取值 gattc_read_cfm_callback 读取回调 gattc_read_req_by_handle 读取函数
gattc_read_req_by_uuid_param_t 使用uuid发起读取请求 gattc_read_req_by_uuid 函数 gattc_read_by_uuid_complete_callback 回调
gattc_write_cfm_callback 有响应写回调 gattc_write_req 写请求(有响应) gattc_write_cmd 写命令(无响应)
gattc_mtu_changed_callback mtu改变回调 gattc_exchange_mtu_req 改函数
gattc_notification_callback 通知回调
gattc_indication_callback 指示回调
gattc_register_client 注册客户端 gattc_unregister_client
```
```
gatts_add_desc_info_t 添加特征描述符信息 gatts_add_descriptor_callback 回调 gatts_add_descriptor 添加函数 gatts_add_descriptor_sync
gatts_add_character_result_t 添加特征结果 gatts_add_characteristic_callback 回调 gatts_add_characteristic_sync 添加特征函数(gatts_add_chara_info_t 添加特征信息) gatts_add_characteristic
gatts_add_service_callback 添加服务回调 gatts_add_service gatts_add_service_sync
gatts_delete_service_callback 删除服务回调 gatts_delete_service gatts_delete_all_services
gatts_start_service_callback 开始服务回调 gatts_start_service
gatts_stop_service_callback 停止服务回调 gatts_stop_service
gatts_mtu_changed_callback mtu改变回调 gatts_set_mtu_size
gatts_req_read_cb_t 收到读请求信息 gatts_read_request_callback
gatts_req_write_cb_t 收到写请求信息 gatts_write_request_callback
gatts_send_rsp_t 响应消息 gatts_send_response 发送响应函数
gatts_ntf_ind_t 通知/指示信息 gatts_notify_indicate 发送函数
gatts_ntf_ind_by_uuid_t uuid发送通知/指示信息 gatts_notify_indicate_by_uuid 发送函数
gatts_register_server 注册服务端 gatts_unregister_server
```
#### 2. 知识点
一般而言设备提供服务,因此设备是server,手机使用设备提供的服务,**因此手机是client**。
[蓝牙低功耗ATT/GATT/Profile/Service/Characteristic规格解读 - iini - 博客园](https://www.cnblogs.com/iini/p/12334646.html)
| **特征的权限与操作类型** | **位掩码** | **功能描述** |
| ----------------------------------------------- | ---------- | ------------------------------------------------------------ |
| `GATT_CHARACTER_PROPERTY_BIT_BROADCAST` | `0x01` | 特征值可广播(如周期性发送传感器数据) |
| `GATT_CHARACTER_PROPERTY_BIT_READ` | `0x02` | 允许客户端读取特征值(需权限匹配) |
| `GATT_CHARACTER_PROPERTY_BIT_WRITE_NO_RSP` | `0x04` | 客户端可无响应写入(单向操作,适用于高频低优先级数据,如控制指令) |
| `GATT_CHARACTER_PROPERTY_BIT_WRITE` | `0x08` | 客户端可写入特征值并需服务器响应(需权限匹配,适用于需确认的操作,如配置参数) |
| `GATT_CHARACTER_PROPERTY_BIT_NOTIFY` | `0x10` | 服务器可主动通知客户端(单向,无确认,适用于实时数据推送,如心率监测) |
| `GATT_CHARACTER_PROPERTY_BIT_INDICATE` | `0x20` | 服务器可指示客户端(需客户端确认,适用于可靠性要求高的场景,如固件升级状态) |
| `GATT_CHARACTER_PROPERTY_BIT_SIGNED_WRITE` | `0x40` | 客户端需签名写入(增强安全性,防止数据篡改,如密钥配置) |
| `GATT_CHARACTER_PROPERTY_BIT_EXTENDED_PROPERTY` | `0x80` | 扩展属性需通过描述符(如`Characteristic Extended Properties`)定义额外特性 |
| 属性权限类型 | 十六进制值 | 功能说明 |
| ----------------------------------------------- | ---------- | -------------------------- |
| `GATT_ATTRIBUTE_PERMISSION_READ` | `0x01` | 允许读取属性值 |
| `GATT_ATTRIBUTE_PERMISSION_WRITE` | `0x02` | 允许写入属性值 |
| `GATT_ATTRIBUTE_PERMISSION_ENCRYPTION_NEED` | `0x04` | 需加密通信(如AES) |
| `GATT_ATTRIBUTE_PERMISSION_AUTHENTICATION_NEED` | `0x08` | 需身份认证(如配对) |
| `GATT_ATTRIBUTE_PERMISSION_AUTHORIZATION_NEED` | `0x10` | 需用户授权(如弹窗确认) |
| `GATT_ATTRIBUTE_PERMISSION_MITM_NEED` | `0x20` | 需防中间人攻击(MITM保护) |
**开发注意事项**
1. **属性与权限的匹配**:
- 若特征启用 `WRITE` 或 `SIGNED_WRITE`,需在 GATT 服务中配置对应的权限(如 `GATT_PERM_WRITE_ENCRYPTED`)。
2. **Notify/Indicate 的使能条件**:
- 客户端需通过 `CCC Descriptor`(Client Characteristic Configuration)启用通知或指示(写入 `0x0001` 或 `0x0002`)。
3. **扩展属性的实现**:
- 当启用 `EXTENDED_PROPERTY` 时,需添加 `Characteristic Extended Properties` 描述符(如定义`Reliable Write`或`Writable Auxiliaries`)。
4. **性能与功耗权衡**:
- `NOTIFY` 无确认机制,适用于低功耗场景;`INDICATE` 可靠性高但增加通信延迟
##### 2.1 AUDIO
AVRCP是**Audio/Video Remote Control Profile**缩写,用于音视频远程控制。比如音量、电量等。**TG**:target目标是接收控制器发送过来的命令帧并生成相应响应帧的设备,如手机。
[蓝牙专题之AVRCP协议 - 知乎](https://zhuanlan.zhihu.com/p/626073628)
[蓝牙音乐之A2DP协议 - 知乎](https://zhuanlan.zhihu.com/p/618781513)
```
bt_avrcp_tg_register_audio_cbk bt_avrcp_tg_degister_audio_cbk 回调注册与反注册。
bt_avrcp_tg_pass_through_response_cbks 按键回调
bt_avrcp_tg_get_media_status_cbks AVRCP事件回调
```
##### 2.2 广播数据类型
蓝牙广播数据类型是指在蓝牙设备广播时所包含的数据类型。这些数据类型按照蓝牙核心规范和通用访问配置文件(Generic Access Profile)进行定义和使用。每个广播数据结构由三个部分组成:长度(Length)、数据类型(AD Type)和数据(AD Data)。
广播数据类型定义
以下是一些常见的蓝牙广播数据类型及其定义:
- **GAP_ADTYPE_FLAGS (0x01)**: 标志位,用于指示设备的发现模式[1](https://blog.csdn.net/freemote/article/details/119345553)。见ble_adv_flag gap_ble_scan_result_evt_type_t
- **GAP_ADTYPE_16BIT_MORE (0x02)**: 部分16位UUID服务列表[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_16BIT_COMPLETE (0x03)**: 完整的16位UUID服务列表[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_32BIT_MORE (0x04)**: 部分32位UUID服务列表[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_32BIT_COMPLETE (0x05)**: 完整的32位UUID服务列表[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_128BIT_MORE (0x06)**: 部分128位UUID服务列表[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_128BIT_COMPLETE (0x07)**: 完整的128位UUID服务列表[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_LOCAL_NAME_SHORT (0x08)**: 缩短的本地名称[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_LOCAL_NAME_COMPLETE (0x09)**: 完整的本地名称[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。见ble_local_name_st
- **GAP_ADTYPE_POWER_LEVEL (0x0A)**: 发射功率级别,范围从-127到+127 dBm[1](https://blog.csdn.net/freemote/article/details/119345553)。见ble_tx_power_level_st
- **GAP_ADTYPE_OOB_CLASS_OF_DEVICE (0x0D)**: 设备类别[1](https://blog.csdn.net/freemote/article/details/119345553)。
- **GAP_ADTYPE_SERVICE_DATA (0x16)**: 服务数据,包含16位UUID[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_PUBLIC_TARGET_ADDR (0x17)**: 公共目标地址[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_RANDOM_TARGET_ADDR (0x18)**: 随机目标地址[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_APPEARANCE (0x19)**: 外观[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。 见ble_appearance_st
- **GAP_ADTYPE_ADV_INTERVAL (0x1A)**: 广播间隔[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_LE_BD_ADDR (0x1B)**: LE蓝牙设备地址[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_LE_ROLE (0x1C)**: LE角色[2](https://blog.csdn.net/chengdong1314/article/details/55051653)。
- **GAP_ADTYPE_MANUFACTURER_SPECIFIC (0xFF)**: 厂商特定数据[1](https://blog.csdn.net/freemote/article/details/119345553)。
##### 2.3 更新连接参数
```c
typedef struct {
uint16_t conn_handle; /*!< @if Eng connection id
@else 连接 ID @endif */
uint16_t interval_min; /*!< @if Eng min interval
@else 最小间隔 @endif */
uint16_t interval_max; /*!< @if Eng max interval
@else 最大间隔 @endif */
uint16_t slave_latency; /*!< @if Eng slave reply min latency
@else 从设备回复最小间隔 @endif */
uint16_t timeout_multiplier; /*!< @if Eng interval for disconnection due to timeout
@else 超时断连间隔 @endif */
} gap_conn_param_update_t;
```
| 成员 | 作用 | 取值范围 |
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **`conn_handle`** | 标识当前连接的句柄,用于区分多设备同时连接的场景4。 | 由协议栈分配,通常为0x0000~0x0EFF4。 |
| **`interval_min`** | 主设备与从设备之间**最小通信间隔**,影响实时性。若设置过小可能导致功耗增加48。 | 6~3200(对应7.5ms~4s)4。 |
| **`interval_max`** | **最大通信间隔**,需满足 `interval_min ≤ interval_max`,否则参数更新失败48。 | 同`interval_min`。 |
| **`slave_latency`** | 允许从设备**跳过的连接事件数**(如设置为2,从设备可每3个事件响应一次),用于降低从机功耗4。 | 0~499,典型值为0(实时响应)或5~10(低功耗场景)4。 |
| **`timeout_multiplier**` | 连接超时阈值,超时后触发断连。计算公式:`timeout = N * 10ms`48。 | 10~3200(对应100ms~32s),需满足 `timeout > (1+slave_latency)*interval_max`4。 |
```c
typedef struct {
uint16_t conn_handle; /*!< @if Eng interval
@else 连接句柄 @endif */
uint8_t all_phys; /*!< @if Eng interval
@else 所有PHY @endif */
uint8_t tx_phys; /*!< @if Eng interval
@else 发送PHY @endif */
uint8_t rx_phys; /*!< @if Eng interval
@else 接收PHY @endif */
uint16_t phy_options; /*!< @if Eng interval
@else PHY选项 @endif */
} gap_le_set_phy_t;
```
| 成员 | 作用 | 典型取值与示例 |
| ----------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
| **`conn_handle`** | 标识当前连接的句柄,用于多设备场景下的参数区分5。 | 由协议栈分配(如Nordic SDK中通常为0x0000~0x0EFF)5。 |
| **`all_phys`** | 主设备支持的PHY类型,通过位掩码表示。例如:`0x01`表示支持1M PHY,`0x04`表示支持编码PHY5。 | `0x03`(支持1M和2M PHY)5。 |
| **`tx_phys`** | 指定发送数据时使用的PHY类型。若设置为`0`,则使用默认PHY(通常为1M)5。 | `0x01`(1M PHY)、`0x02`(2M PHY)、`0x04`(编码PHY)5。 |
| **`rx_phys`** | 指定接收数据时使用的PHY类型。若设置为`0`,则与发送PHY一致5。 | 同`tx_phys`。 |
| **`phy_options`** | 配置PHY通信的偏好选项,如是否优先使用高编码效率或长距离模式6。 | `0x01`(偏好编码PHY)、`0x02`(允许自动切换PHY)6。 |
##### 2.4 CCC
`CCC Descriptor`(Client Characteristic Configuration Descriptor,客户端特征配置描述符)是蓝牙GATT协议中用于管理特征值(Characteristic)通知(Notify)和指示(Indicate)功能的关键属性16。其核心细节如下:
------
###### 1. **功能与定义**
- **核心作用**:允许客户端(如手机)配置服务端(如传感器)是否启用特征的**通知**或**指示**功能16。
- **UUID标识**:固定为 `0x2902`(标准UUID)6。
- **必要性**:若特征需支持通知或指示,**必须包含此描述符**,否则客户端无法启用对应功能6。
------
###### 2. **数值格式与操作**
| 数值(16位) | 功能 |
| ------------ | -------------------- |
| `0x0000` | 禁用通知和指示 |
| `0x0001` | 启用通知(Notify) |
| `0x0002` | 启用指示(Indicate) |
- **写入流程**:客户端通过向描述符写入上述数值触发配置68。
- **权限要求**:服务端需为`CCC Descriptor`设置**可写权限**(如`GATT_ATTRIBUTE_PERMISSION_WRITE`)68。
------
###### 3. **开发注意事项**
- 服务端实现:
- 需监听客户端对`CCC Descriptor`的写入事件,动态开启/关闭通知功能68。
- 若未正确处理写入请求,客户端可能收到`ATT_ERROR_WRITE_NOT_PERMITTED`错误6。
- **客户端兼容性**:部分低功耗设备可能限制同时启用通知和指示(需根据硬件手册确认)6。
------
###### 4. **典型应用场景**
**示例:心率监测服务**
```
cCopy Code// 服务端定义支持通知的心率特征
gatts_add_chara_info_t heart_rate_chara = {
.chara_uuid = {0x2A37}, // 心率特征UUID
.properties = GATT_PROP_NOTIFY, // 声明支持通知
.permissions = GATT_ATTRIBUTE_PERMISSION_READ,
.value_len = 1,
.value = &heart_rate_value
};
// 关联CCC描述符(UUID=0x2902)
gatts_add_desc_info_t ccc_desc = {
.desc_uuid = {0x2902},
.permissions = GATT_ATTRIBUTE_PERMISSION_READ | GATT_ATTRIBUTE_PERMISSION_WRITE,
.value = {0x00, 0x00} // 初始禁用通知
};
```
- **客户端操作**:写入`0x0001`至`CCC Descriptor`后,服务端开始定期推送心率数据68。
------
###### 5. **安全与权限**
- **加密要求**:若需安全通信,可额外设置`GATT_ATTRIBUTE_PERMISSION_ENCRYPTION_NEED`权限。
- **MITM保护**:敏感场景建议启用`GATT_ATTRIBUTE_PERMISSION_MITM_NEED`防御中间人攻击。
------
通过合理配置`CCC Descriptor`,开发者可实现高效的低功耗蓝牙数据推送,同时保障通信安全性与兼容性。
##### 2.5 蓝牙character和attribute的最大长度
在蓝牙协议中,`Characteristic`(特征值)和`Attribute`(属性)的最大长度受协议版本及实现限制,需分层解析如下:
------
###### 1. **`Attribute`(属性层)**
- **ATT层规范**:
属性值(Attribute Value)的最大长度由`ATT_MTU`(最大传输单元)决定。
- **BLE 4.0/4.1**:`ATT_MTU`默认为 **23字节**,扣除协议头后应用层可用 **20字节**。
- **BLE 4.2/5.0+**:`ATT_MTU`可扩展至 **247字节**(实际可用负载为 **244字节**)。
- **数据分片**:
若数据超过`ATT_MTU`,需分片传输(如长数据拆分多次发送)。
------
###### 2. **`Characteristic`(特征值层)**
- **规范限制**:
GATT协议规定特征值的理论最大长度为 **512字节**。
- **实现约束**:
实际最大长度受蓝牙协议栈或硬件限制,常见实现如 **255字节**7 或更低(如部分芯片仅支持 **244字节**以匹配`ATT_MTU`)。
------
###### 3. **关键差异与注意事项**
| 类别 | 层级 | 典型最大值 | 影响因素 |
| ---------------- | ------ | --------------------------- | -------------------- |
| `Attribute` | ATT层 | 23B(旧版)或247B(新版)3 | BLE版本、MTU协商 |
| `Characteristic` | GATT层 | 512B(理论)或255B(实际)7 | 协议栈实现、内存限制 |
- 开发建议:
- **兼容性优先**:若无协商大MTU需求,默认按 **20字节**分段发送数据。
- **动态适配**:通过`MTU Exchange`过程协商更大的`ATT_MTU`以提升传输效率。
------
总结
- **`Attribute`长度**由`ATT_MTU`直接限制,与BLE版本强相关。
- **`Characteristic`长度**理论上可达512字节,但实际受协议栈裁剪,需结合硬件手册验证。
##### 2.6 主要服务和次要服务
在蓝牙GATT协议中,服务(Service)根据其功能核心性分为**主要服务(Primary Service)**和**次要服务(Secondary Service)**,二者差异如下:
| **对比维度** | **主要服务** | **次要服务** | |
| ------------ | ------------------------------------------ | -------------------------------------------- | ---- |
| **定义** | 表征设备的核心功能(如心率监测、数据传输) | 辅助主要服务或其他次要服务实现功能的附属模块 | 59 |
| **独立性** | 可独立存在并被设备直接调用 | 依赖主要服务或关联服务才能发挥作用 | 59 |
| **服务声明** | 在广播包中主动声明,便于客户端发现 | 通常不直接广播,需通过主要服务间接引用 | 59 |
| **应用场景** | 设备核心功能(如智能手环的心率服务) | 辅助功能(如电池状态、设备配置参数) | 57 |
| **协议层次** | 包含完整特性的功能集合 | 作为其他服务的组成部分或扩展模块 | 59 |
------
**技术实现要点**
1. **服务声明方式**
- 主要服务:在GATT属性表中通过
```
PRIMARY_SERVICE
```
标识,并直接暴露给客户端。
```
// 属性表片段示例(主要服务声明)
{ATT_UUID_SIZE, primaryServiceUUID, PERMIT_READ, 0, &serviceHandle}
```
- **次要服务**:通过`INCLUDE_SERVICE`字段嵌入到主要服务或其他次要服务中,需通过`ble_gatts_service_add()`动态绑定9。
2. **交互逻辑**
- 客户端通过`DISCOVER_PRIMARY_SERVICES`指令主动发现主要服务;次要服务需通过`DISCOVER_INCLUDED_SERVICES`指令遍历关联关系910。
- 次要服务常用于共享通用功能(如设备信息、时间同步),减少协议栈冗余实现59。
3. **开发实践**
- **核心功能优先**:设备的核心能力(如传感器数据采集)应定义为主要服务;辅助模块(如日志存储)适合作为次要服务57。
- **资源占用**:次要服务可复用特性描述符(Descriptor),降低内存占用,适合资源受限的嵌入式设备9。
------
**典型应用示例**
1. **智能手环场景**
- **主要服务**:心率监测(UUID: `0x180D`)5。
- **次要服务**:电池状态(UUID: `0x180F`)辅助主服务记录续航时间57。
2. **工业传感器场景**
- **主要服务**:温度数据采集(自定义UUID)。
- **次要服务**:校准参数存储(通过`INCLUDE`功能嵌入主服务)9。
------
**设计注意事项**
- **客户端兼容性**:部分旧版本蓝牙协议栈可能不支持次要服务的动态引用,需在主服务中提供兼容性接口910。
- **服务嵌套深度**:避免过多层级的服务嵌套,防止协议栈解析复杂度增加9。
- **UUID冲突规避**:次要服务若需跨设备共享,应使用SIG标准UUID或自定义唯一标识10。
通过合理划分主次服务,可优化蓝牙设备的功能模块化设计,提升协议栈运行效率59。
##### 2.7 服务端写回调
gap_ble_register_callbacks注册gap_ble_callbacks_t结构体,包含连接状态回调函数。
gatts_register_callbacks注册write_request_cb函数用于属性写。参数包含gatts_req_write_cb_t结构体,定义了蓝牙GATT服务端处理写请求时的关键参数,其字段解析如下:
------
**核心字段说明**
| 字段 | 类型 | 说明 |
| ---------------- | ---------- | ----------------------------------------------------------- |
| `request_id` | `uint16_t` | 请求唯一标识符,用于跟踪异步操作 |
| `handle` | `uint16_t` | 目标属性的句柄(如特征值句柄) |
| `offset` | `uint16_t` | 写入数据的字节偏移量(用于分片写入) |
| `need_rsp` | `bool` | 是否需回复响应(如`Write Without Response`场景设为`false`) |
| `need_authorize` | `bool` | 是否需授权(安全相关操作) |
| `is_prep` | `bool` | 是否为`Prepare Write`请求(长数据分片写入的中间步骤) |
| `length` | `uint16_t` | 写入数据的实际长度(单位:字节) |
| `value` | `uint8_t*` | 指向写入数据的指针(需动态管理内存) |
------
**典型应用场景**
1. 普通写入
- 直接通过`handle`定位属性,`value`传递数据,`need_rsp`控制是否回复
2. 分片写入
- 使用`is_prep`标记分片请求,`offset`记录当前偏移,最后执行`Execute Write`
3. 安全写入
- 当`need_authorize=true`时,需先通过鉴权流程
------
**开发注意事项**
- **内存管理**:`value`指针指向的数据需由调用者分配/释放,避免内存泄漏7
- **线程安全**:若在多线程环境使用,需对共享数据(如`handle`)加锁1
- **错误处理**:检查`length`是否超过目标属性的最大长度限制7
该结构体常见于蓝牙协议栈(如ESP-IDF、Zephyr等)的GATT服务端实现中17。
## 6. SLE低功耗星闪
#### 1. API
```
sle_announce_level_t 被发现方可发现等级
sle_announce_gt_role_t 角色协商,Terminal对应蓝牙主机(被发现),Grant对应蓝牙从机(扫描)
sle_announce_mode_t 是否可连接可扫描
sle_filter_policy_t 过滤扫描和连接请求
sle_announce_param_t 公开参数 sle_set_announce_param
sle_announce_enable_t 公开使能参数
sle_announce_remove_callback 删除广播 sle_remove_announce
sle_announce_data_t 设置广播数据 sle_set_announce_data
sle_start_announce 开始广播 sle_announce_enable_callback
sle_stop_announce 停止广播 sle_announce_disable_callback sle_announce_terminal_callback
sle_seek_phy_t PHY类型:1M 2M 4M
sle_seek_type_t 主动和被动扫描
sle_seek_filter_t 扫描所有,还是只有白名单发现扫描包
sle_seek_param_t 设置扫描参数 sle_set_seek_param
sle_start_seek_callback 开始扫描 sle_start_seek
sle_seek_disable_callback 停止扫描 sle_stop_seek
sle_seek_result_callback(sle_seek_result_info_t) 扫描结果
sle_conn_param_t 连接参数
enable_sle sle_enable_callback 使能
disable_sle sle_disable_callback 失能
sle_dfr_callback 设备发现与注册(Device Finding and Registration, DFR)回调函数,响应以下事件:
设备发现:扫描到星闪设备时触发,返回设备信息(如UUID、信号强度等)
连接状态变更:如连接建立、断开或异常中断时通知应用层
数据传输反馈:在透传模式下,接收数据完成或发送数据超时后触发状态报告
```
```
sle_pair_state_t 配对状态
sle_disc_reason_t 断连元婴
sle_acb_state_t 连接状态
sle_crypto_algo_t 加密算法类型
sle_key_deriv_algo_t 秘钥分发算法类型
sle_integr_chk_ind_t 加密和完整性开关类型
sle_radio_frame_t 星闪无线帧类型
sle_phy_tx_rx_t PHY类型:1M 2M 4M
sle_phy_tx_rx_pilot_density_t 星闪发送/接收导频密度指示
sle_connect_param_update_callback(sle_connection_param_update_evt_t) 连接参数更新回调
sle_connect_param_update_req_callback(sle_connection_param_update_req_t) 对端请求 连接参数更新回调
sle_update_connect_param(sle_connection_param_update_t) 更新连接参数
sle_default_connection_param_set(sle_default_connect_param_t) 设置默认连接参数
sle_auth_complete_callback(sle_auth_info_evt_t) 认证结果
sle_pair_complete_callback 配对回调
sle_set_phy_param(sle_set_phy_t) sle_set_phy_callback 设置PHY参数
sle_connection_register_callbacks 注册连接回调
sle_customize_max_pwr 自定义功率设置
sle_glp_register_callbacks 注册星闪(SLE)协议通用链路层(GLP)的事件回调
```
#### 2. 知识点
##### 2.1 SLE SSAP 协议解析
SLE SSAP(**星闪服务访问协议**)是华为星闪技术(SLE)中用于设备连接与数据传输的核心协议,主要涉及 **连接管理**、**加密通信** 和 **服务控制** 等功能^1]2[6^。以下是其关键特性及实现细节:
------
**1. 协议核心功能**
| 功能模块 | 作用 | 实现方式 |
| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **连接建立** | 主控设备与星闪设备建立通信链路,支持低功耗、低延迟场景^2][6^。 | 使用 **华为通用UUID** 初始化 SSAP 连接,确保协议兼容性^2][3^。 |
| **加密传输** | 保障控制指令与数据的机密性,防止非法窃听或篡改2。 | 通过 **会话密钥协商**(SN1、SN2)实现端到端加密,控制报文需经加密处理2。 |
| **服务发现与控制** | 支持设备状态查询、服务信息获取及远程控制^2][3^。 | 基于 `customSecData` 服务传输指令,包含读写属性和通知机制^2][3^。 |
------
**2. 连接控制流程**
SSAP 连接流程分为三个阶段,需严格遵循协议规范:
1. **建立连接**
- 主控设备通过 **广播或定向扫描** 发现星闪设备,使用预定义的 UUID 发起 SSAP 连接^2][6^。
- 示例:OTA 升级时需通过 `华为通用UUID` 建立连接,区分控制通道(`otaCtrl`)与数据通道(`otaData`)1。
2. **密钥协商**
- 双方设备通过 `createSession` 指令创建会话,动态生成密钥(SN1、SN2),用于后续加密传输2。
- 控制通道(如 `otaCtrl`)采用 **带应答的写操作**,确保指令可靠性1。
3. **数据传输**
- **控制指令**:通过加密后的 `customSecData` 服务传输,如查询设备状态、升级参数协商等^2][3^。
- **数据通道**:大文件(如 OTA 升级包)通过 `otaData` 通道传输,采用 **无应答写操作** 提升效率1。
------
**3. 典型应用场景**
- **OTA 固件升级**
- 控制通道与数据通道需 **共存**,确保升级过程中设备可控1。
- 升级包拆分为多个数据包传输,接收端校验完整性后执行更新1。
- **设备状态管理**
- 主控设备可实时查询从机状态(如电量、服务列表),或下发控制指令(如开关机)^2][3^。
- **低功耗通信**
- 通过优化连接间隔和从机延迟(如跳过非必要事件),降低设备功耗^2][6^。
##### 2.2 SLE_HADM_Manager
`SLE_HADM_Manager`(星闪高可用性设备管理模块)是星闪(SLE)协议中用于 **设备连接管理、链路维护及故障恢复** 的核心组件,通常实现以下关键功能1:
#### **1. 功能模块**
| 模块 | 作用 |
| ------------ | ------------------------------------------------------------ |
| **连接管理** | 负责设备连接的建立、维护与释放,支持主从设备间的低延迟通信,确保链路稳定性1。 |
| **状态监控** | 实时监测链路质量(如信号强度、丢包率)及设备状态(如电量、服务列表),触发异常告警1。 |
| **故障恢复** | 在链路中断时自动尝试重连或切换备用链路,支持动态调整传输参数(如重传次数、超时阈值)1。 |
## 7. KEIL
**MicroLIB**:提供精简C库,降低与Execute-only的兼容风险
**X/O Base**:X/O基地址。用于定义**非初始化数据段(ZI段)的起始地址**
**R/O Base**:R/O基地址。
**R/W Base**:R/W基地址。
**disable Warnings**:警用指定警告。
| 选项 | 说明 |
| ----------------------------------- | ------------------------------------------------------------ |
| **Execute only Code** | 权限控制:Execute only Code选项用于生成仅允许执行的代码段(.text),禁止通过内存读取指令(如通过指针访问代码区数据),从而增强固件防逆向与防篡改能力。
代码体积优化:启用后编译器会自动移除未调用的函数、变量等冗余代码,显著减少最终生成的二进制文件体积 |
| **Link Time Optimization** | 全局优化进一步减少未引用代码 |
| **Split Load and Store Multiple** | 内存访问优化:当代码中存在连续加载(Load)或存储(Store)多个寄存器操作时,该选项会将单条LDM/STM指令拆分为多条独立指令,避免因访问不同内存区域(如片内与片外)导致的时序冲突或总线争用。典型应用场景:需同时操作片内Flash代码与片外扩展RAM数据的项目。
兼容性提升:部分芯片硬件对LDM/STM指令的地址对齐有严格限制,启用此选项可规避因非对齐访问引发的硬件异常 |
| **One ELF Section per Function** | 代码体积优化:将每个函数编译为独立ELF段,链接器可精准移除未调用的函数,减少最终二进制文件的Code和RO-data段大小。适用于大型工程中存在大量未使用函数库的场景(如第三方组件),显著降低固件体积。
内存地址分配灵活性:独立ELF段允许开发者通过分散加载文件(Scatter File)手动指定函数的内存地址,适用于需要严格内存分区的场景(如RTOS任务隔离) |
| **Plain Char is Signed** | Keil的ARM编译器默认将char类型视为无符号(unsigned char),范围0~255,与标准C语言中char默认有符号(-128~127)的行为不同。
验证方法:编写char i=0; if(i < 0){ ... }时,若编译器报警告(条件永远为假),表明char被当作无符号类型处理。
选项作用:char将变为有符号类型(signed char),范围-128~127,与标准C语言行为一致。 |
| **Read-Only Position Independent** | ROPI允许只读段(如代码和常量)在内存中**动态加载到任意地址**运行,无需静态固定基址,适用于需要动态链接或分区固件升级的场。需与RWPI 配合使用 |
| **Read-Write Position Independent** | 读写段位置独立允许RW数据(如全局变量、静态变量)在内存中动态加载,无需固定地址,常用于支持固件动态加载或多区域内存管理场景。
与只读段(RO Data) 不同,RW段需在运行时初始化,其地址独立性直接影响内存利用效率与代码兼容性 |
| **No Auto Includes** | 不自动添加头文件(一般不勾选)。
不勾选该选项,编译器就会在Keil安装路径寻找你工程中.h文件。举例:我们定义uint8_t是定义在stdint.h文件里面的,但是我们工程目录下一般是没有stdint.h文件。这时候,编译器就会在Keil路径下去寻找stdint.h文件。 |
| **use RTTI** | 运行时类型信息(Runtime Type Information)。
配置编译器选项:勾选Enable RTTI(部分编译器版本需手动添加--rtti编译选项)。使用AC6编译器时,需在Misc Controls字段添加-frtti以显式启用RTTI2。
代码要求:使用typeid或dynamic_cast前必须包含\头文件,否则触发编译错误 |
| **assembler option** | Auto:默认选项,由Keil自动选择汇编器(可能因版本或代码格式不兼容导致错误)。
asmasm (Asm Syntax):传统ARM汇编器,适用于标准ARM汇编语法
asmclang (Asm Syntax):基于LLVM的汇编器,支持更严格的语法检查
asmclang (GUN Syntax):基于LLVM的汇编器,支持GNU汇编语法规则(如mov %eax, %ebx),与传统ARM汇编器(如asmasm)的指令格式存在差异 |
| **Thumb Mode** | 代码密度优化:Thumb指令采用16位编码,相较ARM指令(32位)可减少30%-40%的代码体积,显著降低Flash占用,适用于存储资源受限的嵌入式设备。
性能平衡:在16位内存总线系统中(如早期LPC2000系列),Thumb模式执行效率可达ARM模式的160%,尤其适合频繁访问内存的密集型任务。
混合模式支持:ARMv7及以上架构支持Thumb-2技术,允许16位与32位指令混合编码,兼顾代码密度与运算性能。 |
FreeRTOS的`portable`目录下**RVDS文件夹**代码仅适用于AC5编译器,AC6需使用**GCC目录**(如`portable/GCC/ARM_CM4F`)的移植文件。
AC6编译器严格遵循GNU语法,**不支持RVDS风格的汇编指令**(如`PRESERVE8`、`__forceinline`)。
修改内联关键字:将portmacro.h中的#define portFORCE_INLINE __forceinline替换为static inline。
调整汇编格式:删除PRESERVE8指令,改用GNU语法的.thumb_func或.syntax unified声明。
```c
// 原RVDS风格代码(错误)
__asm void prvStartFirstTask(void) {
PRESERVE8
ldr r0, =0xE000ED08
}
// 修改为GNU语法(正确)
__asm void prvStartFirstTask(void) {
.thumb_func
ldr r0, =0xE000ED08
}
```
**注释:移植报错是因为port的include路径不对应(使用的RVDS的头文件,又用的GCC的.c),AC6的assembler option不选asmasm 就行。**
## 8. 内存检查
1. **静态代码分析工具**
- 使用Keil内置的 **PC-Lint** 或 **Cppcheck** 集成,检测潜在内存越界、未初始化变量等问题。
- 示例配置:在 **Options for Target → C/C++ → Misc Controls** 中添加静态分析命令。
2. **动态调试手段**
- **硬件断点与Watchpoint**:通过Keil调试器(如ULINK、J-Link)设置内存访问断点,实时捕获非法地址访问。
- **Memory Protection Unit (MPU)**:在Cortex-M系列芯片中启用MPU,配置内存区域保护权限,阻止非法访问并触发异常48。
3. **自定义内存检测机制**
- 在代码中植入 **内存池检测逻辑**,通过重载内存分配函数实现边界检查。
## 9. FSMC
FSMC只能操作SRAM和PSRAM,不能操作DRAM,因为不需要刷新。FMC才能操作DRAM。一般芯片只提供操作内部闪存的FMC。
编程中ROM占用指的是存储在FLASH中的代码大小,而物理上ROM和FLASH存在区别。
### 9.1 Flash(闪存)
#### **一、结构与工作原理**
1. **NOR Flash**
- 采用 **随机存取架构**,数据线与地址线分离,支持直接按字节或字访问,类似传统RAM36。
- 存储单元独立连接,无需导通整列即可读取,适用于代码存储及直接执行(XIP,eXecute In Place)68。
2. **NAND Flash**
- 基于 **串行存取架构**,存储单元串联,数据线与地址线复用,需以页(512B-4KB)为单位读写34。
- 访问时需导通整列单元,结构复杂度高但存储密度更大,支持3D堆叠技术78。
------
#### **二、性能差异**
| **维度** | **NOR Flash** | **NAND Flash** |
| ----------------- | ------------------------------------- | ---------------------------------- |
| **读取速度** | 随机读取快(类似RAM),适合代码执行68 | 顺序读取快,随机访问需预加载整页37 |
| **写入/擦除速度** | 擦除速度慢(数百ms/扇区)67 | 擦除速度快(2ms/块)67 |
| **寿命** | 擦写循环达10万次,可靠性高68 | 擦写循环约1万次,需坏块管理68 |
------
#### **三、容量与成本**
- NOR:
- 主流容量1MB-128MB,45纳米制程(如华邦电)提升集成度,单位成本高16;
- 低电压(1.2V)型号适配穿戴设备与IoT低功耗需求1。
- NAND:
- 容量可达TB级,24纳米制程量产(如华邦电)叠加3D堆叠技术,单位成本低15;
- 适合大数据存储,但需额外主控芯片管理坏块25。
------
#### **四、应用场景**
- NOR Flash:
- 嵌入式系统启动代码、医疗设备固件存储(支持XIP)14;
- 物联网传感器配置存储(低功耗、小容量)16。
- NAND Flash:
- 消费电子大容量存储(如SSD、U盘)、AI训练数据集存储25;
- 工业级NAND用于数据中心冷数据存储2。
------
**总结**
- **NOR优势**:快速代码执行、高可靠性、低功耗,适合关键系统和小容量场景16;
- **NAND优势**:大容量、低成本、高密度,适配消费电子和数据存储45;
- **技术趋势**:NOR向超低电压演进,NAND通过3D堆叠突破物理极限
### 9.2 SRAM
------
#### **一、存储单元与结构**
1. **SRAM**(静态随机存储器)
- **存储单元**:基于6T结构(6个晶体管),通过双稳态电路锁存数据,无需刷新56;
- **接口特性**:直接随机访问,无需复杂控制器,适合高速缓存26。
2. **PSRAM**(伪静态随机存储器)
- **存储单元**:采用DRAM的1T+1C(1晶体管+1电容)结构,但支持自刷新机制,无需外部刷新电路58;
- **接口特性**:兼容SRAM接口协议,简化设计复杂度57。
3. **DRAM**(动态随机存储器)
- **存储单元**:1T+1C结构,依赖电容存储电荷,需定期外部刷新(约64ms/次)25;
- **接口特性**:需专用内存控制器管理刷新时序,适合大容量场景26。
4. **SDRAM**(同步动态随机存储器)
- **增强特性**:在DRAM基础上引入同步时钟信号,提升数据传输效率(如DDR技术)46。
------
#### **二、性能对比**
| **维度** | **SRAM** | **PSRAM** | **DRAM/SDRAM** |
| ------------ | ------------------ | -------------------------- | --------------------------- |
| **访问速度** | 极快(几纳秒)26 | 中等(几十纳秒)57 | 较慢(需预充电+刷新延迟)24 |
| **容量密度** | 低(MB级)56 | 中等(4MB-1GB)58 | 高(GB-TB级)25 |
| **功耗** | 静态功耗高68 | 动态功耗低,自刷新功耗中58 | 动态刷新功耗高26 |
| **成本** | 高(6T结构复杂)56 | 接近DRAM(1T1C结构)58 | 最低(高密度制程)25 |
------
#### **三、应用场景**
- SRAM:
- CPU高速缓存(L1/L2)、FPGA实时数据处理36;
- 对速度敏感且容量需求小的场景(如传感器寄存器)26。
- PSRAM:
- 嵌入式系统数据缓冲(如智能音箱音频处理)78;
- 物联网设备内存扩展(如ESP32 SPI接口PSRAM)7。
- DRAM/SDRAM:
- 计算机主内存、AI训练数据暂存(依赖高带宽)24;
- 消费电子大容量存储(如手机运行内存)26。
------
#### **四、技术演进**
- **SRAM**:向低功耗优化(22nm以下制程),但容量瓶颈显著6;
- **PSRAM**:串行接口迭代(如Octal SPI),带宽突破3Gbps,适配边缘计算58;
- **DRAM/SDRAM**:3D堆叠技术(如HBM)提升密度,DDR5带宽达6.4Gbps46。
------
**总结**
- **SRAM**以 **超高速、低延迟** 为核心优势,但受限于 **高成本、低密度**;
- **PSRAM**平衡 **接口简易性、适中容量与成本**,填补SRAM与DRAM间的需求空白;
- **DRAM/SDRAM**凭借 **超高密度、低成本** 主导大容量存储市场,但依赖复杂控制器
### 9.3 ROM与Flash区别
------
#### **一、定义与结构**
1. **ROM(只读存储器)**
- **定义**:非易失性存储器,数据出厂时固化,用户无法直接修改,主要用于存储固定程序(如计算机BIOS、嵌入式系统启动代码)18。
- **类型**:包括掩膜ROM(MROM)、可编程ROM(PROM)、紫外线擦除ROM(EPROM)和电擦除ROM(EEPROM)16。
- **存储单元**:基于传统半导体工艺,电路结构简单,仅支持一次性写入或特殊条件擦写(如紫外线或高电压)13。
2. **Flash(闪存)**
- **定义**:一种可擦写的非易失性存储器,属于广义EEPROM,但以“块”(512B-4KB)为单位擦写15。
- **类型**:分为NOR Flash(支持代码直接执行)和NAND Flash(大容量存储,如SSD、U盘)16。
- **存储单元**:采用浮栅晶体管结构,支持电信号快速擦写,集成密度高45。
------
#### **二、性能对比**
| **维度** | **ROM** | **Flash** |
| ----------------- | -------------------------------------------- | ------------------------------------------------------------ |
| **读取速度** | 顺序读取快(类似RAM),但随机访问能力弱16 | NOR Flash随机读取快(支持代码直接执行),NAND Flash顺序读写快(达7GB/s)14 |
| **写入/擦除能力** | 仅支持一次性写入或特殊条件擦写(如紫外线)36 | 支持电信号快速擦写(如NAND Flash擦写次数达3000次)15 |
| **容量范围** | 较小(KB~MB级)16 | 大容量(MB~TB级),3D NAND技术突破物理限制47 |
------
#### **三、应用场景**
- ROM:
- **关键系统固件**:计算机BIOS、汽车ECU芯片、工业设备控制代码(需长期稳定运行)18;
- **小容量配置存储**:物联网传感器参数固化(抗干扰性强)36。
- Flash:
- **大容量存储**:智能手机存储(eMMC/UFS)、固态硬盘(SSD)、AI训练数据集存储14;
- **灵活数据管理**:物联网设备固件升级、智能家居模块动态配置(支持在线更新)15。
------
#### **四、可靠性及技术趋势**
- 可靠性:
- **ROM**:无擦写次数限制,但物理损坏风险高(如紫外线导致EPROM老化)16;
- **Flash**:依赖磨损均衡(Wear Leveling)和纠错码(ECC)延长寿命,但擦写次数有限(如TLC闪存约3000次)15。
- 技术演进:
- **ROM**:向低功耗EEPROM发展(如汽车电子固化配置)8;
- **Flash**:3D堆叠技术(如176层NAND)提升密度,QLC技术降低单位存储成本47。
------
**总结**
- **ROM**以 **高稳定性、抗干扰性** 为核心优势,但 **灵活性差**,适合长期固化场景;
- **Flash**凭借 **高密度、可擦写、低成本** 主导动态存储市场,适配大容量与频繁更新需求
## 10. STM32CubeMx
### 10.1 **CMSIS_V1**与**CMSIS_V2**
问题:配置CMSIS_V2延时函数异常,无法正常闪灯,延时函数卡住。CMSIS_V1正常。
在STM32CubeMX中配置FreeRTOS时,**CMSIS_V1**与**CMSIS_V2**接口的核心差异及选择策略如下:
------
#### 一、核心差异对比
| **特性** | **CMSIS_V1** | **CMSIS_V2** |
| ---------------- | -------------------------- | ---------------------------------------- |
| **适用内核** | Cortex-M0/M0+/M3/M4/M713 | Cortex-M全系及Cortex-A系列37 |
| **功能覆盖** | 基础调度、信号量、队列等36 | 支持动态对象、多核、事件标志等扩展功能37 |
| **内存占用** | 较小(适合资源受限场景)38 | 较大(功能扩展导致)37 |
| **编译器兼容性** | 需ARMCC v5或旧版GCC24 | 支持ARMCC v6、GCC、IAR等现代工具链28 |
------
#### 二、接口选择策略
1. **CMSIS_V1适用场景**
- **硬件条件**:Cortex-M0/M3等低端芯片,内存资源紧张38。
- **功能需求**:仅需基本任务调度、同步机制(如信号量、消息队列)36。
- **开发环境**:Keil MDK搭配ARMCC v5编译器24。
2. **CMSIS_V2适用场景**
- **硬件条件**:Cortex-M4/M7/A系列,资源充足37。
- **功能需求**:动态任务创建、多核支持、软件定时器等高级功能78。
- **开发环境**:GCC/IAR或ARMCC v6编译器28。
------
#### 三、CubeMX配置要点
1. **接口版本设置**
- 路径:`Middleware > FREERTOS > Mode > Interface`13。
- 若需兼容旧代码或编译器,选V1;新项目优先选V223。
2. **时基源调整**
- **必要性**:避免HAL库与RTOS共享SysTick导致优先级冲突56。
-
操作步骤
:
- 进入`SYS > Timebase Source`,切换至TIM1/TIM6/TIM7等定时器46。
- 确保新时基定时器的NVIC优先级高于FreeRTOS内核中断(如PendSV)68。
3. **代码生成差异**
-
API风格
:
- V1使用`osThreadNew()`等传统CMSIS-RTOS接口36。
- V2引入更规范的`osThreadAttr_t`结构体配置任务属性8。
- **编译验证**:若使用V2时出现`__forceinline`错误,需降级CubeMX包版本至1.8.5或修改头文件宏定义2。
------
#### 四、常见问题处理
1. **编译报错**
- **CMSIS版本不匹配**:检查CubeMX中选择的接口版本与代码中API是否一致(如V2工程误用V1头文件)23。
- **编译器兼容性**:ARMCC v6需搭配CMSIS_V2,否则需手动调整代码或切换编译器24。
2. **时基功能异常**
- **HAL_Delay失效**:确认新时基定时器已启用中断,且未与其他外设冲突68。
- **调度延迟**:检查时基定时器频率(推荐1kHz)与FreeRTOS的`configTICK_RATE_HZ`是否一致36。
------
通过以上对比与配置指引,可依据项目需求合理选择CMSIS接口版本,兼顾资源效率与功能扩展性37。
### 10.2 IWDG与WWDG
------
#### 一、功能定位差异
| **特性** | **IWDG (独立看门狗)** | **WWDG (窗口看门狗)** |
| ---------------- | ----------------------------------- | -------------------------------------------- |
| **复位触发条件** | 计数器减至0时复位14 | 计数器低于下限(0x40)或超出窗口范围时复位12 |
| **时钟源** | 独立LSI(约32kHz)38 | 系统时钟APB1(需主时钟稳定)78 |
| **计数器位数** | 12位递减计数器24 | 7位递减计数器78 |
| **中断支持** | 无 | 支持提前唤醒中断(EWI)78 |
| **适用场景** | 对时间精度要求低,需独立运行的场合4 | 需严格时间窗口控制的实时任务17 |
------
#### 二、配置参数与计算
1. **IWDG超时时间**
- 公式:Tout=(RLR+1)×分频系数LSI频率(LSI≈32kHz)*T**o**u**t*=*L**S**I*频率(*R**L**R*+1)×分频系数(*L**S**I*≈32*k**Hz*)示例:分频系数32,RLR=3999,则超时时间约4秒24。
2. **WWDG窗口时间**
- 参数组成:
- **窗口上限值**(CFGR配置)
- **计数器初值**(CTLR配置,需≥0x40)
- 窗口范围:计数器值须在窗口上限值,0x40窗口上限值,0*x*40区间内喂狗27。
------
#### 三、典型应用场景
1. **IWDG适用场景**
- 主程序可能因干扰陷入死循环的场景(如工业传感器监测)46。
- 需要硬件级独立保护的场合(即使主时钟失效仍可复位)34。
2. **WWDG适用场景**
- 实时性要求高的任务(如电机控制中周期喂狗保证时序)17。
- 需检测“过早喂狗”异常行为(如任务执行顺序错误)16。
------
#### 四、代码配置要点
1. **IWDG初始化(HAL库示例)**
```
cCopy CodeIWDG_HandleTypeDef hiwdg;
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_32; // 分频系数32
hiwdg.Init.Reload = 3999; // 重载值
HAL_IWDG_Init(&hiwdg); // 初始化
HAL_IWDG_Refresh(&hiwdg); // 喂狗操作
```
注:喂狗需定期调用`HAL_IWDG_Refresh()`67。
2. **WWDG初始化(含窗口配置)**
```
cCopy CodeWWDG_HandleTypeDef hwwdg;
hwwdg.Instance = WWDG;
hwwdg.Init.Prescaler = WWDG_PRESCALER_8; // 8分频
hwwdg.Init.Window = 0x5F; // 窗口上限值
hwwdg.Init.Counter = 0x7F; // 计数器初始值
hwwdg.Init.EWIMode = WWDG_EWI_ENABLE; // 启用提前唤醒中断
HAL_WWDG_Init(&hwwdg);
```
注:需在中断服务函数中处理`HAL_WWDG_EarlyWakeupCallback()`67。
------
#### 五、常见问题与调试
1. **IWDG未复位问题**
- **原因**:未正确启用LSI时钟或喂狗间隔超时48。
- **排查**:检查`__HAL_RCC_IWDG_CLK_ENABLE()`调用及分频系数匹配6。
2. **WWDG误复位**
- **原因**:喂狗时间不在窗口范围内或中断未处理27。
- **调试**:通过在线调试器监测计数器值变化,验证窗口边界7。
------
通过合理选择IWDG/WWDG并配置参数,可显著提升嵌入式系统抗干扰能力与实时性14。
### 10.3 HAL库PREFETCH_ENABLE
#### 一、核心作用
`HAL_PREFETCH_ENABLE` 是 STM32 HAL 库中用于 **使能 Flash 预取缓冲(Prefetch Buffer)** 的宏定义,通过优化 Flash 访问时序提升代码执行效率27。
- **硬件机制**:通过设置 `FLASH->ACR` 寄存器的 `PRFTBE` 位,允许 CPU 预取 Flash 指令以减少等待周期27。
- **性能影响**:使能后,Flash 访问速度提升显著(尤其在主频 >24MHz 时),适用于需高频运行的实时任务26。
------
#### 二、配置方式
1. **自动配置**
- 在 `HAL_Init()` 函数中,默认调用 `__HAL_FLASH_PREFETCH_BUFFER_ENABLE()` 宏完成预取使能23。
- 代码示例(HAL 库源码片段):
```
cCopy Code// 使能预取缓冲(STM32CubeMX 生成代码中的 HAL_Init 实现)
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
```
2. **手动配置**
- 若需动态调整,可直接操作寄存器:
```
cCopy Code
FLASH->ACR |= FLASH_ACR_PRFTBE; // 使能预取缓冲
```
------
#### 三、使用条件与注意事项
1. **硬件支持**
- 仅适用于支持 Flash 预取功能的 STM32 系列(如 F1/F4/F7/H7 等)27。
- 需确保 Flash 等待周期(`LATENCY`)与主频匹配,否则可能引发总线错误27。
2. **配置顺序**
- 应在系统时钟初始化 **之前** 完成预取使能,避免时序冲突23。
- 典型配置流程:
```
cCopy CodeHAL_Init(); // 包含预取使能
SystemClock_Config(); // 配置时钟树
```
3. **调试问题**
- 若未正确使能,可能导致代码执行速度下降或异常跳转6。
- 可通过 `FLASH->ACR` 寄存器值验证 `PRFTBE` 位是否置 12。
------
合理使用 `HAL_PREFETCH_ENABLE` 可显著提升 STM32 运行效率,建议在 HAL 库初始化阶段默认启用此功能23。
### 10.4 HAL库回调宏
#### 一、核心作用
`USE_HAL_ADC_REGISTER_CALLBACKS` 是 STM32 HAL 库中用于 **启用 ADC 回调函数动态注册机制** 的编译宏,允许用户自定义 ADC 事件(如转换完成、错误中断等)的处理逻辑,替代默认的弱定义(`__weak`)回调函数28。
- 功能机制:
- 开启该宏后,可通过 `HAL_ADC_RegisterCallback()` 动态绑定自定义回调函数,提升代码灵活性28。
- 默认情况下,HAL 库提供 `HAL_ADC_ConvCpltCallback()` 等弱定义函数,用户需手动覆盖实现25。
------
#### 二、配置与使用流程
1. **启用宏定义**
- 在
```
stm32fxx_hal_conf.h
```
文件中取消注释或添加以下定义:
```
#define USE_HAL_ADC_REGISTER_CALLBACKS 1
```
此操作激活 ADC 回调注册功能。
2. **注册自定义回调函数**
- 在代码中调用
```
HAL_ADC_RegisterCallback()
```
绑定事件处理函数:
```
// 注册转换完成回调函数
HAL_ADC_RegisterCallback(&hadc, HAL_ADC_CONVERSION_COMPLETE_CB_ID, My_ConvCompleteCallback);
```
支持的回调类型包括:
- `HAL_ADC_CONVERSION_COMPLETE_CB_ID`(转换完成)
- `HAL_ADC_ERROR_CB_ID`(ADC 错误)
- 其他事件类型(具体参考 HAL 库头文件)28。
3. **实现回调函数**
- 自定义回调函数需与 HAL 库原型一致,例如:
```
void My_ConvCompleteCallback(ADC_HandleTypeDef *hadc) {
// 处理转换完成事件(如读取 ADC 数据)
uint32_t adc_value = HAL_ADC_GetValue(hadc);
}
```
------
#### 三、与默认弱定义回调的对比
| **对比项** | `USE_HAL_ADC_REGISTER_CALLBACKS` 启用状态 | 默认弱定义回调模式 |
| -------------- | ----------------------------------------- | ------------------------- |
| **代码灵活性** | 支持运行时动态绑定,便于模块化设计28 | 需静态覆盖 `__weak` 函数2 |
| **维护性** | 回调函数集中管理,降低耦合度8 | 分散在多个文件中,易冲突2 |
| **适用场景** | 多实例 ADC 或需动态切换回调的场景68 | 单一固定回调场景2 |
------
#### 四、注意事项
1. **初始化顺序**
- 需在 `HAL_ADC_Init()` **之前** 注册回调函数,否则注册无效8。
2. **中断使能**
- 即使启用了回调注册,仍需配置 ADC 中断并使能 NVIC(如使用中断模式)。
```
HAL_ADC_Start_IT(&hadc); // 启动 ADC 中断模式
```
3. **资源冲突**
- 避免在中断服务函数中执行耗时操作,建议仅设置标志位并通过主循环处理26。
------
通过合理使用 `USE_HAL_ADC_REGISTER_CALLBACKS`,可增强 ADC 事件处理的灵活性,适用于复杂嵌入式系统中需要动态适配不同处理逻辑的场景26。
## 10.5 HAL_Init
HAL_Init会先后调用HAL_InitTick和HAL_MspInit。
HAL_InitTick:默认使用systick作为基准时间源,并配置1ms tick。当FreeRTOS使用systick时需要设置最低优先级,但是HAL库时基需要高优先级保证准确,此时需要自定义HAL_InitTick配置其它定时器给HAL使用。
HAL_MspInit:初始化底层硬件,比如开启时钟、设置debug类型、设置PendSV_IRQn优先级。
程序开发包含stm32f1xx.h即可,根据USE_HAL_DRIVER确认是否包含stm32f1xx_hal.h,根据STM32F103xB等宏包含对应芯片寄存器定义(stm32f103xb.h包含core_cm3.h可以设置中断分组和优先级等,stm32f1xx_hal_cortex.h有封装),一些额外的宏执行bit和原子操作。
stm32f1xx_hal.h包含HAL_Init等函数,也包含stm32f1xx_hal_conf.h(include所有头文件)。
**注意:USE_RTOS未来使用,即使使用了FreeRTOS该宏也为0。**
有了HAL库,system_stm32f1xx.c中的SystemCoreClockUpdate函数无需调用,使用HAL_RCC_ClockConfig即可,但是该文件中的全局变量SystemCoreClock等还是会被更新。SystemInit由.s启动文件中的重启中断调用。
## 10.6 FATFS
`_USE_LFN` (长文件名缩写)是 FATFS 文件系统中控制长文件名支持的宏,用于突破默认的 8.3 文件名格式限制(即主文件名 8 字符 + 扩展名 3 字符)14。其作用包括:
1. **文件名扩展**:支持更长的文件名(如 `"2025-05-07_log.txt"`),提升文件命名灵活性。
2. **字符编码兼容**:结合 `FF_CODE_PAGE` 配置,可支持多语言字符(如中文文件名需设置 `FF_CODE_PAGE=936`)。
| 宏(这些宏都要加上FF前缀,在CubeMx中省略了) | 说明 |
| -------------------------------------------- | ------------------------------------------------------------ |
| _FS_TINY | 该选项切换小缓冲区配置。(0:正常,1:微小)
在小型配置中,文件对象(FIL)的大小减少了_MAX_SS字节。文件系统对象(FATFS)中的公共扇区缓冲区用于文件数据传输,而不是从文件对象中消除私有扇区缓冲区。 |
| _FS_READONLY | 该选项切换只读配置。(0:Read/Write或1:Read-only)
只读配置删除写入API函数,f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()以及可选的写入函数。 |
| _FS_MINIMIZE | 该选项定义最小化级别,以删除一些基本的API函数。
0:表示开启所有基本功能。
1:删除f_stat()、f_getfree()、f_unlink()、f_mkdir()、f_chmod()、f_utime()、f_truncate()、f_rename()函数。
2:除1外,删除f_opendir(), f_readdir()和f_closedir()。
3:除2外,删除f_lseek()函数。 |
| _USE_STRFUNC | 该选项切换字符串函数,f_gets(), f_put (), f_puts()和f_printf()。
0:关闭字符串功能。
1:使能,不进行LF-CRLF转换。
2:启用LF-CRLF转换。 |
| _USE_FIND | 该选项切换过滤目录读取特性和相关函数f_findfirst()和f_findnext()。(0:Disable或1:Enable) |
| _USE_MKFS | 该选项切换f_mkfs()函数。(0:Disable或1:Enable) |
| _USE_FASTSEEK | 此选项切换快速查找功能(0:Disable或1:Enable)。通过使用内存上的CLMT(集群链接映射表),快速查找特性支持无需FAT访问的快速向后/长查找操作。它也适用于f_read和f_write函数,但是,当文件处于快速查找模式时,f_write和f_lseek函数不能扩展文件大小。需要使用f_lseek(fp, CREATE_LINKMAP)给cltbl内存赋值,DWORD缓存大小为(文件片段数+ 1)* 2。 |
| _USE_LABEL | 这个选项切换卷标签函数,f_getlabel()和f_setlabel()。(0:Disable或1:Enable) |
| _USE_FORWARD | 该选项切换f_forward()函数。(0:Disable或1:Enable)
要启用它,也需要将_FS_TINY设置为1。 |
| _CODE_PAGE | 该选项指定要在目标系统上使用的OEM代码页。不正确的代码页设置可能导致文件打开失败。936表示GBK |
| \_USE_LFN/_MAX_LFN | \_USE_LFN选项切换LFN特性。
0:关闭LFN特性。\_MAX_LFN没有作用。
1:在BSS上启用带静态工作缓冲区的LFN。总是不线程安全的。
2:在STACK上启用带动态工作缓冲区的LFN。
3:在HEAP上启用具有动态工作缓冲区的LFN。
当启用LFN特性时,Unicode处理函数(option/ Unicode .c)必须添加到项目中。LFN工作缓冲区占用(_MAX_LFN + 1) * 2字节。当使用stack作为工作缓冲区时,要注意堆栈溢出。当使用heap内存作为工作缓冲区时,内存管理函数ff_memalloc()和ff_memfree()必须添加到项目中。 |
| _LFN_UNICODE | 该选项在API上切换字符编码。(0:ANSI/OEM或1:Unicode)
要使用Unicode字符串作为路径名,请启用LFN特性并将_LFN_UNICODE设置为1。这个选项也会影响字符串I/O函数的行为。 |
| _STRF_ENCODE | 当_LFN_UNICODE为1时,该选项选择要通过字符串I/O函数f_gets()、f_put()、f_puts和f_printf()对文件进行读/写的字符编码。
0: ANSI / OEM
1: UTF-16LE
2: UTF-16BE
3: utf - 8
当_LFN_UNICODE为0时,此选项不起作用。 |
| _FS_RPATH | 该选项配置相对路径特性。
0:关闭相对路径特性,并移除相关功能。
1:开启相对路径特性。F_chdir()和f_chdrive()可用。
2:除1外,还有f_getcwd()函数。
注意,通过f_readdir()读取的目录项受此选项的影响。 |
| _VOLUMES | 要使用的卷(逻辑驱动器)的数量 |
| \_STR_VOLUME_ID/_VOLUME_STRS | 0:只使用0-9作为驱动器ID, 1:使用字符串作为驱动器ID
当“_STR_VOLUME_ID”设置为1时,也可以使用预定义的字符串作为路径名中的驱动器号。_VOLUME_STRS定义每个逻辑驱动器的驱动器ID字符串。条目数必须等于_VOLUMES。驱动器ID字符串的有效字符为:A-Z和0-9。 |
| _MULTI_PARTITION | 0:表示单分区,1:表示多分区
此选项切换多分区特性。默认情况下(0),每个逻辑驱动器号绑定到相同的物理驱动器号,并且只挂载物理驱动器上找到的FAT卷。当启用多分区特性时(1),每个逻辑驱动器号绑定到VolToPart[]中列出的任意物理驱动器和分区。f_fdisk()函数也可用。 |
| \_MIN_SS/_MAX_SS | 这些选项配置要支持的扇区大小范围。(512, 1024, 2048或4096)对于大多数系统,所有类型的存储卡和硬盘,始终设置512。但对于板载闪存和某些类型的光学介质,可能需要更大的值。当_MAX_SS大于_MIN_SS时,将fatf配置为可变扇区大小,需要对disk_ioctl()函数执行GET_SECTOR_SIZE命令。**注释:STM32F103C8T6闪存page大小为1K,Cubemx生成的代码默认为512是错的,添加FATFS组件时需要配置。** |
| _USE_TRIM | 此选项切换ATA-TRIM功能。(0:Disable或1:Enable)
要启用Trim特性,还应该对disk_ioctl()函数执行CTRL_TRIM命令。 |
| _FS_NOFSINFO | 如果您需要知道FAT32卷上正确的空闲空间,请设置该选项的0位,并且在卷挂载后第一次使用f_getfree()函数将强制进行全FAT扫描。位1控制最后分配的集群号的使用。
bit0=0:如果可用,在FSINFO中使用空闲集群计数。
bit0=1:不相信FSINFO中的空闲集群计数。
bit1=0:如果可用,使用FSINFO中最后分配的集群号。
bit1=1:不信任FSINFO中最后分配的集群号。 |
| _FS_NORTC | _FS_NORTC选项用于开关时间戳特性。如果系统没有RTC功能或不需要有效的时间戳,请将_FS_NORTC设置为1以禁用时间戳特性。所有被fatf修改的对象都有一个固定的时间戳,由_NORTC_MON、_NORTC_MDAY和_NORTC_YEAR定义。
启用时间戳特性(_fs_nortc == 0)时,需要在项目中增加get_fattime()函数从RTC读取当前时间。_NORTC_MON、_NORTC_MDAY和_NORTC_YEAR不起作用。
这些选项在只读配置(_FS_READONLY == 1)下不起作用。 |
| _FS_LOCK | _FS_LOCK选项切换文件锁定特性,控制重复文件打开和非法操作打开对象。当_FS_READONLY为1时,该选项必须为0。
0:关闭文件锁定特性。为避免卷损坏,应用程序应避免非法打开、删除和重命名打开对象。
>0:开启文件锁定特性。该值定义了在文件锁控制下可以同时打开的文件/子目录的数量。注意,文件锁特性与重入无关。 |
| _FS_REENTRANT | \_FS_REENTRANT选项切换FatFs模块本身的可重入性(线程安全)。注意,不管这个选项是什么,对不同卷的文件访问总是可重入的,而卷控制函数,f_mount()、f_mkfs()和f_fdisk()函数,总是不可重入的。该特性只控制对同一卷的文件/目录访问。
0:禁止重入。“\_FS_TIMEOUT”和“\_SYNC_t”不生效。
1:使能重入。此外,用户提供的同步处理程序,ff_req_grant(), ff_rel_grant(), ff_del_syncobj()和ff_cre_syncobj()函数,必须添加到项目中。示例可在option/ sycall .c中获得。
\_FS_TIMEOUT以tick为单位定义超时时间。
_SYNC_t定义依赖于O/S的同步对象类型。例如HANDLE, ID, OS_EVENT*,SemaphoreHandle_t等。**上面需要实现的函数是旧版的,新版ffsystem.c根据OS_TYPE宏,提供了操作系统互斥体默认实现,一般不需要自己实现。** |
| _WORD_ACCESS | \_WORD_ACCESS选项是唯一与平台相关的选项。它定义了对FAT卷上的WORD数据使用哪种访问方法。
0:逐字节访问。始终与所有平台兼容。
1:Word访问。除非满足以下两个条件,否则不要选择此选项。
所有指令允许地址不对齐的内存访问。内存上的字节顺序是小端序。
如果是这种情况,还可以将_WORD_ACCESS设置为1以减少代码大小。下表显示了一些处理器类型的允许设置。Cortex-M0/Cortex-M3都只能设置为0 |
### 1. 适配
_USE_WRITE:是否使能disk_write函数,默认使能。\_USE_IOCTL:是否使能disk_ioctl函数,默认使能。
FATFS_LinkDriver第一个入参提供回调,赋能FATFS操作底层硬件的能力。
### 2. 64-bit LBA
#### 一、核心定义与背景
**64-bit LBA(Logical Block Addressing)** 是一种扩展存储设备寻址能力的方案,通过 **64 位地址空间** 实现对存储块的定位,突破传统 32-bit LBA 的 **2.19 TB 容量限制**(基于 512B 扇区)8。其理论最大寻址容量可达 **9.4 ZB(9.4 × 10²¹ 字节)**,彻底解决大容量存储设备(如 3TB+ 硬盘、企业级存储阵列)的兼容性问题34。
#### 二、关键应用场景
1. **GPT 分区表支持**
- GPT(GUID 分区表)依赖 64-bit LBA 实现对大容量硬盘的分区管理,成为现代操作系统(Windows Vista/7+、Linux、macOS)的默认方案48。
- MBR 分区表因 32-bit LBA 限制仅支持 ≤2.19 TB 容量,而 GPT 分区表通过 64-bit 寻址支持全容量硬盘48。
2. **硬件与协议适配**
- **存储接口**:SATA/SAS 控制器需支持 64-bit LBA 以驱动大容量硬盘7;
- **RAID 系统**:企业级 RAID 卡(如 Rocket RAID 2320)通过 64-bit LBA 实现多硬盘阵列的无缝扩展5;
- **工业设备**:硬盘拷贝机(如 UHA-107DC)利用 64-bit LBA 实现无容量限制的数据复制与校验3。
3. **系统固件依赖**
- UEFI 取代传统 BIOS 的核心优势之一是原生支持 GPT 分区表及 64-bit LBA,确保从大容量硬盘启动系统8。
------
#### 三、技术对比与实现细节
| 特性 | 32-bit LBA | 64-bit LBA |
| ---------------- | ------------------------- | ------------------------------------------ |
| **最大容量** | 2.19 TB(2³² × 512B) | 9.4 ZB(2⁶⁴ × 512B) |
| **分区表类型** | MBR(主引导记录) | GPT(GUID 分区表) |
| **兼容性支持** | 传统 BIOS | UEFI 固件 |
| **典型应用场景** | 旧式 PC、嵌入式小容量存储 | 数据中心、高性能计算、SSD/HDD 大容量存储38 |
------
#### 四、开发与部署注意事项
1. **操作系统配置**
- **Windows**:需使用 GPT 分区表并启用 UEFI 启动模式8;
- **Linux**:通过 `parted` 或 `gdisk` 工具管理 GPT 分区,文件系统需支持 64-bit 寻址(如 ext4)4。
2. **硬件与驱动适配**
- 存储控制器(如 RAID 卡)需明确支持 64-bit LBA5;
- 嵌入式系统中,文件系统(如 FATFS)需配置 `FF_LBA64` 宏以启用 64-bit 寻址6。
3. **数据安全与容错**
- GPT 分区表通过 **主分区表 + 备份分区表** 设计避免 MBR 单点故障风险48;
- 工业级设备(如 UHA-107DC)结合 SHA-256 校验与 64-bit LBA 确保数据完整性3。
------
64-bit LBA 技术通过扩展寻址能力,为现代存储系统提供了底层支持,推动了大容量存储设备在云计算、AI 训练等场景的广泛应用34。
### 3. `f_forward`
------
#### 一、核心功能与特性
`f_forward` 是 FATFS 文件系统中用于 **直接转发文件数据到输出流** 的接口,专为 **资源受限的小型存储系统** 设计。其核心特性包括:
- **无缓冲区依赖**:直接通过用户提供的流处理函数传输数据,无需额外内存缓冲12;
- **低内存占用**:适用于 `_FS_TINY == 1` 配置的存储系统,最小化 RAM 消耗12;
- **实时性支持**:适用于音频播放、传感器数据流传输等需连续处理的场景12。
------
#### 二、应用场景
1. 音频流播放
- 实时读取音频文件(如 WAV 格式),通过 `f_forward` 直接将数据发送至 DAC 模块,避免缓冲延迟12。
2. 日志流式存储
- 将传感器数据实时写入文件时,通过 `f_forward` 减少中间缓存操作,降低内存占用17。
3. 网络数据传输
- 结合 TCP/IP 协议栈,将文件内容直接转发至网络接口,适用于嵌入式 HTTP 文件传输7。
------
#### 三、实现要求与接口规范
需满足以下条件启用 `f_forward`:
1. 配置宏定义
```
#define _USE_FORWARD 1 /* 启用 f_forward */
#define _FS_TINY 1 /* 启用 Tiny 模式 */
```
2. 函数原型
```
FRESULT f_forward (
FIL* fp, // 文件对象指针
UINT (*func)(const BYTE*, UINT), // 流处理函数(用户实现)
UINT btr // 需转发的字节数
);
```
3. 流处理函数实现示例
```
UINT StreamFunc(const BYTE *buff, UINT btr) {
// 示例:通过 UART 发送数据
if (HAL_UART_Transmit(&huart1, buff, btr, 1000) != HAL_OK)
return 0; // 传输失败时返回 0
return btr; // 返回实际传输的字节数
}
```
------
#### 四、注意事项
1. 错误处理
- 若 `f_forward` 返回的已转发字节数小于请求值(`*ByteFwd < ByteToFwd`),需检查文件结束(EOF)或流处理函数是否因忙状态中断12;
2. 性能优化
- 流处理函数应尽量减少阻塞操作(如等待硬件空闲),避免拖慢整体传输效率18;
3. 兼容性限制
- 仅当 `_FS_TINY == 1` 时可用,且不支持长文件名(需 `_USE_LFN == 0`)27。
------
通过 `f_forward`,FATFS 在嵌入式系统中实现了高效、低资源占用的流式数据传输,尤其适合实时性与内存敏感型应用12。
### 4. FAT 文件系统格式化与分区策略详解
------
#### 一、FAT 子类型判定规则
根据 Microsoft FAT 规范,FAT12/FAT16/FAT32 的子类型 **仅由卷的簇数量决定**,与文件系统参数无关:
- **FAT12**:簇数量 ≤ 4085(12 位寻址)
- **FAT16**:4085 < 簇数量 ≤ 65525(16 位寻址)
- **FAT32**:簇数量 > 65525(28 位有效寻址)
**关键公式**:
```
簇数量 = (卷大小 - 系统保留区域) / 簇大小
```
若指定的 `FAT 类型` + `簇大小` 无法满足簇数量阈值,`f_mkfs` 返回 `FR_MKFS_ABORTED`。
优先自动选择 FAT 类型
```
/* 不强制指定 FAT 类型,由系统根据卷大小自动选择 */
f_mkfs("0:", 0, 0, work, sizeof(work));
```
------
#### 二、簇大小(Allocation Unit)权衡
1. **空间效率 vs 性能**
- 小簇(如 512B):
- 空间利用率高(小文件占用空间少);
- 性能低(频繁 FAT 表访问,读写碎片化)。
- 大簇(如 32KB):
- 空间利用率低(小文件浪费空间);
- 性能高(顺序读写优化,减少 FAT 更新频率)。
2. **推荐策略**
- **大容量卷(GB 级)**:默认使用 32KB 或更大簇(自动选择);
- **小文件密集场景**:手动指定较小簇(如 4KB),但需权衡性能损失。
------
#### 三、分区模式与 `f_mkfs` 行为
1. **非分区模式(SFD 模式)**
- 触发条件:
- `FF_MULTI_PARTITION == 0`(单分区模式)且 `FM_SFD` 标志被指定;
- FAT 卷直接占据物理驱动器全部空间(无分区表,LBA 0 起始)。
- **适用场景**:软盘、U 盘(需兼容 Super-Floppy 格式的系统)。
2. **分区模式(MBR/GPT)**
- 触发条件:
- `FF_MULTI_PARTITION == 0` 且未指定 `FM_SFD`;
- 自动创建独占全盘的分区,并在其中构建 FAT 卷。
- **适用场景**:硬盘、SD 卡(需分区表)。
3. **多分区模式(`FF_MULTI_PARTITION == 1`)**
- 强制约束:
- 需通过 `VolToPart[]` 映射表指定目标分区;
- `FM_SFD` 标志无效,必须在已有分区上操作;
- 若分区不存在,返回 `FR_MKFS_ABORTED`。
- 预分区工具:
- 使用 `f_fdisk` 或第三方工具(如 `fdisk`/`gparted`)预先划分分区。
------
#### 四、分区格式兼容性
| 格式 | 特点 | 支持条件 |
| ------- | ---------------------------------------- | --------------------- |
| **MBR** | 传统分区表,最多 4 个主分区,兼容性强 | 默认支持 |
| **GPT** | 支持大容量(>2TB)、多分区,需 64 位 LBA | 需启用 `FF_LBA64 = 1` |
| **SFD** | 无分区表,FAT 卷直接覆盖物理驱动器 | 需指定 `FM_SFD` 标志 |
------
#### 五、设计注意事项
1. **系统兼容性**
- Windows 可能拒绝挂载非标准分区格式(如 SFD 模式的大容量 U 盘);
- 嵌入式系统若将存储设备映射为多个逻辑驱动器(`pdrv`),需采用 SFD 模式。
2. **错误处理**
- `FR_MKFS_ABORTED`:
- 检查簇大小合法性(需为 512B 的整数倍);
- 验证分区是否存在(多分区模式下);
- 确认卷容量与 FAT 类型匹配(如 FAT32 需 ≥ 512MB)。
3. **性能优化**
- **大文件存储**:优先选择 exFAT(支持单文件 >4GB,簇大小灵活);
- **混合文件类型**:使用默认簇大小(`au = 0`),由系统自动优化。
------
#### 六、代码示例
```
// 示例 1:格式化物理驱动器为 SFD 模式(无分区表)
f_mkfs("0:", FM_FAT32 | FM_SFD, 0, work, sizeof(work));
// 示例 2:在多分区模式下格式化第二个分区
VolToPart[0] = {0, 2}; // pdrv=0, 分区号=2
f_mkfs("0:", FM_FAT32, 32768, work, sizeof(work));
```
------
**总结**:FAT 子类型与簇大小需根据存储介质特性(容量、文件类型)和系统兼容性要求选择,分区模式(SFD/MBR/GPT)需适配目标平台的磁盘管理规则。通过合理配置 `f_mkfs` 参数与预处理分区,可避免格式化错误并优化文件系统性能。
### 5. `f_mount`/`f_mkfs`/`f_fdisk`
------
#### 一、`f_mount` 函数
1. **核心功能**
- 注册/注销 FAT 文件系统工作区,建立逻辑驱动器与物理存储设备的映射关系38;
- 挂载操作不涉及物理介质初始化,仅加载文件系统元数据(FAT 表、根目录等)。
2. **调用场景**
- **文件操作前**:必须通过 `f_mount` 挂载目标逻辑驱动器,否则无法执行文件读写操作3;
- **多分区模式**:需通过 `VolToPart[]` 映射表指定具体分区号8。
3. **参数说明**
```
FRESULT f_mount(FATFS* fs, const TCHAR* path, BYTE opt);
```
- `fs`:文件系统对象指针(需预先分配内存);
- `path`:逻辑驱动器路径(如 "0:");
- `opt`:挂载选项(0=卸载,1=挂载)。
4. **错误码示例**
- `FR_NOT_READY`:物理设备未初始化或介质未检测到3;
- `FR_INVALID_DRIVE`:逻辑驱动器号无效8。
------
#### 二、`f_mkfs` 函数
1. **核心功能**
- 格式化存储介质,创建 FAT12/FAT16/FAT32 文件系统38;
- 支持自动生成分区表(默认)或跳过分区表(SFD 模式)。
2. **参数约束**
```
FRESULT f_mkfs(const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len);
```
- `opt`:格式化选项(`FM_FAT12`/`FM_FAT16`/`FM_FAT32` 或自动选择 `0`);
- `au`:簇大小(单位:字节,需为 512 的整数倍);
- `work`/`len`:格式化缓冲区指针及长度(建议 ≥ `FF_MAX_SS`)。
3. **关键逻辑**
- **分区模式**:未指定 `FM_SFD` 时自动创建 MBR 分区表,独占全盘8;
- **簇数量判定**:FAT 子类型由 `卷容量 / 簇大小` 计算结果决定,与显式参数冲突时返回 `FR_MKFS_ABORTED`3。
4. **设计建议**
- 大容量设备(≥32GB)推荐 `au=32768`(32KB),平衡性能与空间利用率8;
- 嵌入式 Flash 使用 `FM_SFD` 模式时需确保介质起始位置无残留数据3。
------
#### 三、`f_fdisk` 函数
1. **核心功能**
- 创建/删除 MBR 或 GPT 分区表,支持多分区配置45;
- 需在 `FF_MULTI_PARTITION == 1` 时调用,与 `f_mkfs` 配合使用8。
2. **调用流程**
```
1. 初始化物理设备 → 2. 调用 f_fdisk 创建分区表 → 3. 使用 f_mkfs 格式化分区
```
3. **参数示例**
```
FRESULT f_fdisk(BYTE pdrv, const LBA_t ptbl[], void* work);
```
- `ptbl[]`:分区表数组(定义起始扇区、容量等参数);
- `work`:操作缓冲区(需 ≥ `FF_MAX_SS`)。
4. **错误处理**
- `FR_DISK_ERR`:物理设备访问失败(如扇区读写错误)3;
- `FR_INVALID_PARAMETER`:分区表参数越界或重叠5。
------
#### 四、函数调用关系与典型场景
| 场景 | 调用顺序 | 关键配置 |
| -------------------- | ---------------------------- | ------------------------- |
| **单分区设备初始化** | `f_mount → f_mkfs → f_mount` | `opt=0`(自动 FAT 类型)3 |
| **多分区设备初始化** | `f_fdisk → f_mkfs → f_mount` | `FF_MULTI_PARTITION=1`8 |
| **SFD 模式格式化** | `f_mount → f_mkfs(FM_SFD)` | 禁用分区表3 |
------
#### 五、代码示例(多分区配置)
```
// 1. 分区表定义(2个主分区)
LBA_t ptbl[] = {
{0, 100000}, // 分区1:0~100000扇区
{100001, 0} // 分区2:剩余空间
};
// 2. 创建分区表
FRESULT res = f_fdisk(0, ptbl, work_buf);
if (res != FR_OK) abort();
// 3. 格式化分区1为FAT32
res = f_mkfs("0:1", FM_FAT32, 4096, work_buf, sizeof(work_buf));
if (res != FR_OK) abort();
// 4. 挂载分区1
FATFS fs;
res = f_mount(&fs, "0:1", 1);
```
------
**总结**:`f_mount` 负责文件系统挂载,`f_mkfs` 实现格式化与 FAT 子类型选择,`f_fdisk` 用于多分区管理。三者配合使用时需注意参数兼容性与调用顺序,避免因配置冲突导致操作失败35。
## 10.7 兼容性
USE_OBSOLETE_USER_CODE_SECTION_0
警告:用户段0不再被使用(从CubeMx版本4.16.0开始)将来会被抑制。
保留以确保在迁移项目时与以前的CubeMx版本向后兼容。
在删除部分内容之前,应该在新用户部分中复制先前添加的用户代码。
#### 一、功能定位
`USE_OBSOLETE_USER_CODE_SECTION_0` 是用于控制代码中过时功能模块是否启用的条件编译宏,常见于代码迁移或兼容性维护场景。其核心作用包括:
1. **代码兼容性管理**:通过宏开关控制是否编译旧版本代码段,避免直接删除代码导致版本历史丢失或兼容性问题2。
2. **渐进式迁移支持**:在重构过程中保留旧代码逻辑,同时通过宏定义逐步引导开发者迁移到新实现28。
------
#### 二、典型应用场景
1. **旧功能标记与替换**:
```
cCopy Code#define USE_OBSOLETE_USER_CODE_SECTION_0 0 // 禁用废弃代码段
#if USE_OBSOLETE_USER_CODE_SECTION_0
// 旧版用户代码(已废弃)
void legacy_user_auth() {
/* ... */
}
#else
// 新版用户认证逻辑
void user_auth_v2() {
/* ... */
}
#endif
```
**说明**:通过宏值切换新旧代码逻辑,需配套更新文档说明迁移路径28。
2. **版本控制与历史追溯**:
- 保留废弃代码段可配合 SVN/Git 版本控制系统回溯历史实现,同时通过编译警告提示开发者优先使用新接口27。
------
#### 三、配置建议
| 宏值 | 用途 | 推荐场景 |
| ---- | -------------- | -------------------- |
| `1` | 启用废弃代码段 | 临时回滚或兼容性测试 |
| `0` | 禁用废弃代码段 | 正式环境或新功能开发 |
**注意事项**:
- 启用废弃代码时需同步检查依赖项(如旧版 `AUTHSVR` 模块的接口兼容性)8。
- 宏命名建议包含版本号(如 `USE_OBSOLETE_USER_CODE_SECTION_0`),便于多版本并行管理2。
------
#### 四、调试与维护
1. **编译警告增强**:
```
#if USE_OBSOLETE_USER_CODE_SECTION_0
#pragma message("警告: 已启用废弃用户代码段,建议迁移至新实现!")
#endif
```
**说明**:通过编译器提示加速代码迁移进程2。
2. **日志记录**:
- 在废弃代码段中添加日志输出,监控其实际调用频率,评估迁移优先级8。
------
通过合理使用 `USE_OBSOLETE_USER_CODE_SECTION_0` 宏,可平衡代码维护成本与系统稳定性,实现平滑过渡27。
## 10.8 LittleFS
相比其它文件系统:资源占用少、磨损均衡、断电处理(不会损坏、恢复到上次状态)
### 1. LittleFS `lfs_config` 结构体解析与配置指南
#### **一、核心结构定义**
`lfs_config` 是 LittleFS 与底层存储设备交互的核心配置接口,需在移植时由开发者实现其成员函数与参数45。定义如下:
```
cCopy Codestruct lfs_config {
void *context; // 用户自定义上下文(如设备句柄),可传入回调中
// 块设备操作函数
int (*read)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int (*prog)(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int (*erase)(const struct lfs_config *c, lfs_block_t block);
int (*sync)(const struct lfs_config *c);
// 设备参数
lfs_size_t read_size; // 最小读取单元(通常为1字节)
lfs_size_t prog_size; // 最小写入单元(如SPI Flash的页大小)
lfs_size_t block_size; // 擦除块大小(如4KB)
lfs_size_t block_count; // 总块数
int32_t block_cycles; // 在littlefs清除元数据日志并将元数据移动到另一个块之前的擦除周期数。建议值在100-1000范围内,较大的值具有较好的性能,但代价是磨损分布不一致。设置为-1表示禁用块级磨损均衡。
lfs_size_t lookahead_size; // lookahead缓冲区的大小(以字节为单位)。较大的lookahead缓冲区会增加分配过程中找到的块数量。lookahead缓冲区以紧凑的位图形式存储,因此每个字节的RAM可以跟踪8个块。注:默认覆盖1/8块,大小=block_count/8/8
lfs_size_t compact_thresh; // lfs_fs_gc期间元数据压缩的阈值(以字节为单位)。超过此阈值的元数据对将在lfs_fs_gc期间进行压缩。缺省值0表示block_size的88%大小,但将来可能会更改默认值。注意,这只影响lfs_fs_gc。正常的压缩仍然只发生在满时。设置为-1表示禁用lfs_fs_gc过程中的元数据压缩。
// 其他高级参数
int (*lock)(const struct lfs_config *c);
int (*unlock)(const struct lfs_config *c);
// 缓存
lfs_size_t cache_size; // 以字节为单位的块缓存大小。littlefs需要一个读缓存、一个编程缓存和每个文件一个额外的缓存。较大的缓存可以通过存储更多的数据和减少磁盘访问次数来提高性能。必须是读取和编程大小的倍数,以及块大小的因子。
void *read_buffer; // 可选,静态分配才使用。必须为cache_size。
void *prog_buffer; // 可选,静态分配才使用。必须为cache_size。
void *lookahead_buffer; // 可选,静态分配才使用。必须为lookahead_size。
// Optional upper limit on length of file names in bytes. No downside for
// larger names except the size of the info struct which is controlled by
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX or name_max stored on
// disk when zero.
lfs_size_t name_max; // 可选的文件名长度上限(以字节为单位)。除了info结构体会变大之外,较大的名称没有缺点。默认值0表示LFS_NAME_MAX或磁盘上的name_max。
lfs_size_t file_max; // 可选的以字节为单位的文件上限。对于较大的文件没有缺点,但必须是<= LFS_FILE_MAX。默认值0表示LFS_FILE_MAX或磁盘上的file_max。
lfs_size_t attr_max; // 自定义属性上限,同上
lfs_size_t metadata_max; // 元数据对总空间的可选上限(以字节为单位)。在具有大块(例如128kB)的设备上,将其设置为较小的大小(2-8kB)可以帮助绑定元数据压缩时间。必须为<= block_size。当为零时默认为block_size。
lfs_size_t inline_max; // 可选的以字节为单位的内联文件上限。内联文件位于元数据中,减少了存储需求,但在提高与元数据相关的性能方面可能受到限制。必须<= cache_size, <= attr_max,和<= block_size/8。当为零时默认为最大可能的inline_max。设置为-1禁用内联文件。
uint32_t disk_version; // 开启LFS_MULTIVERSION时使用。以16位主版本+ 16位次版本的形式写入时使用的磁盘版本。这将元数据限制在旧的次要版本所支持的范围内。注意,一些特性将丢失。当为零时,默认为最新的次要版本。
};
```
#### **二、关键成员配置详解**
1. **操作函数实现**
- `read`/`prog`/`erase`:必须实现,完成存储设备的读、写、擦除操作。
```
cCopy Code// 示例:SPI Flash的prog函数实现
int blk_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
SPI_Flash_Write(block * c->block_size + off, buffer, size);
return 0;
}
```
- **`sync`**:可选,用于刷新设备缓存(如写入完成后的确认操作)4。
2. **设备参数设置**
- **`block_size`/`block_count`**:必须与物理设备匹配(如W25Q32的块大小为4KB,共1024块)45;
- **`prog_size`**:设为设备编程页大小(如256字节),若支持任意字节写入可设为15;
- **`lookahead`**:影响磨损均衡效率,建议值为 `block_count/8` 且不小于328。
3. **多线程支持**
- **`lock`/`unlock`**:在多任务环境中需实现互斥锁,防止并发冲突4。
#### **三、移植步骤示例(STM32平台)**
1. **初始化配置结构体**
```
struct lfs_config config = {0};
config.context = &spi_flash; // SPI设备句柄
config.read = spi_flash_read;
config.prog = spi_flash_prog;
config.erase = spi_flash_erase;
config.sync = spi_flash_sync;
// 参数设置
config.read_size = 1;
config.prog_size = 256;
config.block_size = 4096;
config.block_count = 1024;
config.lookahead = 32;
```
2. **挂载文件系统**
```
lfs_t lfs;
int err = lfs_mount(&lfs, &config);
if (err) {
// 挂载失败,尝试格式化
lfs_format(&lfs, &config);
lfs_mount(&lfs, &config);
}
```
#### **四、常见问题与调试**
1. **返回错误码 `LFS_ERR_CORRUPT`**
- 检查 `block_size` 是否与设备实际擦除块大小一致4;
- 验证 `prog` 函数是否严格按 `prog_size` 对齐写入5。
2. **性能优化**
- 增大 `lookahead` 提升磨损均衡效率,但会增加RAM占用(每bit对应一个块)8;
- 对齐文件操作至 `prog_size` 边界,减少底层碎片写入5。
#### **五、高级配置(自定义内存管理)**
通过 `LFS_CONFIG` 宏覆盖默认配置,实现静态内存分配(适合无动态内存系统)3:
```
cCopy Code// lfs_config.h
#define LFS_CONFIG
#include // 自定义内存池与断言
cCopy Code// custom_lfs_config.h
void* lfs_malloc(size_t size);
void lfs_free(void* p);
```
**参考移植案例**:STM32F103 平台完整配置见 [4],鸿蒙系统集成方法见 [1][6]。
### 2. 注意事项
定义LFS_CONFIG宏:(-DLFS_CONFIG=lfs_config.h),可以用lfs_config.h替换lfs_util.h,使用自己的配置。
定义LFS_DEFINES宏:(-DLFS_DEFINES=my_defines.h),可以添加自己的宏和其它定义,比如`#define LFS_MALLOC(sz) my_malloc(sz)`、LFS_FREE、LFS_TRACE等。
| 宏 | 说明 |
| ------------------------------------- | ------------------------------------------------------------ |
| LFS_NO_DEBUG/LFS_NO_WARN/LFS_NO_ERROR | printf没有适配,会进入HardFault_Handler,定义LFS_NO_DEBUG避免打印。默认开启 |
| LFS_NO_MALLOC | 不开启malloc动态内存分配。默认开启 |
| LFS_NO_ASSERT | 不开启assert断言。默认开启 |
| LFS_YES_TRACE | 是否启用trace跟踪打印。默认关闭 |
| LFS_NO_INTRINSICS | 不开启\__builtin_clz前导0特性。默认开启 |
| LFS_CRC | 用户自己提供计算CRC的函数,如硬件crc加速 |
| LFS_READONLY | LFS只支持只读 |
| LFS_THREADSAFE | 需要提供加锁解锁回调,提供线程安全 |
| LFS_MULTIVERSION | 支持多版本,需在lfs_config配置版本号 |
lfs_file_opencfg:开启了静态分配、或者自定义属性时使用。
| 结构体 | 主要成员 | 说明 |
| --------------- | ------------- | ------------------------------------------------------------ |
| lfs_config | | lfs初始化配置信息 |
| lfs_info | | 文件信息:类型、大小、名字 |
| lfs_fsinfo | | 文件系统信息,基本与lfs_config一致 |
| lfs_file_config | void *buffer; | 可选的静态分配文件缓冲区。必须为cache_size。默认情况下,lfs_malloc用于分配该缓冲区。 |
| | lfs_attr | 自定义属性列表,会保存在磁盘上。lfs_file_opencfg打开时可配置,后续还可以修改 |
| lfs_dir_t | | 目录 结构体 |
| lfs_file_t | | 文件结构体 |
| lfs_t | | 文件系统结构体 |
## 11. W25Q64JV
它们是理想的代码阴影到RAM,执行代码直接从双/四SPI (XIP)和存储语音,文本和数据。器件工作在2.7V至3.6V的电源上,下电时的电流消耗低至1µA。所有设备都提供节省空间的包装。
W25Q64JV阵列被组织成32,768个可编程页面,每个页面256字节。每次最多可编程256字节。页可以以16为一组(4KB扇区擦除)、128为一组(32KB块擦除)、256为一组(64KB块擦除)或整个芯片(芯片擦除)进行擦除。W25Q64JV分别有2,048个可擦除扇区和128个可擦除块。较小的4KB扇区为需要数据和参数存储的应用程序提供了更大的灵活性。(参见图2。)
W25Q64JV支持标准串行外设接口(SPI),双/四路I/O SPI:串行时钟,芯片选择,串行数据I/ 0 (DI), I/ 1 (DO), I/O2和I/O3。支持高达133MHz的W25Q64JV SPI时钟频率,允许双I/O的等效时钟速率266MHz (133MHz x 2)和Quad I/O的等效时钟速率532MHz (133MHz x 4),使用快速读取双/Quad I/O。这些传输速率可以超过标准的异步8位和16位并行闪存。
此外,该设备支持JEDEC标准制造商和设备ID,以及64位唯一序列号和三个256字节的安全寄存器。
- **状态寄存器保护**:/WP引脚并非直接保护 Flash 数据,而是通过 控制状态寄存器(SR)的写权限间接实现数据保护。
- 当 `/WP` 引脚有效(如低电平)时,结合状态寄存器的 **SRP(Status Register Protect)位**,可锁定状态寄存器,阻止对保护位(如 BPx)的修改,从而维持 Flash 受保护区域的只读属性。
- **物理区域保护**(部分型号):
某些 Flash(如 W25Qxx 系列)在 `/WP` 有效时,可 **固定保护首块或末块存储区域**,使其无法被擦写。
/HOLD引脚允许设备在被主动选择时暂停。当/HOLD处于低电平时,当/CS处于低电平时,DO引脚将处于高阻抗,并且DI和CLK引脚上的信号将被忽略(不关心)。当/HOLD为高值时,设备可以恢复运行。当多个设备共享相同的SPI信号时,/HOLD功能非常有用。/HOLD引脚激活低。当状态寄存器-2的QE位设置为Quad I/O时,/HOLD引脚功能不可用,因为该引脚用于IO3。
W25Q64JV写保护特性
- 当VCC低于阈值时,设备复位
- 上电后延时写禁用
- 写入启用/禁用指令,并在擦除或程序后自动写入禁用
- 对状态寄存器进行软件和硬件(/WP引脚)写保护
- 额外的单个块/扇区锁用于阵列保护
- 使用下电指令进行写保护
- 锁定状态寄存器的写保护,直到下次上电
- 一次性程序(OTP)写保护阵列和安全寄存器使用状态寄存器(特定flow支持)
在上电或下电时,当VCC低于VWI的阈值时,W25Q64JV将保持复位状态(参见上电时序和电压水平和图43)。重置时,所有操作都被禁用,并且不识别任何指令。在上电期间,在VCC电压超过VWI后,所有程序和擦除相关指令将进一步禁用tPUW的时间延迟。这包括写使能,页程序,扇区擦除,块擦除,芯片擦除和写状态寄存器指令。请注意,芯片选择引脚(/CS)必须在上电时跟踪VCC供电电平,直到VCC最小电平和tVSL延时达到为止,并且它还必须在下电时跟踪VCC供电电平,以防止不利的命令序列。如果需要上拉电阻上/CS可以用来完成这一点。
上电后,设备自动置于写禁用状态,状态寄存器写使能锁存(WEL)设置为0。在接受页程序、扇区擦除、块擦除、芯片擦除或写状态寄存器指令之前,必须发出写使能指令。在完成一个程序,擦除或写指令后,写使能锁存器(WEL)自动清除为写禁止状态0。
软件控制的写保护是方便使用写状态寄存器指令和设置状态寄存器保护(SRP, SRL)和块保护(CMP, TB, BP[3:0])位。这些设置允许将一部分或整个内存阵列配置为只读。与写保护(/WP)引脚一起使用,可以在硬件控制下启用或禁用对状态寄存器的更改。有关进一步信息,请参阅状态寄存器部分。此外,Power-down指令提供了额外的写保护级别,因为除了Release Power-down指令外,所有指令都被忽略。
W25Q64JV还提供了另一种使用单个块锁的写保护方法。每个64KB的块(除了顶部和底部块,总共126个块)和顶部/底部块中的每个4KB扇区(总共32个扇区)都配备了一个单独的块锁定位。当锁位为0时,对应的扇区或块可以被擦除或编程;当锁位设置为1时,发出到相应扇区或块的Erase或Program命令将被忽略。当设备上电时,所有单个块锁定位将为1,因此整个存储器阵列免受擦除/程序的保护。必须发出“单个块解锁(39h)”指令来解锁任何特定扇区或块。
状态寄存器-3中的WPS位用于决定应该使用哪种写保护方案。当WPS=0(出厂默认值)时,设备将只使用CMP, SEC, TB, BP[2:0]位来保护阵列的特定区域;当WPS=1时,设备将利用单个块锁进行写保护。
### 1. 状态寄存器
W25Q64JV提供了三个状态寄存器和配置寄存器。读取状态寄存器-1/2/3指令可用于提供闪存阵列可用性的状态,设备是否启用或禁用写,写保护状态,Quad SPI设置,安全寄存器锁定状态,擦除/程序暂停状态,输出驱动程序强度,上电。写状态寄存器指令可用于配置设备写保护功能、四SPI设置、安全寄存器OTP锁定和输出驱动程序强度。对状态寄存器的写访问由非易失性状态寄存器保护位(SRL)的状态、写使能指令以及在标准/双SPI操作期间控制。
| 状态 | 说明 |
| ----------------- | ------------------------------------------------------------ |
| **BUSY** | BUSY是状态寄存器(S0)中的只读位,当设备执行页编程、四页编程、扇区擦除、块擦除、芯片擦除、写状态寄存器或擦除/编程安全寄存器指令时,该寄存器被设置为1状态。在此期间,设备将忽略除读状态寄存器和擦除/编程暂停指令之外的其他指令(see tW, tPP, tSE, tBE, and tCE in AC Characteristics)。当程序、擦除或写状态/安全寄存器指令完成时,BUSY位将被清除为0状态,表明设备已准备好接受进一步的指令。 |
| **WEL** | 写使能锁存(WEL)是状态寄存器(S1)中的一个只读位,在执行写使能指令后被设置为1。当设备禁止写时,WEL状态位清零。写禁用状态发生在上电或以下任何指令之后:写禁用、页编程、四页编程、扇区擦除、块擦除、芯片擦除、写状态寄存器、擦除安全寄存器和程序安全寄存器。 |
| **BP2, BP1, BP0** | 块保护位(BP2, BP1, BP0)是状态寄存器(S4, S3和S2)中的非易失读写位,提供写保护控制和状态。块保护位可以使用写状态寄存器指令设置(参见交流特性中的tW)。存储器阵列的全部、无或部分可以不受程序和擦除指令的保护(参见状态寄存器存储器保护表)。“块保护位”的出厂默认设置为“0”,表示不保护任何阵列。**理解:写保护大小。** |
| **TB** | 非易失性顶部/底部位(TB)控制块保护位(BP2、BP1、BP0)是否从阵列的顶部(TB=0)或底部(TB=1)进行保护,如状态寄存器内存保护表所示。出厂默认设置为TB=0。根据SRP/SRL和WEL位的状态,可以通过写入状态寄存器指令来设置TB位。**理解:保护低地址或高地址。** |
| **SEC** | 非易失性扇区/块保护位(SEC)控制块保护位(BP2、BP1、BP0)是否保护阵列顶部(TB=0)或底部(TB=1)的4KB扇区(SEC=1)或64KB块(SEC=0),如状态寄存器内存保护表所示。默认设置为SEC=0。**理解:保护扇区。** |
| **CMP** | 补码保护位(CMP)是状态寄存器(S14)中的一个非易失性读写位。它与SEC、TB、BP2、BP1和BP0位结合使用,以提供更大的阵列保护灵活性。一旦CMP设置为1,之前由SEC、TB、BP2、BP1和BP0设置的阵列保护将被反转。例如,当CMP=0时,前64KB块可以受到保护,而其余部分则不受保护;当CMP=1时,前64KB块将不再受保护,而其余部分则变为只读。详情请参阅状态寄存器内存保护表。默认设置为CMP=0。**理解:保护区变为不保护,不保护区变为保护。** |
| **SRP, SRL** | 见下表 |
| **SUS** | 暂停状态位是状态寄存器(S15)中的只读位,在执行擦除/编程暂停(75h)指令后设置为1。SUS状态位通过擦除/编程恢复(7 Ah)指令以及断电、上电周期被清零为0。 |
| **LB3, LB2, LB1** | 安全寄存器锁定位(LB3、LB2、LB1)是非易失性的单次编程(OTP)位,位于状态寄存器(S13、S12、S11)中,用于提供对安全寄存器的写保护控制和状态。LB3-1的默认状态为0,表示安全寄存器未锁定。LB3-1可以单独使用写入状态寄存器指令设置为1。LB3-1是单次可编程(OTP),一旦设置为1,相应的256字节安全寄存器将永久变为只读。**理解:可以当OTP用,写完后锁定,未锁定可随意读写。** |
| **QE** | 四通道使能(QE)位是状态寄存器(S9)中的一个非易失性读写位,用于启用四通道SPI操作。当QE位设置为0状态(对于带有“IM”和“JM”订购选项的部件,默认出厂设置),/HOLD功能被启用,设备以标准/双SPI模式运行。当QE位设置为1(对于带有“IQ/IN”和“JQ”订购选项的部件,默认出厂固定设置),四通道IO2和IO3引脚被启用,/HOLD功能被禁用,设备以标准/双/四SPI模式运行。 |
| **WPS** | WPS位用于选择应使用的写保护方案。当WPS=0时,设备将使用CMP、SEC、TB、BP[2:0]位的组合来保护内存阵列中的特定区域。当WPS=1时,设备将利用单个块锁来保护任何单独的扇区或块。所有单个块锁位的默认值在设备上电或复位后均为1。**理解:通过命令对单个块加锁解锁。** |
| **DRV1, DRV0** | DRV1和DRV0位用于确定读操作的输出驱动强度。25%、50%、75%、100% |
状态寄存器保护:
| SRL | SRP | /WP | Status Register | 说明 |
| ---- | ---- | ---- | --------------- | ------------------------------------------------------------ |
| 0 | 0 | X | 软件保护 | /WP引脚无控制。在写入使能指令后,可以对状态寄存器进行写入,WEL=1。[出厂默认值] |
| 0 | 1 | 0 | 硬件保护 | 当/WP引脚处于低电平时,状态寄存器被锁定,无法写入。 |
| 0 | 1 | 1 | 硬件解保护 | 当/WP引脚为高电平时,状态寄存器解锁,并且在写入使能指令之后可以进行写入,WEL=1。 |
| 1 | X | X | 电源锁定 | 状态寄存器受到保护,直到下一个关机和开机周期之前不能再次写入。 |
| 1 | X | X | OTP | 状态寄存器受到永久保护,不能写入。(通过添加前缀命令AAh、55h启用) |
Volatile SR Write Enable **50h**:对状态寄存器的修改,下电后会丢失。
**速度差异**
- **Read Data**:
最大时钟频率受限(如 W25Q 系列通常 ≤ 50MHz),适合 **低速率稳定读取**;
- **Fast Read**:
支持更高时钟频率(如 W25Q256JV 可达 133MHz),通过 **dummy 周期优化时序**提升吞吐量
Set Burst with Wrap (77h):开启关闭回绕模式,配合**Fast Read Quad I/O (EBh)**使用。
SFDP(Serial Flash Discoverable Parameters)是 **JEDEC JESD216 标准**定义的一套串行 Flash(NOR/NAND)参数表,用于描述设备的物理特性、功能支持及操作指令集,支持驱动自动适配不同厂商的 Flash 芯片12。其核心作用包括:
- **统一参数标准**:消除不同厂商 Flash 的配置差异,简化驱动开发;
- **动态适配**:通过读取预设表格自动识别容量、页大小、擦写模式等关键参数13。
tPUW:上电后至少5ms才能发写指令