Compare commits
26 Commits
fe107ea9af
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 78eff31de7 | |||
| 565f23ea18 | |||
| 02a05a7a63 | |||
| 4e9ac0d9c3 | |||
| 615a9b13e9 | |||
| cf33171c8f | |||
| 88160a5134 | |||
| 0a707034cc | |||
| d6bc3f6253 | |||
| 4375873083 | |||
| fc88bc8752 | |||
| a7f367f6a3 | |||
| c646df5c2a | |||
| 038a2542af | |||
| 46a74f24f3 | |||
| 75cf4c9fbf | |||
| 6d404d136e | |||
| d48be81478 | |||
| 66a1e8fd6a | |||
| f50b9c9e66 | |||
| fbe0726991 | |||
| e06d849007 | |||
| 9ed4c22022 | |||
| 4eb728583b | |||
| 1669cd216d | |||
| f72d08328f |
10
.mxproject
10
.mxproject
File diff suppressed because one or more lines are too long
14
.opencode/command/build.md
Normal file
14
.opencode/command/build.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
description: Build the STM32F4-Base Keil MDK project. Use ONLY when the user asks to build, compile, or verify the project.
|
||||||
|
---
|
||||||
|
|
||||||
|
Run the Keil MDK-ARM build from the project root:
|
||||||
|
|
||||||
|
```
|
||||||
|
cmd /c "cd /d "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM" && build.bat"
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the last 10 lines of `MDK-ARM/build_log.txt` for errors/warnings.
|
||||||
|
Return code 0 = success (0 errors, 0 warnings).
|
||||||
|
Return code 1 = warnings-only (also failure per project rules).
|
||||||
|
Return code 2+ = compile errors.
|
||||||
187
AGENTS.md
187
AGENTS.md
@@ -1,133 +1,94 @@
|
|||||||
# STM32F4-Base
|
# STM32F4-Base
|
||||||
|
|
||||||
## 项目概述
|
> **编译验证:所有代码修改后必须执行 `@build` 验证编译通过(0 错误 0 警告),否则不要提交。**
|
||||||
|
|
||||||
STM32F407ZGTx (Cortex-M4 FPU) 基础固件项目,集成 CH395F 以太网控制器 SPI 驱动、GD5F2GQ5UE SPI NAND Flash 存储(含 FlashDB KVDB/TSDB 数据库)和 TPAFE5160 16位8通道同步采样 ADC 并行接口驱动。
|
## 构建
|
||||||
|
|
||||||
## 目录结构
|
- **仅支持 Keil MDK-ARM v5 (ARMCC)**,无其他工具链
|
||||||
|
- 命令行编译:`@build`(自动执行 `MDK-ARM/build.bat`)
|
||||||
|
- 退出码:0 = 成功 | 1 = 有警告(不通过)| 2+ = 错误,查看 `MDK-ARM/build_log.txt`
|
||||||
|
|
||||||
|
## 硬件
|
||||||
|
|
||||||
|
| 外设 | 接口 | 引脚 |
|
||||||
|
|------|------|------|
|
||||||
|
| CH395F 以太网 | SPI2 | PB12 CS, PB13 SCK, PB14 SDO, PB15 SDI |
|
||||||
|
| GD5F2GQ5UE NAND | SPI1 | PE0 CS, PB3 SCK, PB4 MISO, PB5 MOSI, PB8 WP, PE1 HOLD |
|
||||||
|
| TPAFE5160 ADC | 并行16位 | PG0-PG15 数据, PD3 RD, PD4 CONVST, PD7 BUSY |
|
||||||
|
| USART1 | 串口 | PA9 TX, PA10 RX (115200bps) |
|
||||||
|
| 6x LED | GPIO | PC4, PC5, PB1, PB2, PF11, PF12(低电平点亮) |
|
||||||
|
|
||||||
|
MCU: STM32F407ZGTx @ 168MHz (HSE 8MHz → PLL M=4/N=168/P=2,见 `*.ioc`/`stm32f4xx_hal_conf.h`)
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
```
|
```
|
||||||
STM32F4-Base/
|
Src/main.c → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe5160_init
|
||||||
├── Src/ # CubeMX 生成的外设初始化 + main
|
→ osKernelInitialize → MX_FREERTOS_Init → osKernelStart
|
||||||
├── Inc/ # CubeMX 生成的头文件
|
→ [FreeRTOS tasks: defaultTask, netTask, ftpTask]
|
||||||
├── Drivers/
|
|
||||||
│ ├── BSP/
|
|
||||||
│ │ ├── CH395F/ # CH395F 以太网芯片驱动(手写)
|
|
||||||
│ │ ├── GD5F2GQ5UE/ # GD5F2GQ5UE NAND Flash 驱动(手写)
|
|
||||||
│ │ │ ├── gd5f2gq5ue.h/c # 底层 SPI 驱动
|
|
||||||
│ │ │ ├── fal_flash_gd5f2gq5ue.c # FAL 设备适配层
|
|
||||||
│ │ │ ├── fal_cfg.h # FAL 设备表 + 分区表
|
|
||||||
│ │ │ └── fdb_cfg.h # FlashDB 功能配置
|
|
||||||
│ │ └── TPAFE5160/ # TPAFE5160 ADC 并行接口驱动(手写)
|
|
||||||
│ ├── STM32F4xx_HAL_Driver/ # ST HAL 库(CubeMX 生成)
|
|
||||||
│ └── CMSIS/ # ARM CMSIS(CubeMX 生成)
|
|
||||||
├── Lib/
|
|
||||||
│ └── FlashDB/ # FlashDB 数据库库(v2.2.99)
|
|
||||||
│ ├── src/ # FlashDB 核心源码
|
|
||||||
│ ├── inc/ # FlashDB 头文件
|
|
||||||
│ └── port/fal/ # FAL 抽象层
|
|
||||||
├── MDK-ARM/ # Keil MDK 工程文件
|
|
||||||
├── docs/ # 参考文档
|
|
||||||
└── STM32F407-Demo.ioc # STM32CubeMX 项目源文件
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- **defaultTask**:FTL + FatFS 初始化测试(开机一次性)
|
||||||
|
- **netTask**:`net_init()` → `net_poll()` + `net_process_messages()` 每 10ms
|
||||||
|
- **ftpTask**:`lftpd_start("/", 21, &ftp)` 延迟 7s 后启动
|
||||||
|
|
||||||
|
### 任务栈
|
||||||
|
|
||||||
|
| Task | 栈大小 |
|
||||||
|
|---|---|
|
||||||
|
| defaultTask | 2048 |
|
||||||
|
| netTask | 4096 |
|
||||||
|
| ftpTask | 4096 |
|
||||||
|
|
||||||
|
## CH395F 设计原则
|
||||||
|
|
||||||
|
- **单连接 socket**:Socket 0 复用监听和数据通道,收到 CONNECT 后切换为数据通道,DISCONNECT/TIMEOUT 后重新 OPEN → LISTEN
|
||||||
|
- **中断统一处理**:所有 CH395F 中断在 `net_poll()` 中通过 `GET_GLOB_INT_STATUS` 集中读取并清除,不得在 ISR 或其他地方单独处理
|
||||||
|
|
||||||
|
## NET 网络层
|
||||||
|
|
||||||
|
- `net_socket.h/c` — BSD Socket API,多连接模式下 Socket 0 监听、Socket 1~7 数据
|
||||||
|
- `net_select.h/c` — select/poll I/O 多路复用
|
||||||
|
- 线程安全通过消息队列(`netMsgQueue`)实现,所有 CH395F 操作在 netTask 中串行处理
|
||||||
|
- 网络初始化顺序(多连接):`ch395f_set_fun_para(0x02)` → `ch395f_init()` → 配置 Socket 1~7(缓冲区+端口+协议,不 OPEN)→ 配置 Socket 0(TCP LISTEN)
|
||||||
|
- **新文件需手动添加到 Keil MDK 工程**才能编译
|
||||||
|
|
||||||
## 关键文件
|
## 关键文件
|
||||||
|
|
||||||
| 路径 | 说明 |
|
| 路径 | 说明 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Src/main.c` | 程序入口,初始化序列及主循环 |
|
| `Src/main.c` | 入口,CubeMX 生成 + USER CODE 区域 |
|
||||||
| `Drivers/BSP/CH395F/ch395f.c/h` | CH395F 以太网芯片 SPI 驱动 |
|
| `Src/freertos.c` | FreeRTOS task 创建,CubeMX 生成 + USER CODE 区域 |
|
||||||
| `Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c/h` | GD5F2GQ5UE NAND Flash SPI 驱动 |
|
| `App/task/net_task.c` | netTask 主循环(网络初始化 + 轮询) |
|
||||||
| `Drivers/BSP/GD5F2GQ5UE/fal_flash_gd5f2gq5ue.c` | FAL 设备适配层 |
|
| `Drivers/BSP/NET/` | BSD Socket API 网络抽象层 |
|
||||||
| `Drivers/BSP/TPAFE5160/tpafe5160.c/h` | TPAFE5160 ADC 并行接口驱动 |
|
| `Drivers/BSP/CH395F/` | CH395F SPI 驱动 |
|
||||||
| `MDK-ARM/STM32F407-Demo.uvprojx` | Keil MDK 项目文件 |
|
| `Drivers/BSP/GD5F2GQ5UE/` | NAND Flash 驱动 + dhara FTL + FatFS diskio |
|
||||||
| `STM32F407-Demo.ioc` | STM32CubeMX 项目源文件 |
|
| `Drivers/BSP/TPAFE5160/` | ADC 并行接口驱动 |
|
||||||
|
| `Drivers/BSP/SD2506/` | RTC 驱动 |
|
||||||
|
| `Drivers/BSP/RS485/` | RS-485 通信(UART5 + PD0 DE) |
|
||||||
|
|
||||||
## 构建
|
## 驱动关键点
|
||||||
|
|
||||||
仅支持 Keil MDK-ARM v5 (ARMCC)。打开 `MDK-ARM/STM32F407-Demo.uvprojx` 编译。
|
详见 `docs/` 目录:
|
||||||
|
- `CH395F_Trap_Records.md` — CH395F 已知陷阱
|
||||||
- 编译器:ARMCC V5.06 update 7
|
- `GD5F2GQ5UE_Trap_Records.md` — GD5F2GQ5UE 已知陷阱
|
||||||
- 优化等级:`-O4` (项目级),`spi.c`/`usart.c` / HAL 源文件使用 `-O0`
|
- `CH395F_Test_Guide.md` — 网络测试说明
|
||||||
- C 标准:C99
|
|
||||||
- 全局宏定义:`USE_HAL_DRIVER, STM32F407xx`
|
|
||||||
|
|
||||||
## 硬件配置
|
|
||||||
|
|
||||||
- **主频:** HSE 25MHz → PLL 168MHz (4/168/2)
|
|
||||||
- **6 个 LED:** PC4, PC5, PB1, PB2, PF11, PF12(低电平点亮)
|
|
||||||
- **CH395F:** SPI2 (PB12 CS, PB13 SCK, PB14 SDO, PB15 SDI)
|
|
||||||
- **GD5F2GQ5UE:** SPI1 (PE0 CS, PB3 SCK, PB4 MISO, PB5 MOSI, PB8 WP, PE1 HOLD)
|
|
||||||
- **TPAFE5160:** 并行16位 (PG0-PG15 数据, PD3 RD, PD4 CONVST, PD7 BUSY, PD1 FRSTDATA, PF13-15 OS[2:0])
|
|
||||||
- **USART1:** PA9 TX, PA10 RX (115200bps)
|
|
||||||
|
|
||||||
## 启动顺序
|
|
||||||
|
|
||||||
```
|
|
||||||
HAL_Init() → SystemClock_Config() → MX_GPIO_Init() → MX_USART1_UART_Init() → MX_SPI2_Init() → MX_SPI1_Init() → gd5f2gq5ue_init() → fdb_kvdb_init()
|
|
||||||
```
|
|
||||||
|
|
||||||
## 代码规范
|
|
||||||
|
|
||||||
参考 `嵌入式C语言代码规范(V1.0).md`,关键要点:
|
|
||||||
|
|
||||||
- 缩进:4 空格,禁止 Tab
|
|
||||||
- 命名:小写字母+下划线;全局变量 `g_` 前缀,静态 `s_`,指针 `p_`,数组 `a_`
|
|
||||||
- 函数注释块需包含:函数功能、入口参数、返回值、限定条件、函数说明
|
|
||||||
- 大括号:K&R 风格(左大括号不换行)
|
|
||||||
- 文件头注释:模块名称、功能、平台、作者、日期、修改记录
|
|
||||||
- 头文件保护宏:`__MODULE_NAME_H` 格式,带 `extern "C"`
|
|
||||||
|
|
||||||
## 注意
|
## 注意
|
||||||
|
|
||||||
- `Inc/` 和 `Src/` 中 CubeMX 生成的文件带有 `USER CODE BEGIN`/`END` 标记,自定义代码应写在这些区域之间
|
- `Inc/` 和 `Src/` 中 CubeMX 生成的文件带有 `USER CODE BEGIN`/`END` 标记,自定义代码写在这些区域之间
|
||||||
- `ch395f.c/h`、`gd5f2gq5ue.c/h`、`fal_flash_gd5f2gq5ue.c` 为纯手工代码,不受 CubeMX 保护
|
- `Drivers/BSP/` 下的驱动文件为纯手工代码,不受 CubeMX 保护
|
||||||
- `tpafe5160.c/h` 为纯手工代码,不受 CubeMX 保护
|
- CH395F 每次 SPI 事务需用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
|
||||||
- CH395F 每次 SPI 事务需调用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
|
- CH395F SPI PCB设计问题,现在分频系数必须为 8,速度相对较低
|
||||||
- GD5F2GQ5UE 的 `gd5f2gq5ue.c` 中声明了 `extern SPI_HandleTypeDef hspi1`,需确保 SPI1 已初始化
|
- FreeRTOS V10.3.1 via CMSIS-RTOS V2,HAL 时基用 TIM7(非 SysTick)
|
||||||
- FlashDB 使用 FAL 模式,`fdb_cfg.h` 中定义 `FDB_USING_FAL_MODE`,`fal_cfg.h` 中定义分区表
|
- NVIC 优先级分组 4 位,外设中断优先级 ≥ 5(`configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`)
|
||||||
- FlashDB 详细使用说明见 `FlashDB使用说明.md`
|
|
||||||
- `sd2506.c/h` 为纯手工代码,不受 CubeMX 保护
|
|
||||||
|
|
||||||
## CH395F 驱动关键点
|
## 源码编辑与编码安全(铁律)
|
||||||
|
|
||||||
- 初始化必须按手册9.2.1节顺序:`SET_MAC` → `SET_IP/GWIP/MASK` → `INIT_CH395` → `SET_PHY`
|
本工程源码含大量中文注释,曾因错误的文本重写方式导致整文件乱码(`U+FFFD` 替换符,不可逆)。以下规则必须遵守:
|
||||||
- **IP/网关/掩码必须在 `INIT_CH395` 之前设置**,INIT 会读取并锁定当前寄存器值到协议栈,之后再设 IP 无效
|
|
||||||
- **`SET_PHY` 必须在 `INIT_CH395` 之后**,它会复位 MAC/PHY 建立物理链路,不影响已锁定的协议栈参数
|
|
||||||
- **`CMD_PING_ENABLE` 不需要显式调用**,INIT 后默认可用
|
|
||||||
- 每次 SPI 事务需调用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
|
|
||||||
|
|
||||||
## GD5F2GQ5UE 驱动关键点
|
- **绝不用 `Get-Content` / `Set-Content` / `cmd` 文本重写含中文的源码文件**。这类"整文件重写"会按系统默认编码重新解码再写回,多字节中文必被 `U+FFFD` 替换且不可逆。
|
||||||
|
- **改代码只用 Edit 工具**(精确子串替换,不重新编解码整个文件,安全);批量替换用 Edit 的 `replaceAll`。
|
||||||
- SPI Mode 0(CPOL=0, CPHA=0),时钟 42MHz
|
- 必须脚本级替换时,使用显式且一致的编码(如 Python `open(p, encoding="utf-8")` 同编码回写),并先确认工程编码(Keil 工程常用 GBK)——但**最稳的是别碰**,只用 Edit / git。
|
||||||
- 初始化必须按顺序:复位 → 读 ID → 使能 ECC(B0h=10h)→ 解除块保护(A0h=00h)
|
- **发现乱码先别急着 `git checkout`**:`git checkout` 会丢弃所有未提交工作,使损失扩大。先评估能否用 Edit 仅改坏掉的那几行。
|
||||||
- SET_FEATURE 命令前必须先发写使能(06h)
|
- 验证是否真损坏用字节级检查(统计 `EF BF BD` 出现次数),不要依赖终端/Read 渲染判断。
|
||||||
- 块擦除(D8h)参数是字节地址(块编号 × 128KB),不是块编号
|
|
||||||
- 读取 ID(9Fh)返回 3 字节,第 0 字节无意义,第 1 字节 MID,第 2 字节 DID
|
|
||||||
|
|
||||||
## FlashDB 分区规划
|
|
||||||
|
|
||||||
| 分区名 | 偏移 | 大小 | 用途 |
|
|
||||||
|--------|------|------|------|
|
|
||||||
| fdb_kvdb1 | 0 | 64MB | KVDB 键值数据库 |
|
|
||||||
| fdb_tsdb1 | 64MB | 64MB | TSDB 时序数据库 |
|
|
||||||
|
|
||||||
## TPAFE5160 驱动关键点
|
|
||||||
|
|
||||||
- AD7606 P2P 兼容替代品,并行接口协议一致
|
|
||||||
- 并行模式:CS 接地(始终选中),PAR/SER/BYTE SEL 接 GND(并行),DB15/BYTE SEL 接 GND(非字节模式)
|
|
||||||
- 数据总线 DB[15:0] 接 GPIOG[15:0],通过 `(uint16_t)GPIOG->IDR` 一次读取16位
|
|
||||||
- CONVST 上升沿触发全部8通道同步采样,BUSY 高电平表示转换中
|
|
||||||
- RD 下降沿输出通道数据,按通道1~8顺序依次输出
|
|
||||||
- FRSTDATA 在第一个 RD 下降沿变高,指示通道1数据就绪
|
|
||||||
- 过采样 OS[2:0] 在 BUSY 下降沿锁存,无过采样时 tCONV=1.74µs,64倍过采样时 tCONV=167µs
|
|
||||||
- 读取时序:168MHz 下 GPIO 写操作 + 5个 NOP (~30ns) 覆盖 t10=22ns 和 t14=21ns 要求
|
|
||||||
- 硬件 RANGE 接 GND → ±5V 量程,LSB=152.59µV
|
|
||||||
|
|
||||||
## 已知问题
|
|
||||||
|
|
||||||
### CH395F 与 RTL8305NBI 自动协商不兼容
|
|
||||||
|
|
||||||
CH395F 与 RTL8305NBI-CG 直连(经网络变压器)时,自动协商始终失败(返回 `PHY_DISCONN`),但强制 100M 全双工工作正常。强制 10M 全双工同样失败。
|
|
||||||
|
|
||||||
**解决方案:** 初始化协议栈后调用 `ch395f_set_phy(CH395F_PHY_100M_FULL)` 跳过自动协商。
|
|
||||||
|
|||||||
36
App/app_cfg.h
Normal file
36
App/app_cfg.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Application Configuration
|
||||||
|
* 模块功能:应用层全局配置宏,控制功能模块的编译开关
|
||||||
|
* 适用平台:STM32F407ZGTx
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __APP_CFG_H
|
||||||
|
#define __APP_CFG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 系统时钟源选择
|
||||||
|
* 0 - DWT CYCCNT(默认,0 外设开销)
|
||||||
|
* 1 - TIM2(32-bit 定时器,独立于 DWT)
|
||||||
|
*/
|
||||||
|
#define APP_SYS_TIME_SRC 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 功能模块使能开关
|
||||||
|
*/
|
||||||
|
#define APP_NET_ENABLE
|
||||||
|
#define APP_ADC_ENABLE
|
||||||
|
#define APP_RS485_ENABLE
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __APP_CFG_H */
|
||||||
42
App/app_main.c
Normal file
42
App/app_main.c
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Application Main Entry
|
||||||
|
* 模块功能:应用初始化流程编排、各模块启动、主循环
|
||||||
|
* 适用平台:STM32F407ZGTx
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "app_main.h"
|
||||||
|
#include "app_cfg.h"
|
||||||
|
|
||||||
|
#define DBG_TAG "[APP]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:应用层初始化
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:0 - 成功,-1 - 失败
|
||||||
|
* 限定条件:CubeMX 外设初始化已完成
|
||||||
|
* 函数说明:在 CubeMX 外设初始化之后、FreeRTOS 调度器启动之前调用
|
||||||
|
*/
|
||||||
|
int app_main_init(void)
|
||||||
|
{
|
||||||
|
DBG_INFO("App init OK");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:应用主循环
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:无
|
||||||
|
* 限定条件:FreeRTOS 调度器启动后不再返回
|
||||||
|
* 函数说明:空闲任务可调用的应用层处理入口
|
||||||
|
*/
|
||||||
|
void app_main_run(void)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
/* 应用层空闲处理 */
|
||||||
|
}
|
||||||
|
}
|
||||||
27
App/app_main.h
Normal file
27
App/app_main.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Application Main Entry
|
||||||
|
* 模块功能:应用层入口,由 main.c 调用完成所有应用初始化与启动
|
||||||
|
* 适用平台:STM32F407ZGTx
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __APP_MAIN_H
|
||||||
|
#define __APP_MAIN_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int app_main_init(void);
|
||||||
|
void app_main_run(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __APP_MAIN_H */
|
||||||
0
App/protocol/.gitkeep
Normal file
0
App/protocol/.gitkeep
Normal file
369
App/sys_clock.c
Normal file
369
App/sys_clock.c
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:System Clock — Wall Clock Time Maintenance
|
||||||
|
* 模块功能:实现系统墙钟时间维护,包括 RTC 初始读取、HAL Tick 漂移维持
|
||||||
|
* 适用平台:STM32F407ZGTx (Cortex-M4 FPU, 168MHz)
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sys_clock.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define DBG_TAG "[CLK]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* RTC 校时(开发期一次性)
|
||||||
|
* 使能 RTC_DEFAULT_INIT_ENABLE 后:
|
||||||
|
* RTC_DEFAULT_INIT_FORCE=1:每次启动都写入默认时间(首次/覆盖校时用)
|
||||||
|
* RTC_DEFAULT_INIT_FORCE=0:仅当 RTC 时间无效(越界)时写入
|
||||||
|
* 校时完成后建议将 FORCE 置 0(或注释 ENABLE),避免每次开机覆盖用户设的时间。
|
||||||
|
* 修改日期/时间时同步更新下列宏。
|
||||||
|
* ============================================================ */
|
||||||
|
#define RTC_DEFAULT_INIT_ENABLE 1
|
||||||
|
#define RTC_DEFAULT_INIT_FORCE 0
|
||||||
|
#define RTC_DEFAULT_YEAR 2026
|
||||||
|
#define RTC_DEFAULT_MONTH 8
|
||||||
|
#define RTC_DEFAULT_DAY 24
|
||||||
|
#define RTC_DEFAULT_HOUR 0
|
||||||
|
#define RTC_DEFAULT_MINUTE 0
|
||||||
|
#define RTC_DEFAULT_SECOND 0
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 内部状态:系统墙钟
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
static volatile uint32_t s_base_tick = 0; /* HAL_GetTick() 基准 */
|
||||||
|
static volatile uint32_t s_base_unix_secs = UINT32_MAX;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 内部辅助函数:Unix time ↔ sd2506_time_t 转换
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:根据公历日期计算星期几
|
||||||
|
* 入口参数:y - 年份 uint16_t 1970~2099
|
||||||
|
* m - 月份 uint8_t 1~12
|
||||||
|
* d - 日 uint8_t 1~31
|
||||||
|
* 返回值:星期编号 uint8_t 0=Sunday, 1=Monday, ... 6=Saturday
|
||||||
|
* 限定条件:输入为公历合法日期
|
||||||
|
* 函数说明:1. 采用 Zeller 公式(变种),结果 0 表示周日
|
||||||
|
* 2. 与 unix_to_sd2506() 内星期算法保持一致
|
||||||
|
*/
|
||||||
|
static uint8_t rtc_calc_week(uint16_t y, uint8_t m, uint8_t d)
|
||||||
|
{
|
||||||
|
uint32_t wy = y;
|
||||||
|
uint32_t wm = m;
|
||||||
|
|
||||||
|
if (wm <= 2) {
|
||||||
|
wy--;
|
||||||
|
wm += 12u;
|
||||||
|
}
|
||||||
|
/* Zeller 公式:世纪项 + 月项 + 日项,模 7 得星期(0=周日) */
|
||||||
|
return (uint8_t)(((uint32_t)(wy % 100u + wy / 100u / 4u - wy / 100u / 15u) +
|
||||||
|
(uint32_t)((26u * (wm + 1u)) / 10u) + (uint32_t)(d % 100u)) % 7u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将 Unix 时间戳(自 1970-01-01 00:00:00 起的秒数)转换为 SD2506 RTC 时间结构
|
||||||
|
* 入口参数:unix_secs - Unix 时间戳 uint32_t 0~0x7FFFFFFF(~2038)
|
||||||
|
* p_sd_time - 输出时间结构指针 sd2506_time_t* 不得为 NULL
|
||||||
|
* 出口参数:p_sd_time - 填充年/月/日/时/分/秒/星期
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:p_sd_time 必须指向有效缓冲区
|
||||||
|
* 函数说明:1. 先取模拆分时分秒,再按累计天数推算年月日
|
||||||
|
* 2. 星期由 Zeller 公式(与 rtc_calc_week 同算法)得出
|
||||||
|
* 3. SD2506 年份字段为相对值(0~99,+2000),故回写时减 2000
|
||||||
|
*/
|
||||||
|
static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
|
||||||
|
{
|
||||||
|
uint32_t y, m, d;
|
||||||
|
uint8_t h, mi, s;
|
||||||
|
int is_leap;
|
||||||
|
|
||||||
|
/* 时分秒 */
|
||||||
|
{
|
||||||
|
uint64_t total = (uint64_t)unix_secs;
|
||||||
|
s = (uint8_t)(total % 60uLL);
|
||||||
|
total /= 60uLL;
|
||||||
|
mi = (uint8_t)(total % 60uLL);
|
||||||
|
total /= 60uLL;
|
||||||
|
h = (uint8_t)(total % 24uL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 年月日 */
|
||||||
|
{
|
||||||
|
uint64_t day_count = ((uint64_t)unix_secs / 86400uL);
|
||||||
|
|
||||||
|
y = 1970u;
|
||||||
|
while (day_count >= 365uLL) {
|
||||||
|
uint64_t diy = 365uLL;
|
||||||
|
if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) {
|
||||||
|
diy = 366uLL;
|
||||||
|
}
|
||||||
|
if (day_count < diy) break;
|
||||||
|
day_count -= diy;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_leap = ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0));
|
||||||
|
uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
if (is_leap) mdays[1] = 29;
|
||||||
|
|
||||||
|
m = 0;
|
||||||
|
while (m < 12u) {
|
||||||
|
if (day_count < (uint64_t)mdays[m]) break;
|
||||||
|
day_count -= mdays[m];
|
||||||
|
m++;
|
||||||
|
}
|
||||||
|
d = (uint32_t)(day_count + 1uL);
|
||||||
|
|
||||||
|
/* 星期(Zeller's congruence,0=Sunday) */
|
||||||
|
uint64_t wy = y;
|
||||||
|
uint64_t wm = m;
|
||||||
|
if (wm <= 2) {
|
||||||
|
wy--;
|
||||||
|
wm += 12uL;
|
||||||
|
}
|
||||||
|
/* Zeller 公式同 rtc_calc_week():世纪项 + 月项 + 日项,模 7 得星期(0=周日) */
|
||||||
|
uint8_t week = ((uint32_t)(wy % 100uLL + wy / 100uLL / 4uLL - wy / 100uLL / 15uLL) +
|
||||||
|
(uint32_t)(((uint64_t)26 * (wm + 1uL)) / 10uLL) +
|
||||||
|
(uint32_t)(d % 100uLL)) % 7uLL;
|
||||||
|
|
||||||
|
/* sd2506_time_t.year 采用完整年份(如 2026),与 sd2506_set_time
|
||||||
|
* 中 year-2000、sd2506_get_time 中 +2000 的约定保持一致;
|
||||||
|
* 此处绝不能再减 2000,否则写 RTC 时会被二次减 2000 导致年份错乱。 */
|
||||||
|
p_sd_time->year = (uint16_t)y;
|
||||||
|
p_sd_time->month = m + 1uL; /* 月份从 0 → 1 */
|
||||||
|
p_sd_time->day = d;
|
||||||
|
p_sd_time->hour = h;
|
||||||
|
p_sd_time->minute = mi;
|
||||||
|
p_sd_time->second = s;
|
||||||
|
p_sd_time->week = week;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* 系统墙钟时间维护
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:初始化系统墙钟时间基准
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功(始终返回 0)
|
||||||
|
* 限定条件:CubeMX 外设初始化完成、SD2506 RTC 驱动已初始化
|
||||||
|
* 函数说明:1. 从 SD2506 读取初始时间,必要时按编译期默认宏校时(RTC_DEFAULT_INIT_*)
|
||||||
|
* 2. 将 RTC 时间换算为 Unix epoch 秒数
|
||||||
|
* 3. 记录当前 HAL Tick 与 Unix 秒数为基准,供 sys_clock_get() 后续推算
|
||||||
|
* 4. 注意:sd2506_get_time() 返回的 year 已是完整年份(驱动内 +2000),
|
||||||
|
* 换算时不得再加 2000,否则年份虚增导致 Unix 秒数超 uint32 回绕
|
||||||
|
*/
|
||||||
|
int sys_clock_init(void)
|
||||||
|
{
|
||||||
|
sd2506_time_t sd_time;
|
||||||
|
uint32_t year, month, day, hour, minute, second;
|
||||||
|
int64_t unix_secs = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Step 1:从 SD2506 硬件 RTC 读取初始时间
|
||||||
|
* ============================================================
|
||||||
|
* 注意:sd2506_get_time() 返回的 year 已是完整年份(如 2026,
|
||||||
|
* 驱动内部已 +2000)。此处【禁止】再 +2000,否则年份虚增到 4026,
|
||||||
|
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。 */
|
||||||
|
sd2506_get_time(&sd_time);
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Step 1.1:开发期校时(可选,由 RTC_DEFAULT_INIT_* 宏控制)
|
||||||
|
* ============================================================
|
||||||
|
* 作用:RTC 出厂/掉电异常导致时间非法时,写入一个可用初始值;
|
||||||
|
* FORCE=1 时每次启动都强制写入,便于首次烧录或硬件时钟丢失。
|
||||||
|
* 判定"时间无效":年份越界、月/日越界(仅粗判,不细校 2 月 29 日等)。 */
|
||||||
|
#if defined(RTC_DEFAULT_INIT_ENABLE)
|
||||||
|
{
|
||||||
|
int rtc_invalid = (sd_time.year < 2000 || sd_time.year > 2099 ||
|
||||||
|
sd_time.month < 1 || sd_time.month > 12 ||
|
||||||
|
sd_time.day < 1 || sd_time.day > 31);
|
||||||
|
#if (RTC_DEFAULT_INIT_FORCE == 1)
|
||||||
|
rtc_invalid = 1; /* 强制每次启动校时:会覆盖用户/网络已设的正确时间,仅限开发期 */
|
||||||
|
#endif
|
||||||
|
if (rtc_invalid) {
|
||||||
|
sd2506_time_t init_t;
|
||||||
|
/* 用编译期默认宏构造初始时间;week 由 Zeller 公式(rtc_calc_week)推算 */
|
||||||
|
init_t.year = RTC_DEFAULT_YEAR;
|
||||||
|
init_t.month = RTC_DEFAULT_MONTH;
|
||||||
|
init_t.day = RTC_DEFAULT_DAY;
|
||||||
|
init_t.hour = RTC_DEFAULT_HOUR;
|
||||||
|
init_t.minute = RTC_DEFAULT_MINUTE;
|
||||||
|
init_t.second = RTC_DEFAULT_SECOND;
|
||||||
|
init_t.week = rtc_calc_week(init_t.year, init_t.month, init_t.day);
|
||||||
|
|
||||||
|
/* 写回 SD2506:断电后该时间被保留,下次上电即为有效值 */
|
||||||
|
if (sd2506_set_time(&init_t) == SD2506_OK) {
|
||||||
|
DBG_INFO("sys_clock_init: RTC set %04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
init_t.year, init_t.month, init_t.day,
|
||||||
|
init_t.hour, init_t.minute, init_t.second);
|
||||||
|
sd2506_get_time(&sd_time); /* 重新读取校准后的值,确保后续换算用真实 RTC 数据 */
|
||||||
|
} else {
|
||||||
|
DBG_ERROR("sys_clock_init: RTC default write FAIL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 取出各时间字段(year 已是完整年份,直接用于下文 Unix 换算) */
|
||||||
|
year = (uint32_t)sd_time.year;
|
||||||
|
month = (uint32_t)sd_time.month;
|
||||||
|
day = (uint32_t)sd_time.day;
|
||||||
|
hour = (uint32_t)sd_time.hour;
|
||||||
|
minute = (uint32_t)sd_time.minute;
|
||||||
|
second = (uint32_t)sd_time.second;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Step 2:将 RTC 日历时间换算为 Unix epoch 秒数
|
||||||
|
* ============================================================
|
||||||
|
* Unix 时间定义:自 1970-01-01 00:00:00 起经过的秒数。
|
||||||
|
* 算法:总天数(1970~去年整年 + 今年到上月 + 本月已过天)× 86400
|
||||||
|
* + 当天时分秒。
|
||||||
|
* 用 int64_t / uint64_t 累加避免中间溢出;最终结果(受 uint32_t 限制)
|
||||||
|
* 有效范围约 1970-01-01 ~ 2038-01-19,超出将回绕(见文档第 12 节)。 */
|
||||||
|
{
|
||||||
|
uint64_t total_days = 0;
|
||||||
|
|
||||||
|
/* 2.1 累加 1970 至(今年-1)的整年天数:闰年 366、平年 365
|
||||||
|
* 闰年规则:能被 4 整除但不能被 100 整除,或能被 400 整除 */
|
||||||
|
for (i = 1970; i < year; i++) {
|
||||||
|
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0)) {
|
||||||
|
total_days += 366uLL;
|
||||||
|
} else {
|
||||||
|
total_days += 365uLL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2.2 判断今年是否闰年,构造各月天数表(2 月特判) */
|
||||||
|
int is_leap = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
|
||||||
|
uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||||
|
if (is_leap) mdays[1] = 29; /* 闰年 2 月 29 天 */
|
||||||
|
|
||||||
|
/* 2.3 累加今年 1 月至(本月-1)月的天数 */
|
||||||
|
for (i = 0; i < month - 1; i++) {
|
||||||
|
total_days += mdays[i];
|
||||||
|
}
|
||||||
|
/* 2.4 加上本月已过天数(day-1:从今天 0 点起算) */
|
||||||
|
total_days += day - 1uL;
|
||||||
|
|
||||||
|
/* 2.5 总天数×86400(一天秒数)+ 当日时分秒 = 绝对 Unix 秒数 */
|
||||||
|
unix_secs = (int64_t)(total_days * 86400LL) + hour * 3600LL + minute * 60LL + second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Step 3:建立"墙钟基准"(基准法核心)
|
||||||
|
* ============================================================
|
||||||
|
* 记录当前 HAL Tick 与对应的 Unix 秒数。此后 sys_clock_get() 仅用
|
||||||
|
* now_unix = s_base_unix_secs + (HAL_GetTick() - s_base_tick)/1000
|
||||||
|
* 推算当前时间,运行期不再访问 I2C RTC,零阻塞、无累积误差。 */
|
||||||
|
s_base_tick = HAL_GetTick();
|
||||||
|
s_base_unix_secs = (uint32_t)unix_secs;
|
||||||
|
|
||||||
|
DBG_INFO("sys_clock_init: RTC initial time %04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
year, month, day, hour, minute, second);
|
||||||
|
|
||||||
|
DBG_INFO("sys_clock_init: wall clock base = unix %lu, base_tick=%lu",
|
||||||
|
(unsigned long)s_base_unix_secs, (unsigned long)s_base_tick);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取当前系统墙钟时间(Unix 时间戳)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:当前 Unix 秒数 uint32_t 未初始化时返回 UINT32_MAX
|
||||||
|
* 限定条件:sys_clock_init() 已成功调用
|
||||||
|
* 函数说明:1. 以基准 Tick 与基准 Unix 秒数为起点,按当前 HAL Tick 与 1000ms
|
||||||
|
* 的整除差推算流逝秒数(避免每毫秒累加,减少漂移与溢出风险)
|
||||||
|
* 2. 若推算结果为负(基准未建立),返回 UINT32_MAX 表示无效
|
||||||
|
*/
|
||||||
|
uint32_t sys_clock_get(void)
|
||||||
|
{
|
||||||
|
uint32_t now_tick = HAL_GetTick();
|
||||||
|
int64_t unix_secs = (int64_t)s_base_unix_secs + (int64_t)((uint32_t)(now_tick - s_base_tick) / 1000uL);
|
||||||
|
|
||||||
|
if (unix_secs < 0) {
|
||||||
|
return UINT32_MAX; /* 未初始化 */
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint32_t)unix_secs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取当前墙钟时间的可读字符串(YYYY-MM-DD HH:MM:SS)
|
||||||
|
* 入口参数:buf - 输出字符串缓冲区 char* 不得为 NULL
|
||||||
|
* buf_size - 缓冲区大小 size_t 须 ≥ 20(含结尾 '\0')
|
||||||
|
* 出口参数:buf - 填充格式化时间字符串
|
||||||
|
* 返回值:buf 指针(便于链式调用)
|
||||||
|
* 限定条件:buf 指向至少 buf_size 字节的有效缓冲区;sys_clock_init() 已调用
|
||||||
|
* 函数说明:1. 内部先取 Unix 秒数,未初始化时输出占位串 "----/--/-- --:--:--"
|
||||||
|
* 2. 通过 unix_to_sd2506() 转换为 SD2506 结构后格式化
|
||||||
|
* 3. 使用 snprintf 防止缓冲区溢出
|
||||||
|
*/
|
||||||
|
char *sys_clock_get_str(char *buf, size_t buf_size)
|
||||||
|
{
|
||||||
|
uint32_t unix_secs = sys_clock_get();
|
||||||
|
if (unix_secs == UINT32_MAX) {
|
||||||
|
snprintf(buf, buf_size, "----/--/-- --:--:--");
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd2506_time_t sd;
|
||||||
|
unix_to_sd2506(unix_secs, &sd);
|
||||||
|
|
||||||
|
snprintf(buf, buf_size, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
sd.year, sd.month, sd.day,
|
||||||
|
sd.hour, sd.minute, sd.second);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置(更新)系统墙钟时间基准,并可写回 SD2506 RTC
|
||||||
|
* 入口参数:unix_secs - 新的 Unix 时间戳 uint32_t 0~0x7FFFFFFF
|
||||||
|
* sd_time - 对应的 RTC 时间结构指针 sd2506_time_t* 可为 NULL(仅更新基准)
|
||||||
|
* 出口参数:无
|
||||||
|
* 返回值:0 - 成功(始终返回 0)
|
||||||
|
* 限定条件:unix_secs 为合法 Unix 时间戳
|
||||||
|
* 函数说明:1. 重置基准 Tick 与基准 Unix 秒数,使后续 sys_clock_get() 以此为准
|
||||||
|
* 2. 若 sd_time 非 NULL,将其写回 SD2506 RTC,实现断电保持
|
||||||
|
*/
|
||||||
|
int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time)
|
||||||
|
{
|
||||||
|
/* Step 1: 更新墙钟基准 */
|
||||||
|
s_base_tick = HAL_GetTick();
|
||||||
|
s_base_unix_secs = unix_secs;
|
||||||
|
|
||||||
|
DBG_INFO("sys_clock_set: wall clock updated to %lu", (unsigned long)unix_secs);
|
||||||
|
|
||||||
|
/* Step 2: 写回 SD2506 RTC(若提供了 sd_time) */
|
||||||
|
if (sd_time != NULL) {
|
||||||
|
if (sd2506_set_time(sd_time) == SD2506_OK) {
|
||||||
|
DBG_INFO("sys_clock_set: SD2506 RTC written");
|
||||||
|
} else {
|
||||||
|
DBG_ERROR("sys_clock_set: SD2506 RTC write FAIL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sys_clock_set_unix(uint32_t unix_secs)
|
||||||
|
{
|
||||||
|
sd2506_time_t sd;
|
||||||
|
|
||||||
|
/* 内部静态函数:将 Unix 秒数拆分为 SD2506 时间结构 */
|
||||||
|
unix_to_sd2506(unix_secs, &sd);
|
||||||
|
|
||||||
|
/* 更新运行期基准并写回 RTC,实现掉电保持 */
|
||||||
|
return sys_clock_set(unix_secs, &sd);
|
||||||
|
}
|
||||||
75
App/sys_clock.h
Normal file
75
App/sys_clock.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:System Clock — Wall Clock Time Maintenance
|
||||||
|
* 模块功能:基于 HAL Tick + SD2506 RTC 的系统墙钟时间维护
|
||||||
|
* 启动时从 SD2506 RTC 读取初始时间,运行期间以 HAL Tick 计数维持
|
||||||
|
* 适用平台:STM32F407ZGTx (Cortex-M4 FPU, 168MHz)
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __SYS_CLOCK_H
|
||||||
|
#define __SYS_CLOCK_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "sd2506.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:系统时钟初始化(从 SD2506 RTC 读取初始时间)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功, -1 - 失败
|
||||||
|
* 限定条件:sd2506_init() 已调用
|
||||||
|
* 函数说明:读取 SD2506 RTC 当前值转换为 Unix timestamp,设为系统墙钟初始时间
|
||||||
|
*/
|
||||||
|
int sys_clock_init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取当前系统墙钟时间(Unix epoch seconds)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:Unix time (秒),失败返回 UINT32_MAX
|
||||||
|
* 限定条件:sys_clock_init() 已调用
|
||||||
|
* 函数说明:基于 HAL Tick 计数 + 上次校准时刻的 Unix 秒数计算当前时间,
|
||||||
|
* 精度为毫秒级
|
||||||
|
*/
|
||||||
|
uint32_t sys_clock_get(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:格式化系统墙钟时间为字符串
|
||||||
|
* 入口参数:buf - 输出缓冲区(至少 20 字节)
|
||||||
|
* buf_size - 缓冲区大小
|
||||||
|
* 返回值:buf 指针,失败返回 NULL
|
||||||
|
* 限定条件:sys_clock_init() 已调用
|
||||||
|
* 函数说明:格式 "YYYY-MM-DD HH:MM:SS"
|
||||||
|
*/
|
||||||
|
char *sys_clock_get_str(char *buf, size_t buf_size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置系统墙钟时间
|
||||||
|
* 入口参数:unix_secs - Unix epoch time (秒)
|
||||||
|
* sd_time - SD2506 RTC 时间结构体(可选,非 NULL 时同时写入 RTC)
|
||||||
|
* 返回值:0 - 成功, -1 - 失败
|
||||||
|
* 限定条件:sys_clock_init() 已调用
|
||||||
|
* 函数说明:更新系统墙钟基准 + 写回 SD2506 RTC 保持长期准确性
|
||||||
|
*/
|
||||||
|
int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:以 Unix 时间戳设置系统墙钟并写回 SD2506 RTC
|
||||||
|
* 入口参数:unix_secs - Unix epoch time (秒,本地时间) uint32_t 0~0x7FFFFFFF
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:sys_clock_init() 已调用
|
||||||
|
* 函数说明:内部将 Unix 秒数转换为 sd2506_time_t 后调用 sys_clock_set(),
|
||||||
|
* 便于时间同步模块(如 PC 推送)直接以时间戳校时
|
||||||
|
*/
|
||||||
|
int sys_clock_set_unix(uint32_t unix_secs);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __SYS_CLOCK_H */
|
||||||
45
App/task/adc_task.c
Normal file
45
App/task/adc_task.c
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:ADC Sampling Task
|
||||||
|
* 模块功能:TPAFE5160 8 通道同步采样,数据缓存与通知
|
||||||
|
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "adc_task.h"
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
#include "tpafe5160.h"
|
||||||
|
|
||||||
|
#define DBG_TAG "[ADC_TASK]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:ADC 任务初始化
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:0 - 成功,-1 - 失败
|
||||||
|
* 限定条件:TPAFE5160 硬件已上电
|
||||||
|
* 函数说明:配置采样参数,准备数据缓冲区
|
||||||
|
*/
|
||||||
|
int adc_task_init(void)
|
||||||
|
{
|
||||||
|
DBG_INFO("adc_task_init");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:ADC 任务主函数
|
||||||
|
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||||||
|
* 返 回 值:无
|
||||||
|
* 限定条件:adc_task_init() 已成功调用
|
||||||
|
* 函数说明:定时触发 TPAFE5160 同步采样并处理数据
|
||||||
|
*/
|
||||||
|
void adc_task_func(void const *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
osDelay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
App/task/adc_task.h
Normal file
25
App/task/adc_task.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:ADC Sampling Task
|
||||||
|
* 模块功能:TPAFE5160 8 通道同步采样,数据缓存与通知
|
||||||
|
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ADC_TASK_H
|
||||||
|
#define __ADC_TASK_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int adc_task_init(void);
|
||||||
|
void adc_task_func(void const *arg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __ADC_TASK_H */
|
||||||
114
App/task/net_task.c
Normal file
114
App/task/net_task.c
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Application Task
|
||||||
|
* 模块功能:CH395F 协议栈初始化 + 轮询 + TCP/UDP 业务处理
|
||||||
|
* 与 net_socket.h 配合,在 FreeRTOS 独立任务中运行
|
||||||
|
* 适用平台:STM32F407ZGTx + CH395F
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
* v1.1 2026-07-19 王建锋 migrate from freertos.c StartNetTask
|
||||||
|
* v1.2 2026-07-21 王建锋 网络初始化移入本任务(net_init + TCP 监听)
|
||||||
|
* v1.3 2026-07-22 王建锋 单连接模式重构,增加大文件/边界/KeepAlive 测试
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "net_task.h"
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
#include "net_socket.h"
|
||||||
|
#include "net_select.h"
|
||||||
|
#include "ch395f.h"
|
||||||
|
|
||||||
|
#define DBG_TAG "[NET_TASK]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/* 网络初始化完成标志(供 ch395f_test_task 等外部任务等待) */
|
||||||
|
volatile uint8_t g_net_ready = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:CH395F 协议栈一次性初始化(两种构建模式共用)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:0 - 成功,-1 - 失败
|
||||||
|
* 函数说明:net_init → MAC 打印 → PHY 强制 100M 全双工 → 置 g_net_ready。
|
||||||
|
* NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)netTask 不创建,
|
||||||
|
* 由 ch395fTestTask 调用后独占 CH395F。
|
||||||
|
*/
|
||||||
|
int net_stack_bring_up(void)
|
||||||
|
{
|
||||||
|
DBG_INFO("=== Network Init ===");
|
||||||
|
|
||||||
|
if (net_init("192.168.1.100", "255.255.255.0", "192.168.1.1") != 0) {
|
||||||
|
DBG_ERROR("net init: FAIL");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
DBG_INFO("net init: OK");
|
||||||
|
|
||||||
|
uint8_t mac[6];
|
||||||
|
ch395f_get_mac_addr(mac);
|
||||||
|
DBG_INFO("MAC: %02X:%02X:%02X:%02X:%02X:%02X",
|
||||||
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
uint8_t ipinf[20];
|
||||||
|
ch395f_get_ip_inf(ipinf);
|
||||||
|
DBG_INFO("IP_INF: IP=%d.%d.%d.%d GW=%d.%d.%d.%d MASK=%d.%d.%d.%d",
|
||||||
|
ipinf[0], ipinf[1], ipinf[2], ipinf[3],
|
||||||
|
ipinf[4], ipinf[5], ipinf[6], ipinf[7],
|
||||||
|
ipinf[8], ipinf[9], ipinf[10], ipinf[11]);
|
||||||
|
|
||||||
|
ch395f_set_phy(CH395F_PHY_100M_FULL);
|
||||||
|
HAL_Delay(3000);
|
||||||
|
uint8_t phy = ch395f_get_phy_status();
|
||||||
|
DBG_INFO("PHY: %s",
|
||||||
|
(phy == CH395F_PHY_100M_FULL) ? "100M FULL" :
|
||||||
|
(phy == CH395F_PHY_100M_HALF) ? "100M HALF" :
|
||||||
|
(phy == CH395F_PHY_10M_FULL) ? "10M FULL" :
|
||||||
|
(phy == CH395F_PHY_10M_HALF) ? "10M HALF" :
|
||||||
|
(phy == CH395F_PHY_DISCONN) ? "DISCONNECT" : "UNKNOWN");
|
||||||
|
|
||||||
|
g_net_ready = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:网络任务初始化
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:0 - 成功,-1 - 失败
|
||||||
|
* 限定条件:CH395F 协议栈已初始化
|
||||||
|
* 函数说明:创建 TCP Server 监听 Socket,注册回调等
|
||||||
|
*/
|
||||||
|
int net_task_init(void)
|
||||||
|
{
|
||||||
|
DBG_INFO("net_task_init");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:网络任务主函数
|
||||||
|
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||||||
|
* 返 回 值:无
|
||||||
|
* 函数说明:启动后先调用 net_stack_bring_up() 初始化 CH395F 协议栈,
|
||||||
|
* 再进入轮询循环处理网络事件。
|
||||||
|
* 仅 NET 层测试模式(阶段 6~10)会创建本任务;
|
||||||
|
* 硬件层模式下 netTask 不创建,CH395F 由 ch395fTestTask 独占。
|
||||||
|
*/
|
||||||
|
void net_task_func(void const *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
osDelay(2000);
|
||||||
|
|
||||||
|
if (net_stack_bring_up() != 0) {
|
||||||
|
for (;;) {
|
||||||
|
osDelay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_INFO("netTask: started (single-socket mode)");
|
||||||
|
|
||||||
|
/* ==================== 主轮询循环 ==================== */
|
||||||
|
for (;;) {
|
||||||
|
net_poll();
|
||||||
|
net_process_messages();
|
||||||
|
osDelay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
App/task/net_task.h
Normal file
31
App/task/net_task.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Application Task
|
||||||
|
* 模块功能:网络协议栈初始化、轮询、TCP/UDP 业务处理
|
||||||
|
* 适用平台:STM32F407ZGTx + CH395F
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
* v1.1 2026-08-24 王建锋 提取 net_stack_bring_up() 供硬件层/NET 层两种测试模式共用
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_TASK_H
|
||||||
|
#define __NET_TASK_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* CH395F 协议栈一次性初始化(net_init + MAC 打印 + PHY 配置 + g_net_ready 置位)。
|
||||||
|
* 返回 0 成功 / -1 失败。NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)
|
||||||
|
* netTask 不创建,由 ch395fTestTask 调用后独占 CH395F。 */
|
||||||
|
int net_stack_bring_up(void);
|
||||||
|
|
||||||
|
int net_task_init(void);
|
||||||
|
void net_task_func(void const *arg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NET_TASK_H */
|
||||||
45
App/task/rs485_task.c
Normal file
45
App/task/rs485_task.c
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:RS485 Communication Task
|
||||||
|
* 模块功能:RS-485 轮询收发、协议帧解析与响应
|
||||||
|
* 适用平台:STM32F407ZGTx
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rs485_task.h"
|
||||||
|
#include "cmsis_os.h"
|
||||||
|
#include "rs485.h"
|
||||||
|
|
||||||
|
#define DBG_TAG "[RS485_TASK]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:RS485 任务初始化
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:0 - 成功,-1 - 失败
|
||||||
|
* 限定条件:UART 和 GPIO 已由 CubeMX 初始化
|
||||||
|
* 函数说明:配置 RS-485 通信参数,注册接收回调
|
||||||
|
*/
|
||||||
|
int rs485_task_init(void)
|
||||||
|
{
|
||||||
|
DBG_INFO("rs485_task_init");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:RS485 任务主函数
|
||||||
|
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||||||
|
* 返 回 值:无
|
||||||
|
* 限定条件:rs485_task_init() 已成功调用
|
||||||
|
* 函数说明:轮询接收缓冲区并解析协议帧
|
||||||
|
*/
|
||||||
|
void rs485_task_func(void const *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
osDelay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
App/task/rs485_task.h
Normal file
25
App/task/rs485_task.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:RS485 Communication Task
|
||||||
|
* 模块功能:RS-485 数据收发与协议解析
|
||||||
|
* 适用平台:STM32F407ZGTx
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RS485_TASK_H
|
||||||
|
#define __RS485_TASK_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int rs485_task_init(void);
|
||||||
|
void rs485_task_func(void const *arg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __RS485_TASK_H */
|
||||||
138
App/time_sync.c
Normal file
138
App/time_sync.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Time Sync — PC 本地时间推送接收(TCP)
|
||||||
|
* 模块功能:在独立任务 timeSyncTask 中以 TCP Server 方式监听端口,接收 PC 推送的本地时间包,
|
||||||
|
* 调用 sys_clock_set_unix() 更新系统墙钟并写回 SD2506 RTC
|
||||||
|
* 适用平台:STM32F407ZGTx + CH395F
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-08-27
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-08-27 王建锋 创建初始版本(UDP 推送,netTask 内轮询)
|
||||||
|
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
|
||||||
|
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
|
||||||
|
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "time_sync.h"
|
||||||
|
#include "net_socket.h"
|
||||||
|
#include "sys_clock.h"
|
||||||
|
|
||||||
|
#define DBG_TAG "[TIME_SYNC]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/* 监听端口:PC 推送端(TCP 客户端)需连到本端口 */
|
||||||
|
#define TIME_SYNC_PORT 8888
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:创建并监听 TCP 时间同步服务端口
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:监听 Socket 描述符(>=0),失败返回 -1
|
||||||
|
* 限定条件:net_init() 已成功;可在非 netTask 上下文调用
|
||||||
|
* 函数说明:net_socket/net_bind/net_listen 均为线程安全 API,内部经消息队列由 netTask 串行处理。
|
||||||
|
* 单连接模式下监听 Socket 在收到连接后会转为数据通道,连接关闭后即被释放,
|
||||||
|
* 因此每次调用都会新建监听 Socket(与 lftpd 同模式),失败自动重试。
|
||||||
|
*/
|
||||||
|
static int time_sync_open_listener(void)
|
||||||
|
{
|
||||||
|
int retry;
|
||||||
|
int s = -1;
|
||||||
|
|
||||||
|
for (retry = 0; retry < 5; retry++) {
|
||||||
|
s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||||
|
if (s < 0) {
|
||||||
|
DBG_ERROR("time_sync: net_socket FAIL");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct net_sockaddr_in addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = NET_AF_INET;
|
||||||
|
addr.sin_port = net_htons(TIME_SYNC_PORT);
|
||||||
|
addr.sin_addr.s_addr = NET_INADDR_ANY;
|
||||||
|
|
||||||
|
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||||
|
DBG_ERROR("time_sync: bind port %d FAIL", TIME_SYNC_PORT);
|
||||||
|
net_close(s);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (net_listen(s) != 0) {
|
||||||
|
DBG_ERROR("time_sync: listen FAIL");
|
||||||
|
net_close(s);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_INFO("time_sync: TCP listen on :%d (sock=%d)", TIME_SYNC_PORT, s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:处理一个已建立的时间同步连接
|
||||||
|
* 入口参数:conn_sock - 已建立的连接 Socket 描述符(单连接模式下即监听 Socket 自身)
|
||||||
|
* 返回值:无
|
||||||
|
* 函数说明:阻塞读取 PC 发来的时间包("TIME"+uint32 大端 本地时间),解析后写回 RTC,然后关闭连接
|
||||||
|
*/
|
||||||
|
static void time_sync_handle_conn(int conn_sock)
|
||||||
|
{
|
||||||
|
uint8_t buf[16];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = net_recv(conn_sock, buf, sizeof(buf), 0);
|
||||||
|
if (len >= 8) {
|
||||||
|
if (buf[0] == 'T' && buf[1] == 'I' && buf[2] == 'M' && buf[3] == 'E') {
|
||||||
|
uint32_t epoch = ((uint32_t)buf[4] << 24) |
|
||||||
|
((uint32_t)buf[5] << 16) |
|
||||||
|
((uint32_t)buf[6] << 8) |
|
||||||
|
((uint32_t)buf[7]);
|
||||||
|
sys_clock_set_unix(epoch);
|
||||||
|
DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
net_close(conn_sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
|
||||||
|
* 入口参数:argument - 未使用
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:net_init() 已由 netTask 完成;netTask 正在处理消息队列
|
||||||
|
* 函数说明:等待网络栈就绪后,循环:建监听 -> 等连接 -> 处理 -> 关闭 -> 重建监听。
|
||||||
|
* 全部 socket 操作经消息队列由 netTask 串行执行,与本任务/其它网络任务并发安全。
|
||||||
|
*/
|
||||||
|
void timeSyncTask(void *argument)
|
||||||
|
{
|
||||||
|
(void)argument;
|
||||||
|
|
||||||
|
/* 等网络栈起来(与 StartFtpTask 的 7s 延迟同风格,确保 netTask 已开始处理消息) */
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(8000));
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int ls = time_sync_open_listener();
|
||||||
|
if (ls < 0) {
|
||||||
|
DBG_ERROR("time_sync: open listener FAIL, retry later");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 等待 PC 连接:net_accept 在无连接时立即返回 -1,循环重试同一监听 Socket */
|
||||||
|
int conn = -1;
|
||||||
|
do {
|
||||||
|
conn = net_accept(ls, NULL, NULL);
|
||||||
|
if (conn < 0) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
} while (conn < 0);
|
||||||
|
|
||||||
|
time_sync_handle_conn(conn);
|
||||||
|
/* conn(单连接模式下即 ls)已被 net_close 释放,下次循环重建监听 */
|
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(50));
|
||||||
|
}
|
||||||
|
}
|
||||||
40
App/time_sync.h
Normal file
40
App/time_sync.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Time Sync — PC 本地时间推送接收(TCP)
|
||||||
|
* 模块功能:以 TCP Server 方式监听 TIME_SYNC_PORT,接收 PC 推送的本地时间包,
|
||||||
|
* 更新系统墙钟并写回 SD2506 RTC
|
||||||
|
* 适用平台:STM32F407ZGTx + CH395F
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-08-27
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-08-27 王建锋 创建初始版本(UDP 推送)
|
||||||
|
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __TIME_SYNC_H
|
||||||
|
#define __TIME_SYNC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 监听端口:PC 推送端(TCP 客户端)需连到本端口 */
|
||||||
|
#define TIME_SYNC_PORT 8888
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
|
||||||
|
* 入口参数:argument - FreeRTOS 传入参数(未使用)
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:net_init() 已由 netTask 完成;网络消息队列由 netTask 处理
|
||||||
|
* 函数说明:以 TCP Server 方式监听 TIME_SYNC_PORT,接收 PC 推送的本地时间包,
|
||||||
|
* 经消息队列(由 netTask 串行执行)完成 socket 操作,更新系统墙钟。
|
||||||
|
* 该任务与 FTP 等其它网络任务并发安全。
|
||||||
|
*/
|
||||||
|
void timeSyncTask(void *argument);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __TIME_SYNC_H */
|
||||||
122
App/util/crc.c
Normal file
122
App/util/crc.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:CRC Utility
|
||||||
|
* 模块功能:CRC-16 Modbus (poly=0x8005, init=0xFFFF, refin=true, refout=true, xorout=0x0000)
|
||||||
|
* CRC-8 (poly=0x07, init=0x00, refin=false, refout=false, xorout=0x00)
|
||||||
|
* 适用平台:通用嵌入式平台
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "crc.h"
|
||||||
|
|
||||||
|
static const uint16_t s_crc16_table[256] = {
|
||||||
|
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||||
|
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||||
|
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
||||||
|
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
||||||
|
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
||||||
|
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
||||||
|
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
||||||
|
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
||||||
|
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||||
|
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
||||||
|
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
||||||
|
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
||||||
|
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
||||||
|
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
||||||
|
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
||||||
|
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
||||||
|
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
||||||
|
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||||
|
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
||||||
|
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
||||||
|
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
||||||
|
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
||||||
|
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
||||||
|
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
||||||
|
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
||||||
|
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
||||||
|
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||||
|
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
||||||
|
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
||||||
|
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
||||||
|
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
||||||
|
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t s_crc8_table[256] = {
|
||||||
|
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
|
||||||
|
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
||||||
|
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
|
||||||
|
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
|
||||||
|
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
|
||||||
|
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
|
||||||
|
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
|
||||||
|
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
|
||||||
|
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
|
||||||
|
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
|
||||||
|
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
|
||||||
|
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
|
||||||
|
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
|
||||||
|
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
|
||||||
|
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
|
||||||
|
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
|
||||||
|
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
|
||||||
|
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
|
||||||
|
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
|
||||||
|
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
|
||||||
|
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
|
||||||
|
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
|
||||||
|
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
|
||||||
|
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
|
||||||
|
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
|
||||||
|
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
|
||||||
|
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
|
||||||
|
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
|
||||||
|
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
|
||||||
|
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
|
||||||
|
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
|
||||||
|
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:计算 CRC-16 Modbus
|
||||||
|
* 入口参数:data - 数据指针
|
||||||
|
* len - 数据长度
|
||||||
|
* 返 回 值:16 位 CRC 值
|
||||||
|
* 限定条件:data 为非空指针
|
||||||
|
* 函数说明:多项式 0x8005,初始值 0xFFFF,结果异或 0x0000
|
||||||
|
*/
|
||||||
|
uint16_t crc16_modbus(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
crc = (crc >> 8) ^ s_crc16_table[(crc ^ data[i]) & 0xFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:计算 CRC-8
|
||||||
|
* 入口参数:data - 数据指针
|
||||||
|
* len - 数据长度
|
||||||
|
* 返 回 值:8 位 CRC 值
|
||||||
|
* 限定条件:data 为非空指针
|
||||||
|
* 函数说明:多项式 0x07,初始值 0x00
|
||||||
|
*/
|
||||||
|
uint8_t crc8(const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t crc = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
crc = s_crc8_table[crc ^ data[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
28
App/util/crc.h
Normal file
28
App/util/crc.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:CRC Utility
|
||||||
|
* 模块功能:CRC-16 Modbus & CRC-8 查表计算
|
||||||
|
* 适用平台:通用嵌入式平台
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CRC_H
|
||||||
|
#define __CRC_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
uint16_t crc16_modbus(const uint8_t *data, size_t len);
|
||||||
|
uint8_t crc8(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __CRC_H */
|
||||||
164
App/util/ringbuf.c
Normal file
164
App/util/ringbuf.c
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Ring Buffer
|
||||||
|
* 模块功能:字节环形缓冲区实现,head == tail 为空,head + 1 == tail 为满
|
||||||
|
* 适用平台:通用嵌入式平台
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ringbuf.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:初始化环形缓冲区
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* pool - 存储池指针
|
||||||
|
* size - 存储池大小(字节)
|
||||||
|
* 返 回 值:无
|
||||||
|
* 限定条件:rb 和 pool 均为非空指针
|
||||||
|
* 函数说明:size 保留一个空位用于区分空/满状态
|
||||||
|
*/
|
||||||
|
void ringbuf_init(ringbuf_t *rb, uint8_t *pool, size_t size)
|
||||||
|
{
|
||||||
|
rb->buf = pool;
|
||||||
|
rb->size = size;
|
||||||
|
rb->head = 0;
|
||||||
|
rb->tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:写入数据到环形缓冲区
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* data - 源数据指针
|
||||||
|
* len - 待写入长度
|
||||||
|
* 返 回 值:实际写入字节数
|
||||||
|
* 限定条件:rb 和 data 均为非空指针
|
||||||
|
* 函数说明:空间不足时写入能容纳的最大字节数
|
||||||
|
*/
|
||||||
|
size_t ringbuf_put(ringbuf_t *rb, const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
size_t cap = 0;
|
||||||
|
size_t n = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
cap = rb->size - 1 - ringbuf_avail(rb);
|
||||||
|
n = (len < cap) ? len : cap;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
rb->buf[rb->head] = data[i];
|
||||||
|
rb->head = (rb->head + 1) % rb->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:从环形缓冲区读取数据
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* data - 目标数据指针
|
||||||
|
* len - 期望读取长度
|
||||||
|
* 返 回 值:实际读取字节数
|
||||||
|
* 限定条件:rb 和 data 均为非空指针
|
||||||
|
* 函数说明:读取后数据从缓冲区移除
|
||||||
|
*/
|
||||||
|
size_t ringbuf_get(ringbuf_t *rb, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
size_t n = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
n = ringbuf_avail(rb);
|
||||||
|
n = (n < len) ? n : len;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
data[i] = rb->buf[rb->tail];
|
||||||
|
rb->tail = (rb->tail + 1) % rb->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:预读取数据(不移除)
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* data - 目标数据指针
|
||||||
|
* len - 期望读取长度
|
||||||
|
* 返 回 值:实际可读取字节数
|
||||||
|
* 限定条件:rb 和 data 均为非空指针
|
||||||
|
* 函数说明:与 ringbuf_get 的区别是不移动 tail 指针
|
||||||
|
*/
|
||||||
|
size_t ringbuf_peek(const ringbuf_t *rb, uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
size_t n = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
n = ringbuf_avail(rb);
|
||||||
|
n = (n < len) ? n : len;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
idx = (rb->tail + i) % rb->size;
|
||||||
|
data[i] = rb->buf[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取缓冲区中有效数据长度
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* 返 回 值:有效数据字节数
|
||||||
|
* 限定条件:rb 为非空指针
|
||||||
|
*/
|
||||||
|
size_t ringbuf_avail(const ringbuf_t *rb)
|
||||||
|
{
|
||||||
|
if (rb->head >= rb->tail) {
|
||||||
|
return rb->head - rb->tail;
|
||||||
|
}
|
||||||
|
return rb->size - rb->tail + rb->head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取缓冲区剩余空间
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* 返 回 值:剩余空间字节数(保留一个空位)
|
||||||
|
* 限定条件:rb 为非空指针
|
||||||
|
*/
|
||||||
|
size_t ringbuf_space(const ringbuf_t *rb)
|
||||||
|
{
|
||||||
|
return rb->size - 1 - ringbuf_avail(rb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:判断缓冲区是否为空
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* 返 回 值:1 - 空,0 - 非空
|
||||||
|
* 限定条件:rb 为非空指针
|
||||||
|
*/
|
||||||
|
int ringbuf_is_empty(const ringbuf_t *rb)
|
||||||
|
{
|
||||||
|
return (rb->head == rb->tail) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:判断缓冲区是否为满
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* 返 回 值:1 - 满,0 - 非满
|
||||||
|
* 限定条件:rb 为非空指针
|
||||||
|
*/
|
||||||
|
int ringbuf_is_full(const ringbuf_t *rb)
|
||||||
|
{
|
||||||
|
return (ringbuf_avail(rb) == rb->size - 1) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:重置缓冲区
|
||||||
|
* 入口参数:rb - 缓冲区控制块指针
|
||||||
|
* 返 回 值:无
|
||||||
|
* 限定条件:rb 为非空指针
|
||||||
|
*/
|
||||||
|
void ringbuf_reset(ringbuf_t *rb)
|
||||||
|
{
|
||||||
|
rb->head = 0;
|
||||||
|
rb->tail = 0;
|
||||||
|
}
|
||||||
42
App/util/ringbuf.h
Normal file
42
App/util/ringbuf.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Ring Buffer
|
||||||
|
* 模块功能:线程安全的字节环形缓冲区(单生产者-单消费者模型)
|
||||||
|
* 适用平台:通用嵌入式平台
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-19
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __RINGBUF_H
|
||||||
|
#define __RINGBUF_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *buf;
|
||||||
|
size_t size;
|
||||||
|
size_t head;
|
||||||
|
size_t tail;
|
||||||
|
} ringbuf_t;
|
||||||
|
|
||||||
|
void ringbuf_init(ringbuf_t *rb, uint8_t *pool, size_t size);
|
||||||
|
size_t ringbuf_put(ringbuf_t *rb, const uint8_t *data, size_t len);
|
||||||
|
size_t ringbuf_get(ringbuf_t *rb, uint8_t *data, size_t len);
|
||||||
|
size_t ringbuf_peek(const ringbuf_t *rb, uint8_t *data, size_t len);
|
||||||
|
size_t ringbuf_avail(const ringbuf_t *rb);
|
||||||
|
size_t ringbuf_space(const ringbuf_t *rb);
|
||||||
|
int ringbuf_is_empty(const ringbuf_t *rb);
|
||||||
|
int ringbuf_is_full(const ringbuf_t *rb);
|
||||||
|
void ringbuf_reset(ringbuf_t *rb);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __RINGBUF_H */
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@ extern "C" {
|
|||||||
#define CH395F_CMD_GET_CMD_STATUS 0x2CU
|
#define CH395F_CMD_GET_CMD_STATUS 0x2CU
|
||||||
#define CH395F_CMD_GET_GLOB_INT_STATUS 0x29U
|
#define CH395F_CMD_GET_GLOB_INT_STATUS 0x29U
|
||||||
#define CH395F_CMD_GET_GLOB_INT_STATUS_ALL 0x19U
|
#define CH395F_CMD_GET_GLOB_INT_STATUS_ALL 0x19U
|
||||||
|
#define CH395F_CMD_GET_UNREACH_IPPT 0x28U
|
||||||
#define CH395F_CMD_GET_PHY_STATUS 0x26U
|
#define CH395F_CMD_GET_PHY_STATUS 0x26U
|
||||||
#define CH395F_CMD_GET_DHCP_STATUS 0x42U
|
#define CH395F_CMD_GET_DHCP_STATUS 0x42U
|
||||||
|
|
||||||
@@ -109,6 +110,33 @@ extern "C" {
|
|||||||
/* 3 字节输入 + 数据输入(socket + 长度 + 数据) */
|
/* 3 字节输入 + 数据输入(socket + 长度 + 数据) */
|
||||||
#define CH395F_CMD_WRITE_SEND_BUF_SN 0x39U
|
#define CH395F_CMD_WRITE_SEND_BUF_SN 0x39U
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TCP MSS(CH395F_CMD_SET_TCP_MSS 参数)
|
||||||
|
* 由 net_init 经 ch395f_set_tcp_mss() 下发;此处作为单一真值源,
|
||||||
|
* DMA 缓冲大小亦由此推导(见下)。当前选取依据:8 Socket 共享 24KB 缓冲时,
|
||||||
|
* 各 Socket 接收缓冲 = 2×MSS,故 MSS 取 1024 为最大值(详见 net_socket.c 分配注释)。
|
||||||
|
*/
|
||||||
|
#define CH395F_TCP_MSS 1024U
|
||||||
|
|
||||||
|
/* 内部缓冲 RAM 每块字节数(手册 §5.45:48 块 × 512B = 24KB) */
|
||||||
|
#define CH395F_RAM_BLOCK_SIZE 512U
|
||||||
|
/* 单 Socket 缓冲块数由 MSS 推导:接收 = 2×MSS(满足手册 §9.2.8 接收 ≥ 2×MSS),发送 = 1×MSS;向上取整 */
|
||||||
|
#define CH395F_RECV_BLOCKS ((2U * CH395F_TCP_MSS + CH395F_RAM_BLOCK_SIZE - 1U) / CH395F_RAM_BLOCK_SIZE)
|
||||||
|
#define CH395F_SEND_BLOCKS ((CH395F_TCP_MSS + CH395F_RAM_BLOCK_SIZE - 1U) / CH395F_RAM_BLOCK_SIZE)
|
||||||
|
/* 单 Socket 硬件收发缓冲字节数(= 块数 × 块大小)。写入发送 FIFO 的单次长度严禁超过此值,
|
||||||
|
* 否则会写穿有限的发送缓冲(见 Trap 20)。 */
|
||||||
|
#define CH395F_SEND_BUF_SIZE_BYTES (CH395F_SEND_BLOCKS * CH395F_RAM_BLOCK_SIZE)
|
||||||
|
#define CH395F_RECV_BUF_SIZE_BYTES (CH395F_RECV_BLOCKS * CH395F_RAM_BLOCK_SIZE)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SPI DMA 事务限制
|
||||||
|
* CH395F_SPI_DMA_MAX_PAYLOAD: 单次 DMA 事务最大有效载荷(总缓冲 - 4 字节命令头)
|
||||||
|
* 接收方向需一次性读空整个 Socket 接收缓冲(= 2×MSS,避免 Trap 13 部分读导致数据不可见),
|
||||||
|
* 故总缓冲 = 2×MSS + 4(4 为命令头开销),有效载荷 = 2×MSS。
|
||||||
|
*/
|
||||||
|
#define CH395F_SPI_DMA_BUF_SIZE (CH395F_TCP_MSS * 2U + 4U)
|
||||||
|
#define CH395F_SPI_DMA_MAX_PAYLOAD (CH395F_SPI_DMA_BUF_SIZE - 4U)
|
||||||
|
|
||||||
/* 其他命令 */
|
/* 其他命令 */
|
||||||
#define CH395F_CMD_SET_TCP_MSS 0x50U
|
#define CH395F_CMD_SET_TCP_MSS 0x50U
|
||||||
#define CH395F_CMD_SET_RECV_BUF 0x52U
|
#define CH395F_CMD_SET_RECV_BUF 0x52U
|
||||||
@@ -124,19 +152,26 @@ extern "C" {
|
|||||||
#define CH395F_CMD_WRITE_GPIO_REG 0xEDU
|
#define CH395F_CMD_WRITE_GPIO_REG 0xEDU
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CH395 错误码(来自手册 CH395INC.H 引用)
|
* CH395 命令执行状态码(CMD_GET_CMD_STATUS 返回值,与芯片手册一致)
|
||||||
*/
|
*/
|
||||||
#define CH395F_ERR_SUCCESS 0x00U
|
#define CH395F_ERR_SUCCESS 0x00U /* 成功 */
|
||||||
#define CH395F_ERR_BUSY 0x01U
|
#define CH395F_ERR_BUSY 0x10U /* 忙,命令正在执行 */
|
||||||
#define CH395F_ERR_CMD 0x02U
|
#define CH395F_ERR_MEM 0x11U /* 内存管理错误 */
|
||||||
#define CH395F_ERR_MAC 0x03U
|
#define CH395F_ERR_BUF 0x12U /* 缓冲区错误 */
|
||||||
#define CH395F_ERR_PHY 0x04U
|
#define CH395F_ERR_TIMEOUT 0x13U /* 超时 */
|
||||||
#define CH395F_ERR_IP_CONFLI 0x05U
|
#define CH395F_ERR_RTE 0x14U /* 路由错误 */
|
||||||
#define CH395F_ERR_SOCK 0x06U
|
#define CH395F_ERR_ABRT 0x15U /* 连接中止 */
|
||||||
#define CH395F_ERR_SOCK_BUSY 0x07U
|
#define CH395F_ERR_RST 0x16U /* 连接复位 */
|
||||||
#define CH395F_ERR_SOCK_CLOSED 0x08U
|
#define CH395F_ERR_CLSD 0x17U /* 连接关闭 */
|
||||||
#define CH395F_ERR_SOCK_ERR 0x09U
|
#define CH395F_ERR_CONN 0x18U /* 无连接 */
|
||||||
#define CH395F_ERR_UNKNOW 0xFFU
|
#define CH395F_ERR_VAL 0x19U /* 值错误 */
|
||||||
|
#define CH395F_ERR_ARG 0x1AH /* 参数错误 */
|
||||||
|
#define CH395F_ERR_USE 0x1BH /* 已被使用 */
|
||||||
|
#define CH395F_ERR_IF 0x1CH /* MAC 错误 */
|
||||||
|
#define CH395F_ERR_ISCONN 0x1DH /* 已连接 */
|
||||||
|
#define CH395F_ERR_OPEN 0x20U /* 已打开 */
|
||||||
|
#define CH395F_CMD_RET_ABORT 0x5FU /* 命令中止(来自官方库) */
|
||||||
|
#define CH395F_ERR_UNKNOW 0xFAU /* 未知错误」 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PHY 状态码
|
* PHY 状态码
|
||||||
@@ -148,6 +183,13 @@ extern "C" {
|
|||||||
#define CH395F_PHY_100M_HALF 0x10U /* 100M 半双工 */
|
#define CH395F_PHY_100M_HALF 0x10U /* 100M 半双工 */
|
||||||
#define CH395F_PHY_AUTO_NEGOTIATE 0x20U /* 自动协商 */
|
#define CH395F_PHY_AUTO_NEGOTIATE 0x20U /* 自动协商 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 功能参数标志位(CMD_SET_FUN_PARA)
|
||||||
|
*/
|
||||||
|
#define CH395F_FUN_PARA_TCP_SERVER (1UL << 1) /* TCP Server 多连接模式 */
|
||||||
|
#define CH395F_FUN_PARA_SOCKET_CLOSE (1UL << 3) /* Socket 关闭控制(0=芯片自动,1=外部控制) */
|
||||||
|
#define CH395F_FUN_PARA_DISABLE_SEND_OK (1UL << 4) /* 禁用 SEND_OK 中断 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 协议类型
|
* 协议类型
|
||||||
*/
|
*/
|
||||||
@@ -178,7 +220,7 @@ extern "C" {
|
|||||||
#define CH395F_TCP_TIME_WAIT 0x0AU
|
#define CH395F_TCP_TIME_WAIT 0x0AU
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 全局中断状态位
|
* 全局中断状态位(CMD_GET_GLOB_INT_STATUS 1字节版,仅Socket 0~3)
|
||||||
*/
|
*/
|
||||||
#define CH395F_GINT_STAT_UNREACH 0x01U
|
#define CH395F_GINT_STAT_UNREACH 0x01U
|
||||||
#define CH395F_GINT_STAT_IP_CONFLI 0x02U
|
#define CH395F_GINT_STAT_IP_CONFLI 0x02U
|
||||||
@@ -190,15 +232,25 @@ extern "C" {
|
|||||||
#define CH395F_GINT_STAT_SOCK3 0x80U
|
#define CH395F_GINT_STAT_SOCK3 0x80U
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Socket 中断状态位
|
* 全局中断状态位(CMD_GET_GLOB_INT_STATUS_ALL 2字节版,支持Socket 0~7)
|
||||||
|
* 低字节高字节分别定义
|
||||||
*/
|
*/
|
||||||
#define CH395F_SINT_STAT_CONNECT 0x01U
|
#define CH395F_GINT_STAT_SOCK4 0x0100U
|
||||||
#define CH395F_SINT_STAT_DISCONNECT 0x02U
|
#define CH395F_GINT_STAT_SOCK5 0x0200U
|
||||||
#define CH395F_SINT_STAT_SEND_OK 0x04U
|
#define CH395F_GINT_STAT_SOCK6 0x0400U
|
||||||
#define CH395F_SINT_STAT_SENBUF_FREE 0x08U
|
#define CH395F_GINT_STAT_SOCK7 0x0800U
|
||||||
#define CH395F_SINT_STAT_RECV_OK 0x10U
|
#define CH395F_GINT_STAT_DHCPV6 0x8000U
|
||||||
#define CH395F_SINT_STAT_DISCARD 0x20U
|
|
||||||
#define CH395F_SINT_STAT_SOCK_TIMEOUT 0x40U
|
/*
|
||||||
|
* Socket 中断状态位(CMD_GET_INT_STATUS_SN 返回值,来自手册表5-x)
|
||||||
|
*/
|
||||||
|
#define CH395F_SINT_STAT_SENBUF_FREE 0x01U /* bit0: 发送缓冲区空闲 */
|
||||||
|
#define CH395F_SINT_STAT_SEND_OK 0x02U /* bit1: 发送成功 */
|
||||||
|
#define CH395F_SINT_STAT_RECV_OK 0x04U /* bit2: 接收缓冲区非空 */
|
||||||
|
#define CH395F_SINT_STAT_CONNECT 0x08U /* bit3: TCP 连接成功 */
|
||||||
|
#define CH395F_SINT_STAT_DISCONNECT 0x10U /* bit4: TCP 连接断开 */
|
||||||
|
#define CH395F_SINT_STAT_DISCARD 0x20U /* bit5: 保留 */
|
||||||
|
#define CH395F_SINT_STAT_SOCK_TIMEOUT 0x40U /* bit6: 超时 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 类型定义区 - 驱动返回码
|
* 类型定义区 - 驱动返回码
|
||||||
@@ -277,6 +329,15 @@ ch395f_status_t ch395f_reset(void);
|
|||||||
*/
|
*/
|
||||||
uint8_t ch395f_get_cmd_status(void);
|
uint8_t ch395f_get_cmd_status(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:轮询等待命令执行完成
|
||||||
|
* 入口参数:timeout_ms - 超时时间(毫秒) uint32_t
|
||||||
|
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 或 CH395F_CMD_RET_ABORT)
|
||||||
|
* 限定条件:在发送长执行命令(OPEN/TCP_LISTEN/CONNECT/DISCONNECT/CLOSE/DHCP)后调用
|
||||||
|
* 函数说明:每 20ms 查询一次,直到命令完成或超时
|
||||||
|
*/
|
||||||
|
uint8_t ch395f_poll_cmd_status(uint32_t timeout_ms);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:初始化 CH395(包含 MAC、PHY、TCP/IP 协议栈)
|
* 函数功能:初始化 CH395(包含 MAC、PHY、TCP/IP 协议栈)
|
||||||
* 返回值:CH395F_STATUS_OK - 成功,CH395F_STATUS_TIMEOUT - 超时失败
|
* 返回值:CH395F_STATUS_OK - 成功,CH395F_STATUS_TIMEOUT - 超时失败
|
||||||
@@ -382,7 +443,7 @@ uint8_t ch395f_get_dhcp_status(void);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置 Socket 协议类型
|
* 函数功能:设置 Socket 协议类型
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* proto - 协议类型 uint8_t CH395F_PROTO_TYPE_xxx
|
* proto - 协议类型 uint8_t CH395F_PROTO_TYPE_xxx
|
||||||
* 限定条件:Socket 未打开
|
* 限定条件:Socket 未打开
|
||||||
*/
|
*/
|
||||||
@@ -390,7 +451,7 @@ void ch395f_set_proto_type(uint8_t sock, uint8_t proto);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置 Socket 目标 IP 地址
|
* 函数功能:设置 Socket 目标 IP 地址
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* p_ip - 4 字节目标 IP 指针 uint8_t*
|
* p_ip - 4 字节目标 IP 指针 uint8_t*
|
||||||
* 限定条件:指针非空
|
* 限定条件:指针非空
|
||||||
*/
|
*/
|
||||||
@@ -398,7 +459,7 @@ void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置 Socket 目标端口(小端序)
|
* 函数功能:设置 Socket 目标端口(小端序)
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* port - 目标端口 uint16_t
|
* port - 目标端口 uint16_t
|
||||||
* 限定条件:无
|
* 限定条件:无
|
||||||
*/
|
*/
|
||||||
@@ -406,7 +467,7 @@ void ch395f_set_des_port(uint8_t sock, uint16_t port);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置 Socket 源端口(小端序)
|
* 函数功能:设置 Socket 源端口(小端序)
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* port - 源端口 uint16_t
|
* port - 源端口 uint16_t
|
||||||
* 限定条件:无
|
* 限定条件:无
|
||||||
*/
|
*/
|
||||||
@@ -414,38 +475,43 @@ void ch395f_set_sour_port(uint8_t sock, uint16_t port);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:打开 Socket
|
* 函数功能:打开 Socket
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功) uint8_t
|
||||||
* 限定条件:协议类型、目标 IP、端口已设置
|
* 限定条件:协议类型、目标 IP、端口已设置
|
||||||
*/
|
*/
|
||||||
void ch395f_open_socket(uint8_t sock);
|
uint8_t ch395f_open_socket(uint8_t sock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:关闭 Socket
|
* 函数功能:关闭 Socket
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功) uint8_t
|
||||||
* 限定条件:Socket 已打开
|
* 限定条件:Socket 已打开
|
||||||
*/
|
*/
|
||||||
void ch395f_close_socket(uint8_t sock);
|
uint8_t ch395f_close_socket(uint8_t sock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:启动 TCP 监听模式
|
* 函数功能:启动 TCP 监听模式
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功) uint8_t
|
||||||
* 限定条件:Socket 已打开且协议类型为 TCP
|
* 限定条件:Socket 已打开且协议类型为 TCP
|
||||||
*/
|
*/
|
||||||
void ch395f_tcp_listen(uint8_t sock);
|
uint8_t ch395f_tcp_listen(uint8_t sock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:启动 TCP 连接
|
* 函数功能:启动 TCP 连接
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功) uint8_t
|
||||||
* 限定条件:Socket 已打开且协议类型为 TCP
|
* 限定条件:Socket 已打开且协议类型为 TCP
|
||||||
*/
|
*/
|
||||||
void ch395f_tcp_connect(uint8_t sock);
|
uint8_t ch395f_tcp_connect(uint8_t sock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:断开 TCP 连接
|
* 函数功能:断开 TCP 连接
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* 返回值:命令执行状态码(CH395F_ERR_SUCCESS 表示成功) uint8_t
|
||||||
* 限定条件:TCP 已建立连接
|
* 限定条件:TCP 已建立连接
|
||||||
*/
|
*/
|
||||||
void ch395f_tcp_disconnect(uint8_t sock);
|
uint8_t ch395f_tcp_disconnect(uint8_t sock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数声明区 - Socket 数据传输
|
* 函数声明区 - Socket 数据传输
|
||||||
@@ -453,7 +519,7 @@ void ch395f_tcp_disconnect(uint8_t sock);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:向 Socket 发送缓冲区写入数据
|
* 函数功能:向 Socket 发送缓冲区写入数据
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* p_data - 数据指针 uint8_t*
|
* p_data - 数据指针 uint8_t*
|
||||||
* len - 数据长度 uint16_t
|
* len - 数据长度 uint16_t
|
||||||
* 限定条件:指针非空,长度大于 0,TCP 已连接或 UDP 已打开
|
* 限定条件:指针非空,长度大于 0,TCP 已连接或 UDP 已打开
|
||||||
@@ -462,7 +528,7 @@ void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:从 Socket 接收缓冲区读取数据
|
* 函数功能:从 Socket 接收缓冲区读取数据
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* p_data - 输出缓冲区指针 uint8_t*
|
* p_data - 输出缓冲区指针 uint8_t*
|
||||||
* len - 待读取数据长度 uint16_t
|
* len - 待读取数据长度 uint16_t
|
||||||
* 限定条件:指针非空,长度大于 0,接收缓冲区有数据
|
* 限定条件:指针非空,长度大于 0,接收缓冲区有数据
|
||||||
@@ -471,7 +537,7 @@ void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:获取 Socket 接收缓冲区数据长度
|
* 函数功能:获取 Socket 接收缓冲区数据长度
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* 返回值:接收数据长度 uint16_t
|
* 返回值:接收数据长度 uint16_t
|
||||||
* 限定条件:Socket 已打开
|
* 限定条件:Socket 已打开
|
||||||
*/
|
*/
|
||||||
@@ -482,20 +548,173 @@ uint16_t ch395f_get_recv_len(uint8_t sock);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:获取全局中断状态
|
* 函数功能:获取全局中断状态(2 字节版,支持 Socket 0~7)
|
||||||
* 返回值:中断状态字节 uint8_t
|
* 返回值:中断状态 uint16_t(低字节=Socket 0~3 + PHY/DHCP,高字节=Socket 4~7 + DHCPv6)
|
||||||
* 限定条件:芯片已初始化
|
* 限定条件:芯片已初始化
|
||||||
*/
|
*/
|
||||||
uint8_t ch395f_get_glob_int_status(void);
|
uint16_t ch395f_get_glob_int_status_all(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:获取 Socket 中断状态
|
* 函数功能:获取 Socket 中断状态
|
||||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 3
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
* 返回值:Socket 中断状态字节 uint8_t
|
* 返回值:Socket 中断状态字节 uint8_t
|
||||||
* 限定条件:芯片已初始化
|
* 限定条件:芯片已初始化
|
||||||
*/
|
*/
|
||||||
uint8_t ch395f_get_sock_int_status(uint8_t sock);
|
uint8_t ch395f_get_sock_int_status(uint8_t sock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取 Socket 状态(2 字节输出)
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* status - 2 字节输出缓冲区 [0]=Socket状态, [1]=TCP状态
|
||||||
|
* 限定条件:芯片已初始化
|
||||||
|
* 函数说明:Socket 状态: 00H=CLOSED, 05H=OPEN
|
||||||
|
* TCP 状态: 00H=CLOSED, 01H=LISTEN, 02H=SYN_SENT, 03H=SYN_RCVD, 04H=ESTABLISHED
|
||||||
|
*/
|
||||||
|
void ch395f_get_socket_status(uint8_t sock, uint8_t *status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数声明区 - Socket 远端信息
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取远端 IP 和端口
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* p_buf - 6 字节输出缓冲区(4字节IP + 2字节端口)
|
||||||
|
* 限定条件:TCP Server 模式下连接建立后调用
|
||||||
|
* 函数说明:IP 低字节在前,端口低字节在前
|
||||||
|
*/
|
||||||
|
void ch395f_get_remot_ipp(uint8_t sock, uint8_t *p_buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数声明区 - 功能参数设置
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 CH395 功能参数
|
||||||
|
* 入口参数:para - 4 字节功能参数 uint32_t
|
||||||
|
* bit0: 未使用,必须为 0
|
||||||
|
* bit1: TCP Server 多连接模式使能
|
||||||
|
* bit2: 低功耗模式
|
||||||
|
* bit3: Socket 关闭控制(0=芯片自动关闭,1=外部关闭)
|
||||||
|
* bit4: 禁用 SEND_OK 中断
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_fun_para(uint32_t para);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数声明区 - TCP MSS 设置
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 TCP MSS
|
||||||
|
* 入口参数:mss - MSS 值 uint16_t 60~1460
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_tcp_mss(uint16_t mss);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数声明区 - Socket 缓冲区设置
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 Socket 接收缓冲区
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* start_block - 起始块地址 uint8_t
|
||||||
|
* block_count - 块个数 uint8_t
|
||||||
|
* 限定条件:必须在 OPEN_SOCKET_SN 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_recv_buf(uint8_t sock, uint8_t start_block, uint8_t block_count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 Socket 发送缓冲区
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* start_block - 起始块地址 uint8_t
|
||||||
|
* block_count - 块个数 uint8_t
|
||||||
|
* 限定条件:必须在 OPEN_SOCKET_SN 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_send_buf(uint8_t sock, uint8_t start_block, uint8_t block_count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:清空 Socket 接收缓冲区
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* 限定条件:Socket 已打开
|
||||||
|
*/
|
||||||
|
void ch395f_clear_recv_buf(uint8_t sock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 TCP KeepAlive 空闲时间
|
||||||
|
* 入口参数:time - 空闲时间(毫秒,必须为 500 的倍数) uint32_t
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_keepalive_idle(uint32_t time);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 TCP KeepAlive 探测间隔
|
||||||
|
* 入口参数:time - 间隔时间(毫秒,必须为 500 的倍数) uint32_t
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_keepalive_intvl(uint32_t time);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 TCP KeepAlive 探测次数
|
||||||
|
* 入口参数:cnt - 探测次数 uint8_t
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_keepalive_cnt(uint8_t cnt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:启用/禁用指定 Socket 的 KeepAlive
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* enable - 1 启用,0 禁用 uint8_t
|
||||||
|
* 限定条件:Socket 已打开
|
||||||
|
*/
|
||||||
|
void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数声明区 - TCP 重传参数设置
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 TCP 重传次数
|
||||||
|
* 入口参数:cnt - 重传次数 uint8_t(0=使用默认值5)
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_retrans_count(uint8_t cnt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 TCP 重传周期
|
||||||
|
* 入口参数:period - 重传周期(毫秒) uint16_t
|
||||||
|
* 限定条件:必须在 ch395f_init() 之前调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_retrans_period(uint16_t period);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数声明区 - ARP / TTL / 不可达信息
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 ARP 重传参数
|
||||||
|
* 入口参数:period_100ms - ARP 重传周期(单位 100ms,0=默认值=10即1秒) uint8_t
|
||||||
|
* cnt - ARP 重传次数(0=默认值=3,255=无限重传) uint8_t
|
||||||
|
* 限定条件:应在 ch395f_init() 之后调用
|
||||||
|
*/
|
||||||
|
void ch395f_set_arp(uint8_t period_100ms, uint8_t cnt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:设置 Socket TTL
|
||||||
|
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||||
|
* ttl - TTL 值 uint8_t 最大 128
|
||||||
|
* 限定条件:Socket 已打开,TCP Server 模式下不生效
|
||||||
|
*/
|
||||||
|
void ch395f_set_ttl(uint8_t sock, uint8_t ttl);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取不可达信息(用于调试连接失败原因)
|
||||||
|
* 出口参数:p_buf - 8 字节输出缓冲区(类型+协议码+端口+IP)
|
||||||
|
* 限定条件:收到 GINT_STAT_UNREACH 中断后调用
|
||||||
|
*/
|
||||||
|
void ch395f_get_unreach_info(uint8_t *p_buf);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef __FAL_CFG_H
|
|
||||||
#define __FAL_CFG_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 模块名称:FAL 配置
|
|
||||||
* 模块功能:定义 FAL Flash 设备表和分区表
|
|
||||||
* 适用平台:STM32F407ZGT6
|
|
||||||
* 作者:王建锋
|
|
||||||
* 创建日期:2026-07-16
|
|
||||||
* 修改记录:
|
|
||||||
* 2026-07-16 王建锋 创建初始版本
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* FAL 调试开关:0-关闭,1-开启 */
|
|
||||||
#define FAL_DEBUG 0
|
|
||||||
|
|
||||||
/* 启用分区表配置 */
|
|
||||||
#define FAL_PART_HAS_TABLE_CFG
|
|
||||||
|
|
||||||
/* ======================== Flash 设备表 ======================== */
|
|
||||||
|
|
||||||
extern const struct fal_flash_dev gd5f2gq5ue_flash;
|
|
||||||
|
|
||||||
#define FAL_FLASH_DEV_TABLE \
|
|
||||||
{ \
|
|
||||||
&gd5f2gq5ue_flash, \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ======================== 分区表 ======================== */
|
|
||||||
/*
|
|
||||||
* 分区名称 设备名称 偏移 大小
|
|
||||||
* KVDB: 64MB @ 0
|
|
||||||
* TSDB: 64MB @ 64MB
|
|
||||||
*/
|
|
||||||
#ifdef FAL_PART_HAS_TABLE_CFG
|
|
||||||
#define FAL_PART_TABLE \
|
|
||||||
{ \
|
|
||||||
{FAL_PART_MAGIC_WORD, "fdb_kvdb1", "gd5f2gq5ue", 0, 64*1024*1024, 0}, \
|
|
||||||
{FAL_PART_MAGIC_WORD, "fdb_tsdb1", "gd5f2gq5ue", 64*1024*1024, 64*1024*1024, 0}, \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __FAL_CFG_H */
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
/*
|
|
||||||
* 模块名称:FAL Flash 设备适配
|
|
||||||
* 模块功能:将 GD5F2GQ5UE 驱动接口适配到 FAL 框架
|
|
||||||
* 适用平台:STM32F407ZGT6
|
|
||||||
* 作者:王建锋
|
|
||||||
* 创建日期:2026-07-16
|
|
||||||
* 修改记录:
|
|
||||||
* 2026-07-16 王建锋 创建初始版本
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* 头文件包含区 */
|
|
||||||
#include "fal_def.h"
|
|
||||||
#include "gd5f2gq5ue.h"
|
|
||||||
|
|
||||||
/* ======================== FAL 操作函数适配 ======================== */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 函数功能:Flash 设备初始化适配
|
|
||||||
* 入口参数:无
|
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
|
||||||
* 限定条件:SPI 和 GPIO 已由 CubeMX 初始化完成
|
|
||||||
* 函数说明:调用底层驱动的初始化函数
|
|
||||||
*/
|
|
||||||
static int gd5f_fal_init(void)
|
|
||||||
{
|
|
||||||
return gd5f2gq5ue_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 函数功能:Flash 读取适配
|
|
||||||
* 入口参数:offset - 起始字节偏移 long
|
|
||||||
* p_buf - 数据缓冲区 uint8_t*
|
|
||||||
* size - 读取字节数 size_t
|
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
|
||||||
* 限定条件:gd5f_fal_init() 已成功调用
|
|
||||||
* 函数说明:直接转发到底层驱动的读取函数
|
|
||||||
*/
|
|
||||||
static int gd5f_fal_read(long offset, uint8_t *p_buf, size_t size)
|
|
||||||
{
|
|
||||||
return gd5f2gq5ue_read(offset, p_buf, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 函数功能:Flash 写入适配
|
|
||||||
* 入口参数:offset - 起始字节偏移 long
|
|
||||||
* p_buf - 数据缓冲区 const uint8_t*
|
|
||||||
* size - 写入字节数 size_t
|
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
|
||||||
* 限定条件:目标区域已擦除
|
|
||||||
* 函数说明:直接转发到底层驱动的写入函数
|
|
||||||
*/
|
|
||||||
static int gd5f_fal_write(long offset, const uint8_t *p_buf, size_t size)
|
|
||||||
{
|
|
||||||
return gd5f2gq5ue_write(offset, p_buf, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 函数功能:Flash 擦除适配
|
|
||||||
* 入口参数:offset - 起始字节偏移 long
|
|
||||||
* size - 擦除字节数 size_t
|
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
|
||||||
* 限定条件:gd5f_fal_init() 已成功调用
|
|
||||||
* 函数说明:直接转发到底层驱动的擦除函数
|
|
||||||
*/
|
|
||||||
static int gd5f_fal_erase(long offset, size_t size)
|
|
||||||
{
|
|
||||||
return gd5f2gq5ue_erase(offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ======================== FAL Flash 设备定义 ======================== */
|
|
||||||
|
|
||||||
/* GD5F2GQ5UE FAL 设备实例,总容量256MB,块大小128KB */
|
|
||||||
const struct fal_flash_dev gd5f2gq5ue_flash = {
|
|
||||||
.name = "gd5f2gq5ue",
|
|
||||||
.addr = 0,
|
|
||||||
.len = GD5F_TOTAL_SIZE,
|
|
||||||
.blk_size = GD5F_BLOCK_SIZE,
|
|
||||||
.ops = {
|
|
||||||
.init = gd5f_fal_init,
|
|
||||||
.read = gd5f_fal_read,
|
|
||||||
.write = gd5f_fal_write,
|
|
||||||
.erase = gd5f_fal_erase,
|
|
||||||
},
|
|
||||||
.write_gran = 8,
|
|
||||||
};
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#ifndef __FDB_CFG_H
|
|
||||||
#define __FDB_CFG_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 模块名称:FlashDB 配置
|
|
||||||
* 模块功能:配置 FlashDB 数据库功能开关和参数
|
|
||||||
* 适用平台:STM32F407ZGT6
|
|
||||||
* 作者:王建锋
|
|
||||||
* 创建日期:2026-07-16
|
|
||||||
* 修改记录:
|
|
||||||
* 2026-07-16 王建锋 创建初始版本
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 启用 KVDB(键值数据库) */
|
|
||||||
#define FDB_USING_KVDB
|
|
||||||
|
|
||||||
/* KVDB 版本号变化时自动更新到最新默认值(默认关闭) */
|
|
||||||
/* #define FDB_KV_AUTO_UPDATE */
|
|
||||||
|
|
||||||
/* 启用 TSDB(时序数据库) */
|
|
||||||
#define FDB_USING_TSDB
|
|
||||||
|
|
||||||
/* 使用 FAL 存储模式(非文件模式) */
|
|
||||||
#define FDB_USING_FAL_MODE
|
|
||||||
|
|
||||||
/* Flash 写入粒度,单位:bit,STM32F4 字节可编程 = 8 */
|
|
||||||
#define FDB_WRITE_GRAN 8
|
|
||||||
|
|
||||||
/* 调试输出使能 */
|
|
||||||
/*#define FDB_DEBUG_ENABLE*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __FDB_CFG_H */
|
|
||||||
@@ -7,32 +7,86 @@
|
|||||||
* 修改记录:
|
* 修改记录:
|
||||||
* 2026-07-16 王建锋 创建初始版本
|
* 2026-07-16 王建锋 创建初始版本
|
||||||
* 2026-07-17 王建锋 切换硬件 SPI,修正擦除地址,参考 NuttX 驱动
|
* 2026-07-17 王建锋 切换硬件 SPI,修正擦除地址,参考 NuttX 驱动
|
||||||
|
* 2026-08-27 补充函数注释(代码规范 V1.0);修正工厂坏块扫描为首页;增加 BBT 持久化
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 头文件包含区 */
|
/* 头文件包含区 */
|
||||||
#include "gd5f2gq5ue.h"
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "gd5f2gq5ue.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 调试输出配置
|
||||||
|
*/
|
||||||
|
#define DBG_TAG "[NAND]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
/* 私有宏定义区 */
|
/* 私有宏定义区 */
|
||||||
#define GD5F_SPI_TIMEOUT 100
|
#define GD5F_SPI_TIMEOUT 100
|
||||||
|
#define GD5F_BBT_SIZE (GD5F_TOTAL_BLOCKS / 8)
|
||||||
|
|
||||||
|
/* BBT 静态数组(1 bit 表示一个块,0=好块 1=坏块) */
|
||||||
|
static uint8_t s_bbt[GD5F_BBT_SIZE];
|
||||||
|
|
||||||
|
/* BBT 持久化:保留块池(从 NAND 顶部向下挑选出厂好块,存储 BBT 副本) */
|
||||||
|
#define GD5F_BBT_HDR_SIZE 16
|
||||||
|
static uint32_t s_bbt_pool_blocks[GD5F_BBT_POOL_COUNT]; /* BBT 保留池块号列表(出厂好块) */
|
||||||
|
static uint32_t s_bbt_pool_count = 0; /* 实际可用池块数(<= GD5F_BBT_POOL_COUNT) */
|
||||||
|
static uint32_t s_usable_blocks = GD5F_TOTAL_BLOCKS; /* dhara 可见块数(= 最低池块号) */
|
||||||
|
static uint32_t s_bbt_write_slot = 0; /* 下一写入槽(轮转指针) */
|
||||||
|
static uint32_t s_bbt_version = 0; /* 当前持久化 BBT 版本号 */
|
||||||
|
static uint8_t s_bbt_slot_dead = 0; /* 池块损坏位掩码 */
|
||||||
|
static uint8_t s_bbt_page_buf[GD5F_PAGE_SIZE]; /* BBT 读写页缓冲(静态,避免占栈) */
|
||||||
|
static uint8_t s_bbt_best[GD5F_BBT_SIZE]; /* 最高版本位图缓存 */
|
||||||
|
static const uint8_t s_bbt_magic[4] = { 'G', 'B', 'B', 'T' }; /* BBT 存储魔数 "GBBT" */
|
||||||
|
|
||||||
/* 外部 SPI 句柄声明 */
|
/* 外部 SPI 句柄声明 */
|
||||||
extern SPI_HandleTypeDef hspi1;
|
extern SPI_HandleTypeDef hspi1;
|
||||||
|
|
||||||
|
/* DMA 完成标志(ch395f.c 中的 HAL_SPI_TxRxCpltCallback 会设置此标志) */
|
||||||
|
volatile uint8_t g_spi1_dma_done;
|
||||||
|
|
||||||
|
/* DMA 阈值(字节数超过此值使用 DMA 传输) */
|
||||||
|
#define GD5F_DMA_THRESHOLD 32
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:SPI1 TX DMA 完成回调(PROGRAM_LOAD 数据发送用)
|
||||||
|
* 入口参数:hspi - 触发回调的 SPI 句柄 SPI_HandleTypeDef*
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:由 HAL DMA 中断调用
|
||||||
|
* 函数说明:仅当 SPI1 传输完成时置位 g_spi1_dma_done 标志
|
||||||
|
*/
|
||||||
|
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||||
|
if (hspi->Instance == SPI1) {
|
||||||
|
g_spi1_dma_done = 1U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:SPI1 RX DMA 完成回调(READ_FROM_CACHE 数据接收用)
|
||||||
|
* 函数说明:HAL_SPI_Receive_DMA() 在 2 线 master 模式下
|
||||||
|
* 内部走 HAL_SPI_TransmitReceive_DMA 但 state=BUSY_RX,
|
||||||
|
* 完成回调是 HAL_SPI_RxCpltCallback 而非 TxRxCpltCallback
|
||||||
|
*/
|
||||||
|
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||||
|
if (hspi->Instance == SPI1) {
|
||||||
|
g_spi1_dma_done = 1U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================== 私有函数声明 ======================== */
|
/* ======================== 私有函数声明 ======================== */
|
||||||
|
|
||||||
static int gd5f_wait_busy(uint32_t timeout_ms);
|
|
||||||
static int gd5f_write_enable(void);
|
|
||||||
static int gd5f_read_status(uint8_t *p_status);
|
|
||||||
static int gd5f_page_read(uint32_t page_addr);
|
|
||||||
static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
|
||||||
size_t size);
|
|
||||||
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
||||||
const uint8_t *p_buf, size_t size);
|
const uint8_t *p_buf, size_t size);
|
||||||
static int gd5f_set_feature(uint8_t addr, uint8_t data);
|
static int gd5f_set_feature(uint8_t addr, uint8_t data);
|
||||||
static int gd5f_block_erase(uint32_t block_addr);
|
|
||||||
|
|
||||||
/* ======================== 私有函数定义 ======================== */
|
/* BBT 持久化内部函数 */
|
||||||
|
static int gd5f_private_bbt_locate_pool(void);
|
||||||
|
static uint32_t gd5f_private_crc32(const uint8_t *p_buf, uint32_t len);
|
||||||
|
static int gd5f_private_bbt_find_best(void);
|
||||||
|
static int gd5f_bbt_load(void);
|
||||||
|
static int gd5f_bbt_save(void);
|
||||||
|
|
||||||
|
/* ======================== SPI 原语定义 ======================== */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:等待芯片操作完成(轮询 OIP 位)
|
* 函数功能:等待芯片操作完成(轮询 OIP 位)
|
||||||
@@ -41,8 +95,7 @@ static int gd5f_block_erase(uint32_t block_addr);
|
|||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时
|
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时
|
||||||
*/
|
*/
|
||||||
static int gd5f_wait_busy(uint32_t timeout_ms)
|
int gd5f_wait_busy(uint32_t timeout_ms) {
|
||||||
{
|
|
||||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||||
uint8_t addr = GD5F_REG_STATUS;
|
uint8_t addr = GD5F_REG_STATUS;
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
@@ -69,10 +122,8 @@ static int gd5f_wait_busy(uint32_t timeout_ms)
|
|||||||
* 入口参数:无
|
* 入口参数:无
|
||||||
* 返回值:0 - 成功
|
* 返回值:0 - 成功
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:CS# 拉低后发送 06h 命令再拉高
|
* 函数说明:CS# 拉低后发 06h 命令再拉高 */
|
||||||
*/
|
int gd5f_write_enable(void) {
|
||||||
static int gd5f_write_enable(void)
|
|
||||||
{
|
|
||||||
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
|
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
|
||||||
|
|
||||||
GD5F_CS_LOW();
|
GD5F_CS_LOW();
|
||||||
@@ -84,13 +135,11 @@ static int gd5f_write_enable(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取状态寄存器
|
* 函数功能:读取状态寄存器
|
||||||
* 入口参数:p_status - 状态值输出指针 uint8_t* 不为 NULL
|
* 出口参数:p_status - 状态值输出指针 uint8_t* 不为 NULL
|
||||||
* 返回值:0 - 成功
|
* 返回值:0 - 成功
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发送 0Fh + C0h 地址后读取1字节状态值
|
* 函数说明:发 0Fh + C0h 地址后读 1 字节状态 */
|
||||||
*/
|
int gd5f_read_status(uint8_t *p_status) {
|
||||||
static int gd5f_read_status(uint8_t *p_status)
|
|
||||||
{
|
|
||||||
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
uint8_t cmd = GD5F_CMD_GET_FEATURE;
|
||||||
uint8_t addr = GD5F_REG_STATUS;
|
uint8_t addr = GD5F_REG_STATUS;
|
||||||
|
|
||||||
@@ -105,14 +154,13 @@ static int gd5f_read_status(uint8_t *p_status)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:页读取(将数据从存储阵列加载到内部缓存)
|
* 函数功能:页读取(将数据从存储阵列加载到内部缓存)
|
||||||
* 入口参数:page_addr - 页地址 uint32_t
|
* 入口参数:page_addr - 页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其它 - 错误
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发送 13h + 3字节行地址,等待 OIP 清零
|
* 函数说明:发 13h + 3字节行地址,等待 OIP 清零
|
||||||
*/
|
*/
|
||||||
static int gd5f_page_read(uint32_t page_addr)
|
int gd5f_page_read(uint32_t page_addr) {
|
||||||
{
|
uint8_t cmd[4] = {0};
|
||||||
uint8_t cmd[4];
|
|
||||||
|
|
||||||
cmd[0] = GD5F_CMD_PAGE_READ;
|
cmd[0] = GD5F_CMD_PAGE_READ;
|
||||||
cmd[1] = (page_addr >> 16) & 0xFF;
|
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||||
@@ -129,16 +177,14 @@ static int gd5f_page_read(uint32_t page_addr)
|
|||||||
/*
|
/*
|
||||||
* 函数功能:从内部缓存读取数据
|
* 函数功能:从内部缓存读取数据
|
||||||
* 入口参数:column - 列地址(页内偏移) uint16_t 0 - 2047
|
* 入口参数:column - 列地址(页内偏移) uint16_t 0 - 2047
|
||||||
* p_buf - 数据输出缓冲区 uint8_t* 不为 NULL
|
* size - 读取字节数 size_t > 0
|
||||||
* size - 读取字节数 size_t > 0
|
* 出口参数:p_buf - 数据输出缓冲区 uint8_t* 不为 NULL
|
||||||
* 返回值:0 - 成功
|
* 返回值:0 - 成功
|
||||||
* 限定条件:必须先调用 gd5f_page_read 完成数据加载
|
* 限定条件:必须先调用 gd5f_page_read 完成数据加载
|
||||||
* 函数说明:发送 0Bh + 2字节列地址 +1字节 dummy 后读取数据
|
* 函数说明:发 0Bh + 2字节列地址 +1字节 dummy 后读取数据 */
|
||||||
*/
|
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
||||||
static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
size_t size) {
|
||||||
size_t size)
|
uint8_t cmd[4] = {0};
|
||||||
{
|
|
||||||
uint8_t cmd[4];
|
|
||||||
|
|
||||||
cmd[0] = GD5F_CMD_READ_FROM_CACHE;
|
cmd[0] = GD5F_CMD_READ_FROM_CACHE;
|
||||||
cmd[1] = (column >> 8) & 0xFF;
|
cmd[1] = (column >> 8) & 0xFF;
|
||||||
@@ -147,7 +193,24 @@ static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
|||||||
|
|
||||||
GD5F_CS_LOW();
|
GD5F_CS_LOW();
|
||||||
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
||||||
HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
|
|
||||||
|
if (size > GD5F_DMA_THRESHOLD) {
|
||||||
|
uint32_t tick_start = HAL_GetTick();
|
||||||
|
g_spi1_dma_done = 0U;
|
||||||
|
if (HAL_SPI_Receive_DMA(&hspi1, p_buf, (uint16_t)size) != HAL_OK) {
|
||||||
|
GD5F_CS_HIGH();
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
while (g_spi1_dma_done == 0U) {
|
||||||
|
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||||
|
HAL_SPI_DMAStop(&hspi1);
|
||||||
|
GD5F_CS_HIGH();
|
||||||
|
return GD5F_BUSY_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
|
||||||
|
}
|
||||||
GD5F_CS_HIGH();
|
GD5F_CS_HIGH();
|
||||||
|
|
||||||
return GD5F_OK;
|
return GD5F_OK;
|
||||||
@@ -157,32 +220,73 @@ static int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
|
|||||||
* 函数功能:页编程(将数据写入指定页)
|
* 函数功能:页编程(将数据写入指定页)
|
||||||
* 入口参数:page_addr - 页地址 uint32_t
|
* 入口参数:page_addr - 页地址 uint32_t
|
||||||
* column - 列地址 uint16_t 0 - 2047
|
* column - 列地址 uint16_t 0 - 2047
|
||||||
* p_buf - 数据缓冲区 const uint8_t*
|
* p_buf - 数据缓冲区 const uint8_t*
|
||||||
* size - 写入字节数 size_t > 0
|
* size - 写入字节数 size_t > 0
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其它 - 错误 * 限定条件:目标区域已擦除
|
||||||
* 限定条件:目标区域已擦除
|
|
||||||
* 函数说明:1. 写使能 - 02h 加载数据 - 10h 执行编程 - 等待完成
|
* 函数说明:1. 写使能 - 02h 加载数据 - 10h 执行编程 - 等待完成
|
||||||
* 2. 编程完成后检查 P_FAIL 位
|
* 2. 编程完成后检查 P_FAIL 位 */
|
||||||
*/
|
|
||||||
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
||||||
const uint8_t *p_buf, size_t size)
|
const uint8_t *p_buf, size_t size) {
|
||||||
{
|
int ret;
|
||||||
int ret = GD5F_OK;
|
|
||||||
uint8_t cmd[4];
|
|
||||||
uint8_t status = 0;
|
|
||||||
|
|
||||||
gd5f_write_enable();
|
gd5f_write_enable();
|
||||||
|
ret = gd5f_program_load(column, p_buf, size);
|
||||||
|
if (ret != GD5F_OK) return ret;
|
||||||
|
return gd5f_program_exec(page_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将数据加载到内部缓存(PROGRAM LOAD)
|
||||||
|
* 入口参数:column - 列地址(页内偏移) uint16_t 0 - 2047
|
||||||
|
* p_buf - 待写入数据缓冲区 const uint8_t* 不为 NULL
|
||||||
|
* size - 写入字节数 size_t > 0
|
||||||
|
* 返回值:0 - 成功,其它 - 错误
|
||||||
|
* 限定条件:SPI 已初始化,目标区域已擦除
|
||||||
|
* 函数说明:发 02h + 2字节列地址后发送数据;超过 DMA 阈值走 DMA
|
||||||
|
*/
|
||||||
|
int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
|
||||||
|
uint8_t cmd[3];
|
||||||
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
|
cmd[0] = GD5F_CMD_PROGRAM_LOAD;
|
||||||
cmd[1] = (column >> 8) & 0xFF;
|
cmd[1] = (uint8_t)(column >> 8);
|
||||||
cmd[2] = column & 0xFF;
|
cmd[2] = (uint8_t)(column);
|
||||||
|
|
||||||
GD5F_CS_LOW();
|
GD5F_CS_LOW();
|
||||||
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
|
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
|
||||||
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size,
|
|
||||||
GD5F_SPI_TIMEOUT);
|
if (size > GD5F_DMA_THRESHOLD) {
|
||||||
|
uint32_t tick_start = HAL_GetTick();
|
||||||
|
g_spi1_dma_done = 0U;
|
||||||
|
if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
|
||||||
|
!= HAL_OK) {
|
||||||
|
GD5F_CS_HIGH();
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
while (g_spi1_dma_done == 0U) {
|
||||||
|
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
|
||||||
|
HAL_SPI_DMAStop(&hspi1);
|
||||||
|
GD5F_CS_HIGH();
|
||||||
|
return GD5F_BUSY_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT);
|
||||||
|
}
|
||||||
GD5F_CS_HIGH();
|
GD5F_CS_HIGH();
|
||||||
|
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:执行页编程(PROGRAM EXECUTE)
|
||||||
|
* 入口参数:page_addr - 目标页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1
|
||||||
|
* 返回值:0 - 成功,其它 - 错误(GD5F_PROGRAM_FAIL 等)
|
||||||
|
* 限定条件:已先调用 gd5f_program_load 加载数据
|
||||||
|
* 函数说明:发 10h + 3字节页地址触发编程,等待完成后检查 P_FAIL 位
|
||||||
|
*/
|
||||||
|
int gd5f_program_exec(uint32_t page_addr) {
|
||||||
|
int ret;
|
||||||
|
uint8_t cmd[4];
|
||||||
|
uint8_t status;
|
||||||
|
|
||||||
cmd[0] = GD5F_CMD_PROGRAM_EXEC;
|
cmd[0] = GD5F_CMD_PROGRAM_EXEC;
|
||||||
cmd[1] = (page_addr >> 16) & 0xFF;
|
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||||
cmd[2] = (page_addr >> 8) & 0xFF;
|
cmd[2] = (page_addr >> 8) & 0xFF;
|
||||||
@@ -193,28 +297,37 @@ static int gd5f_page_program(uint32_t page_addr, uint16_t column,
|
|||||||
GD5F_CS_HIGH();
|
GD5F_CS_HIGH();
|
||||||
|
|
||||||
ret = gd5f_wait_busy(1000);
|
ret = gd5f_wait_busy(1000);
|
||||||
if (ret != GD5F_OK) {
|
if (ret != GD5F_OK) return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
gd5f_read_status(&status);
|
gd5f_read_status(&status);
|
||||||
if (status & GD5F_STATUS_P_FAIL) {
|
if (status & GD5F_STATUS_P_FAIL) return GD5F_PROGRAM_FAIL;
|
||||||
return GD5F_PROGRAM_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GD5F_OK;
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:检查上次读操作的 ECC 状态
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 无 ECC 错误,GD5F_ECC_ERROR - 存在不可纠正 ECC 错误
|
||||||
|
* 限定条件:SPI 已初始化,ECC 已使能
|
||||||
|
* 函数说明:读取状态寄存器 ECCS[5:4] 位,0x02 表示不可纠正错误
|
||||||
|
*/
|
||||||
|
int gd5f_check_ecc(void) {
|
||||||
|
uint8_t status;
|
||||||
|
gd5f_read_status(&status);
|
||||||
|
uint8_t ecc = (status >> 4) & 0x03;
|
||||||
|
if (ecc == 0x02) return GD5F_ECC_ERROR;
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置 Feature 寄存器
|
* 函数功能:设置 Feature 寄存器
|
||||||
* 入口参数:addr - 寄存器地址 uint8_t
|
* 入口参数:addr - 寄存器地址 uint8_t
|
||||||
* data - 写入数据 uint8_t
|
* data - 写入数据 uint8_t
|
||||||
* 返回值:0 - 成功
|
* 返回值:0 - 成功
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:先写使能,发送 1Fh + 地址 + 数据,等待操作完成
|
* 函数说明:先写使能,发 1Fh + 地址 + 数据,等待操作完成 */
|
||||||
*/
|
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
|
||||||
static int gd5f_set_feature(uint8_t addr, uint8_t data)
|
|
||||||
{
|
|
||||||
uint8_t cmd[3];
|
uint8_t cmd[3];
|
||||||
|
|
||||||
gd5f_write_enable();
|
gd5f_write_enable();
|
||||||
@@ -237,22 +350,20 @@ static int gd5f_set_feature(uint8_t addr, uint8_t data)
|
|||||||
* 入口参数:block_addr - 块编号 uint32_t 0 - 2047
|
* 入口参数:block_addr - 块编号 uint32_t 0 - 2047
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其他 - 错误码
|
||||||
* 限定条件:gd5f2gq5ue_init() 已调用
|
* 限定条件:gd5f2gq5ue_init() 已调用
|
||||||
* 函数说明:1. 写使能 - D8h + 3字节字节地址 - 等待完成
|
* 函数说明:1. 写使能 - D8h + 3字节页地址 - 等待完成
|
||||||
* 2. 擦除完成后检查 E_FAIL 位
|
* 2. 擦除完成后检查 E_FAIL */
|
||||||
*/
|
int gd5f_block_erase(uint32_t block_addr) {
|
||||||
static int gd5f_block_erase(uint32_t block_addr)
|
|
||||||
{
|
|
||||||
int ret = GD5F_OK;
|
int ret = GD5F_OK;
|
||||||
uint8_t cmd[4];
|
uint8_t cmd[4];
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
uint32_t byte_addr = block_addr * GD5F_BLOCK_SIZE;
|
uint32_t page_addr = block_addr * GD5F_PAGES_PER_BLOCK;
|
||||||
|
|
||||||
gd5f_write_enable();
|
gd5f_write_enable();
|
||||||
|
|
||||||
cmd[0] = GD5F_CMD_BLOCK_ERASE;
|
cmd[0] = GD5F_CMD_BLOCK_ERASE;
|
||||||
cmd[1] = (byte_addr >> 16) & 0xFF;
|
cmd[1] = (page_addr >> 16) & 0xFF;
|
||||||
cmd[2] = (byte_addr >> 8) & 0xFF;
|
cmd[2] = (page_addr >> 8) & 0xFF;
|
||||||
cmd[3] = byte_addr & 0xFF;
|
cmd[3] = page_addr & 0xFF;
|
||||||
|
|
||||||
GD5F_CS_LOW();
|
GD5F_CS_LOW();
|
||||||
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
|
||||||
@@ -271,6 +382,106 @@ static int gd5f_block_erase(uint32_t block_addr)
|
|||||||
return GD5F_OK;
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:扫描所有块构建 BBT(在 ECC 使能前调用)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:SPI 已初始化,ECC 尚未使能
|
||||||
|
* 函数说明:读取每个块首页(page 0)的 spare byte 0,
|
||||||
|
* 若不为 0xFF 则为出厂坏块(符合 datasheet §12.4)
|
||||||
|
*/
|
||||||
|
static int gd5f_bbt_scan(void) {
|
||||||
|
uint32_t block;
|
||||||
|
uint8_t spare[8];
|
||||||
|
uint32_t bad_count = 0;
|
||||||
|
|
||||||
|
memset(s_bbt, 0, GD5F_BBT_SIZE);
|
||||||
|
|
||||||
|
for (block = 0; block < GD5F_TOTAL_BLOCKS; block++) {
|
||||||
|
uint32_t page = block << 6;
|
||||||
|
|
||||||
|
gd5f_page_read(page);
|
||||||
|
gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, 1);
|
||||||
|
|
||||||
|
if (spare[0] != 0xFF) {
|
||||||
|
s_bbt[block >> 3] |= (1 << (block & 7));
|
||||||
|
bad_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_INFO("BBT scan: %lu bad blocks found", bad_count);
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:清空 RAM 中的 BBT
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:仅清空内存位图,不影响闪存中的持久化副本
|
||||||
|
*/
|
||||||
|
void gd5f2gq5ue_bbt_clear(void) {
|
||||||
|
memset(s_bbt, 0, GD5F_BBT_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:重建 BBT(重新扫描出厂坏块并持久化,覆盖运行时注入的坏块)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:GD5F_OK - 成功,其他 - 错误码
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记,覆盖 RAM BBT,
|
||||||
|
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除 TC-STO-03 等运行时
|
||||||
|
* 标记的坏块。调用后建议再执行 nand_ftl_format() 清空 dhara map。
|
||||||
|
*/
|
||||||
|
int gd5f2gq5ue_bbt_rebuild(void) {
|
||||||
|
DBG_INFO("Rebuilding BBT from factory scan...");
|
||||||
|
|
||||||
|
/* 1. 关闭 ECC 以读取原始出厂坏块标记(与 gd5f2gq5ue_init 的扫描前提一致) */
|
||||||
|
gd5f_set_feature(0xB0, 0x00);
|
||||||
|
gd5f_wait_busy(100);
|
||||||
|
|
||||||
|
/* 2. 重新扫描出厂坏块(覆盖 RAM BBT,剔除运行时注入的坏块) */
|
||||||
|
gd5f_bbt_scan();
|
||||||
|
|
||||||
|
/* 3. 重新使能 ECC */
|
||||||
|
gd5f_set_feature(0xB0, 0x10);
|
||||||
|
gd5f_wait_busy(100);
|
||||||
|
|
||||||
|
/* 4. 持久化重建后的 BBT(覆盖持久化区中的脏副本) */
|
||||||
|
if (gd5f_bbt_save() != GD5F_OK) {
|
||||||
|
DBG_ERROR("BBT rebuild: persist failed");
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_INFO("BBT rebuilt and persisted");
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:通过调试串口打印当前 BBT 统计与坏块列表
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:最多打印前 32 个坏块块号与偏移
|
||||||
|
*/
|
||||||
|
void gd5f2gq5ue_print_bbt(void) {
|
||||||
|
uint32_t bad_count = 0;
|
||||||
|
for (uint32_t b = 0; b < GD5F_TOTAL_BLOCKS; b++) {
|
||||||
|
if ((s_bbt[b >> 3] >> (b & 7)) & 1) bad_count++;
|
||||||
|
}
|
||||||
|
DBG_INFO("BBT: %lu/%lu blocks bad", bad_count, GD5F_TOTAL_BLOCKS);
|
||||||
|
if (bad_count > 0) {
|
||||||
|
uint32_t shown = 0;
|
||||||
|
for (uint32_t b = 0; b < GD5F_TOTAL_BLOCKS && shown < 32; b++) {
|
||||||
|
if ((s_bbt[b >> 3] >> (b & 7)) & 1) {
|
||||||
|
DBG_INFO(" block %lu (offset 0x%08lX)", b, b * GD5F_BLOCK_SIZE);
|
||||||
|
shown++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bad_count > 32) DBG_INFO(" ... and %lu more", bad_count - 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================== 公共函数定义 ======================== */
|
/* ======================== 公共函数定义 ======================== */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -280,11 +491,12 @@ static int gd5f_block_erase(uint32_t block_addr)
|
|||||||
* 限定条件:SPI1 和相关 GPIO 已由 CubeMX 初始化完成
|
* 限定条件:SPI1 和相关 GPIO 已由 CubeMX 初始化完成
|
||||||
* 函数说明:1. 发送复位命令并等待完成
|
* 函数说明:1. 发送复位命令并等待完成
|
||||||
* 2. 读取芯片 ID 并校验
|
* 2. 读取芯片 ID 并校验
|
||||||
* 3. 使能内部 ECC (B0h bit4)
|
* 3. 扫描出厂坏块构建 BBT(ECC 使能前,读每块首页 spare[0])
|
||||||
* 4. 解除所有块保护 (A0h = 0x00)
|
* 4. 使能内部 ECC (B0h bit4)
|
||||||
|
* 5. 解除所有块保护 (A0h = 0x00)
|
||||||
|
* 6. 定位保留块池并加载持久化 BBT(ECC 使能后)
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_init(void)
|
int gd5f2gq5ue_init(void) {
|
||||||
{
|
|
||||||
int ret = GD5F_OK;
|
int ret = GD5F_OK;
|
||||||
uint8_t mid = 0;
|
uint8_t mid = 0;
|
||||||
uint8_t did = 0;
|
uint8_t did = 0;
|
||||||
@@ -295,41 +507,60 @@ int gd5f2gq5ue_init(void)
|
|||||||
|
|
||||||
HAL_Delay(10);
|
HAL_Delay(10);
|
||||||
|
|
||||||
|
DBG_INFO("Resetting NAND...");
|
||||||
ret = gd5f2gq5ue_reset();
|
ret = gd5f2gq5ue_reset();
|
||||||
if (ret != GD5F_OK) {
|
if (ret != GD5F_OK) {
|
||||||
|
DBG_ERROR("Reset failed: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
HAL_Delay(5);
|
HAL_Delay(5);
|
||||||
|
|
||||||
|
DBG_INFO("Reading NAND ID...");
|
||||||
ret = gd5f2gq5ue_read_id(&mid, &did);
|
ret = gd5f2gq5ue_read_id(&mid, &did);
|
||||||
if (ret != GD5F_OK) {
|
if (ret != GD5F_OK) {
|
||||||
|
DBG_ERROR("Read ID failed: %d", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBG_INFO("NAND ID: MID=0x%02X, DID=0x%02X", mid, did);
|
||||||
|
|
||||||
if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
|
if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
|
||||||
|
DBG_ERROR("ID mismatch: expected MID=0x%02X DID=0x%02X, got MID=0x%02X DID=0x%02X",
|
||||||
|
GD5F_MANUFACTURER_ID, GD5F_DEVICE_ID, mid, did);
|
||||||
return GD5F_ID_MISMATCH;
|
return GD5F_ID_MISMATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBG_INFO("Scanning bad blocks...");
|
||||||
|
gd5f_bbt_scan();
|
||||||
|
|
||||||
|
DBG_INFO("Enabling ECC...");
|
||||||
gd5f_set_feature(0xB0, 0x10);
|
gd5f_set_feature(0xB0, 0x10);
|
||||||
gd5f_wait_busy(100);
|
gd5f_wait_busy(100);
|
||||||
|
|
||||||
|
DBG_INFO("Unlocking block protection...");
|
||||||
gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
|
gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
|
||||||
gd5f_wait_busy(100);
|
gd5f_wait_busy(100);
|
||||||
|
|
||||||
|
DBG_INFO("Locating BBT pool...");
|
||||||
|
gd5f_private_bbt_locate_pool();
|
||||||
|
|
||||||
|
DBG_INFO("Loading persisted BBT...");
|
||||||
|
gd5f_bbt_load();
|
||||||
|
|
||||||
|
DBG_INFO("NAND init OK");
|
||||||
return GD5F_OK;
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取芯片 ID(MID + DID)
|
* 函数功能:读取芯片 ID(MID + DID)
|
||||||
* 入口参数:mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
|
* 出口参数:p_mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
|
||||||
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
|
* p_did - 设备 ID 输出指针 uint8_t* 不为 NULL
|
||||||
* 返回值:0 - 成功
|
* 返回值:0 - 成功
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
|
* 函数说明:发 9Fh 命令后接 1 字节 dummy + MID + DID
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did)
|
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
|
||||||
{
|
|
||||||
uint8_t cmd = GD5F_CMD_READ_ID;
|
uint8_t cmd = GD5F_CMD_READ_ID;
|
||||||
uint8_t id_buf[3] = {0};
|
uint8_t id_buf[3] = {0};
|
||||||
|
|
||||||
@@ -338,8 +569,8 @@ int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did)
|
|||||||
HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
|
HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
|
||||||
GD5F_CS_HIGH();
|
GD5F_CS_HIGH();
|
||||||
|
|
||||||
*mid = id_buf[1];
|
*p_mid = id_buf[1];
|
||||||
*did = id_buf[2];
|
*p_did = id_buf[2];
|
||||||
|
|
||||||
return GD5F_OK;
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
@@ -347,16 +578,19 @@ int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did)
|
|||||||
/*
|
/*
|
||||||
* 函数功能:从 NAND 读取数据(支持跨页)
|
* 函数功能:从 NAND 读取数据(支持跨页)
|
||||||
* 入口参数:offset - 起始字节偏移 long 0 ~ 总容量-1
|
* 入口参数:offset - 起始字节偏移 long 0 ~ 总容量-1
|
||||||
* p_buf - 数据缓冲区 uint8_t* 不为 NULL
|
* size - 读取字节数 size_t > 0
|
||||||
* size - 读取字节数 size_t > 0
|
* 出口参数:p_buf - 数据缓冲区 uint8_t* 不为 NULL
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其它 - 错误
|
||||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
* 函数说明:自动处理跨页读取
|
* 函数说明:自动处理跨页读取;offset+size 越界返回错误
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size)
|
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
|
||||||
{
|
|
||||||
int ret = GD5F_OK;
|
int ret = GD5F_OK;
|
||||||
|
|
||||||
|
if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
|
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
|
||||||
uint16_t column = offset % GD5F_PAGE_SIZE;
|
uint16_t column = offset % GD5F_PAGE_SIZE;
|
||||||
@@ -386,17 +620,20 @@ int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:向 NAND 写入数据(支持跨页)
|
* 函数功能:向 NAND 写入数据(支持跨页)
|
||||||
* 入口参数:offset - 起始字节偏移 long 0 ~ 总容量-1
|
* 入口参数:offset - 起始字节偏移 long 0 ~ 总容量-1
|
||||||
* p_buf - 数据缓冲区 const uint8_t* 不为 NULL
|
* p_buf - 数据缓冲区 const uint8_t* 不为 NULL
|
||||||
* size - 写入字节数 size_t > 0
|
* size - 写入字节数 size_t > 0
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其它 - 错误
|
||||||
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
|
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
|
||||||
* 函数说明:自动处理跨页写入
|
* 函数说明:自动处理跨页写入;offset+size 越界返回错误
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size)
|
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
|
||||||
{
|
|
||||||
int ret = GD5F_OK;
|
int ret = GD5F_OK;
|
||||||
|
|
||||||
|
if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
|
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
|
||||||
uint16_t column = offset % GD5F_PAGE_SIZE;
|
uint16_t column = offset % GD5F_PAGE_SIZE;
|
||||||
@@ -421,14 +658,13 @@ int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:擦除块(按块擦除,最小单位 128KB)
|
* 函数功能:擦除块(按块擦除,最小单位 128KB)
|
||||||
* 入口参数:offset - 起始字节偏移 long 必须块对齐
|
* 入口参数:offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
|
||||||
* size - 擦除字节数 size_t 必须块大小整数倍
|
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其它 - 错误码
|
||||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
* 函数说明:擦除操作以块为单位
|
* 函数说明:循环擦除 [offset, offset+size) 覆盖的每个块,偏移/长度不对齐返回错误
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_erase(long offset, size_t size)
|
int gd5f2gq5ue_erase(long offset, size_t size) {
|
||||||
{
|
|
||||||
int ret = GD5F_OK;
|
int ret = GD5F_OK;
|
||||||
|
|
||||||
if (offset % GD5F_BLOCK_SIZE != 0) {
|
if (offset % GD5F_BLOCK_SIZE != 0) {
|
||||||
@@ -455,13 +691,11 @@ int gd5f2gq5ue_erase(long offset, size_t size)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:复位芯片
|
* 函数功能:复位芯片
|
||||||
* 入口参数:无
|
|
||||||
* 返回值:0 - 成功
|
* 返回值:0 - 成功
|
||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发送 FFh 复位命令后等待 5ms
|
* 函数说明:发 FFh 复位命令后等待 5ms
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_reset(void)
|
int gd5f2gq5ue_reset(void) {
|
||||||
{
|
|
||||||
uint8_t cmd = GD5F_CMD_RESET;
|
uint8_t cmd = GD5F_CMD_RESET;
|
||||||
|
|
||||||
GD5F_CS_LOW();
|
GD5F_CS_LOW();
|
||||||
@@ -472,3 +706,257 @@ int gd5f2gq5ue_reset(void)
|
|||||||
|
|
||||||
return GD5F_OK;
|
return GD5F_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:查询块是否坏块
|
||||||
|
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||||
|
* 返回值:0 - 好块,1 - 坏块
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:通过 BBT 查询
|
||||||
|
*/
|
||||||
|
int gd5f2gq5ue_is_block_bad(uint32_t block) {
|
||||||
|
if (block >= GD5F_TOTAL_BLOCKS) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return (s_bbt[block >> 3] >> (block & 7)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:标记块为坏块
|
||||||
|
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:更新 BBT
|
||||||
|
*/
|
||||||
|
void gd5f2gq5ue_mark_block_bad(uint32_t block) {
|
||||||
|
if (block >= GD5F_TOTAL_BLOCKS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t byte = block >> 3;
|
||||||
|
uint8_t bit = (uint8_t)(1U << (block & 7));
|
||||||
|
if (s_bbt[byte] & bit) {
|
||||||
|
return; /* 已是坏块,幂等,无需重复持久化 */
|
||||||
|
}
|
||||||
|
s_bbt[byte] |= bit;
|
||||||
|
gd5f_bbt_save(); /* 持久化到保留块池(带版本号 + CRC) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================== BBT 持久化实现 ======================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:从 NAND 顶部向下挑选出厂好块作为 BBT 保留池
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f_bbt_scan() 已完成(s_bbt 已就绪)
|
||||||
|
* 函数说明:保留池不交给 dhara 管理;最低池块号以上的块全部排除,
|
||||||
|
* 并将可用块数 s_usable_blocks 设为最低池块号
|
||||||
|
*/
|
||||||
|
static int gd5f_private_bbt_locate_pool(void) {
|
||||||
|
uint32_t cnt = 0;
|
||||||
|
uint32_t b = GD5F_TOTAL_BLOCKS;
|
||||||
|
|
||||||
|
while (b-- > 0 && cnt < GD5F_BBT_POOL_COUNT) {
|
||||||
|
if (((s_bbt[b >> 3] >> (b & 7)) & 1) == 0) {
|
||||||
|
s_bbt_pool_blocks[cnt++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s_bbt_pool_count = cnt;
|
||||||
|
if (cnt > 0) {
|
||||||
|
s_usable_blocks = s_bbt_pool_blocks[cnt - 1]; /* 最低池块 = dhara 可见上限 */
|
||||||
|
}
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:CRC32 计算(IEEE 802.3,多项式 0xEDB88320)
|
||||||
|
* 入口参数:p_buf - 数据缓冲区 const uint8_t* 不为 NULL
|
||||||
|
* len - 数据长度 uint32_t > 0
|
||||||
|
* 返回值:32 位 CRC 校验值
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:采用位级(bit-by-bit)实现,初始值 0xFFFFFFFF
|
||||||
|
*/
|
||||||
|
static uint32_t gd5f_private_crc32(const uint8_t *p_buf, uint32_t len) {
|
||||||
|
uint32_t crc = 0xFFFFFFFFU;
|
||||||
|
for (uint32_t i = 0; i < len; i++) {
|
||||||
|
crc ^= p_buf[i];
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
uint32_t mask = (crc & 1U) ? 0xEDB88320U : 0U;
|
||||||
|
crc = (crc >> 1) ^ mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:在保留池中找出版本号最高的有效 BBT 副本
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f_private_bbt_locate_pool() 已成功执行
|
||||||
|
* 函数说明:校验 magic / len / CRC;结果缓存到 s_bbt_best / s_bbt_version,
|
||||||
|
* 并设置下一轮转写入槽 s_bbt_write_slot
|
||||||
|
*/
|
||||||
|
static int gd5f_private_bbt_find_best(void) {
|
||||||
|
uint32_t best_ver = 0;
|
||||||
|
int best_slot = -1;
|
||||||
|
|
||||||
|
s_bbt_slot_dead = 0;
|
||||||
|
for (uint32_t i = 0; i < s_bbt_pool_count; i++) {
|
||||||
|
uint32_t block = s_bbt_pool_blocks[i];
|
||||||
|
uint32_t page = block * GD5F_PAGES_PER_BLOCK;
|
||||||
|
uint32_t ver, len, crc;
|
||||||
|
|
||||||
|
gd5f_page_read(page);
|
||||||
|
gd5f_read_from_cache(0, s_bbt_page_buf, GD5F_BBT_HDR_SIZE + GD5F_BBT_SIZE);
|
||||||
|
if (memcmp(s_bbt_page_buf, s_bbt_magic, 4) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
memcpy(&ver, s_bbt_page_buf + 4, 4);
|
||||||
|
memcpy(&len, s_bbt_page_buf + 8, 4);
|
||||||
|
memcpy(&crc, s_bbt_page_buf + 12, 4);
|
||||||
|
if (len != GD5F_BBT_SIZE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (gd5f_private_crc32(s_bbt_page_buf + GD5F_BBT_HDR_SIZE, GD5F_BBT_SIZE) != crc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (best_slot < 0 || (int32_t)ver > (int32_t)best_ver) {
|
||||||
|
best_ver = ver;
|
||||||
|
best_slot = (int)i;
|
||||||
|
memcpy(s_bbt_best, s_bbt_page_buf + GD5F_BBT_HDR_SIZE, GD5F_BBT_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best_slot >= 0) {
|
||||||
|
s_bbt_version = best_ver;
|
||||||
|
s_bbt_write_slot = (uint32_t)(best_slot + 1) % s_bbt_pool_count;
|
||||||
|
} else {
|
||||||
|
s_bbt_write_slot = 0;
|
||||||
|
}
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:加载持久化 BBT(OR 进 RAM BBT)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f_private_bbt_locate_pool() 已成功执行
|
||||||
|
* 函数说明:工厂扫描已填好块,持久化副本补充运行期坏块
|
||||||
|
*/
|
||||||
|
static int gd5f_bbt_load(void) {
|
||||||
|
gd5f_private_bbt_find_best();
|
||||||
|
if (s_bbt_pool_count == 0) {
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < GD5F_BBT_SIZE; i++) {
|
||||||
|
s_bbt[i] |= s_bbt_best[i];
|
||||||
|
}
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将当前 RAM BBT 持久化到保留池(轮转写入,掉电安全)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功,GD5F_ERROR - 写入失败
|
||||||
|
* 限定条件:gd5f_private_bbt_locate_pool() 已成功执行
|
||||||
|
* 函数说明:版本号自增;先擦除目标块再编程;损坏块标记后跳到下一槽
|
||||||
|
*/
|
||||||
|
static int gd5f_bbt_save(void) {
|
||||||
|
if (s_bbt_pool_count == 0) {
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
uint32_t slot = s_bbt_write_slot;
|
||||||
|
|
||||||
|
while (s_bbt_slot_dead & (1U << slot)) {
|
||||||
|
slot = (slot + 1) % s_bbt_pool_count;
|
||||||
|
if (slot == s_bbt_write_slot) {
|
||||||
|
break; /* 所有池块均损坏 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t block = s_bbt_pool_blocks[slot];
|
||||||
|
uint32_t page = block * GD5F_PAGES_PER_BLOCK;
|
||||||
|
uint32_t ver = s_bbt_version + 1;
|
||||||
|
uint32_t len = GD5F_BBT_SIZE;
|
||||||
|
uint32_t crc = gd5f_private_crc32(s_bbt, GD5F_BBT_SIZE);
|
||||||
|
|
||||||
|
if (gd5f_block_erase(block) != GD5F_OK) {
|
||||||
|
s_bbt_slot_dead |= (1U << slot);
|
||||||
|
s_bbt_write_slot = (slot + 1) % s_bbt_pool_count;
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(s_bbt_page_buf, s_bbt_magic, 4);
|
||||||
|
memcpy(s_bbt_page_buf + 4, &ver, 4);
|
||||||
|
memcpy(s_bbt_page_buf + 8, &len, 4);
|
||||||
|
memcpy(s_bbt_page_buf + 12, &crc, 4);
|
||||||
|
memcpy(s_bbt_page_buf + GD5F_BBT_HDR_SIZE, s_bbt, GD5F_BBT_SIZE);
|
||||||
|
|
||||||
|
gd5f_write_enable();
|
||||||
|
gd5f_program_load(0, s_bbt_page_buf, GD5F_BBT_HDR_SIZE + GD5F_BBT_SIZE);
|
||||||
|
gd5f_program_exec(page);
|
||||||
|
gd5f_wait_busy(100);
|
||||||
|
|
||||||
|
s_bbt_version = ver;
|
||||||
|
s_bbt_write_slot = (slot + 1) % s_bbt_pool_count;
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取 dhara 可用块数(= 最低保留池块号)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:dhara 可见的块数量(= 保留块池起始块号)
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:保留块池用于存储持久化 BBT,不交给 dhara 管理
|
||||||
|
*/
|
||||||
|
uint32_t gd5f_get_usable_blocks(void) {
|
||||||
|
return s_usable_blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:从闪存读取当前生效的 BBT 位图(测试/调试用)
|
||||||
|
* 入口参数:无
|
||||||
|
* 出口参数:p_bbt - 位图输出缓冲 uint8_t* 不为 NULL,长度 >= GD5F_BBT_SIZE
|
||||||
|
* p_version - 版本号输出 uint32_t* 不为 NULL
|
||||||
|
* 返回值:0 - 成功,GD5F_ERROR - 参数非法
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:返回闪存中版本号最高的有效 BBT 拷贝(供测试验证持久化)
|
||||||
|
*/
|
||||||
|
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version) {
|
||||||
|
if (p_bbt == NULL || p_version == NULL) {
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
gd5f_private_bbt_find_best();
|
||||||
|
memcpy(p_bbt, s_bbt_best, GD5F_BBT_SIZE);
|
||||||
|
*p_version = s_bbt_version;
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:擦除保留块池(撤销持久化 BBT,回到仅工厂扫描态)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:仅供测试/出厂重置;调用方需自行还原 RAM BBT
|
||||||
|
*/
|
||||||
|
int32_t gd5f_bbt_wipe_pool(void) {
|
||||||
|
if (s_bbt_pool_count == 0) {
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < s_bbt_pool_count; i++) {
|
||||||
|
gd5f_block_erase(s_bbt_pool_blocks[i]);
|
||||||
|
}
|
||||||
|
s_bbt_write_slot = 0;
|
||||||
|
s_bbt_version = 0;
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:从保留块池重新加载持久化 BBT 到 RAM(测试/调试用)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:将闪存中版本号最高的有效副本 OR 进 RAM BBT
|
||||||
|
*/
|
||||||
|
int32_t gd5f_bbt_reload(void) {
|
||||||
|
return gd5f_bbt_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/* ======================== 宏定义 ======================== */
|
/* ======================== 宏定义 ======================== */
|
||||||
|
|
||||||
@@ -60,6 +61,9 @@ extern "C" {
|
|||||||
#define GD5F_TOTAL_BLOCKS 2048
|
#define GD5F_TOTAL_BLOCKS 2048
|
||||||
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
|
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
|
||||||
|
|
||||||
|
/* 坏块表持久化:保留块池数量(从 NAND 顶部向下挑选出厂好块存储 BBT) */
|
||||||
|
#define GD5F_BBT_POOL_COUNT 4
|
||||||
|
|
||||||
/* 制造商 ID 和设备 ID */
|
/* 制造商 ID 和设备 ID */
|
||||||
#define GD5F_MANUFACTURER_ID 0xC8
|
#define GD5F_MANUFACTURER_ID 0xC8
|
||||||
#define GD5F_DEVICE_ID 0x52
|
#define GD5F_DEVICE_ID 0x52
|
||||||
@@ -91,7 +95,7 @@ extern "C" {
|
|||||||
* 3. 使能内部 ECC (B0h bit4)
|
* 3. 使能内部 ECC (B0h bit4)
|
||||||
* 4. 解除所有块保护 (A0h = 0x00)
|
* 4. 解除所有块保护 (A0h = 0x00)
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_init(void);
|
int32_t gd5f2gq5ue_init(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取芯片 ID(MID + DID)
|
* 函数功能:读取芯片 ID(MID + DID)
|
||||||
@@ -101,13 +105,13 @@ int gd5f2gq5ue_init(void);
|
|||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
|
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_read_id(uint8_t *mid, uint8_t *did);
|
int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:从 NAND 读取数据(支持跨页)
|
* 函数功能:从 NAND 读取数据(支持跨页)
|
||||||
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
|
* 入口参数:offset - 起始字节偏移 int32_t 0 - GD5F_TOTAL_SIZE-1
|
||||||
* buf - 数据缓冲区 uint8_t* 不为 NULL
|
* buf - 数据缓冲区 uint8_t* 不为 NULL
|
||||||
* size - 读取字节数 size_t > 0
|
* size - 读取字节数 uint32_t > 0
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其他 - 错误码
|
||||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间
|
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间
|
||||||
@@ -116,9 +120,9 @@ int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:向 NAND 写入数据(支持跨页)
|
* 函数功能:向 NAND 写入数据(支持跨页)
|
||||||
* 入口参数:offset - 起始字节偏移 long 0 - GD5F_TOTAL_SIZE-1
|
* 入口参数:offset - 起始字节偏移 int32_t 0 - GD5F_TOTAL_SIZE-1
|
||||||
* buf - 数据缓冲区 uint8_t* 不为 NULL
|
* buf - 数据缓冲区 uint8_t* 不为 NULL
|
||||||
* size - 写入字节数 size_t > 0
|
* size - 写入字节数 uint32_t > 0
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其他 - 错误码
|
||||||
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
|
* 限定条件:gd5f2gq5ue_init() 已成功调用,目标区域已擦除
|
||||||
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
|
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
|
||||||
@@ -127,8 +131,8 @@ int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:擦除块(按块擦除,最小单位 128KB)
|
* 函数功能:擦除块(按块擦除,最小单位 128KB)
|
||||||
* 入口参数:offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
|
* 入口参数:offset - 起始字节偏移 int32_t 必须 GD5F_BLOCK_SIZE 对齐
|
||||||
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
|
* size - 擦除字节数 uint32_t 必须 GD5F_BLOCK_SIZE 整数倍
|
||||||
* 返回值:0 - 成功,其他 - 错误码
|
* 返回值:0 - 成功,其他 - 错误码
|
||||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
* 函数说明:擦除操作以块为单位,offset 和 size 必须块对齐
|
* 函数说明:擦除操作以块为单位,offset 和 size 必须块对齐
|
||||||
@@ -142,7 +146,90 @@ int gd5f2gq5ue_erase(long offset, size_t size);
|
|||||||
* 限定条件:SPI 已初始化
|
* 限定条件:SPI 已初始化
|
||||||
* 函数说明:发送 FFh 复位命令后等待 5ms
|
* 函数说明:发送 FFh 复位命令后等待 5ms
|
||||||
*/
|
*/
|
||||||
int gd5f2gq5ue_reset(void);
|
int32_t gd5f2gq5ue_reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:查询块是否坏块
|
||||||
|
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||||
|
* 返回值:0 - 好块,1 - 坏块
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:通过 BBT(Bad Block Table)查询,BBT 在 init 阶段 ECC 使能前扫描构建
|
||||||
|
*/
|
||||||
|
int32_t gd5f2gq5ue_is_block_bad(uint32_t block);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:标记块为坏块
|
||||||
|
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:更新 RAM 中的 BBT,并持久化到保留块池(带版本号 + CRC,
|
||||||
|
* 轮转写入,掉电安全);仅在管理区内的块才纳入持久化 BBT
|
||||||
|
*/
|
||||||
|
void gd5f2gq5ue_mark_block_bad(uint32_t block);
|
||||||
|
void gd5f2gq5ue_bbt_clear(void);
|
||||||
|
void gd5f2gq5ue_print_bbt(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:重建 BBT(重新扫描出厂坏块并持久化,覆盖运行时注入的坏块)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:GD5F_OK - 成功,其他 - 错误码
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:用于测试(如 TC-STO-03 注入坏块后)或出厂重置后恢复。
|
||||||
|
* 临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记,覆盖 RAM BBT,
|
||||||
|
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除运行时 mark 的坏块。
|
||||||
|
* 调用后建议再执行 nand_ftl_format() 清空 dhara map。
|
||||||
|
*/
|
||||||
|
int gd5f2gq5ue_bbt_rebuild(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取 dhara 可用块数(总块数减去保留块池)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:dhara 可见的块数量(= 保留块池起始块号)
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:保留块池用于存储持久化 BBT,不交给 dhara 管理
|
||||||
|
*/
|
||||||
|
uint32_t gd5f_get_usable_blocks(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:从闪存读取当前生效的 BBT 位图(测试/调试用)
|
||||||
|
* 入口参数:p_bbt - 位图输出缓冲 uint8_t* 不为 NULL,长度 >= GD5F_BBT_SIZE
|
||||||
|
* p_version - 版本号输出 uint32_t* 不为 NULL
|
||||||
|
* 返回值:0 - 成功,其他 - 错误码
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:返回闪存中版本号最高的有效 BBT 拷贝(供测试验证持久化)
|
||||||
|
*/
|
||||||
|
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:擦除保留块池(撤销持久化 BBT,回到仅工厂扫描态)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:仅供测试/出厂重置使用;擦除后下次启动 load 找不到副本,
|
||||||
|
* 仅保留 RAM 中工厂扫描结果(调用方需自行还原 RAM BBT)。
|
||||||
|
*/
|
||||||
|
int32_t gd5f_bbt_wipe_pool(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:从保留块池重新加载持久化 BBT 到 RAM(测试/调试用)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功
|
||||||
|
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||||
|
* 函数说明:将闪存中版本号最高的有效副本 OR 进 RAM BBT
|
||||||
|
*/
|
||||||
|
int32_t gd5f_bbt_reload(void);
|
||||||
|
|
||||||
|
/* ==================== SPI 原语(FTL 共享) ==================== */
|
||||||
|
|
||||||
|
int32_t gd5f_wait_busy(uint32_t timeout_ms);
|
||||||
|
int32_t gd5f_write_enable(void);
|
||||||
|
int32_t gd5f_read_status(uint8_t *p_status);
|
||||||
|
int32_t gd5f_page_read(uint32_t page_addr);
|
||||||
|
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
|
||||||
|
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, uint32_t size);
|
||||||
|
int32_t gd5f_program_exec(uint32_t page_addr);
|
||||||
|
int32_t gd5f_block_erase(uint32_t block_addr);
|
||||||
|
int32_t gd5f_check_ecc(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
403
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c
Normal file
403
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:NAND FTL 适配层(dhara + FatFS diskio 胶水)
|
||||||
|
* 模块功能:实现 dhara NAND HAL 和 FatFS 磁盘 I/O,连接 GD5F2GQ5UE 底层驱动
|
||||||
|
* 适用平台:STM32F407ZGT6
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-20
|
||||||
|
* 修改记录:
|
||||||
|
* 2026-07-20 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 头文件包含区 */
|
||||||
|
#include <string.h>
|
||||||
|
#include "gd5f2gq5ue.h"
|
||||||
|
#include "ff.h"
|
||||||
|
#include "diskio.h"
|
||||||
|
#include "nand.h"
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
/* FTL 分区起始地址(块 0,偏移 0,占用全部 256MB) */
|
||||||
|
#define FTL_FATFS_OFFSET 0U
|
||||||
|
|
||||||
|
/* 调试输出配置 */
|
||||||
|
#define DBG_TAG "[NAND_FTL]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/* 私有宏定义区 */
|
||||||
|
#define FTL_PAGE_SIZE GD5F_PAGE_SIZE
|
||||||
|
#define FTL_SECTORS_PER_PAGE (FTL_PAGE_SIZE / 512)
|
||||||
|
#define FTL_START_BLOCK (FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE)
|
||||||
|
#define FTL_NUM_BLOCKS (GD5F_TOTAL_BLOCKS - FTL_START_BLOCK)
|
||||||
|
#define FTL_START_PAGE (FTL_START_BLOCK * GD5F_PAGES_PER_BLOCK)
|
||||||
|
|
||||||
|
/* dhara 逻辑地址到物理地址转换 */
|
||||||
|
static inline uint32_t ftl_phy_block(dhara_block_t b) { return b + FTL_START_BLOCK; }
|
||||||
|
static inline uint32_t ftl_phy_page(dhara_page_t p) { return p + FTL_START_PAGE; }
|
||||||
|
|
||||||
|
/* ======================== 私有变量 ======================== */
|
||||||
|
|
||||||
|
/* dhara 核心数据结构 */
|
||||||
|
static struct dhara_nand s_nand;
|
||||||
|
static struct dhara_map s_map;
|
||||||
|
static uint8_t s_page_buf[FTL_PAGE_SIZE]; /* dhara 内部页缓冲区 */
|
||||||
|
|
||||||
|
/* 页面缓存(FatFS 读写缓存) */
|
||||||
|
static uint8_t s_cache_buf[FTL_PAGE_SIZE];
|
||||||
|
static dhara_sector_t s_cached_lpn;
|
||||||
|
static uint8_t s_cache_dirty;
|
||||||
|
|
||||||
|
/* 拷贝临时缓冲区 */
|
||||||
|
static uint8_t s_copy_buf[FTL_PAGE_SIZE];
|
||||||
|
|
||||||
|
/* 初始化状态 */
|
||||||
|
static uint8_t s_initialized;
|
||||||
|
|
||||||
|
/* ======================== 私有函数声明 ======================== */
|
||||||
|
|
||||||
|
static int ftl_flush_cache(void);
|
||||||
|
static int ftl_read_page(dhara_sector_t lpn);
|
||||||
|
|
||||||
|
/* SPI 原语从 gd5f2gq5ue.h 获取 */
|
||||||
|
|
||||||
|
/* ======================== 页面缓存管理 ======================== */
|
||||||
|
|
||||||
|
static int ftl_flush_cache(void) {
|
||||||
|
if (!s_cache_dirty) {
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_error_t err;
|
||||||
|
if (dhara_map_write(&s_map, s_cached_lpn, s_cache_buf, &err) < 0) {
|
||||||
|
DBG_ERROR("Cache flush failed: LPN=%lu err=%d", s_cached_lpn, err);
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_cache_dirty = 0;
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ftl_read_page(dhara_sector_t lpn) {
|
||||||
|
dhara_error_t err;
|
||||||
|
|
||||||
|
if (dhara_map_read(&s_map, lpn, s_cache_buf, &err) < 0) {
|
||||||
|
DBG_ERROR("Page read failed: LPN=%lu err=%d", lpn, err);
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_cached_lpn = lpn;
|
||||||
|
s_cache_dirty = 0;
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================== dhara NAND HAL ======================== */
|
||||||
|
|
||||||
|
int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) {
|
||||||
|
(void)n;
|
||||||
|
return gd5f2gq5ue_is_block_bad(ftl_phy_block(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) {
|
||||||
|
(void)n;
|
||||||
|
gd5f2gq5ue_mark_block_bad(ftl_phy_block(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b, dhara_error_t *err) {
|
||||||
|
(void)n;
|
||||||
|
int ret = gd5f_block_erase(ftl_phy_block(b));
|
||||||
|
if (ret == GD5F_ERASE_FAIL || ret != GD5F_OK) {
|
||||||
|
dhara_nand_mark_bad(n, b);
|
||||||
|
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *data, dhara_error_t *err) {
|
||||||
|
int ret;
|
||||||
|
uint32_t phy_pg = ftl_phy_page(p);
|
||||||
|
|
||||||
|
gd5f_write_enable();
|
||||||
|
ret = gd5f_program_load(0, data, FTL_PAGE_SIZE);
|
||||||
|
if (ret != GD5F_OK) {
|
||||||
|
dhara_nand_mark_bad(n, p >> n->log2_ppb);
|
||||||
|
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gd5f_program_exec(phy_pg);
|
||||||
|
if (ret == GD5F_PROGRAM_FAIL || ret != GD5F_OK) {
|
||||||
|
dhara_nand_mark_bad(n, p >> n->log2_ppb);
|
||||||
|
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) {
|
||||||
|
(void)n;
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t buf[64];
|
||||||
|
uint32_t phy_pg = ftl_phy_page(p);
|
||||||
|
|
||||||
|
if (gd5f_page_read(phy_pg) != GD5F_OK) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
gd5f_read_from_cache(0, buf, sizeof(buf));
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(buf); i++) {
|
||||||
|
if (buf[i] != 0xFF) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) {
|
||||||
|
(void)n;
|
||||||
|
uint32_t phy_pg = ftl_phy_page(p);
|
||||||
|
|
||||||
|
if (gd5f_page_read(phy_pg) != GD5F_OK) {
|
||||||
|
dhara_set_error(err, DHARA_E_ECC);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gd5f_check_ecc() != GD5F_OK) {
|
||||||
|
dhara_set_error(err, DHARA_E_ECC);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gd5f_read_from_cache((uint16_t)offset, data, length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t dst, dhara_error_t *err) {
|
||||||
|
(void)n;
|
||||||
|
|
||||||
|
if (dhara_nand_read(n, src, 0, FTL_PAGE_SIZE, s_copy_buf, err) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhara_nand_prog(n, dst, s_copy_buf, err) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================== FatFS 磁盘 I/O ======================== */
|
||||||
|
|
||||||
|
DSTATUS disk_initialize(BYTE pdrv) {
|
||||||
|
if (pdrv != 0) {
|
||||||
|
return STA_NOINIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_initialized) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_INFO("Initializing NAND FTL...");
|
||||||
|
|
||||||
|
s_nand.log2_page_size = 11;
|
||||||
|
s_nand.log2_ppb = 6;
|
||||||
|
/* dhara 可见块数 = 总块数 - 保留块池(BBT 存储区不交给 dhara,避免被擦写) */
|
||||||
|
s_nand.num_blocks = gd5f_get_usable_blocks();
|
||||||
|
|
||||||
|
dhara_map_init(&s_map, &s_nand, s_page_buf, 4);
|
||||||
|
|
||||||
|
memset(s_cache_buf, 0, FTL_PAGE_SIZE);
|
||||||
|
s_cached_lpn = (dhara_sector_t)-1; /* -1 表示缓存未加载;LPN 0 合法,不能用 0 作哨兵 */
|
||||||
|
s_cache_dirty = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
dhara_error_t err;
|
||||||
|
if (dhara_map_resume(&s_map, &err) < 0) {
|
||||||
|
DBG_INFO("No valid map found, creating fresh (err=%d)", err);
|
||||||
|
dhara_map_clear(&s_map);
|
||||||
|
} else {
|
||||||
|
DBG_INFO("Map resumed: %lu/%lu sectors used",
|
||||||
|
dhara_map_size(&s_map), dhara_map_capacity(&s_map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_initialized = 1;
|
||||||
|
DBG_INFO("NAND FTL ready (capacity: %lu pages = %lu MB)",
|
||||||
|
dhara_map_capacity(&s_map),
|
||||||
|
(dhara_map_capacity(&s_map) * FTL_PAGE_SIZE) / (1024 * 1024));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nand_ftl_init(void)
|
||||||
|
{
|
||||||
|
DSTATUS sta = disk_initialize(0);
|
||||||
|
return (sta == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_ftl_deinit(void)
|
||||||
|
{
|
||||||
|
/* 复位 FTL 状态,使下次 nand_ftl_init() 真正重新走 dhara_map_resume,
|
||||||
|
用于测试"掉电恢复"与"坏块注入后重建"场景(模拟设备重启) */
|
||||||
|
s_initialized = 0;
|
||||||
|
s_cached_lpn = (dhara_sector_t)-1;
|
||||||
|
s_cache_dirty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DSTATUS disk_status(BYTE pdrv) {
|
||||||
|
if (pdrv != 0) {
|
||||||
|
return STA_NOINIT;
|
||||||
|
}
|
||||||
|
if (!s_initialized) {
|
||||||
|
return STA_NOINIT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
|
||||||
|
if (pdrv != 0 || !buff) {
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
if (!s_initialized && disk_initialize(pdrv) != 0) {
|
||||||
|
return RES_NOTRDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
|
||||||
|
uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
|
||||||
|
uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);
|
||||||
|
|
||||||
|
if (batch > count) {
|
||||||
|
batch = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lpn != s_cached_lpn) {
|
||||||
|
if (ftl_flush_cache() != GD5F_OK) {
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buff, s_cache_buf + offset, batch * 512);
|
||||||
|
buff += batch * 512;
|
||||||
|
sector += batch;
|
||||||
|
count -= (UINT)batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
|
||||||
|
if (pdrv != 0 || !buff) {
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
if (!s_initialized && disk_initialize(pdrv) != 0) {
|
||||||
|
return RES_NOTRDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
|
||||||
|
uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
|
||||||
|
uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);
|
||||||
|
|
||||||
|
if (batch > count) {
|
||||||
|
batch = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lpn != s_cached_lpn) {
|
||||||
|
if (ftl_flush_cache() != GD5F_OK) {
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(s_cache_buf + offset, buff, batch * 512);
|
||||||
|
s_cache_dirty = 1;
|
||||||
|
|
||||||
|
if (offset == 0 && batch == FTL_SECTORS_PER_PAGE) {
|
||||||
|
if (ftl_flush_cache() != GD5F_OK) {
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buff += batch * 512;
|
||||||
|
sector += batch;
|
||||||
|
count -= (UINT)batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
|
||||||
|
if (pdrv != 0) {
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
if (!s_initialized) {
|
||||||
|
return RES_NOTRDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case CTRL_SYNC:
|
||||||
|
if (ftl_flush_cache() != GD5F_OK) {
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
dhara_error_t err;
|
||||||
|
if (dhara_map_sync(&s_map, &err) < 0) {
|
||||||
|
DBG_ERROR("Map sync failed: %d", err);
|
||||||
|
return RES_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
case GET_SECTOR_COUNT: {
|
||||||
|
LBA_t *p_sectors = (LBA_t *)buff;
|
||||||
|
dhara_sector_t pages = dhara_map_capacity(&s_map);
|
||||||
|
*p_sectors = pages * FTL_SECTORS_PER_PAGE;
|
||||||
|
return RES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GET_SECTOR_SIZE:
|
||||||
|
*(WORD *)buff = 512;
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
case GET_BLOCK_SIZE:
|
||||||
|
*(DWORD *)buff = 1;
|
||||||
|
return RES_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return RES_PARERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================== 公共函数定义 ======================== */
|
||||||
|
|
||||||
|
int nand_ftl_format(void) {
|
||||||
|
dhara_error_t err;
|
||||||
|
|
||||||
|
if (!s_initialized) {
|
||||||
|
if (disk_initialize(0) != 0) {
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftl_flush_cache() != GD5F_OK) {
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_map_clear(&s_map);
|
||||||
|
|
||||||
|
if (dhara_map_sync(&s_map, &err) < 0) {
|
||||||
|
DBG_ERROR("Map sync after clear failed: %d", err);
|
||||||
|
return GD5F_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_cache_dirty = 0;
|
||||||
|
s_cached_lpn = (dhara_sector_t)-1;
|
||||||
|
|
||||||
|
DBG_INFO("NAND FTL formatted, capacity: %lu pages", dhara_map_capacity(&s_map));
|
||||||
|
return GD5F_OK;
|
||||||
|
}
|
||||||
18
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h
Normal file
18
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __NAND_FTL_H
|
||||||
|
#define __NAND_FTL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int nand_ftl_init(void);
|
||||||
|
int nand_ftl_format(void);
|
||||||
|
void nand_ftl_deinit(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
21
Drivers/BSP/NET/lftpd/LICENSE.txt
Normal file
21
Drivers/BSP/NET/lftpd/LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Jason von Nieda <jason@vonnieda.org>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
675
Drivers/BSP/NET/lftpd/lftpd.c
Normal file
675
Drivers/BSP/NET/lftpd/lftpd.c
Normal file
@@ -0,0 +1,675 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "lftpd.h"
|
||||||
|
#include "private/lftpd_status.h"
|
||||||
|
#include "private/lftpd_inet.h"
|
||||||
|
#include "private/lftpd_log.h"
|
||||||
|
#include "private/lftpd_string.h"
|
||||||
|
#include "private/lftpd_io.h"
|
||||||
|
#include "net_socket.h"
|
||||||
|
#include "net_config.h"
|
||||||
|
#include "ff.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *command;
|
||||||
|
int (*handler)(lftpd_client_t *client, const char *arg);
|
||||||
|
} command_t;
|
||||||
|
|
||||||
|
static int cmd_cwd(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_dele(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_epsv(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_feat(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_list(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_nlst(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_noop(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_pass(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_pasv(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_pwd(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_quit(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_retr(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_size(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_stor(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_syst(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_type(lftpd_client_t *client, const char *arg);
|
||||||
|
static int cmd_user(lftpd_client_t *client, const char *arg);
|
||||||
|
|
||||||
|
static command_t commands[] = {
|
||||||
|
{ "CWD", cmd_cwd },
|
||||||
|
{ "DELE", cmd_dele },
|
||||||
|
{ "EPSV", cmd_epsv },
|
||||||
|
{ "FEAT", cmd_feat },
|
||||||
|
{ "LIST", cmd_list },
|
||||||
|
{ "NLST", cmd_nlst },
|
||||||
|
{ "NOOP", cmd_noop },
|
||||||
|
{ "PASS", cmd_pass },
|
||||||
|
{ "PASV", cmd_pasv },
|
||||||
|
{ "PWD", cmd_pwd },
|
||||||
|
{ "QUIT", cmd_quit },
|
||||||
|
{ "RETR", cmd_retr },
|
||||||
|
{ "SIZE", cmd_size },
|
||||||
|
{ "STOR", cmd_stor },
|
||||||
|
{ "SYST", cmd_syst },
|
||||||
|
{ "TYPE", cmd_type },
|
||||||
|
{ "USER", cmd_user },
|
||||||
|
{ NULL, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int send_response(int socket, int code, bool include_code,
|
||||||
|
bool multiline_start, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
char message[256];
|
||||||
|
char response[512];
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(message, sizeof(message), format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (include_code) {
|
||||||
|
if (multiline_start) {
|
||||||
|
snprintf(response, sizeof(response), "%d-%s%s", code, message, CRLF);
|
||||||
|
} else {
|
||||||
|
snprintf(response, sizeof(response), "%d %s%s", code, message, CRLF);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snprintf(response, sizeof(response), "%s%s", message, CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lftpd_inet_write_string(socket, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define send_simple_response(socket, code, format, ...) \
|
||||||
|
send_response(socket, code, true, false, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define send_multiline_response_begin(socket, code, format, ...) \
|
||||||
|
send_response(socket, code, true, true, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define send_multiline_response_line(socket, format, ...) \
|
||||||
|
send_response(socket, 0, false, false, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define send_multiline_response_end(socket, code, format, ...) \
|
||||||
|
send_response(socket, code, true, false, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
static int send_list(int socket, const char *path)
|
||||||
|
{
|
||||||
|
static const char *directory_format = "drw-rw-rw- 1 owner group %13lu Jan 01 1970 %s";
|
||||||
|
static const char *file_format = "-rw-rw-rw- 1 owner group %13lu Jan 01 1970 %s";
|
||||||
|
|
||||||
|
void *dp = lftpd_io_opendir(path);
|
||||||
|
if (dp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[256];
|
||||||
|
uint32_t size;
|
||||||
|
int is_dir;
|
||||||
|
|
||||||
|
while (lftpd_io_readdir(dp, name, sizeof(name), &size, &is_dir) == 0) {
|
||||||
|
if (is_dir) {
|
||||||
|
send_multiline_response_line(socket, directory_format,
|
||||||
|
(unsigned long)size, name);
|
||||||
|
} else {
|
||||||
|
send_multiline_response_line(socket, file_format,
|
||||||
|
(unsigned long)size, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_io_closedir(dp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_nlst(int socket, const char *path)
|
||||||
|
{
|
||||||
|
void *dp = lftpd_io_opendir(path);
|
||||||
|
if (dp == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[256];
|
||||||
|
uint32_t size;
|
||||||
|
int is_dir;
|
||||||
|
|
||||||
|
while (lftpd_io_readdir(dp, name, sizeof(name), &size, &is_dir) == 0) {
|
||||||
|
if (!is_dir) {
|
||||||
|
send_multiline_response_line(socket, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_io_closedir(dp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_file(int socket, const char *path)
|
||||||
|
{
|
||||||
|
int fh = lftpd_io_open_read(path);
|
||||||
|
if (fh < 0) {
|
||||||
|
lftpd_log_error("failed to open file for read");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char buffer[1024];
|
||||||
|
int read_len;
|
||||||
|
|
||||||
|
while ((read_len = lftpd_io_read(fh, buffer, sizeof(buffer))) > 0) {
|
||||||
|
unsigned char *p = buffer;
|
||||||
|
while (read_len > 0) {
|
||||||
|
int write_len = lftpd_inet_write(socket, p, read_len);
|
||||||
|
if (write_len < 0) {
|
||||||
|
lftpd_log_error("write error");
|
||||||
|
lftpd_io_close(fh);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p += write_len;
|
||||||
|
read_len -= write_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_io_close(fh);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int receive_file(int socket, const char *path)
|
||||||
|
{
|
||||||
|
int fh = lftpd_io_open_write(path);
|
||||||
|
if (fh < 0) {
|
||||||
|
lftpd_log_error("failed to open file for write");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char buffer[1024];
|
||||||
|
int err;
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
while ((err = lftpd_inet_read(socket, buffer, sizeof(buffer))) > 0) {
|
||||||
|
total += err;
|
||||||
|
if (lftpd_io_write(fh, buffer, err) != err) {
|
||||||
|
err = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_io_close(fh);
|
||||||
|
|
||||||
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (total == 0) {
|
||||||
|
lftpd_log_error("received 0 bytes");
|
||||||
|
lftpd_io_unlink(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_cwd(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (arg == NULL || strlen(arg) == 0) {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||||
|
|
||||||
|
/* 根目录始终有效,跳过 stat 检查(FatFS FF_FS_RPATH=0 下 stat 根目录可能失败) */
|
||||||
|
if (strcmp(path, "/") != 0) {
|
||||||
|
uint32_t size;
|
||||||
|
int is_dir;
|
||||||
|
if (lftpd_io_stat(path, &size, &is_dir) != 0 || !is_dir) {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(client->directory, path, sizeof(client->directory) - 1);
|
||||||
|
client->directory[sizeof(client->directory) - 1] = '\0';
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
send_simple_response(client->socket, 250, STATUS_250);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_dele(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (arg == NULL || strlen(arg) == 0) {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||||
|
|
||||||
|
uint32_t size;
|
||||||
|
int is_dir;
|
||||||
|
if (lftpd_io_stat(path, &size, &is_dir) != 0) {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dir) {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_io_unlink(path);
|
||||||
|
free(path);
|
||||||
|
send_simple_response(client->socket, 250, STATUS_250);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_epsv(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (client->data_socket >= 0) {
|
||||||
|
lftpd_inet_close(client->data_socket);
|
||||||
|
client->data_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int listener_socket = lftpd_inet_listen(0);
|
||||||
|
if (listener_socket < 0) {
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = lftpd_inet_get_socket_port(listener_socket);
|
||||||
|
|
||||||
|
send_simple_response(client->socket, 229, STATUS_229, port);
|
||||||
|
|
||||||
|
lftpd_log_debug("EPSV listener on port %d", port);
|
||||||
|
|
||||||
|
client->data_socket = listener_socket;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_feat(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_multiline_response_begin(client->socket, 211, STATUS_211);
|
||||||
|
send_multiline_response_line(client->socket, "EPSV");
|
||||||
|
send_multiline_response_line(client->socket, "PASV");
|
||||||
|
send_multiline_response_line(client->socket, "SIZE");
|
||||||
|
send_multiline_response_line(client->socket, "NLST");
|
||||||
|
send_multiline_response_end(client->socket, 211, STATUS_211);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_list(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (client->data_socket == -1) {
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_simple_response(client->socket, 150, STATUS_150);
|
||||||
|
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||||
|
if (data_sock < 0) {
|
||||||
|
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
lftpd_inet_close(client->data_socket);
|
||||||
|
client->data_socket = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = send_list(data_sock, client->directory);
|
||||||
|
lftpd_inet_close(data_sock);
|
||||||
|
client->data_socket = -1;
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
send_simple_response(client->socket, 226, STATUS_226);
|
||||||
|
} else {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_nlst(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (client->data_socket == -1) {
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_simple_response(client->socket, 150, STATUS_150);
|
||||||
|
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||||
|
if (data_sock < 0) {
|
||||||
|
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
lftpd_inet_close(client->data_socket);
|
||||||
|
client->data_socket = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = send_nlst(data_sock, client->directory);
|
||||||
|
lftpd_inet_close(data_sock);
|
||||||
|
client->data_socket = -1;
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
send_simple_response(client->socket, 226, STATUS_226);
|
||||||
|
} else {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_noop(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 200, STATUS_200);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_pass(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 230, STATUS_230);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_pasv(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
int listener_socket = -1;
|
||||||
|
int retry;
|
||||||
|
|
||||||
|
if (client->data_socket >= 0) {
|
||||||
|
lftpd_inet_close(client->data_socket);
|
||||||
|
client->data_socket = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (retry = 0; retry < 3; retry++) {
|
||||||
|
listener_socket = lftpd_inet_listen(0);
|
||||||
|
if (listener_socket >= 0) break;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(200));
|
||||||
|
}
|
||||||
|
if (listener_socket < 0) {
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = lftpd_inet_get_socket_port(listener_socket);
|
||||||
|
|
||||||
|
{
|
||||||
|
struct net_sockaddr_in local_addr;
|
||||||
|
int addrlen = sizeof(local_addr);
|
||||||
|
int err = net_getsockname(client->socket,
|
||||||
|
(struct net_sockaddr *)&local_addr, &addrlen);
|
||||||
|
if (err != 0) {
|
||||||
|
lftpd_log_error("error getting client IP info");
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
lftpd_inet_close(listener_socket);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ip = net_htonl(local_addr.sin_addr.s_addr);
|
||||||
|
send_simple_response(client->socket, 227, STATUS_227,
|
||||||
|
(ip >> 24) & 0xff,
|
||||||
|
(ip >> 16) & 0xff,
|
||||||
|
(ip >> 8) & 0xff,
|
||||||
|
(ip >> 0) & 0xff,
|
||||||
|
(port >> 8) & 0xff, (port >> 0) & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_log_debug("PASV listener on port %d", port);
|
||||||
|
|
||||||
|
client->data_socket = listener_socket;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_pwd(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 257, "\"%s\"", client->directory);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_quit(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 221, STATUS_221);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_retr(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (client->data_socket == -1) {
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_simple_response(client->socket, 150, STATUS_150);
|
||||||
|
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||||
|
if (data_sock < 0) {
|
||||||
|
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
lftpd_inet_close(client->data_socket);
|
||||||
|
client->data_socket = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||||
|
lftpd_log_debug("send '%s'", path);
|
||||||
|
int err = send_file(data_sock, path);
|
||||||
|
lftpd_inet_close(data_sock);
|
||||||
|
client->data_socket = -1;
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
lftpd_log_info("DOWNLOAD '%s' OK", path);
|
||||||
|
send_simple_response(client->socket, 226, STATUS_226);
|
||||||
|
} else {
|
||||||
|
lftpd_log_error("DOWNLOAD '%s' FAILED", path);
|
||||||
|
send_simple_response(client->socket, 450, STATUS_450);
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_size(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (arg == NULL) {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||||
|
lftpd_log_debug("size %s", path);
|
||||||
|
|
||||||
|
uint32_t size;
|
||||||
|
int is_dir;
|
||||||
|
if (lftpd_io_stat(path, &size, &is_dir) == 0 && !is_dir) {
|
||||||
|
send_simple_response(client->socket, 213, "%lu", (unsigned long)size);
|
||||||
|
} else {
|
||||||
|
send_simple_response(client->socket, 550, STATUS_550);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_stor(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
if (client->data_socket == -1) {
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_simple_response(client->socket, 150, STATUS_150);
|
||||||
|
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||||
|
if (data_sock < 0) {
|
||||||
|
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||||
|
send_simple_response(client->socket, 425, STATUS_425);
|
||||||
|
lftpd_inet_close(client->data_socket);
|
||||||
|
client->data_socket = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||||
|
lftpd_log_debug("receive '%s'", path);
|
||||||
|
int err = receive_file(data_sock, path);
|
||||||
|
lftpd_inet_close(data_sock);
|
||||||
|
client->data_socket = -1;
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
uint32_t size = 0;
|
||||||
|
int is_dir = 0;
|
||||||
|
lftpd_io_stat(path, &size, &is_dir);
|
||||||
|
lftpd_log_info("UPLOAD '%s' %lu bytes OK", path, (unsigned long)size);
|
||||||
|
send_simple_response(client->socket, 226, STATUS_226);
|
||||||
|
} else {
|
||||||
|
lftpd_log_error("UPLOAD '%s' FAILED", path);
|
||||||
|
send_simple_response(client->socket, 450, STATUS_450);
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_syst(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 215, "UNIX Type: L8");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_type(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 200, STATUS_200);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_user(lftpd_client_t *client, const char *arg)
|
||||||
|
{
|
||||||
|
send_simple_response(client->socket, 230, STATUS_230);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_control_channel(lftpd_client_t *client)
|
||||||
|
{
|
||||||
|
int err = send_simple_response(client->socket, 220, STATUS_220);
|
||||||
|
if (err != 0) {
|
||||||
|
lftpd_log_error("error sending welcome message");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
char read_buffer[512];
|
||||||
|
|
||||||
|
while (err == 0) {
|
||||||
|
int line_len = lftpd_inet_read_line(client->socket, read_buffer,
|
||||||
|
sizeof(read_buffer));
|
||||||
|
if (line_len != 0) {
|
||||||
|
lftpd_log_error("error reading next command");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index;
|
||||||
|
char *p = strchr(read_buffer, ' ');
|
||||||
|
if (p != NULL) {
|
||||||
|
index = (int)(p - read_buffer);
|
||||||
|
} else {
|
||||||
|
index = (int)strlen(read_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 5) {
|
||||||
|
err = send_simple_response(client->socket, 500, STATUS_500);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char command_tmp[4 + 1];
|
||||||
|
memset(command_tmp, 0, sizeof(command_tmp));
|
||||||
|
memcpy(command_tmp, read_buffer, (size_t)index);
|
||||||
|
|
||||||
|
for (int i = 0; command_tmp[i]; i++) {
|
||||||
|
command_tmp[i] = (char)toupper((int)command_tmp[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matched = false;
|
||||||
|
for (int i = 0; commands[i].command; i++) {
|
||||||
|
if (strcmp(commands[i].command, command_tmp) == 0) {
|
||||||
|
char arg_buf[512];
|
||||||
|
const char *arg = NULL;
|
||||||
|
|
||||||
|
if (index < (int)strlen(read_buffer)) {
|
||||||
|
strncpy(arg_buf, read_buffer + index + 1, sizeof(arg_buf) - 1);
|
||||||
|
arg_buf[sizeof(arg_buf) - 1] = '\0';
|
||||||
|
arg = lftpd_string_trim(arg_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_log_info("CMD %s %s", command_tmp, arg ? arg : "");
|
||||||
|
err = commands[i].handler(client, arg);
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched) {
|
||||||
|
send_simple_response(client->socket, 502, STATUS_502);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
lftpd_inet_close(client->socket);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_start(const char *directory, int port, lftpd_t *lftpd)
|
||||||
|
{
|
||||||
|
memset(lftpd, 0, sizeof(lftpd_t));
|
||||||
|
|
||||||
|
lftpd->directory = directory;
|
||||||
|
lftpd->port = port;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
lftpd->server_socket = lftpd_inet_listen(port);
|
||||||
|
if (lftpd->server_socket < 0) {
|
||||||
|
lftpd_log_error("error creating listener");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_log_info("waiting for connection...");
|
||||||
|
|
||||||
|
int client_socket = lftpd_inet_accept(lftpd->server_socket);
|
||||||
|
if (client_socket < 0) {
|
||||||
|
lftpd_log_error("error accepting client socket");
|
||||||
|
lftpd_inet_close(lftpd->server_socket);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char ip_str[16];
|
||||||
|
struct net_sockaddr_in addr;
|
||||||
|
int addrlen = sizeof(addr);
|
||||||
|
if (net_getsockname(client_socket,
|
||||||
|
(struct net_sockaddr *)&addr, &addrlen) == 0) {
|
||||||
|
net_inet_ntoa(addr.sin_addr.s_addr, ip_str);
|
||||||
|
int remote_port = lftpd_inet_get_socket_port(client_socket);
|
||||||
|
lftpd_log_info("connection received from %s:%d...",
|
||||||
|
ip_str, remote_port);
|
||||||
|
} else {
|
||||||
|
lftpd_log_info("connection received...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lftpd_client_t client;
|
||||||
|
memset(&client, 0, sizeof(client));
|
||||||
|
strncpy(client.directory, directory, sizeof(client.directory) - 1);
|
||||||
|
client.directory[sizeof(client.directory) - 1] = '\0';
|
||||||
|
client.socket = client_socket;
|
||||||
|
client.data_socket = -1;
|
||||||
|
|
||||||
|
lftpd->client = &client;
|
||||||
|
handle_control_channel(&client);
|
||||||
|
lftpd->client = NULL;
|
||||||
|
|
||||||
|
/* standalone mode: socket was closed in handle_control_channel,
|
||||||
|
* loop back to create a new listener */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_stop(lftpd_t *lftpd)
|
||||||
|
{
|
||||||
|
if (lftpd->server_socket >= 0) {
|
||||||
|
lftpd_inet_close(lftpd->server_socket);
|
||||||
|
}
|
||||||
|
if (lftpd->client) {
|
||||||
|
lftpd_inet_close(lftpd->client->socket);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
19
Drivers/BSP/NET/lftpd/lftpd.h
Normal file
19
Drivers/BSP/NET/lftpd/lftpd.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char directory[256];
|
||||||
|
int socket;
|
||||||
|
int data_socket;
|
||||||
|
} lftpd_client_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *directory;
|
||||||
|
int port;
|
||||||
|
int server_socket;
|
||||||
|
lftpd_client_t *client;
|
||||||
|
} lftpd_t;
|
||||||
|
|
||||||
|
int lftpd_start(const char *directory, int port, lftpd_t *lftpd);
|
||||||
|
int lftpd_stop(lftpd_t *lftpd);
|
||||||
166
Drivers/BSP/NET/lftpd/lftpd_inet.c
Normal file
166
Drivers/BSP/NET/lftpd/lftpd_inet.c
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
#define DBG_TAG "[LFT_INET]"
|
||||||
|
|
||||||
|
#include "private/lftpd_inet.h"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
#include "net_socket.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int lftpd_inet_listen(int port)
|
||||||
|
{
|
||||||
|
int retry;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
for (retry = 0; retry < 5; retry++) {
|
||||||
|
s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||||
|
if (s < 0) {
|
||||||
|
if (retry == 0) DBG_ERROR("create socket");
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct net_sockaddr_in addr;
|
||||||
|
addr.sin_family = NET_AF_INET;
|
||||||
|
addr.sin_port = net_htons((uint16_t)port);
|
||||||
|
addr.sin_addr.s_addr = 0;
|
||||||
|
|
||||||
|
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
DBG_ERROR("bind port %d", port);
|
||||||
|
net_close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (net_listen(s) >= 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_close(s);
|
||||||
|
if (retry < 4) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_ERROR("listen failed after 5 retries (total 10s wait)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_get_socket_port(int socket)
|
||||||
|
{
|
||||||
|
struct net_sockaddr_in addr;
|
||||||
|
int addrlen = sizeof(addr);
|
||||||
|
|
||||||
|
if (net_getsockname(socket, (struct net_sockaddr *)&addr, &addrlen) < 0) {
|
||||||
|
DBG_ERROR("error getting socket port number");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)net_ntohs(addr.sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_accept(int socket)
|
||||||
|
{
|
||||||
|
while (1) {
|
||||||
|
int s = net_accept(socket, NULL, NULL);
|
||||||
|
if (s >= 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_accept_timeout(int socket, int timeout_ms)
|
||||||
|
{
|
||||||
|
int elapsed = 0;
|
||||||
|
|
||||||
|
while (elapsed < timeout_ms) {
|
||||||
|
int s = net_accept(socket, NULL, NULL);
|
||||||
|
if (s >= 0) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
/* 数据连接已被对端关闭时提前退出,不等待超时 */
|
||||||
|
net_sock_t *p_sock = net_get_sock(socket);
|
||||||
|
if (p_sock == NULL || p_sock->state != NET_SOCK_STATE_LISTENING) {
|
||||||
|
DBG_ERROR("data socket closed, accept aborted");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
elapsed += 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_ERROR("accept timeout (%dms)", timeout_ms);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len)
|
||||||
|
{
|
||||||
|
memset(buffer, 0, buffer_len);
|
||||||
|
int total_read_len = 0;
|
||||||
|
|
||||||
|
while (total_read_len < (int)buffer_len) {
|
||||||
|
int read_len = net_recv(socket,
|
||||||
|
buffer + total_read_len,
|
||||||
|
buffer_len - total_read_len - 1,
|
||||||
|
0);
|
||||||
|
if (read_len == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (read_len < 0) {
|
||||||
|
return read_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_read_len += read_len;
|
||||||
|
|
||||||
|
char *p = strstr(buffer, "\r\n");
|
||||||
|
if (p != NULL) {
|
||||||
|
*p = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_write_string(int socket, const char *message)
|
||||||
|
{
|
||||||
|
const char *p = message;
|
||||||
|
int length = (int)strlen(message);
|
||||||
|
|
||||||
|
while (length > 0) {
|
||||||
|
int write_len = net_send(socket, p, length, 0);
|
||||||
|
if (write_len < 0) {
|
||||||
|
DBG_ERROR("write error");
|
||||||
|
return write_len;
|
||||||
|
}
|
||||||
|
p += write_len;
|
||||||
|
length -= write_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_write(int socket, const void *buf, int len)
|
||||||
|
{
|
||||||
|
const char *p = (const char *)buf;
|
||||||
|
int remaining = len;
|
||||||
|
|
||||||
|
while (remaining > 0) {
|
||||||
|
int n = net_send(socket, p, remaining, 0);
|
||||||
|
if (n < 0) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
p += n;
|
||||||
|
remaining -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_read(int socket, void *buf, int len)
|
||||||
|
{
|
||||||
|
int n = net_recv(socket, buf, len, 0);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_inet_close(int socket)
|
||||||
|
{
|
||||||
|
return net_close(socket);
|
||||||
|
}
|
||||||
236
Drivers/BSP/NET/lftpd/lftpd_io.c
Normal file
236
Drivers/BSP/NET/lftpd/lftpd_io.c
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "ff.h"
|
||||||
|
#include "private/lftpd_io.h"
|
||||||
|
|
||||||
|
#define DBG_TAG "[LFT_IO]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FatFS 在 FF_USE_LFN=0 时不接受 "/" 前缀路径。
|
||||||
|
* 剥离前导 "/" 后用 FatFS 兼容路径,根目录用 "0:"(驱动号格式)替代。
|
||||||
|
*/
|
||||||
|
static const char *to_fatfs_path(const char *path)
|
||||||
|
{
|
||||||
|
if (path == NULL || *path == '\0') {
|
||||||
|
return "0:";
|
||||||
|
}
|
||||||
|
if (*path == '/') {
|
||||||
|
path++;
|
||||||
|
if (*path == '\0') {
|
||||||
|
return "0:";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s_file_open = 0;
|
||||||
|
static FIL s_ftp_file;
|
||||||
|
|
||||||
|
char *lftpd_io_canonicalize_path(const char *base, const char *name)
|
||||||
|
{
|
||||||
|
if (base == NULL) {
|
||||||
|
base = "";
|
||||||
|
}
|
||||||
|
if (name == NULL) {
|
||||||
|
name = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path;
|
||||||
|
if (name[0] == '/') {
|
||||||
|
path = (char *)malloc(strlen(name) + 1);
|
||||||
|
if (path == NULL) return NULL;
|
||||||
|
strcpy(path, name);
|
||||||
|
} else {
|
||||||
|
size_t len = strlen(base) + 1 + strlen(name) + 1;
|
||||||
|
path = (char *)malloc(len);
|
||||||
|
if (path == NULL) return NULL;
|
||||||
|
snprintf(path, len, "%s/%s", base, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *abs_path = (char *)malloc(strlen(path) + 2);
|
||||||
|
if (abs_path == NULL) {
|
||||||
|
free(path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
abs_path[0] = '\0';
|
||||||
|
|
||||||
|
char *p = path;
|
||||||
|
while (*p != '\0') {
|
||||||
|
while (*p == '/') p++;
|
||||||
|
if (*p == '\0') break;
|
||||||
|
|
||||||
|
char *seg_start = p;
|
||||||
|
while (*p != '\0' && *p != '/') p++;
|
||||||
|
|
||||||
|
char saved = *p;
|
||||||
|
*p = '\0';
|
||||||
|
|
||||||
|
if (strcmp(seg_start, ".") == 0) {
|
||||||
|
/* ignore */
|
||||||
|
} else if (strcmp(seg_start, "..") == 0) {
|
||||||
|
char *slash = strrchr(abs_path, '/');
|
||||||
|
if (slash != NULL) {
|
||||||
|
*slash = '\0';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strcat(abs_path, "/");
|
||||||
|
strcat(abs_path, seg_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = saved;
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
|
||||||
|
if (strlen(abs_path) == 0) {
|
||||||
|
strcpy(abs_path, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
return abs_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_open_read(const char *path)
|
||||||
|
{
|
||||||
|
if (s_file_open) {
|
||||||
|
DBG_ERROR("open_read '%s': already open", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char *fpath = to_fatfs_path(path);
|
||||||
|
FRESULT fr = f_open(&s_ftp_file, fpath, FA_READ);
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
DBG_ERROR("open_read '%s': f_open err=%d", fpath, (int)fr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s_file_open = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_open_write(const char *path)
|
||||||
|
{
|
||||||
|
if (s_file_open) {
|
||||||
|
DBG_ERROR("open_write '%s': already open", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const char *fpath = to_fatfs_path(path);
|
||||||
|
FRESULT fr = f_open(&s_ftp_file, fpath, FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
|
if (fr != FR_OK) {
|
||||||
|
DBG_ERROR("open_write '%s': f_open err=%d", fpath, (int)fr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s_file_open = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_read(int handle, void *buf, int len)
|
||||||
|
{
|
||||||
|
(void)handle;
|
||||||
|
UINT br;
|
||||||
|
if (f_read(&s_ftp_file, buf, (UINT)len, &br) != FR_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (int)br;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_write(int handle, const void *buf, int len)
|
||||||
|
{
|
||||||
|
(void)handle;
|
||||||
|
UINT bw;
|
||||||
|
if (f_write(&s_ftp_file, buf, (UINT)len, &bw) != FR_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((int)bw != len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (int)bw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lftpd_io_close(int handle)
|
||||||
|
{
|
||||||
|
(void)handle;
|
||||||
|
if (s_file_open) {
|
||||||
|
f_close(&s_ftp_file);
|
||||||
|
s_file_open = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_stat(const char *path, uint32_t *p_size, int *p_is_dir)
|
||||||
|
{
|
||||||
|
FILINFO fno;
|
||||||
|
const char *fpath = to_fatfs_path(path);
|
||||||
|
|
||||||
|
if (f_stat(fpath, &fno) != FR_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_size != NULL) {
|
||||||
|
*p_size = (uint32_t)fno.fsize;
|
||||||
|
}
|
||||||
|
if (p_is_dir != NULL) {
|
||||||
|
*p_is_dir = (fno.fattrib & AM_DIR) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_unlink(const char *path)
|
||||||
|
{
|
||||||
|
const char *fpath = to_fatfs_path(path);
|
||||||
|
if (f_unlink(fpath) == FR_OK) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *lftpd_io_opendir(const char *path)
|
||||||
|
{
|
||||||
|
DIR *dp = (DIR *)malloc(sizeof(DIR));
|
||||||
|
if (dp == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
const char *fpath = to_fatfs_path(path);
|
||||||
|
if (f_opendir(dp, fpath) != FR_OK) {
|
||||||
|
free(dp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (void *)dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lftpd_io_readdir(void *dp, char *name, int name_max, uint32_t *p_size, int *p_is_dir)
|
||||||
|
{
|
||||||
|
FILINFO fno;
|
||||||
|
FRESULT fr;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
fr = f_readdir((DIR *)dp, &fno);
|
||||||
|
if (fr != FR_OK || fno.fname[0] == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fno.fname[0] == '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(name, fno.fname, (size_t)(name_max - 1));
|
||||||
|
name[name_max - 1] = '\0';
|
||||||
|
|
||||||
|
if (p_size != NULL) {
|
||||||
|
*p_size = (uint32_t)fno.fsize;
|
||||||
|
}
|
||||||
|
if (p_is_dir != NULL) {
|
||||||
|
*p_is_dir = (fno.fattrib & AM_DIR) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lftpd_io_closedir(void *dp)
|
||||||
|
{
|
||||||
|
if (dp != NULL) {
|
||||||
|
f_closedir((DIR *)dp);
|
||||||
|
free(dp);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Drivers/BSP/NET/lftpd/lftpd_log.c
Normal file
18
Drivers/BSP/NET/lftpd/lftpd_log.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include "private/lftpd_log.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
|
void lftpd_log_internal(const char* level, const char* format, ...) {
|
||||||
|
char buffer[256];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
int err = vsnprintf(buffer, sizeof(buffer), format, args);
|
||||||
|
va_end(args);
|
||||||
|
if (err >= sizeof(buffer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("%s %s\n", level, buffer);
|
||||||
|
}
|
||||||
16
Drivers/BSP/NET/lftpd/lftpd_string.c
Normal file
16
Drivers/BSP/NET/lftpd/lftpd_string.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "private/lftpd_string.h"
|
||||||
|
|
||||||
|
char *lftpd_string_trim(char *s)
|
||||||
|
{
|
||||||
|
char *p = s;
|
||||||
|
for (int i = 0, len = (int)strlen(s); i < len && isspace((int)s[i]); i++) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
for (int i = (int)strlen(p); i >= 0 && isspace((int)p[i]); i--) {
|
||||||
|
p[i] = '\0';
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
14
Drivers/BSP/NET/lftpd/private/lftpd_inet.h
Normal file
14
Drivers/BSP/NET/lftpd/private/lftpd_inet.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int lftpd_inet_listen(int port);
|
||||||
|
int lftpd_inet_get_socket_port(int socket);
|
||||||
|
int lftpd_inet_accept(int socket);
|
||||||
|
int lftpd_inet_accept_timeout(int socket, int timeout_ms);
|
||||||
|
int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len);
|
||||||
|
int lftpd_inet_write_string(int socket, const char *message);
|
||||||
|
int lftpd_inet_write(int socket, const void *buf, int len);
|
||||||
|
int lftpd_inet_read(int socket, void *buf, int len);
|
||||||
|
int lftpd_inet_close(int socket);
|
||||||
18
Drivers/BSP/NET/lftpd/private/lftpd_io.h
Normal file
18
Drivers/BSP/NET/lftpd/private/lftpd_io.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
char *lftpd_io_canonicalize_path(const char *base, const char *name);
|
||||||
|
|
||||||
|
int lftpd_io_open_read(const char *path);
|
||||||
|
int lftpd_io_open_write(const char *path);
|
||||||
|
int lftpd_io_read(int handle, void *buf, int len);
|
||||||
|
int lftpd_io_write(int handle, const void *buf, int len);
|
||||||
|
void lftpd_io_close(int handle);
|
||||||
|
|
||||||
|
int lftpd_io_stat(const char *path, uint32_t *p_size, int *p_is_dir);
|
||||||
|
int lftpd_io_unlink(const char *path);
|
||||||
|
|
||||||
|
void *lftpd_io_opendir(const char *path);
|
||||||
|
int lftpd_io_readdir(void *dp, char *name, int name_max, uint32_t *p_size, int *p_is_dir);
|
||||||
|
void lftpd_io_closedir(void *dp);
|
||||||
7
Drivers/BSP/NET/lftpd/private/lftpd_log.h
Normal file
7
Drivers/BSP/NET/lftpd/private/lftpd_log.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
#define lftpd_log_error(format, ...) DBG_ERROR("[FTP] " format, ##__VA_ARGS__)
|
||||||
|
#define lftpd_log_info(format, ...) DBG_INFO("[FTP] " format, ##__VA_ARGS__)
|
||||||
|
#define lftpd_log_debug(format, ...)
|
||||||
42
Drivers/BSP/NET/lftpd/private/lftpd_status.h
Normal file
42
Drivers/BSP/NET/lftpd/private/lftpd_status.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define STATUS_110 "Restart marker reply."
|
||||||
|
#define STATUS_120 "Service ready in %d minutes."
|
||||||
|
#define STATUS_125 "Data connection already open; transfer starting."
|
||||||
|
#define STATUS_150 "File status okay; about to open data connection."
|
||||||
|
#define STATUS_200 "Command okay."
|
||||||
|
#define STATUS_202 "Command not implemented, superfluous at this site."
|
||||||
|
#define STATUS_211 "System status, or system help reply."
|
||||||
|
#define STATUS_212 "Directory status."
|
||||||
|
#define STATUS_213 "File status."
|
||||||
|
#define STATUS_214 "Help message."
|
||||||
|
#define STATUS_215 "%s system type."
|
||||||
|
#define STATUS_220 "Service ready for new user."
|
||||||
|
#define STATUS_221 "Service closing control connection."
|
||||||
|
#define STATUS_225 "Data connection open; no transfer in progress."
|
||||||
|
#define STATUS_226 "Closing data connection."
|
||||||
|
#define STATUS_227 "Entering Passive Mode (%d,%d,%d,%d,%d,%d)."
|
||||||
|
#define STATUS_229 "Entering Extended Passive Mode (|||%d|)."
|
||||||
|
#define STATUS_230 "User logged in, proceed."
|
||||||
|
#define STATUS_250 "Requested file action okay, completed."
|
||||||
|
#define STATUS_257 "\"%s\" created."
|
||||||
|
#define STATUS_331 "User name okay, need password."
|
||||||
|
#define STATUS_332 "Need account for login."
|
||||||
|
#define STATUS_350 "Requested file action pending further information."
|
||||||
|
#define STATUS_421 "Service not available, closing control connection."
|
||||||
|
#define STATUS_425 "Can't open data connection."
|
||||||
|
#define STATUS_426 "Connection closed; transfer aborted."
|
||||||
|
#define STATUS_450 "Requested file action not taken. File unavailable (e.g., file busy)."
|
||||||
|
#define STATUS_451 "Requested action aborted: local error in processing."
|
||||||
|
#define STATUS_452 "Requested action not taken. Insufficient storage space in system."
|
||||||
|
#define STATUS_500 "Syntax error, command unrecognized. This may include errors such as command line too long."
|
||||||
|
#define STATUS_501 "Syntax error in parameters or arguments."
|
||||||
|
#define STATUS_502 "Command not implemented."
|
||||||
|
#define STATUS_503 "Bad sequence of commands."
|
||||||
|
#define STATUS_504 "Command not implemented for that parameter."
|
||||||
|
#define STATUS_530 "Not logged in."
|
||||||
|
#define STATUS_532 "Need account for storing files."
|
||||||
|
#define STATUS_550 "Requested action not taken. File unavailable (e.g., file not found, no access)."
|
||||||
|
#define STATUS_551 "Requested action aborted: page type unknown."
|
||||||
|
#define STATUS_552 "Requested file action aborted. Exceeded storage allocation (for current directory or dataset)."
|
||||||
|
#define STATUS_553 "Requested action not taken. File name not allowed."
|
||||||
5
Drivers/BSP/NET/lftpd/private/lftpd_string.h
Normal file
5
Drivers/BSP/NET/lftpd/private/lftpd_string.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CRLF "\r\n"
|
||||||
|
|
||||||
|
char* lftpd_string_trim(char* s);
|
||||||
104
Drivers/BSP/NET/net_config.h
Normal file
104
Drivers/BSP/NET/net_config.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Configuration
|
||||||
|
* 模块功能:网络子系统配置宏定义,包括最大Socket数量、缓冲区大小、
|
||||||
|
* 超时时间等参数配置
|
||||||
|
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_CONFIG_H
|
||||||
|
#define __NET_CONFIG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 最大支持的 Socket 数量(CH395F 支持 0~7)
|
||||||
|
*/
|
||||||
|
#ifndef NET_MAX_SOCKETS
|
||||||
|
#define NET_MAX_SOCKETS 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TCP Server 多连接模式配置
|
||||||
|
* 监听 Socket 索引和数据 Socket 起始索引
|
||||||
|
*/
|
||||||
|
#ifndef NET_TCP_SERVER_LISTEN_SOCK
|
||||||
|
#define NET_TCP_SERVER_LISTEN_SOCK 0 /* 监听 Socket 索引 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 缓冲区大小配置
|
||||||
|
*/
|
||||||
|
#ifndef NET_SEND_BUF_SIZE
|
||||||
|
#define NET_SEND_BUF_SIZE 4096 /* 发送缓冲区大小(字节) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NET_RECV_BUF_SIZE
|
||||||
|
#define NET_RECV_BUF_SIZE 4096 /* 接收缓冲区大小(字节) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 超时时间配置(毫秒)
|
||||||
|
* 0 = 无限等待(阻塞模式)
|
||||||
|
*/
|
||||||
|
#ifndef NET_CONNECT_TIMEOUT_MS
|
||||||
|
#define NET_CONNECT_TIMEOUT_MS 5000 /* 连接超时 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NET_ACCEPT_TIMEOUT_MS
|
||||||
|
#define NET_ACCEPT_TIMEOUT_MS 0 /* 接受连接超时(0=无限等待) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NET_SEND_TIMEOUT_MS
|
||||||
|
#define NET_SEND_TIMEOUT_MS 1000 /* 发送超时 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NET_RECV_TIMEOUT_MS
|
||||||
|
#define NET_RECV_TIMEOUT_MS 30000 /* 接收超时(30秒,0=无限等待) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TCP KeepAlive 配置
|
||||||
|
*/
|
||||||
|
#ifndef NET_KEEPALIVE_IDLE_MS
|
||||||
|
#define NET_KEEPALIVE_IDLE_MS 7200000 /* KeepAlive 空闲时间(默认2小时) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NET_KEEPALIVE_INTVL_MS
|
||||||
|
#define NET_KEEPALIVE_INTVL_MS 75000 /* KeepAlive 探测间隔(默认75秒) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NET_KEEPALIVE_CNT
|
||||||
|
#define NET_KEEPALIVE_CNT 9 /* KeepAlive 探测次数(默认9次) */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* select/poll 最大 fd 数量
|
||||||
|
*/
|
||||||
|
#ifndef NET_SELECT_MAX_FDS
|
||||||
|
#define NET_SELECT_MAX_FDS NET_MAX_SOCKETS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 轮询延时配置(毫秒)
|
||||||
|
*/
|
||||||
|
#ifndef NET_POLL_DELAY_MS
|
||||||
|
#define NET_POLL_DELAY_MS 1 /* 非阻塞轮询延时 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 消息队列配置
|
||||||
|
*/
|
||||||
|
#ifndef NET_MSG_QUEUE_LENGTH
|
||||||
|
#define NET_MSG_QUEUE_LENGTH 8 /* 消息队列深度 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NET_CONFIG_H */
|
||||||
320
Drivers/BSP/NET/net_select.c
Normal file
320
Drivers/BSP/NET/net_select.c
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Select Implementation
|
||||||
|
* 模块功能:I/O 多路复用实现,包括 select 和 poll 两种模式,用于同时监控多个 Socket 的读写事件
|
||||||
|
* 适用平台:STM32F407ZGTx + CH395F 以太网芯片
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
* 2026-07-18 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "cmsis_os.h" /* CMSIS-RTOS2 API: osDelay() */
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#include "net_select.h"
|
||||||
|
#include "net_socket.h"
|
||||||
|
#include "ch395f.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 私有函数声明区
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int check_socket_readable(int sockfd);
|
||||||
|
static int check_socket_writable(int sockfd);
|
||||||
|
static int check_socket_error(int sockfd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 公共函数实现区
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:I/O 多路复用(select 模式),等待多个 Socket 的读写事件就绪
|
||||||
|
* 入口参数:nfds - 最大文件描述符数 +1 int > 0
|
||||||
|
* readfds - 读事件集合指针 net_fd_set *(可为 NULL)
|
||||||
|
* writefds - 写事件集合指针 net_fd_set *(可为 NULL)
|
||||||
|
* exceptfds- 异常事件集合指针 net_fd_set *(可为 NULL)
|
||||||
|
* timeout - 超时时间结构体指针 net_timeval *(NULL=无限等待)
|
||||||
|
* 出口参数:readfds/writefds/exceptfds - 仅保留就绪的 Socket 位 net_fd_set *
|
||||||
|
* 返回值:就绪的 Socket 数量(>= 0);-1 表示参数无效 int
|
||||||
|
* 限定条件:net_init() + MX_FREERTOS_Init() 已执行;可在任意任务上下文调用
|
||||||
|
* 函数说明:1. 轮询 Socket 控制块(RAM)中的读/写/异常状态,判断事件是否就绪
|
||||||
|
* 2. 不在此直接访问 CH395F SPI——所有 ch395f 事务统一由 netTask 的 net_poll()
|
||||||
|
* 每 10ms 串行执行并刷新控制块状态(send_ready / readable),本函数仅做
|
||||||
|
* RAM 读取,避免与应用任务发起的 SPI 访问竞争(见 Trap 16)
|
||||||
|
* 3. 阻塞等待直到有事件就绪或超时
|
||||||
|
* 4. 通过 osDelay(1) 让出 CPU,平衡实时性与 CPU 利用率(状态刷新延迟 <=10ms)
|
||||||
|
*/
|
||||||
|
int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
|
||||||
|
net_fd_set *exceptfds, net_timeval *timeout) {
|
||||||
|
uint32_t start_tick;
|
||||||
|
uint32_t timeout_ms = 0;
|
||||||
|
net_fd_set result_read = {0};
|
||||||
|
net_fd_set result_write = {0};
|
||||||
|
net_fd_set result_except = {0};
|
||||||
|
int ready_count = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* 检查输入参数合法性 */
|
||||||
|
if ((0 >= nfds) || (NET_MAX_SOCKETS < nfds)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 计算超时时间(毫秒) */
|
||||||
|
if (NULL != timeout) {
|
||||||
|
timeout_ms = (uint32_t)(timeout->tv_sec * 1000) +
|
||||||
|
(uint32_t)(timeout->tv_usec / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
start_tick = HAL_GetTick();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* 仅读取 netTask 的 net_poll() 已刷新的控制块状态(无 CH395F SPI 访问) */
|
||||||
|
ready_count = 0;
|
||||||
|
|
||||||
|
/* 检查哪个 Socket 有事件 */
|
||||||
|
for (i = 0; i < nfds && i < NET_MAX_SOCKETS; i++) {
|
||||||
|
net_sock_t *p_sock = net_get_sock(i);
|
||||||
|
|
||||||
|
if (NULL == p_sock) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查可读事件 */
|
||||||
|
if ((NULL != readfds) && NET_FD_ISSET(i, readfds)) {
|
||||||
|
if (check_socket_readable(i)) {
|
||||||
|
NET_FD_SET(i, &result_read);
|
||||||
|
ready_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查可写事件 */
|
||||||
|
if ((NULL != writefds) && NET_FD_ISSET(i, writefds)) {
|
||||||
|
if (check_socket_writable(i)) {
|
||||||
|
NET_FD_SET(i, &result_write);
|
||||||
|
ready_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查异常事件 */
|
||||||
|
if ((NULL != exceptfds) && NET_FD_ISSET(i, exceptfds)) {
|
||||||
|
if (check_socket_error(i)) {
|
||||||
|
NET_FD_SET(i, &result_except);
|
||||||
|
ready_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 有事件就绪,返回 */
|
||||||
|
if (0 < ready_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 超时检测 */
|
||||||
|
if (NULL != timeout) {
|
||||||
|
if ((0 < timeout_ms) &&
|
||||||
|
((HAL_GetTick() - start_tick) >= timeout_ms)) {
|
||||||
|
break; /* 超时返回 0 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 让出 CPU:FreeRTOS osDelay() 会正确进入休眠,允许其他任务运行 */
|
||||||
|
osDelay(1); /* 1ms 周期轮询,平衡实时性和 CPU 利用率 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 复制结果集合 */
|
||||||
|
if (NULL != readfds) {
|
||||||
|
readfds->fd_bits = result_read.fd_bits;
|
||||||
|
}
|
||||||
|
if (NULL != writefds) {
|
||||||
|
writefds->fd_bits = result_write.fd_bits;
|
||||||
|
}
|
||||||
|
if (NULL != exceptfds) {
|
||||||
|
exceptfds->fd_bits = result_except.fd_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ready_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:I/O 多路复用(poll 模式),等待多个 Socket 的读写事件就绪
|
||||||
|
* 入口参数:fds - pollfd 数组指针 net_pollfd *
|
||||||
|
* nfds - 数组元素数量 int > 0
|
||||||
|
* timeout - 超时时间(毫秒) int >= 0(-1=无限等待,但本实现按 >=0 处理)
|
||||||
|
* 出口参数:fds[i].revents - 填充实际发生的事件掩码 short
|
||||||
|
* 返回值:就绪的 fd 数量(>= 0);-1 表示参数无效 int
|
||||||
|
* 限定条件:net_init() + MX_FREERTOS_Init() 已执行;可在任意任务上下文调用
|
||||||
|
* 函数说明:1. 轮询 Socket 控制块(RAM)中的读/写/错误/挂起状态,判断事件是否就绪
|
||||||
|
* 2. 不在此直接访问 CH395F SPI——状态由 netTask 的 net_poll() 每 10ms 刷新
|
||||||
|
* 3. 阻塞等待直到有事件就绪或超时
|
||||||
|
* 4. 通过 osDelay(1) 让出 CPU,平衡实时性与 CPU 利用率(状态刷新延迟 <=10ms)
|
||||||
|
*/
|
||||||
|
int net_poll_events(net_pollfd *fds, int nfds, int timeout) {
|
||||||
|
uint32_t start_tick;
|
||||||
|
int ready = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if ((NULL == fds) || (0 >= nfds)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
start_tick = HAL_GetTick();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* 仅读取 netTask 的 net_poll() 已刷新的控制块状态(无 CH395F SPI 访问) */
|
||||||
|
ready = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < nfds; i++) {
|
||||||
|
fds[i].revents = 0;
|
||||||
|
|
||||||
|
/* 检查 fd 有效性 */
|
||||||
|
if ((0 > fds[i].fd) || (NET_MAX_SOCKETS <= fds[i].fd)) {
|
||||||
|
fds[i].revents |= NET_POLLNVAL;
|
||||||
|
ready++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_sock_t *p_sock = net_get_sock(fds[i].fd);
|
||||||
|
|
||||||
|
if (NULL == p_sock) {
|
||||||
|
fds[i].revents |= NET_POLLNVAL;
|
||||||
|
ready++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查可读 */
|
||||||
|
if (fds[i].events & NET_POLLIN) {
|
||||||
|
if (check_socket_readable(fds[i].fd)) {
|
||||||
|
fds[i].revents |= NET_POLLIN;
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查可写 */
|
||||||
|
if (fds[i].events & NET_POLLOUT) {
|
||||||
|
if (check_socket_writable(fds[i].fd)) {
|
||||||
|
fds[i].revents |= NET_POLLOUT;
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查错误 */
|
||||||
|
if (check_socket_error(fds[i].fd)) {
|
||||||
|
fds[i].revents |= NET_POLLERR;
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 检查挂起(连接断开) */
|
||||||
|
if (NET_SOCK_STATE_CLOSED == p_sock->state) {
|
||||||
|
fds[i].revents |= NET_POLLHUP;
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < ready) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 超时检测 */
|
||||||
|
if (0 <= timeout) {
|
||||||
|
if ((HAL_GetTick() - start_tick) >= (uint32_t)timeout) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 让出 CPU:FreeRTOS osDelay() 会正确进入休眠,允许其他任务运行 */
|
||||||
|
osDelay(1); /* 1ms 周期轮询,平衡实时性和 CPU 利用率 */
|
||||||
|
}
|
||||||
|
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 私有函数实现区
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:检查指定 Socket 当前是否可读
|
||||||
|
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||||
|
* 出口参数:无
|
||||||
|
* 返回值:1 - 可读(监听态有新连接,或已连接态接收缓冲非空);0 - 不可读或描述符无效 int
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:1. 监听态 Socket 视为可读(存在可 accept 的新连接)
|
||||||
|
* 2. 已建立连接且接收缓冲区长度大于 0 时视为可读
|
||||||
|
*/
|
||||||
|
static int check_socket_readable(int sockfd) {
|
||||||
|
net_sock_t *p_sock;
|
||||||
|
|
||||||
|
p_sock = net_get_sock(sockfd);
|
||||||
|
if (NULL == p_sock) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 监听 Socket 有新连接时可读 */
|
||||||
|
if (NET_SOCK_STATE_LISTENING == p_sock->state) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 已建立连接或 UDP 等有数据可读:readable 由 net_poll 在 RECV_OK 时置位、
|
||||||
|
* net_recv_locked 读空后清零。避免应用任务直接访问 CH395F SPI(见 Trap 16) */
|
||||||
|
if (p_sock->readable) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:检查指定 Socket 当前是否可写
|
||||||
|
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||||
|
* 出口参数:无
|
||||||
|
* 返回值:1 - 可写(已连接且发送缓冲空闲);0 - 不可写或描述符无效 int
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:1. 仅已建立连接且 send_ready 标志置位的 Socket 视为可写
|
||||||
|
*/
|
||||||
|
static int check_socket_writable(int sockfd) {
|
||||||
|
net_sock_t *p_sock;
|
||||||
|
|
||||||
|
p_sock = net_get_sock(sockfd);
|
||||||
|
if (NULL == p_sock) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 已建立连接的 Socket 发送缓冲区空闲时可写 */
|
||||||
|
if (NET_SOCK_STATE_ESTABLISHED == p_sock->state) {
|
||||||
|
if (p_sock->send_ready) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:检查指定 Socket 是否存在错误状态
|
||||||
|
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||||
|
* 出口参数:无
|
||||||
|
* 返回值:1 - 有错误(含描述符越界或 Socket 未分配);0 - 无错误 int
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:1. 描述符越界视为错误
|
||||||
|
* 2. 未分配(net_get_sock 返回 NULL)的 Socket 视为错误
|
||||||
|
*/
|
||||||
|
static int check_socket_error(int sockfd) {
|
||||||
|
net_sock_t *p_sock;
|
||||||
|
|
||||||
|
/* 无效 fd 视为错误 */
|
||||||
|
if ((0 > sockfd) || (NET_MAX_SOCKETS <= sockfd)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_sock = net_get_sock(sockfd);
|
||||||
|
if (NULL == p_sock) {
|
||||||
|
return 1; /* 未使用的 Socket 视为错误 */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
114
Drivers/BSP/NET/net_select.h
Normal file
114
Drivers/BSP/NET/net_select.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Select API
|
||||||
|
* 模块功能:I/O 多路复用接口,支持 select 和 poll 模式,
|
||||||
|
* 用于同时监控多个 Socket 的读写事件
|
||||||
|
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_SELECT_H
|
||||||
|
#define __NET_SELECT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "net_types.h"
|
||||||
|
#include "net_config.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 文件描述符集合
|
||||||
|
* 每个位对应一个 socket 描述符 (0~7)
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t fd_bits; /* 位掩码 */
|
||||||
|
} net_fd_set;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 时间结构体
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
long tv_sec; /* 秒 */
|
||||||
|
long tv_usec; /* 微秒 */
|
||||||
|
} net_timeval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* poll 事件结构体
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int fd; /* socket 描述符 */
|
||||||
|
short events; /* 请求的事件 */
|
||||||
|
short revents; /* 实际发生的事件 */
|
||||||
|
} net_pollfd;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* poll 事件掩码
|
||||||
|
*/
|
||||||
|
#define NET_POLLIN 0x0001 /* 可读 */
|
||||||
|
#define NET_POLLOUT 0x0004 /* 可写 */
|
||||||
|
#define NET_POLLERR 0x0008 /* 错误 */
|
||||||
|
#define NET_POLLHUP 0x0010 /* 挂起 */
|
||||||
|
#define NET_POLLNVAL 0x0020 /* 无效请求 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fd_set 操作宏
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:清空 fd_set
|
||||||
|
*/
|
||||||
|
#define NET_FD_ZERO(fdset) ((fdset)->fd_bits = 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将 fd 加入 fd_set
|
||||||
|
*/
|
||||||
|
#define NET_FD_SET(fd, fdset) ((fdset)->fd_bits |= (1U << (fd)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将 fd 从 fd_set 移除
|
||||||
|
*/
|
||||||
|
#define NET_FD_CLR(fd, fdset) ((fdset)->fd_bits &= ~(1U << (fd)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:检查 fd 是否在 fd_set 中
|
||||||
|
*/
|
||||||
|
#define NET_FD_ISSET(fd, fdset) (((fdset)->fd_bits & (1U << (fd))) != 0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I/O 多路复用 API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:I/O 多路复用(select 模式)
|
||||||
|
* 入口参数:nfds - 最大 fd + 1(通常为 NET_MAX_SOCKETS)
|
||||||
|
* readfds - 可读事件集合,NULL 表示不关心
|
||||||
|
* writefds - 可写事件集合,NULL 表示不关心
|
||||||
|
* exceptfds - 异常事件集合,NULL 表示不关心
|
||||||
|
* timeout - 超时时间,NULL 表示无限等待
|
||||||
|
* 返回值:就绪的 fd 数量,0=超时,-1=错误
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:此函数会调用 net_poll() 轮询所有 Socket,
|
||||||
|
* 并检查哪些 Socket 有指定的事件发生。
|
||||||
|
*/
|
||||||
|
int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
|
||||||
|
net_fd_set *exceptfds, net_timeval *timeout);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:I/O 多路复用(poll 模式)
|
||||||
|
* 入口参数:fds - net_pollfd 数组
|
||||||
|
* nfds - 数组元素数量
|
||||||
|
* timeout - 超时时间(毫秒),-1 表示无限等待
|
||||||
|
* 返回值:就绪的 fd 数量,0=超时,-1=错误
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:与 select 类似,但使用 pollfd 结构体数组,
|
||||||
|
* 更灵活,支持更多事件类型。
|
||||||
|
*/
|
||||||
|
int net_poll_events(net_pollfd *fds, int nfds, int timeout);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NET_SELECT_H */
|
||||||
1876
Drivers/BSP/NET/net_socket.c
Normal file
1876
Drivers/BSP/NET/net_socket.c
Normal file
File diff suppressed because it is too large
Load Diff
343
Drivers/BSP/NET/net_socket.h
Normal file
343
Drivers/BSP/NET/net_socket.h
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Socket API
|
||||||
|
* 模块功能:BSD Socket API 兼容接口,提供 TCP/UDP 网络通信功能,
|
||||||
|
* 底层基于 CH395F 以太网协议栈芯片
|
||||||
|
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_SOCKET_H
|
||||||
|
#define __NET_SOCKET_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "net_types.h"
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 消息队列类型定义
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NET_MSG_SEND,
|
||||||
|
NET_MSG_RECV,
|
||||||
|
NET_MSG_CLOSE,
|
||||||
|
NET_MSG_CONNECT,
|
||||||
|
NET_MSG_LISTEN,
|
||||||
|
NET_MSG_ACCEPT,
|
||||||
|
} net_msg_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
net_msg_type_t type; /* 操作类型 */
|
||||||
|
int sockfd; /* Socket 描述符 */
|
||||||
|
void *buf; /* 数据缓冲区(send/recv) */
|
||||||
|
int len; /* 数据长度/缓冲区大小 */
|
||||||
|
int flags; /* 接收标志 */
|
||||||
|
TaskHandle_t caller; /* 调用方任务句柄 */
|
||||||
|
int result; /* 操作结果 */
|
||||||
|
/* 连接参数 */
|
||||||
|
struct net_sockaddr_in addr; /* 连接地址 */
|
||||||
|
int addrlen; /* 地址长度 */
|
||||||
|
} net_msg_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 网络初始化接口
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:网络子系统初始化(配置 CH395F + 启用多连接模式)
|
||||||
|
* 入口参数:ip - 本地 IP 地址字符串 "192.168.1.100",NULL 使用 DHCP
|
||||||
|
* mask - 子网掩码字符串 "255.255.255.0",NULL 使用默认
|
||||||
|
* gateway - 网关地址字符串 "192.168.1.1",NULL 使用默认
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:SPI2 已正确初始化
|
||||||
|
* 函数说明:必须在所有网络操作之前调用
|
||||||
|
*/
|
||||||
|
int net_init(const char *ip, const char *mask, const char *gateway);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:轮询所有 Socket 状态(必须在主循环中调用)
|
||||||
|
* 返回值:有事件发生的 socket 数量
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:此函数处理所有 Socket 的中断事件,更新状态,
|
||||||
|
* 并触发事件回调。在非阻塞模式下必须定期调用。
|
||||||
|
*/
|
||||||
|
int net_poll(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 核心 Socket API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:创建 socket
|
||||||
|
* 入口参数:domain - 地址族,仅支持 AF_INET
|
||||||
|
* type - SOCK_STREAM(TCP) 或 SOCK_DGRAM(UDP)
|
||||||
|
* 返回值:socket 描述符 (0~7),失败返回 -1
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:自动分配空闲 Socket 控制块
|
||||||
|
*/
|
||||||
|
int net_socket(int domain, int type);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:绑定本地地址和端口
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* addr - 本地地址结构体指针
|
||||||
|
* addrlen - 地址结构体长度
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:socket 已创建且未绑定
|
||||||
|
* 函数说明:设置 CH395F 源端口
|
||||||
|
*/
|
||||||
|
int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:TCP Server 监听
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:socket 已绑定
|
||||||
|
* 函数说明:内部调用 ch395f_tcp_listen,监听 Socket 自身即为数据通道
|
||||||
|
*/
|
||||||
|
int net_listen(int sockfd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:TCP Server 接受连接
|
||||||
|
* 入口参数:sockfd - 监听 socket 描述符
|
||||||
|
* addr - 输出:客户端地址
|
||||||
|
* addrlen - 输入输出:地址长度
|
||||||
|
* 返回值:新 socket 描述符,失败返回 -1
|
||||||
|
* 限定条件:socket 正在监听
|
||||||
|
* 函数说明:在非阻塞模式下,如果没有新连接,返回 -1 并设置
|
||||||
|
* net_errno 为 NET_ERR_WOULDBLOCK
|
||||||
|
*/
|
||||||
|
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:TCP Client 发起连接
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* addr - 服务器地址
|
||||||
|
* addrlen - 地址长度
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:socket 已创建
|
||||||
|
* 函数说明:连接过程是异步的,需要调用 net_poll() 等待连接完成,
|
||||||
|
* 或使用 select() 等待可写事件
|
||||||
|
*/
|
||||||
|
int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:发送数据(TCP)
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* buf - 数据缓冲区
|
||||||
|
* len - 数据长度
|
||||||
|
* flags - 通常为 0
|
||||||
|
* 返回值:实际发送字节数,失败返回 -1
|
||||||
|
* 限定条件:TCP 连接已建立,指针非空
|
||||||
|
* 函数说明:阻塞等待发送缓冲区空闲,拷贝数据后触发发送
|
||||||
|
*/
|
||||||
|
int net_send(int sockfd, const void *buf, int len, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:接收数据(TCP)
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* buf - 接收缓冲区
|
||||||
|
* len - 缓冲区大小
|
||||||
|
* flags - 通常为 0,可设置 NET_MSG_DONTWAIT 非阻塞
|
||||||
|
* 返回值:实际接收字节数,0=对端关闭,-1=错误
|
||||||
|
* 限定条件:TCP 连接已建立
|
||||||
|
* 函数说明:在阻塞模式下,如果没有数据,函数会等待;
|
||||||
|
* 在非阻塞模式下,如果没有数据,返回 -1 并设置
|
||||||
|
* net_errno 为 NET_ERR_WOULDBLOCK
|
||||||
|
*/
|
||||||
|
int net_recv(int sockfd, void *buf, int len, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:发送 UDP 数据
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* buf - 数据缓冲区
|
||||||
|
* len - 数据长度
|
||||||
|
* flags - 通常为 0
|
||||||
|
* dest_addr - 目标地址
|
||||||
|
* addrlen - 地址长度
|
||||||
|
* 返回值:实际发送字节数,失败返回 -1
|
||||||
|
* 限定条件:socket 已创建,指针非空
|
||||||
|
* 函数说明:设置目标地址后调用 CH395F 发送
|
||||||
|
*/
|
||||||
|
int net_sendto(int sockfd, const void *buf, int len, int flags,
|
||||||
|
const struct net_sockaddr *dest_addr, int addrlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:接收 UDP 数据
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* buf - 接收缓冲区
|
||||||
|
* len - 缓冲区大小
|
||||||
|
* flags - 通常为 0
|
||||||
|
* src_addr - 输出:发送方地址
|
||||||
|
* addrlen - 输入输出:地址长度
|
||||||
|
* 返回值:实际接收字节数,失败返回 -1
|
||||||
|
* 限定条件:socket 已创建,指针非空
|
||||||
|
* 函数说明:读取 CH395F 接收缓冲区,获取对端地址
|
||||||
|
*/
|
||||||
|
int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||||
|
struct net_sockaddr *src_addr, int *addrlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:关闭 socket
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:socket 已打开
|
||||||
|
* 函数说明:释放 Socket 控制块,TCP 先断开连接
|
||||||
|
*/
|
||||||
|
int net_close(int sockfd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 事件回调 API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:注册事件回调
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* cb - 回调函数
|
||||||
|
* arg - 用户参数
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:socket 已创建
|
||||||
|
* 函数说明:回调在 net_poll 上下文中触发
|
||||||
|
*/
|
||||||
|
int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 辅助函数
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取 socket 最后错误码
|
||||||
|
* 返回值:错误码(NET_ERR_xxx)
|
||||||
|
*/
|
||||||
|
int net_get_errno(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将 IP 地址字符串转为网络字节序
|
||||||
|
* 入口参数:cp - 点分十进制字符串 "192.168.1.100"
|
||||||
|
* 返回值:网络字节序 IP,失败返回 0
|
||||||
|
* 限定条件:cp 指针非空
|
||||||
|
* 函数说明:不支持域名解析
|
||||||
|
*/
|
||||||
|
uint32_t net_inet_addr(const char *cp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:将网络字节序 IP 转为字符串
|
||||||
|
* 入口参数:addr - 网络字节序 IP
|
||||||
|
* buf - 输出缓冲区(至少 16 字节)
|
||||||
|
* 返回值:buf 指针
|
||||||
|
* 限定条件:buf 指针非空
|
||||||
|
*/
|
||||||
|
char *net_inet_ntoa(uint32_t addr, char *buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:端口字节序转换(主机序 -> 网络序)
|
||||||
|
* 入口参数:hostshort - 主机序端口
|
||||||
|
* 返回值:网络序端口
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:小端 → 大端
|
||||||
|
*/
|
||||||
|
uint16_t net_htons(uint16_t hostshort);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:端口字节序转换(网络序 -> 主机序)
|
||||||
|
* 入口参数:netshort - 网络序端口
|
||||||
|
* 返回值:主机序端口
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:大端 → 小端
|
||||||
|
*/
|
||||||
|
uint16_t net_ntohs(uint16_t netshort);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:IP 地址字节序转换(主机序 -> 网络序)
|
||||||
|
* 入口参数:hostlong - 主机序 IP
|
||||||
|
* 返回值:网络序 IP
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:小端 → 大端
|
||||||
|
*/
|
||||||
|
uint32_t net_htonl(uint32_t hostlong);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:IP 地址字节序转换(网络序 -> 主机序)
|
||||||
|
* 入口参数:netlong - 网络序 IP
|
||||||
|
* 返回值:主机序 IP
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:大端 → 小端
|
||||||
|
*/
|
||||||
|
uint32_t net_ntohl(uint32_t netlong);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:处理消息队列中的请求(必须在 netTask 主循环中调用)
|
||||||
|
* 限定条件:net_init() 已调用
|
||||||
|
* 函数说明:处理其他任务通过 net_send/net_recv 等接口发送的请求,
|
||||||
|
* 在 netTask 上下文中串行化执行所有 CH395F 操作
|
||||||
|
*/
|
||||||
|
void net_process_messages(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取 Socket 控制块指针(内部使用)
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* 返回值:Socket 控制块指针,失败返回 NULL
|
||||||
|
*/
|
||||||
|
net_sock_t *net_get_sock(int sockfd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:获取 Socket 本地地址(IP + Port)
|
||||||
|
* 入口参数:sockfd - socket 描述符
|
||||||
|
* addr - 输出:本地地址
|
||||||
|
* addrlen - 输入输出:地址结构体长度
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:socket 已创建或已绑定
|
||||||
|
*/
|
||||||
|
int net_getsockname(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 内部 API(需在 netTask 上下文中调用,绕过消息队列)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:发送数据(内部版本,直接操作,不经过消息队列)
|
||||||
|
* 入口参数:p_sock - Socket 控制块指针
|
||||||
|
* buf - 数据缓冲区
|
||||||
|
* len - 数据长度
|
||||||
|
* 返回值:实际发送字节数,失败返回 -1
|
||||||
|
* 限定条件:必须在 netTask 上下文中调用
|
||||||
|
*/
|
||||||
|
int net_send_sock(net_sock_t *p_sock, const void *buf, int len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:接收数据(内部版本,直接操作,不经过消息队列)
|
||||||
|
* 入口参数:p_sock - Socket 控制块指针
|
||||||
|
* buf - 接收缓冲区
|
||||||
|
* len - 缓冲区大小
|
||||||
|
* flags - 通常为 0,可设置 NET_MSG_DONTWAIT
|
||||||
|
* 返回值:实际接收字节数,0=对端关闭,-1=错误
|
||||||
|
* 限定条件:必须在 netTask 上下文中调用
|
||||||
|
*/
|
||||||
|
int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:关闭 socket(内部版本,直接操作,不经过消息队列)
|
||||||
|
* 入口参数:p_sock - Socket 控制块指针
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:必须在 netTask 上下文中调用
|
||||||
|
*/
|
||||||
|
int net_close_sock(net_sock_t *p_sock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:启动 TCP Server 监听(内部版本,直接操作,不经过消息队列)
|
||||||
|
* 入口参数:sockfd - Socket 描述符
|
||||||
|
* 返回值:0 成功,-1 失败
|
||||||
|
* 限定条件:必须在 netTask 上下文中调用
|
||||||
|
*/
|
||||||
|
int net_listen_locked(int sockfd);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NET_SOCKET_H */
|
||||||
178
Drivers/BSP/NET/net_types.h
Normal file
178
Drivers/BSP/NET/net_types.h
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Network Types
|
||||||
|
* 模块功能:网络子系统类型定义,包括地址结构体、Socket状态、
|
||||||
|
* 事件类型、Socket控制块等
|
||||||
|
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NET_TYPES_H
|
||||||
|
#define __NET_TYPES_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "net_config.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 地址族定义
|
||||||
|
*/
|
||||||
|
#define NET_AF_INET 2 /* IPv4 */
|
||||||
|
#define NET_AF_INET6 10 /* IPv6(未支持) */
|
||||||
|
#define NET_AF_UNSPEC 0 /* 未指定 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Socket 类型定义
|
||||||
|
*/
|
||||||
|
#define NET_SOCK_STREAM 1 /* TCP */
|
||||||
|
#define NET_SOCK_DGRAM 2 /* UDP */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 协议号定义
|
||||||
|
*/
|
||||||
|
#define NET_IPPROTO_TCP 6
|
||||||
|
#define NET_IPPROTO_UDP 17
|
||||||
|
#define NET_IPPROTO_ICMP 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 特殊地址定义
|
||||||
|
*/
|
||||||
|
#define NET_INADDR_ANY 0 /* 任意地址 */
|
||||||
|
#define NET_INADDR_NONE 0xFFFFFFFF /* 无效地址 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 接收标志定义
|
||||||
|
*/
|
||||||
|
#define NET_MSG_DONTWAIT 0x01 /* 非阻塞模式 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 网络字节序转换宏
|
||||||
|
*/
|
||||||
|
#ifdef __BIG_ENDIAN
|
||||||
|
#define NET_htonl(x) (x)
|
||||||
|
#define NET_htons(x) (x)
|
||||||
|
#define NET_ntohl(x) (x)
|
||||||
|
#define NET_ntohs(x) (x)
|
||||||
|
#else
|
||||||
|
#define NET_htonl(x) (((uint32_t)(x) << 24) | \
|
||||||
|
(((uint32_t)(x) & 0xFF00) << 8) | \
|
||||||
|
(((uint32_t)(x) & 0xFF0000) >> 8) | \
|
||||||
|
((uint32_t)(x) >> 24))
|
||||||
|
#define NET_htons(x) (((uint16_t)(x) << 8) | ((uint16_t)(x) >> 8))
|
||||||
|
#define NET_ntohl(x) NET_htonl(x)
|
||||||
|
#define NET_ntohs(x) NET_htons(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 套接字地址结构体
|
||||||
|
*/
|
||||||
|
struct net_sockaddr {
|
||||||
|
uint16_t sa_family; /* 地址族 */
|
||||||
|
char sa_data[14]; /* 地址数据 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IPv4 套接字地址结构体
|
||||||
|
*/
|
||||||
|
struct net_sockaddr_in {
|
||||||
|
uint16_t sin_family; /* 地址族,必须为 AF_INET */
|
||||||
|
uint16_t sin_port; /* 端口号(网络字节序) */
|
||||||
|
struct {
|
||||||
|
uint32_t s_addr; /* IP 地址(网络字节序) */
|
||||||
|
} sin_addr;
|
||||||
|
char sin_zero[8]; /* 填充字节 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Socket 错误码定义
|
||||||
|
*/
|
||||||
|
#define NET_OK 0 /* 成功 */
|
||||||
|
#define NET_ERR -1 /* 通用错误 */
|
||||||
|
#define NET_ERR_BADF -2 /* 无效 socket 描述符 */
|
||||||
|
#define NET_ERR_INVAL -3 /* 无效参数 */
|
||||||
|
#define NET_ERR_NOMEM -4 /* 内存不足 */
|
||||||
|
#define NET_ERR_NOTCONN -5 /* 未连接 */
|
||||||
|
#define NET_ERR_ISCONN -6 /* 已连接 */
|
||||||
|
#define NET_ERR_WOULDBLOCK -7 /* 非阻塞模式下操作未完成 */
|
||||||
|
#define NET_ERR_CONNRESET -8 /* 连接被重置 */
|
||||||
|
#define NET_ERR_TIMEDOUT -9 /* 操作超时 */
|
||||||
|
#define NET_ERR_NOSPACE -10 /* 发送缓冲区无空间 */
|
||||||
|
#define NET_ERR_BUSY -11 /* 操作未完成(CH395F BUSY) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Socket 状态枚举
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NET_SOCK_STATE_CLOSED = 0, /* 未使用 */
|
||||||
|
NET_SOCK_STATE_CREATED, /* 已创建,未绑定 */
|
||||||
|
NET_SOCK_STATE_BOUND, /* 已绑定 */
|
||||||
|
NET_SOCK_STATE_LISTENING, /* TCP Server 监听中(仅监听 Socket) */
|
||||||
|
NET_SOCK_STATE_CONNECTING, /* TCP Client 连接中 */
|
||||||
|
NET_SOCK_STATE_ESTABLISHED, /* TCP 已连接 */
|
||||||
|
NET_SOCK_STATE_UDP_OPEN, /* UDP 已打开 */
|
||||||
|
NET_SOCK_STATE_TCP_CLIENT_RECONNECTING, /* TCP Client 重连中 */
|
||||||
|
} net_sock_state_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 网络事件类型枚举
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
NET_EVENT_CONNECTED = 0, /* 连接建立 */
|
||||||
|
NET_EVENT_DISCONNECTED, /* 连接断开 */
|
||||||
|
NET_EVENT_DATA_RECEIVED, /* 收到数据 */
|
||||||
|
NET_EVENT_SEND_COMPLETE, /* 发送完成 */
|
||||||
|
NET_EVENT_TIMEOUT, /* 操作超时 */
|
||||||
|
NET_EVENT_ERROR, /* 错误发生 */
|
||||||
|
} net_event_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 事件回调函数类型定义
|
||||||
|
* 参数:sockfd - socket 描述符
|
||||||
|
* event - 事件类型
|
||||||
|
* arg - 用户参数
|
||||||
|
*/
|
||||||
|
typedef void (*net_event_cb_t)(int sockfd, net_event_t event, void *arg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Socket 控制块结构体
|
||||||
|
* 每个 socket 对应一个控制块,管理其状态和参数
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint8_t ch395_sock; /* CH395F 物理 Socket 索引 (0~7) */
|
||||||
|
net_sock_state_t state; /* Socket 状态 */
|
||||||
|
uint8_t type; /* SOCK_STREAM / SOCK_DGRAM */
|
||||||
|
uint8_t in_use; /* 是否在用标志 */
|
||||||
|
uint16_t local_port; /* 本地端口(主机序) */
|
||||||
|
uint16_t remote_port; /* 远端端口(主机序) */
|
||||||
|
uint32_t remote_ip; /* 远端 IP(网络字节序) */
|
||||||
|
uint8_t remote_ip_arr[4]; /* 远端 IP 数组(大端/网络字节序) */
|
||||||
|
|
||||||
|
/* 事件回调(可选) */
|
||||||
|
net_event_cb_t event_cb;
|
||||||
|
void *event_arg;
|
||||||
|
|
||||||
|
/* 发送就绪标志 */
|
||||||
|
uint8_t send_ready;
|
||||||
|
|
||||||
|
/* 接收可读标志:net_poll 检测到 RECV_OK 时置 1,net_recv_locked 读空后清零。
|
||||||
|
* net_select 据此判断可读性,从而无需在应用/测试任务上下文直接访问 CH395F SPI
|
||||||
|
* (所有 ch395f 事务统一由 netTask 串行执行,避免与 net_poll 竞争,见 Trap 16)。 */
|
||||||
|
uint8_t readable;
|
||||||
|
|
||||||
|
/* 单连接模式自动重监听:连接断开后由 net 层自动回到 LISTEN(BSD 风格监听常驻) */
|
||||||
|
uint8_t auto_relisten;
|
||||||
|
|
||||||
|
/* 统计信息 */
|
||||||
|
uint32_t recv_bytes; /* 累计接收字节数 */
|
||||||
|
uint32_t send_bytes; /* 累计发送字节数 */
|
||||||
|
} net_sock_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __NET_TYPES_H */
|
||||||
@@ -1,133 +1,152 @@
|
|||||||
/*
|
/*
|
||||||
* 模块名称:RS-485 半双工通信驱动
|
* 模块名称:RS-485 半双工通信驱动
|
||||||
* 模块功能:RS-485 半双工通信驱动实现,核心要点:
|
* 模块功能:RS-485 半双工通信驱动实现,封装任意 UART 外设实现 485 方向控制,
|
||||||
* 1. 发送前拉高 DE(切换到发送态)
|
* 提供阻塞发送、中断接收(IDLE 空闲帧检测)。使用 DWT CYCCNT 提供
|
||||||
* 2. HAL_UART_Transmit 内部已等待 TC(发送完成)标志
|
* 微秒级延时,确保 RS485 收发器 DE 使能稳定。
|
||||||
* 3. 发送完毕后拉低 DE(切回接收态)
|
|
||||||
* 4. 使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收
|
|
||||||
* 参考:ST AN3070 应用笔记、controllerstech.com RS485 教程
|
|
||||||
* 适用平台:STM32F4 系列
|
* 适用平台:STM32F4 系列
|
||||||
* 作者:王建锋
|
* 作者:王建锋
|
||||||
* 创建日期:2026-07-18
|
* 创建日期:2026-07-18
|
||||||
* 修改记录:
|
* 修改记录:
|
||||||
* 2026-07-18 王建锋 创建初始版本
|
* 2026-07-18 王建锋 创建初始版本
|
||||||
|
* 2026-07-20 王建锋 修复 DE 时序:添加 DWT 微秒延时替代 for 循环
|
||||||
|
* 2026-07-20 王建锋 清理中文注释乱码,对齐代码规范
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rs485.h"
|
#include "rs485.h"
|
||||||
|
|
||||||
|
/* ======================== 内部辅助函数 ======================== */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 内部辅助宏 - 方向控制
|
* 函数功能:DWT 微秒级延时
|
||||||
|
* 入口参数:us - 延时微秒数 uint32_t 1 - N
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:无(首次调用自动初始化 DWT CYCCNT)
|
||||||
|
* 函数说明:利用 Cortex-M4 DWT CYCCNT 实现精确微秒延时,168MHz 下 1us = 168 计数。
|
||||||
|
* 首次调用时自动使能 DWT 和 CYCCNT,后续只读 CYCCNT 寄存器,不影响其他模块。
|
||||||
*/
|
*/
|
||||||
|
static void delay_us(uint32_t us)
|
||||||
|
{
|
||||||
|
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
|
||||||
|
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||||
|
DWT->CYCCNT = 0;
|
||||||
|
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||||
|
}
|
||||||
|
uint32_t start = DWT->CYCCNT;
|
||||||
|
uint32_t ticks = us * (SystemCoreClock / 1000000uL);
|
||||||
|
while ((DWT->CYCCNT - start) < ticks) { ; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ======================== 方向控制宏 ======================== */
|
||||||
|
|
||||||
#define RS485_DIR_TX(h) HAL_GPIO_WritePin((h)->dir_port, (h)->dir_pin, GPIO_PIN_SET)
|
#define RS485_DIR_TX(h) HAL_GPIO_WritePin((h)->dir_port, (h)->dir_pin, GPIO_PIN_SET)
|
||||||
#define RS485_DIR_RX(h) HAL_GPIO_WritePin((h)->dir_port, (h)->dir_pin, GPIO_PIN_RESET)
|
#define RS485_DIR_RX(h) HAL_GPIO_WritePin((h)->dir_port, (h)->dir_pin, GPIO_PIN_RESET)
|
||||||
|
|
||||||
/*
|
/* ======================== 公共函数实现 ======================== */
|
||||||
* 公共函数实现
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:初始化 RS-485 句柄
|
* 函数功能:初始化 RS-485 句柄
|
||||||
* 入口参数:handle - RS-485 句柄指针
|
* 入口参数:p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
|
||||||
* huart - UART 外设句柄
|
* p_huart - UART 外设句柄指针(已由 CubeMX 初始化) UART_HandleTypeDef*(非空)
|
||||||
* dir_port - DE 引脚 GPIO 端口
|
* p_dir_port - 方向控制 GPIO 端口(如 GPIOD) GPIO_TypeDef*(非空)
|
||||||
* dir_pin - DE 引脚 GPIO 编号
|
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0) uint16_t
|
||||||
* 返回值:无
|
* 返回值:无
|
||||||
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
|
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
|
||||||
* 函数说明:初始化后立即将 DE 拉低,使 RS-485 收发器处于接收状态
|
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
|
||||||
*/
|
*/
|
||||||
void rs485_init(rs485_handle_t *handle,
|
void rs485_init(rs485_handle_t *p_handle,
|
||||||
UART_HandleTypeDef *huart,
|
UART_HandleTypeDef *p_huart,
|
||||||
GPIO_TypeDef *dir_port,
|
GPIO_TypeDef *p_dir_port,
|
||||||
uint16_t dir_pin)
|
uint16_t dir_pin)
|
||||||
{
|
{
|
||||||
handle->huart = huart;
|
p_handle->huart = p_huart;
|
||||||
handle->dir_port = dir_port;
|
p_handle->dir_port = p_dir_port;
|
||||||
handle->dir_pin = dir_pin;
|
p_handle->dir_pin = dir_pin;
|
||||||
handle->rx_size = 0;
|
p_handle->rx_size = 0;
|
||||||
|
|
||||||
/* 默认进入接收状态 */
|
RS485_DIR_RX(p_handle);
|
||||||
RS485_DIR_RX(handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:阻塞方式发送数据
|
* 函数功能:阻塞方式发送数据
|
||||||
* 入口参数:handle - RS-485 句柄指针
|
* 入口参数:p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
|
||||||
* data - 待发送数据缓冲区
|
* p_data - 待发送数据缓冲区 uint8_t*(非空)
|
||||||
* len - 待发送字节数
|
* len - 待发送字节数 uint16_t 1 - N
|
||||||
* timeout - 发送超时(ms),传 0 使用默认值
|
* timeout - 发送超时(ms),传 0 使用 RS485_TIMEOUT_DEFAULT uint32_t 0 / 1 - N
|
||||||
* 返回值:HAL_OK / HAL_TIMEOUT / HAL_ERROR
|
* 返回值:HAL_OK - 发送成功 HAL_StatusTypeDef
|
||||||
* 限定条件:须在主循环或 RTOS 任务中调用,不可在 UART 中断中调用
|
* HAL_TIMEOUT - 发送超时
|
||||||
* 函数说明:
|
* HAL_ERROR - 发送错误
|
||||||
* 时序:DE=HIGH → UART 发送 N 字节 → 等 TC → DE=LOW
|
* 限定条件:须在主循环或 FreeRTOS 任务中调用,不可在 UART 中断中调用
|
||||||
* HAL_UART_Transmit 内部会等待 TXE(发送寄存器空)逐字节搬数据,
|
* 函数说明:发送前拉高 DE,等待 50us 确保 RS485 收发器驱动使能稳定后再启动 UART 发送。
|
||||||
* 最后还会等待 TC(发送完成)标志,确保最后一字节的停止位已移出。
|
* HAL_UART_Transmit 内部等待 TC(发送完成)标志,确保最后一字节完全移出后
|
||||||
* 因此返回后再拉低 DE 是安全的
|
* 再拉低 DE 切回接收状态。
|
||||||
*/
|
*/
|
||||||
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *handle,
|
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||||
const uint8_t *data,
|
const uint8_t *p_data,
|
||||||
uint16_t len,
|
uint16_t len,
|
||||||
uint32_t timeout)
|
uint32_t timeout)
|
||||||
{
|
{
|
||||||
HAL_StatusTypeDef status;
|
HAL_StatusTypeDef status = HAL_OK;
|
||||||
|
|
||||||
if (timeout == 0U) {
|
if (timeout == 0U) {
|
||||||
timeout = RS485_TIMEOUT_DEFAULT;
|
timeout = RS485_TIMEOUT_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1. 切换到发送状态 */
|
RS485_DIR_TX(p_handle);
|
||||||
RS485_DIR_TX(handle);
|
|
||||||
|
|
||||||
/* 2. 阻塞发送(内部等待 TC) */
|
delay_us(50);
|
||||||
status = HAL_UART_Transmit(handle->huart,
|
|
||||||
(uint8_t *)data,
|
status = HAL_UART_Transmit(p_handle->huart,
|
||||||
|
(uint8_t *)p_data,
|
||||||
len,
|
len,
|
||||||
timeout);
|
timeout);
|
||||||
|
|
||||||
/* 3. 发送完毕或超时,切回接收状态 */
|
RS485_DIR_RX(p_handle);
|
||||||
RS485_DIR_RX(handle);
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
|
* 函数功能:启动中断方式接收(IDLE 空闲帧检测)
|
||||||
* 入口参数:handle - RS-485 句柄指针
|
* 入口参数:p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
|
||||||
* buf - 接收缓冲区
|
* p_buf - 接收缓冲区 uint8_t*(非空)
|
||||||
* buf_size - 缓冲区最大容量
|
* buf_size - 缓冲区大小 uint16_t
|
||||||
* 返回值:HAL_OK / HAL_ERROR
|
* 返回值:HAL_OK - 启动成功 HAL_StatusTypeDef
|
||||||
|
* HAL_ERROR - 启动失败
|
||||||
* 限定条件:UART 须已开启全局中断(NVIC 使能)
|
* 限定条件:UART 须已开启全局中断(NVIC 使能)
|
||||||
* 函数说明:
|
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收。
|
||||||
* 使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收。
|
* 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
|
||||||
* 当 UART 总线空闲超过 1 个字符时间后,硬件触发 IDLE 中断,
|
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
|
||||||
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
|
* 用户须在回调中重新调用本函数重新开启接收。
|
||||||
* 用户须在该回调中重新调用本函数重新开启接收
|
|
||||||
*/
|
*/
|
||||||
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *handle,
|
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
|
||||||
uint8_t *buf,
|
uint8_t *p_buf,
|
||||||
uint16_t buf_size)
|
uint16_t buf_size)
|
||||||
{
|
{
|
||||||
handle->rx_size = 0;
|
p_handle->rx_size = 0;
|
||||||
return HAL_UARTEx_ReceiveToIdle_IT(handle->huart, buf, buf_size);
|
return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_buf, buf_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:缓存最近一次接收的字节数
|
* 函数功能:缓存最近一次接收的字节数
|
||||||
* 入口参数:handle - RS-485 句柄指针
|
* 入口参数:p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
|
||||||
* size - 本次接收到的字节数
|
* size - 本次接收到的字节数 uint16_t
|
||||||
* 返回值:无
|
* 返回值:无
|
||||||
* 限定条件:须在 HAL_UARTEx_RxEventCallback 中调用
|
* 限定条件:须在 HAL_UARTEx_RxEventCallback 中调用
|
||||||
|
* 函数说明:将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
|
||||||
*/
|
*/
|
||||||
void rs485_rx_set_size(rs485_handle_t *handle, uint16_t size)
|
void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size)
|
||||||
{
|
{
|
||||||
handle->rx_size = size;
|
p_handle->rx_size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:获取最近一次接收的字节数
|
* 函数功能:获取最近一次接收的字节数
|
||||||
* 入口参数:handle - RS-485 句柄指针
|
* 入口参数:p_handle - RS-485 句柄指针 const rs485_handle_t*(非空)
|
||||||
* 返回值:字节数
|
* 返回值:最近一次接收的字节数 uint16_t
|
||||||
|
* 限定条件:须在 HAL_UARTEx_RxEventCallback 触发后调用
|
||||||
|
* 函数说明:获取 rx_size 字段中缓存的接收字节数
|
||||||
*/
|
*/
|
||||||
uint16_t rs485_rx_get_size(const rs485_handle_t *handle)
|
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle)
|
||||||
{
|
{
|
||||||
return handle->rx_size;
|
return p_handle->rx_size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ typedef struct {
|
|||||||
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
|
* 限定条件:UART 和 GPIO 须先由 CubeMX 完成初始化
|
||||||
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
|
* 函数说明:初始化后默认处于接收状态(DE 引脚拉低)
|
||||||
*/
|
*/
|
||||||
void rs485_init(rs485_handle_t *handle,
|
void rs485_init(rs485_handle_t *p_handle,
|
||||||
UART_HandleTypeDef *huart,
|
UART_HandleTypeDef *p_huart,
|
||||||
GPIO_TypeDef *dir_port,
|
GPIO_TypeDef *p_dir_port,
|
||||||
uint16_t dir_pin);
|
uint16_t dir_pin);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -66,8 +66,8 @@ void rs485_init(rs485_handle_t *handle,
|
|||||||
* 函数说明:发送前自动拉高 DE,发送完成后等待 TC 标志再拉低 DE,
|
* 函数说明:发送前自动拉高 DE,发送完成后等待 TC 标志再拉低 DE,
|
||||||
* 确保最后一字节完全移出后再切换到接收状态
|
* 确保最后一字节完全移出后再切换到接收状态
|
||||||
*/
|
*/
|
||||||
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *handle,
|
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
|
||||||
const uint8_t *data,
|
const uint8_t *p_data,
|
||||||
uint16_t len,
|
uint16_t len,
|
||||||
uint32_t timeout);
|
uint32_t timeout);
|
||||||
|
|
||||||
@@ -82,8 +82,8 @@ HAL_StatusTypeDef rs485_transmit(rs485_handle_t *handle,
|
|||||||
* 收到完整帧后在 RxEventCallback 中通知用户。
|
* 收到完整帧后在 RxEventCallback 中通知用户。
|
||||||
* 用户须在回调中重新调用本函数重新开启接收
|
* 用户须在回调中重新调用本函数重新开启接收
|
||||||
*/
|
*/
|
||||||
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *handle,
|
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
|
||||||
uint8_t *buf,
|
uint8_t *p_buf,
|
||||||
uint16_t buf_size);
|
uint16_t buf_size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -93,7 +93,7 @@ HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *handle,
|
|||||||
* 限定条件:须在 HAL_UART_RxCpltCallback / HAL_UARTEx_RxEventCallback 中调用
|
* 限定条件:须在 HAL_UART_RxCpltCallback / HAL_UARTEx_RxEventCallback 中调用
|
||||||
* 函数说明:将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
|
* 函数说明:将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
|
||||||
*/
|
*/
|
||||||
void rs485_rx_set_size(rs485_handle_t *handle, uint16_t size);
|
void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:获取最近一次接收的字节数
|
* 函数功能:获取最近一次接收的字节数
|
||||||
@@ -101,7 +101,7 @@ void rs485_rx_set_size(rs485_handle_t *handle, uint16_t size);
|
|||||||
* 返回值:最近一次接收的字节数
|
* 返回值:最近一次接收的字节数
|
||||||
* 限定条件:须在 HAL_UARTEx_RxEventCallback 触发后调用
|
* 限定条件:须在 HAL_UARTEx_RxEventCallback 触发后调用
|
||||||
*/
|
*/
|
||||||
uint16_t rs485_rx_get_size(const rs485_handle_t *handle);
|
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,52 @@
|
|||||||
/*
|
/*
|
||||||
* 模块名称:SD2506API-G RTC 实时时钟驱动
|
* 模块名称:SD2506API-G RTC Driver
|
||||||
* 模块功能:SD2506API-G 高精度温补实时时钟模块 I2C 驱动
|
* 模块功能:SD2506API-G 高精度温补实时时钟驱动,I2C 接口
|
||||||
* 适用平台:STM32F407ZGT6,I2C1 接口 (PB6-SCL, PB7-SDA)
|
* 适用平台:STM32F407ZGTx + SD2506API-G 实时时钟芯片(I2C1: PB6-SCL, PB7-SDA)
|
||||||
* 作者:王建锋
|
* 作者:王建锋
|
||||||
* 创建日期:2026-07-17
|
* 创建日期:2026-07-17
|
||||||
* 修改记录:
|
* 修改记录:
|
||||||
* 2026-07-17 王建锋 创建初始版本,参考 SD2506API-G Ver2.0 手册
|
* 2026-07-17 王建锋 创建初始版本,参考 SD2506API-G Ver2.0 手册
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "sd2506.h"
|
#include "sd2506.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
extern I2C_HandleTypeDef hi2c1;
|
extern I2C_HandleTypeDef hi2c1;
|
||||||
|
|
||||||
/* ======================== 内部辅助函数 ======================== */
|
/* ======================== 内部辅助函数 ======================== */
|
||||||
|
|
||||||
uint8_t sd2506_bcd_to_dec(uint8_t bcd)
|
/*
|
||||||
{
|
* 函数功能:BCD 码转十进制数
|
||||||
|
* 入口参数:bcd - BCD 编码值 uint8_t 0x00 - 0x99
|
||||||
|
* 返回值:十进制数值 uint8_t
|
||||||
|
* 限定条件:输入为合法 BCD 编码(高 4 位和低 4 位均 <= 9)
|
||||||
|
* 函数说明:高 4 位为十位,低 4 位为个位,分别乘 10 和加后返回
|
||||||
|
*/
|
||||||
|
uint8_t sd2506_bcd_to_dec(uint8_t bcd) {
|
||||||
return ((bcd >> 4) * 10) + (bcd & 0x0FU);
|
return ((bcd >> 4) * 10) + (bcd & 0x0FU);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t sd2506_dec_to_bcd(uint8_t dec)
|
/*
|
||||||
{
|
* 函数功能:十进制转 BCD 码
|
||||||
|
* 入口参数:dec - 十进制数 uint8_t 0 - 99 */
|
||||||
|
uint8_t sd2506_dec_to_bcd(uint8_t dec) {
|
||||||
return ((dec / 10) << 4) | (dec % 10);
|
return ((dec / 10) << 4) | (dec % 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 写单字节寄存器
|
* 函数功能:写单字节寄存器
|
||||||
* reg: 寄存器地址 (00H~79H)
|
* 入口参数:reg - 寄存器地址 uint8_t 0x00 - 0x79
|
||||||
* val: 写入值
|
* val - 写入数据 uint8_t 0x00 - 0xFF
|
||||||
* 返回: 0=成功, -2=I2C错误
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:I2C 外设已初始化
|
||||||
|
* 函数说明:通过 I2C 向指定地址写入 1 字节数据
|
||||||
|
* 限定条件:I2C 外设已初始化
|
||||||
|
* 函数说明:通过 I2C 向指定地址写入 1 字节
|
||||||
*/
|
*/
|
||||||
static int sd2506_write_reg(uint8_t reg, uint8_t val)
|
static int sd2506_write_reg(uint8_t reg, uint8_t val) {
|
||||||
{
|
|
||||||
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||||
I2C_MEMADD_SIZE_8BIT, &val, 1,
|
I2C_MEMADD_SIZE_8BIT, &val, 1,
|
||||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||||
@@ -43,15 +56,17 @@ static int sd2506_write_reg(uint8_t reg, uint8_t val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 读单字节寄存器
|
* 函数功能:读单字节寄存器
|
||||||
* reg: 寄存器地址 (00H~79H)
|
* 入口参数:reg - 寄存器地址 uint8_t 0x00 - 0x79
|
||||||
* val: 读取值指针
|
* p_val - 读取值指针 uint8_t*(非空)
|
||||||
* 返回: 0=成功, -2=I2C错误
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:I2C 外设已初始化,p_val 为非空指针
|
||||||
|
* 函数说明:通过 I2C 从指定地址读取 1 字节数据
|
||||||
|
* 限定条件:I2C 外设已初始化,val 为非空指针函数说明:通过 I2C 从指定地址读取 1 字节
|
||||||
*/
|
*/
|
||||||
static int sd2506_read_reg(uint8_t reg, uint8_t *val)
|
static int sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
|
||||||
{
|
|
||||||
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||||
I2C_MEMADD_SIZE_8BIT, val, 1,
|
I2C_MEMADD_SIZE_8BIT, p_val, 1,
|
||||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||||
return SD2506_I2C_ERROR;
|
return SD2506_I2C_ERROR;
|
||||||
}
|
}
|
||||||
@@ -59,16 +74,18 @@ static int sd2506_read_reg(uint8_t reg, uint8_t *val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 写多字节寄存器 (从 reg 开始连续写入 len 字节)
|
* 函数功能:写多字节寄存器(连续写)
|
||||||
* reg: 起始寄存器地址
|
* 入口参数:reg - 起始寄存器地址 uint8_t 0x00 - 0x79
|
||||||
* data: 数据缓冲区
|
* p_data - 数据缓冲区指针 uint8_t*(非空)
|
||||||
* len: 数据长度
|
* len - 数据长度 uint8_t 1 - N
|
||||||
* 返回: 0=成功, -2=I2C错误
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:I2C 外设已初始化,p_data 为非空指针
|
||||||
|
* 函数说明:从 reg 开始连续写入 len 字节数据
|
||||||
|
* 限定条件:I2C 外设已初始化,data 为非空指针
|
||||||
*/
|
*/
|
||||||
static int sd2506_write_regs(uint8_t reg, const uint8_t *data, uint8_t len)
|
static int sd2506_write_regs(uint8_t reg, const uint8_t *p_data, uint8_t len) {
|
||||||
{
|
|
||||||
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
|
||||||
I2C_MEMADD_SIZE_8BIT, (uint8_t *)data, len,
|
I2C_MEMADD_SIZE_8BIT, (uint8_t *)p_data, len,
|
||||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||||
return SD2506_I2C_ERROR;
|
return SD2506_I2C_ERROR;
|
||||||
}
|
}
|
||||||
@@ -76,16 +93,18 @@ static int sd2506_write_regs(uint8_t reg, const uint8_t *data, uint8_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 读多字节寄存器 (从 reg 开始连续读取 len 字节)
|
* 函数功能:读多字节寄存器(连续读)
|
||||||
* reg: 起始寄存器地址
|
* 入口参数:reg - 起始寄存器地址 uint8_t 0x00 - 0x79
|
||||||
* data: 数据缓冲区
|
* p_data - 数据缓冲区指针 uint8_t*(非空)
|
||||||
* len: 数据长度
|
* len - 数据长度 uint8_t 1 - N
|
||||||
* 返回: 0=成功, -2=I2C错误
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:I2C 外设已初始化,p_data 为非空指针
|
||||||
|
* 函数说明:从 reg 开始连续读取 len 字节数据
|
||||||
|
* 限定条件:I2C 外设已初始化,data 为非空指针
|
||||||
*/
|
*/
|
||||||
static int sd2506_read_regs(uint8_t reg, uint8_t *data, uint8_t len)
|
static int sd2506_read_regs(uint8_t reg, uint8_t *p_data, uint8_t len) {
|
||||||
{
|
|
||||||
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
|
||||||
I2C_MEMADD_SIZE_8BIT, data, len,
|
I2C_MEMADD_SIZE_8BIT, p_data, len,
|
||||||
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
|
||||||
return SD2506_I2C_ERROR;
|
return SD2506_I2C_ERROR;
|
||||||
}
|
}
|
||||||
@@ -93,38 +112,41 @@ static int sd2506_read_regs(uint8_t reg, uint8_t *data, uint8_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 开启写保护 (允许写入 00H~71H 寄存器)
|
* 函数功能:开启写保护(允许写入 00H~71H 寄存器)
|
||||||
* 顺序: 先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功 2 - I2C 错误
|
||||||
|
* 限定条件:I2C 外设已初始化
|
||||||
|
* 函数说明:顺序:先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
|
||||||
*/
|
*/
|
||||||
static int sd2506_write_enable(void)
|
static int sd2506_write_enable(void) {
|
||||||
{
|
int ret = 0;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* 先置 WRTC1=1 (bit6=1), 其它位参考手册: 0x84 */
|
/* 写允许顺序(SD2506/SD24xx 手册要求):先置 WRTC1(CTR2 bit7)=1,
|
||||||
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
|
* 再置 WRTC2(CTR1 bit2) + WRTC3(CTR1 bit7)=1。顺序反了芯片不会解开写保护。 */
|
||||||
|
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x80U);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
/* 再置 WRTC2=1(bit5) + WRTC3=1(bit4): 0x8C */
|
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
|
||||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x8CU);
|
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 关闭写保护 (禁止写入 00H~71H 寄存器)
|
* 函数功能:关闭写保护(禁止写入 00H~71H 寄存器)
|
||||||
* 顺序: 先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
|
* 入口参数:无
|
||||||
|
* 返回值:0 - 成功 2 - I2C 错误
|
||||||
|
* 限定条件:I2C 外设已初始化
|
||||||
|
* 函数说明:顺序:先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
|
||||||
*/
|
*/
|
||||||
static int sd2506_write_disable(void)
|
static int sd2506_write_disable(void) {
|
||||||
{
|
int ret = 0;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* 先清 WRTC2=0, WRTC3=0: 0x00 */
|
/* 写禁止顺序(手册要求):先清 WRTC2/WRTC3(CTR1),再清 WRTC1(CTR2) */
|
||||||
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
|
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x00U);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
/* 再清 WRTC1=0, 同时 ARST=1 使能自动复位: 0x20 */
|
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
|
||||||
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
|
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
@@ -132,28 +154,40 @@ static int sd2506_write_disable(void)
|
|||||||
|
|
||||||
/* ======================== 公共 API 实现 ======================== */
|
/* ======================== 公共 API 实现 ======================== */
|
||||||
|
|
||||||
int sd2506_init(void)
|
/*
|
||||||
{
|
* 函数功能:初始化 SD2506API-G RTC
|
||||||
int ret;
|
* 入口参数:无
|
||||||
uint8_t id[8];
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:CubeMX 已完成 I2C1 初始化(PB6-SCL, PB7-SDA)
|
||||||
|
* 函数说明:1. 读取芯片 ID 验证通信
|
||||||
|
* 2. 上电重置充电寄存器 (0x18=0x82)
|
||||||
|
* 3. 配置 24 小时制、开自动复位
|
||||||
|
* 限定条件:CubeMX 已完成I2C1 初始化
|
||||||
|
* 函数说明:读取芯片 ID 验证通信
|
||||||
|
* 2. 上电重置充电寄存器18H=82H
|
||||||
|
* 3. 配置 24 小时制、开自动复位
|
||||||
|
*/
|
||||||
|
int sd2506_init(void) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t a_id[8] = {0};
|
||||||
|
|
||||||
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
|
/* 验证 I2C 通信: 尝试读取 8 字节 ID */
|
||||||
ret = sd2506_read_regs(SD2506_REG_ID_START, id, SD2506_ID_SIZE);
|
ret = sd2506_read_regs(SD2506_REG_ID_START, a_id, SD2506_ID_SIZE);
|
||||||
if (ret != SD2506_OK) {
|
if (ret != SD2506_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 上电重置充电寄存器 18H = 82H (开启充电, 5K电阻)
|
/* 上电重置充电寄存器 18H = 82H (开启充电 5K电阻)
|
||||||
* 手册强烈建议每次上电时重置此值 */
|
* 手册强烈建议每次上电时重置此寄存器 */
|
||||||
ret = sd2506_write_reg(SD2506_REG_CHARGE, 0x82U);
|
ret = sd2506_write_reg(SD2506_REG_CHARGE, 0x82U);
|
||||||
if (ret != SD2506_OK) {
|
if (ret != SD2506_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 配置: 24小时制, 自动复位使能
|
/* 配置: 24小时制,写允许状态下 0FH 的 WRTC 位必须为 1
|
||||||
* 0FH = 0x20 (bit5=ARST=1, 其它标志位清零)
|
* 0FH = 0x20 (bit5=ARST=1, 其它标志位清零)
|
||||||
* 写入时需注意: 写允许状态下 0FH 的 WRTC 位必须为1
|
* 注意: 写入时需注意 0FH 的 WRTC 位必须为 1
|
||||||
* 此处直接写入 0x20 即可 (ARST=1, 无 WRTC 位) */
|
* 此处直接写入 0x20 即可 (ARST=1, 的 WRTC 位) */
|
||||||
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
|
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
|
||||||
if (ret != SD2506_OK) {
|
if (ret != SD2506_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
@@ -162,30 +196,38 @@ int sd2506_init(void)
|
|||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_set_time(const sd2506_time_t *time)
|
/*
|
||||||
{
|
* 函数功能:设置 RTC 时间日期
|
||||||
int ret;
|
* 入口参数:p_time - 时间结构体指针,包含要设置的时间
|
||||||
uint8_t buf[7];
|
* 返回值:0 - 成功,2 - I2C 错误
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* 函数说明:1. 先开写保护
|
||||||
|
* 2. 一次性写入 7 字节时间数据 (00H~06H)
|
||||||
|
* 3. 关闭写保护
|
||||||
|
*/
|
||||||
|
int sd2506_set_time(const sd2506_time_t *p_time) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t buf[7] = {0};
|
||||||
|
|
||||||
if (time == NULL) {
|
if (p_time == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 组装 7 字节时间数据 (BCD 码) */
|
/* 组装 7 字节时间数据 (BCD 码) */
|
||||||
buf[0] = sd2506_dec_to_bcd(time->second); /* 00H: 秒 */
|
buf[0] = sd2506_dec_to_bcd(p_time->second); /* 00H: 秒 */
|
||||||
buf[1] = sd2506_dec_to_bcd(time->minute); /* 01H: 分 */
|
buf[1] = sd2506_dec_to_bcd(p_time->minute); /* 01H: 分 */
|
||||||
buf[2] = sd2506_dec_to_bcd(time->hour) | 0x80U; /* 02H: 时 (bit7=1, 24小时制) */
|
buf[2] = sd2506_dec_to_bcd(p_time->hour) | 0x80U; /* 02H: 时(bit7=1, 24小时制) */
|
||||||
buf[3] = sd2506_dec_to_bcd(time->week); /* 03H: 星期 */
|
buf[3] = sd2506_dec_to_bcd(p_time->week); /* 03H: 星期 */
|
||||||
buf[4] = sd2506_dec_to_bcd(time->day); /* 04H: 日 */
|
buf[4] = sd2506_dec_to_bcd(p_time->day); /* 04H: 日 */
|
||||||
buf[5] = sd2506_dec_to_bcd(time->month); /* 05H: 月 */
|
buf[5] = sd2506_dec_to_bcd(p_time->month); /* 05H: 月 */
|
||||||
buf[6] = sd2506_dec_to_bcd(time->year - 2000U); /* 06H: 年 */
|
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 06H: 年 */
|
||||||
|
|
||||||
/* 开启写保护 */
|
/* 开启写保护 */
|
||||||
ret = sd2506_write_enable();
|
ret = sd2506_write_enable();
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
/* 一次性写入 7 字节时间数据 (00H~06H)
|
/* 一次性写入 7 字节时间数据 (00H~06H)
|
||||||
* 手册要求: 不可单独写某一个时间寄存器,否则可能引起错误进位 */
|
* 手册要求: 不可单独写某一个时间寄存器,否则可能引起错误进入 */
|
||||||
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
|
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
@@ -196,39 +238,54 @@ int sd2506_set_time(const sd2506_time_t *time)
|
|||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_get_time(sd2506_time_t *time)
|
/*
|
||||||
{
|
* 函数功能:读取 RTC 时间日期
|
||||||
int ret;
|
* 入口参数:p_time - 输出时间结构体指针 sd2506_time_t*(非空)
|
||||||
uint8_t buf[7];
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* 函数说明:一次读取 7 字节时间数据 (00H~06H),所有实时数据被锁存避免错误
|
||||||
|
* note: sd2506_init() must be called first
|
||||||
|
*/
|
||||||
|
int sd2506_get_time(sd2506_time_t *p_time) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t buf[7] = {0};
|
||||||
|
|
||||||
if (time == NULL) {
|
if (p_time == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 一次读取 7 字节时间数据 (00H~06H)
|
/* 一次读取 7 字节时间数据 (00H~06H)
|
||||||
* 手册说明: 读取时所有实时数据被锁存,避免错读 */
|
* 手册说明: 读取时所有实时数据被锁存,避免错误 */
|
||||||
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
|
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
|
||||||
if (ret != SD2506_OK) {
|
if (ret != SD2506_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
time->second = sd2506_bcd_to_dec(buf[0] & 0x7FU); /* 00H: 秒 */
|
p_time->second = sd2506_bcd_to_dec(buf[0] & 0x7FU); /* 00H: 秒 */
|
||||||
time->minute = sd2506_bcd_to_dec(buf[1] & 0x7FU); /* 01H: 分 */
|
p_time->minute = sd2506_bcd_to_dec(buf[1] & 0x7FU); /* 01H: 分 */
|
||||||
time->hour = sd2506_bcd_to_dec(buf[2] & 0x7FU); /* 02H: 屏蔽 bit7 (12/24标志) */
|
p_time->hour = sd2506_bcd_to_dec(buf[2] & 0x7FU); /* 02H: 屏蔽 bit7 (12/24标志) */
|
||||||
time->week = sd2506_bcd_to_dec(buf[3] & 0x07U); /* 03H: 星期 */
|
p_time->week = sd2506_bcd_to_dec(buf[3] & 0x07U); /* 03H: 星期 */
|
||||||
time->day = sd2506_bcd_to_dec(buf[4] & 0x3FU); /* 04H: 日 */
|
p_time->day = sd2506_bcd_to_dec(buf[4] & 0x3FU); /* 04H: 日 */
|
||||||
time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: 月 */
|
p_time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: 月 */
|
||||||
time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: 年 */
|
p_time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: 年 */
|
||||||
|
|
||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_get_temperature(int8_t *temp)
|
/*
|
||||||
{
|
* 函数功能:读取内部温度
|
||||||
int ret;
|
* 入口参数:p_temp - 输出温度值指针 int8_t*(非空)
|
||||||
uint8_t val;
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* 函数说明:读取寄存器 0x16,bit7 为符号位
|
||||||
|
* return: 0=OK, -2=I2C error
|
||||||
|
* note: reads register 0x16
|
||||||
|
*/
|
||||||
|
int sd2506_get_temperature(int8_t *p_temp) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t val = 0;
|
||||||
|
|
||||||
if (temp == NULL) {
|
if (p_temp == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,52 +296,79 @@ int sd2506_get_temperature(int8_t *temp)
|
|||||||
|
|
||||||
/* bit7 为符号位, 其余为温度值 */
|
/* bit7 为符号位, 其余为温度值 */
|
||||||
if (val & 0x80U) {
|
if (val & 0x80U) {
|
||||||
/* 负温度: 取补码 */
|
/* 负温度 取补码 */
|
||||||
*temp = (int8_t)(val | 0xF0U);
|
*p_temp = (int8_t)(val | 0xF0U);
|
||||||
} else {
|
} else {
|
||||||
/* 正温度 */
|
/* 正温度 */
|
||||||
*temp = (int8_t)(val & 0x7FU);
|
*p_temp = (int8_t)(val & 0x7FU);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_get_battery_voltage(uint16_t *voltage)
|
/*
|
||||||
{
|
* 函数功能:读取电池电压(毫伏)
|
||||||
int ret;
|
* 入口参数:p_voltage - 输出电压指针 uint16_t*(非空)
|
||||||
uint8_t val_high, val_low;
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* 函数说明:组合 9 位值 (bit8=BAT8_VAL, bit7~0=BAT_VL),转换为毫伏
|
||||||
|
* return: 0=OK, -2=I2C error
|
||||||
|
* note: combines 9-bit value
|
||||||
|
*/
|
||||||
|
int sd2506_get_battery_voltage(uint16_t *p_voltage) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t val_high = 0;
|
||||||
|
uint8_t val_low = 0;
|
||||||
|
|
||||||
if (voltage == NULL) {
|
if (p_voltage == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 读取 1AH bit7 (BAT8_VAL) 和 1BH (BAT_VL) */
|
/* 读取 1AH bit7 (BAT8_VAL) 与 1BH (BAT_VL) */
|
||||||
ret = sd2506_read_reg(SD2506_REG_CTR5, &val_high);
|
ret = sd2506_read_reg(SD2506_REG_CTR5, &val_high);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
ret = sd2506_read_reg(SD2506_REG_BAT_VAL, &val_low);
|
ret = sd2506_read_reg(SD2506_REG_BAT_VAL, &val_low);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
/* 组合 9 位: bit8=BAT8_VAL(1AH bit7), bit7~0=BAT_VL(1BH)
|
/* 组合 9 位值 bit8=BAT8_VAL(1AH bit7), bit7~0=BAT_VL(1BH)
|
||||||
* 如 1AH=80H, 1BH=30H => 电压 = 0x130 = 304 => 3.04V => 3040mV */
|
* 例: 1AH=80H, 1BH=30H => 电压 = 0x130 = 304 => 3.04V => 3040mV */
|
||||||
uint16_t raw = ((val_high & 0x80U) << 1) | val_low;
|
uint16_t raw = ((val_high & 0x80U) << 1) | val_low;
|
||||||
*voltage = raw * 10; /* 转换为毫伏 (raw 单位 0.01V) */
|
*p_voltage = raw * 10; /* 转换为毫伏 (raw 单位 0.01V) */
|
||||||
|
|
||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_get_id(uint8_t id[8])
|
/*
|
||||||
{
|
* 函数功能:读取 8 字节芯片 ID
|
||||||
if (id == NULL) {
|
* 入口参数:p_id - 输出缓冲区指针 uint8_t[8](非空)
|
||||||
|
* 返回值:0-成功,2-I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* 函数说明:读取寄存器 0x72~0x79
|
||||||
|
* return: 0=OK, -2=I2C error
|
||||||
|
* note: reads registers 0x72-0x79
|
||||||
|
*/
|
||||||
|
int sd2506_get_id(uint8_t p_id[8]) {
|
||||||
|
if (p_id == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd2506_read_regs(SD2506_REG_ID_START, id, SD2506_ID_SIZE);
|
return sd2506_read_regs(SD2506_REG_ID_START, p_id, SD2506_ID_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_read_sram(uint8_t addr, uint8_t *buf, uint8_t len)
|
/*
|
||||||
{
|
* 函数功能:读取 SRAM 数据
|
||||||
if (buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
* 入口参数:addr - 起始地址 uint8_t 0-69
|
||||||
|
* p_buf - 输出缓冲区指针 uint8_t*(非空)
|
||||||
|
* len - 读取长度 uint8_t 1-N
|
||||||
|
* 返回值:0-成功,-1=参数错误,-2=I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功;addr+len <= SD2506_SRAM_SIZE(70)
|
||||||
|
* 函数说明:SRAM 范围 0x2C~0x71
|
||||||
|
* return: 0=OK, -1=param err, -2=I2C err
|
||||||
|
* note: SRAM range 0x2C-0x71
|
||||||
|
*/
|
||||||
|
int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len) {
|
||||||
|
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,12 +376,21 @@ int sd2506_read_sram(uint8_t addr, uint8_t *buf, uint8_t len)
|
|||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd2506_read_regs(SD2506_REG_SRAM_START + addr, buf, len);
|
return sd2506_read_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_write_sram(uint8_t addr, const uint8_t *buf, uint8_t len)
|
/*
|
||||||
{
|
* 函数功能:写入 SRAM 数据
|
||||||
if (buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
* 入口参数:addr - 起始地址 uint8_t 0-69
|
||||||
|
* p_buf - 输入数据指针 uint8_t*(非空)
|
||||||
|
* len - 写入长度 uint8_t 1-N
|
||||||
|
* 返回值:0-成功,-1=参数错误,-2=I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功;addr+len <= SD2506_SRAM_SIZE(70)
|
||||||
|
* 函数说明:SRAM 写入需走写保护流程(虽 SRAM 本身无需)
|
||||||
|
* return: 0=OK, -1=param err, -2=I2C err
|
||||||
|
*/
|
||||||
|
int sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len) {
|
||||||
|
if (p_buf == NULL || len == 0 || addr >= SD2506_SRAM_SIZE) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,36 +398,44 @@ int sd2506_write_sram(uint8_t addr, const uint8_t *buf, uint8_t len)
|
|||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SRAM 无需开写保护即可写入 (写保护仅对 00H~71H 有效, SRAM 是 2CH~71H)
|
/* SRAM 无需开写保护即可写入 (写保护仅对 00H~71H 有效, SRAM 为 2CH~71H)
|
||||||
* 但为安全起见, SRAM 写入也走写保护流程 */
|
* 但为安全起见, SRAM 写入也走写保护流程 */
|
||||||
int ret;
|
int ret = 0;
|
||||||
ret = sd2506_write_enable();
|
ret = sd2506_write_enable();
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, buf, len);
|
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
ret = sd2506_write_disable();
|
ret = sd2506_write_disable();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_set_alarm(const sd2506_time_t *time, uint8_t mask)
|
/*
|
||||||
{
|
* 函数功能:设置闹钟中断
|
||||||
int ret;
|
* 入口参数:p_time - 报警时间结构体指针 sd2506_time_t*(非空)
|
||||||
uint8_t buf[8];
|
* mask - 报警匹配掩码 uint8_t
|
||||||
|
* 返回值:0-成功,-2=I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* 函数说明:写入 8 字节报警数据 (07H~0EH),使能 CTR2.INTAE+INTS0+IM
|
||||||
|
* return: 0=OK, -2=I2C error
|
||||||
|
*/
|
||||||
|
int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t buf[8] = {0};
|
||||||
|
|
||||||
if (time == NULL) {
|
if (p_time == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 组装 8 字节报警数据 (07H~0EH) */
|
/* 组装 8 字节报警数据 (07H~0EH) */
|
||||||
buf[0] = sd2506_dec_to_bcd(time->second) & 0x7FU; /* 07H: 秒报警 */
|
buf[0] = sd2506_dec_to_bcd(p_time->second) & 0x7FU; /* 07H: 秒报 */
|
||||||
buf[1] = sd2506_dec_to_bcd(time->minute) & 0x7FU; /* 08H: 分报警 */
|
buf[1] = sd2506_dec_to_bcd(p_time->minute) & 0x7FU; /* 08H: 分报 */
|
||||||
buf[2] = sd2506_dec_to_bcd(time->hour) & 0x3FU; /* 09H: 时报警 (最高位始终为0) */
|
buf[2] = sd2506_dec_to_bcd(p_time->hour) & 0x3FU; /* 09H: 时报(最高位始终为0) */
|
||||||
buf[3] = 0x00U; /* 0AH: 星期报警 */
|
buf[3] = 0x00U; /* 0AH: 星期报警 */
|
||||||
buf[4] = sd2506_dec_to_bcd(time->day) & 0x3FU; /* 0BH: 日报警 */
|
buf[4] = sd2506_dec_to_bcd(p_time->day) & 0x3FU; /* 0BH: 日报 */
|
||||||
buf[5] = sd2506_dec_to_bcd(time->month) & 0x1FU; /* 0CH: 月报警 */
|
buf[5] = sd2506_dec_to_bcd(p_time->month) & 0x1FU; /* 0CH: 月报 */
|
||||||
buf[6] = sd2506_dec_to_bcd(time->year - 2000U); /* 0DH: 年报警 */
|
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 0DH: 年报 */
|
||||||
buf[7] = mask; /* 0EH: 报警允许 */
|
buf[7] = mask; /* 0EH: 报警允许 */
|
||||||
|
|
||||||
/* 开启写保护 */
|
/* 开启写保护 */
|
||||||
@@ -345,7 +446,7 @@ int sd2506_set_alarm(const sd2506_time_t *time, uint8_t mask)
|
|||||||
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
|
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
|
|
||||||
/* 使能报警中断: CTR2 的 INTAE=1, INTS1=0, INTS0=1, IM=1 (周期性) */
|
/* 使能报警中断: CTR2 中 INTAE=1, INTS1=0, INTS0=1, IM=1 (周期中断) */
|
||||||
ret = sd2506_write_reg(SD2506_REG_CTR2,
|
ret = sd2506_write_reg(SD2506_REG_CTR2,
|
||||||
SD2506_CTR2_INTAE | SD2506_CTR2_INTS0 | SD2506_CTR2_IM);
|
SD2506_CTR2_INTAE | SD2506_CTR2_INTS0 | SD2506_CTR2_IM);
|
||||||
if (ret != SD2506_OK) return ret;
|
if (ret != SD2506_OK) return ret;
|
||||||
@@ -357,10 +458,18 @@ int sd2506_set_alarm(const sd2506_time_t *time, uint8_t mask)
|
|||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_clear_alarm(void)
|
/*
|
||||||
{
|
* 函数功能:清除闹钟中断标志
|
||||||
int ret;
|
* 入口参数:无
|
||||||
uint8_t val;
|
* 返回值:0-成功,-2=I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功;ARST=1 时自动清除 INTAF
|
||||||
|
* 函数说明:读取 CTR1 即可清除 INTAF 标志
|
||||||
|
* return: 0=OK, -2=I2C error
|
||||||
|
* note: reading CTR1 clears INTAF
|
||||||
|
*/
|
||||||
|
int sd2506_clear_alarm(void) {
|
||||||
|
int ret = 0;
|
||||||
|
uint8_t val = 0;
|
||||||
|
|
||||||
/* 读取 CTR1, ARST=1 时自动清除 INTAF */
|
/* 读取 CTR1, ARST=1 时自动清除 INTAF */
|
||||||
ret = sd2506_read_reg(SD2506_REG_CTR1, &val);
|
ret = sd2506_read_reg(SD2506_REG_CTR1, &val);
|
||||||
@@ -371,11 +480,18 @@ int sd2506_clear_alarm(void)
|
|||||||
return SD2506_OK;
|
return SD2506_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd2506_read_ctr1(uint8_t *val)
|
/*
|
||||||
{
|
* 函数功能:读取控制寄存器 1 (CTR1)
|
||||||
if (val == NULL) {
|
* 入口参数:p_val - 输出值指针 uint8_t*(非空)
|
||||||
|
* 返回值:0-成功,-2=I2C 错误 int
|
||||||
|
* 限定条件:sd2506_init() 已调用成功
|
||||||
|
* return: 0=OK, -2=I2C error
|
||||||
|
*/
|
||||||
|
int sd2506_read_ctr1(uint8_t *p_val) {
|
||||||
|
if (p_val == NULL) {
|
||||||
return SD2506_ERROR;
|
return SD2506_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd2506_read_reg(SD2506_REG_CTR1, val);
|
return sd2506_read_reg(SD2506_REG_CTR1, p_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ extern "C" {
|
|||||||
#define SD2506_REG_AL_EN 0x0EU /* 报警允许寄存器 */
|
#define SD2506_REG_AL_EN 0x0EU /* 报警允许寄存器 */
|
||||||
|
|
||||||
/* 控制寄存器 */
|
/* 控制寄存器 */
|
||||||
#define SD2506_REG_CTR1 0x0FH /* 控制寄存器1 */
|
#define SD2506_REG_CTR1 0x0FU /* 控制寄存器1 */
|
||||||
#define SD2506_REG_CTR2 0x10U /* 控制寄存器2 */
|
#define SD2506_REG_CTR2 0x10U /* 控制寄存器2 */
|
||||||
#define SD2506_REG_CTR3 0x11U /* 控制寄存器3 */
|
#define SD2506_REG_CTR3 0x11U /* 控制寄存器3 */
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ int sd2506_init(void);
|
|||||||
* 3. 关闭写保护
|
* 3. 关闭写保护
|
||||||
* 注意:不可单独写某一个时间寄存器
|
* 注意:不可单独写某一个时间寄存器
|
||||||
*/
|
*/
|
||||||
int sd2506_set_time(const sd2506_time_t *time);
|
int sd2506_set_time(const sd2506_time_t *p_time);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取 RTC 时间日期
|
* 函数功能:读取 RTC 时间日期
|
||||||
@@ -191,7 +191,7 @@ int sd2506_set_time(const sd2506_time_t *time);
|
|||||||
* 2. BCD 转十进制
|
* 2. BCD 转十进制
|
||||||
* 3. 屏蔽小时 bit7 (12/24标志位)
|
* 3. 屏蔽小时 bit7 (12/24标志位)
|
||||||
*/
|
*/
|
||||||
int sd2506_get_time(sd2506_time_t *time);
|
int sd2506_get_time(sd2506_time_t *p_time);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取芯片内部温度
|
* 函数功能:读取芯片内部温度
|
||||||
@@ -200,7 +200,7 @@ int sd2506_get_time(sd2506_time_t *time);
|
|||||||
* 限定条件:sd2506_init() 已调用
|
* 限定条件:sd2506_init() 已调用
|
||||||
* 函数说明:读取 16H 寄存器,bit7 为符号位,范围 -40~+85
|
* 函数说明:读取 16H 寄存器,bit7 为符号位,范围 -40~+85
|
||||||
*/
|
*/
|
||||||
int sd2506_get_temperature(int8_t *temp);
|
int sd2506_get_temperature(int8_t *p_temp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取电池电压 (毫伏)
|
* 函数功能:读取电池电压 (毫伏)
|
||||||
@@ -210,7 +210,7 @@ int sd2506_get_temperature(int8_t *temp);
|
|||||||
* 函数说明:1. 读取 1AH bit7 (BAT8_VAL) 和 1BH (BAT_VL)
|
* 函数说明:1. 读取 1AH bit7 (BAT8_VAL) 和 1BH (BAT_VL)
|
||||||
* 2. 组合 9 位数据得到电压值 (如 0x135 = 309 = 3.09V)
|
* 2. 组合 9 位数据得到电压值 (如 0x135 = 309 = 3.09V)
|
||||||
*/
|
*/
|
||||||
int sd2506_get_battery_voltage(uint16_t *voltage);
|
int sd2506_get_battery_voltage(uint16_t *p_voltage);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取芯片 8 字节 ID
|
* 函数功能:读取芯片 8 字节 ID
|
||||||
@@ -219,7 +219,7 @@ int sd2506_get_battery_voltage(uint16_t *voltage);
|
|||||||
* 限定条件:sd2506_init() 已调用
|
* 限定条件:sd2506_init() 已调用
|
||||||
* 函数说明:读取 72H~79H 共 8 字节唯一 ID
|
* 函数说明:读取 72H~79H 共 8 字节唯一 ID
|
||||||
*/
|
*/
|
||||||
int sd2506_get_id(uint8_t id[8]);
|
int sd2506_get_id(uint8_t p_id[8]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取用户 SRAM 数据
|
* 函数功能:读取用户 SRAM 数据
|
||||||
@@ -230,7 +230,7 @@ int sd2506_get_id(uint8_t id[8]);
|
|||||||
* 限定条件:sd2506_init() 已调用
|
* 限定条件:sd2506_init() 已调用
|
||||||
* 函数说明:SRAM 地址范围 0~69,对应寄存器 2CH~71H
|
* 函数说明:SRAM 地址范围 0~69,对应寄存器 2CH~71H
|
||||||
*/
|
*/
|
||||||
int sd2506_read_sram(uint8_t addr, uint8_t *buf, uint8_t len);
|
int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:写入用户 SRAM 数据
|
* 函数功能:写入用户 SRAM 数据
|
||||||
@@ -242,7 +242,7 @@ int sd2506_read_sram(uint8_t addr, uint8_t *buf, uint8_t len);
|
|||||||
* 函数说明:SRAM 地址范围 0~69,对应寄存器 2CH~71H
|
* 函数说明:SRAM 地址范围 0~69,对应寄存器 2CH~71H
|
||||||
* SRAM 无需开写保护即可写入
|
* SRAM 无需开写保护即可写入
|
||||||
*/
|
*/
|
||||||
int sd2506_write_sram(uint8_t addr, const uint8_t *buf, uint8_t len);
|
int sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置报警中断
|
* 函数功能:设置报警中断
|
||||||
@@ -255,7 +255,7 @@ int sd2506_write_sram(uint8_t addr, const uint8_t *buf, uint8_t len);
|
|||||||
* 3. 使能报警中断 INTAE
|
* 3. 使能报警中断 INTAE
|
||||||
* 4. 关写保护
|
* 4. 关写保护
|
||||||
*/
|
*/
|
||||||
int sd2506_set_alarm(const sd2506_time_t *time, uint8_t mask);
|
int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:清除报警中断标志
|
* 函数功能:清除报警中断标志
|
||||||
@@ -271,7 +271,7 @@ int sd2506_clear_alarm(void);
|
|||||||
* 入口参数:val - 输出值指针
|
* 入口参数:val - 输出值指针
|
||||||
* 返回值:0 - 成功,-2 - I2C 通信错误
|
* 返回值:0 - 成功,-2 - I2C 通信错误
|
||||||
*/
|
*/
|
||||||
int sd2506_read_ctr1(uint8_t *val);
|
int sd2506_read_ctr1(uint8_t *p_val);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:BCD 码转十进制
|
* 函数功能:BCD 码转十进制
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
/*
|
/*
|
||||||
* 模块名称:TPAFE5160 16位8通道同步采样ADC驱动
|
* 模块名称:TPAFE5160 16<EFBFBD>?通道同步采样ADC驱动
|
||||||
* 模块功能:提供 TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接口
|
* 模块功能:提<EFBFBD>?TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接<EFBFBD>? * 适用平台:STM32F407ZGT6,并<EFBC8C>?6位数据总线<E680BB>?GPIOG[15:0]
|
||||||
* 适用平台:STM32F407ZGT6,并行16位数据总线接 GPIOG[15:0]
|
* 作者:王建<EFBFBD>? * 创建日期<E697A5>?026-07-17
|
||||||
* 作者:王建锋
|
* 修改记录<EFBFBD>? * 2026-07-17 王建<E78E8B>? 创建初始版本,参<EFBC8C>?AD7606 并行驱动<E9A9B1>?TPAFE5160 手册
|
||||||
* 创建日期:2026-07-17
|
* 2026-07-17 王建<E78E8B>? 增加 EXTI 中断读取模式
|
||||||
* 修改记录:
|
|
||||||
* 2026-07-17 王建锋 创建初始版本,参考 AD7606 并行驱动及 TPAFE5160 手册
|
|
||||||
* 2026-07-17 王建锋 增加 EXTI 中断读取模式
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 头文件包含区 */
|
/* 头文件包含区 */
|
||||||
#include "tpafe5160.h"
|
#include "tpafe5160.h"
|
||||||
|
#include "main.h"
|
||||||
/* ======================== 私有宏定义 ======================== */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 并行读取时序延时 (168MHz 主频,1 NOP ≈ 5.95ns)
|
* 调试输出配置
|
||||||
*
|
|
||||||
* TPAFE5160 并行时序要求 (VDRIVE > 2.7V):
|
|
||||||
* t10 (RD 低脉宽) ≥ 22ns
|
|
||||||
* t11 (RD 高脉宽) ≥ 10ns
|
|
||||||
* t14 (数据访问时间) ≤ 21ns (从 RD 下降沿算起)
|
|
||||||
* t15 (数据保持时间) ≥ 6ns (从 RD 下降沿算起)
|
|
||||||
*
|
|
||||||
* GPIO 写操作本身约 1 个 AHB 周期 (~6ns),
|
|
||||||
* 加上端口延迟 (~12-18ns),实际 RD 引脚翻转滞后约 18-24ns。
|
|
||||||
* 因此 NOP 延时只需覆盖数据建立时间即可。
|
|
||||||
*/
|
*/
|
||||||
#define TP_NOP_5() __NOP(); __NOP(); __NOP(); __NOP(); __NOP()
|
#define DBG_TAG "[ADC]"
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
/* RD 低脉宽延时:GPIO写(~6ns) + 5NOP(~30ns) > 22ns */
|
/* ======================== 私有宏定<E5AE8F>?======================== */
|
||||||
#define TP_RD_LOW_DLY() do { TPAFE5160_RD_LOW(); TP_NOP_5(); } while (0)
|
|
||||||
|
|
||||||
/* RD 高脉宽延时:GPIO写(~6ns) + 3NOP(~18ns) > 10ns */
|
/*
|
||||||
#define TP_RD_HIGH_DLY() do { TPAFE5160_RD_HIGH(); __NOP(); __NOP(); __NOP(); } while (0)
|
* 并行读取时序延时 (168MHz 主频<E4B8BB>? NOP <20>?5.95ns)
|
||||||
|
*
|
||||||
|
* TPAFE5160 并行时序要求 (VDRIVE > 2.7V)<29>? * t10 (RD 低脉<E4BD8E>? <20>?22ns
|
||||||
|
* t11 (RD 高脉<E9AB98>? <20>?10ns
|
||||||
|
* t14 (数据访问时间) <20>?21ns (<28>?RD 下降沿算<E6B2BF>?
|
||||||
|
* t15 (数据保持时间) <20>?6ns (<28>?RD 下降沿算<E6B2BF>?
|
||||||
|
*
|
||||||
|
* GPIO 写操作本身约 1 <20>?AHB 周期 (~6ns)<29>? * 加上端口延迟 (~12-18ns),实<EFBC8C>?RD 引脚翻转滞后<E6BB9E>?18-24ns<6E>? * 因此 NOP 延时只需覆盖数据建立时间即可<E58DB3>? */
|
||||||
|
#define TPAFE5160_NOP_5() __NOP(); __NOP(); __NOP(); __NOP(); __NOP()
|
||||||
|
|
||||||
/* CONVST 脉冲延时:上升沿前需保证低电平,5NOP 覆盖 t5 ≥ 20ns */
|
/* RD 低脉宽延时:GPIO<49>?~6ns) + 5NOP(~30ns) > 22ns */
|
||||||
#define TP_CONVST_PULSE() do { \
|
#define TPAFE5160_RD_LOW_DLY() do { TPAFE5160_RD_LOW(); TPAFE5160_NOP_5(); } while (0)
|
||||||
|
|
||||||
|
/* RD 高脉宽延时:GPIO<49>?~6ns) + 3NOP(~18ns) > 10ns */
|
||||||
|
#define TPAFE5160_RD_HIGH_DLY() do { TPAFE5160_RD_HIGH(); __NOP(); __NOP(); __NOP(); } while (0)
|
||||||
|
|
||||||
|
/* CONVST 脉冲延时:上升沿前需保证低电平,5NOP 覆盖 t5 <20>?20ns */
|
||||||
|
#define TPAFE5160_CONVST_PULSE() do { \
|
||||||
TPAFE5160_CONVST_LOW(); \
|
TPAFE5160_CONVST_LOW(); \
|
||||||
TP_NOP_5(); \
|
TPAFE5160_NOP_5(); \
|
||||||
TPAFE5160_CONVST_HIGH(); \
|
TPAFE5160_CONVST_HIGH(); \
|
||||||
TP_NOP_5(); \
|
TPAFE5160_NOP_5(); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* ======================== 私有函数声明 ======================== */
|
/* ======================== 私有函数声明 ======================== */
|
||||||
@@ -52,34 +52,34 @@ static void tpafe5160_read_channels(uint8_t count, int16_t *buf);
|
|||||||
/*
|
/*
|
||||||
* 函数功能:初始化 TPAFE5160(设置过采样、等待就绪)
|
* 函数功能:初始化 TPAFE5160(设置过采样、等待就绪)
|
||||||
* 入口参数:无
|
* 入口参数:无
|
||||||
* 返回值:0 - 成功,-2 - BUSY 超时
|
* 返回值:0 - 成功<EFBFBD>?2 - BUSY 超时
|
||||||
* 限定条件:CubeMX 已完成 GPIO 初始化
|
* 限定条件:CubeMX 已完<EFBFBD>?GPIO 初始<EFBFBD>? * 函数说明<E8AFB4>?. 设置过采样为无过采样 (000)
|
||||||
* 函数说明:1. 设置过采样为无过采样 (000)
|
|
||||||
* 2. 确保 RD 为高、CONVST 为低
|
* 2. 确保 RD 为高、CONVST 为低
|
||||||
* 3. 等待 BUSY 释放(转换空闲)
|
* 3. 等待 BUSY 释放(转换空闲)
|
||||||
*/
|
*/
|
||||||
int tpafe5160_init(void)
|
int tpafe5160_init(void) {
|
||||||
{
|
|
||||||
/* 设置默认过采样:无过采样 (OS[2:0] = 000) */
|
/* 设置默认过采样:无过采样 (OS[2:0] = 000) */
|
||||||
tpafe5160_set_os(TP_OS_NONE);
|
tpafe5160_set_os(TPAFE5160_OS_NONE);
|
||||||
|
|
||||||
/* 确保控制引脚处于空闲状态 */
|
/* 确保控制引脚处于空闲状<EFBFBD>?*/
|
||||||
TPAFE5160_RD_HIGH();
|
TPAFE5160_RD_HIGH();
|
||||||
TPAFE5160_CONVST_LOW();
|
TPAFE5160_CONVST_LOW();
|
||||||
|
|
||||||
/* 等待 BUSY 释放,确保上电后无残留转换 */
|
/* 等待 BUSY 释放,确保上电后无残留转<EFBFBD>?*/
|
||||||
return tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
int ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||||||
|
if (ret != TPAFE5160_OK) {
|
||||||
|
DBG_ERROR("Init wait BUSY timeout");
|
||||||
|
} else {
|
||||||
|
DBG_INFO("Init OK");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:设置过采样率
|
* 函数功能:设置过采样<EFBFBD>? * 入口参数:os - 过采样率枚举<E69E9A>? tpafe5160_os_t
|
||||||
* 入口参数:os - 过采样率枚举值 tpafe5160_os_t
|
* 返回值:<EFBFBD>? * 限定条件:GPIO 已初始化
|
||||||
* 返回值:无
|
* 函数说明:通过 OS[2:0] 引脚设置过采样率,在下一<E4B88B>?BUSY 下降沿锁存生<E5AD98>? */
|
||||||
* 限定条件:GPIO 已初始化
|
void tpafe5160_set_os(tpafe5160_os_t os) {
|
||||||
* 函数说明:通过 OS[2:0] 引脚设置过采样率,在下一次 BUSY 下降沿锁存生效
|
|
||||||
*/
|
|
||||||
void tpafe5160_set_os(tpafe5160_os_t os)
|
|
||||||
{
|
|
||||||
TPAFE5160_OS0(os & 0x01); /* OS0 = bit0 */
|
TPAFE5160_OS0(os & 0x01); /* OS0 = bit0 */
|
||||||
TPAFE5160_OS1((os >> 1) & 0x01); /* OS1 = bit1 */
|
TPAFE5160_OS1((os >> 1) & 0x01); /* OS1 = bit1 */
|
||||||
TPAFE5160_OS2((os >> 2) & 0x01); /* OS2 = bit2 */
|
TPAFE5160_OS2((os >> 2) & 0x01); /* OS2 = bit2 */
|
||||||
@@ -88,27 +88,22 @@ void tpafe5160_set_os(tpafe5160_os_t os)
|
|||||||
/*
|
/*
|
||||||
* 函数功能:启动一次转换(CONVST 上升沿触发)
|
* 函数功能:启动一次转换(CONVST 上升沿触发)
|
||||||
* 入口参数:无
|
* 入口参数:无
|
||||||
* 返回值:无
|
* 返回值:<EFBFBD>? * 限定条件:GPIO 已初始化
|
||||||
* 限定条件:GPIO 已初始化
|
* 函数说明:产<EFBFBD>?CONVST 脉冲上升沿,启动全部8通道同步采样与转<E4B88E>? */
|
||||||
* 函数说明:产生 CONVST 脉冲上升沿,启动全部8通道同步采样与转换
|
void tpafe5160_start_conv(void) {
|
||||||
*/
|
TPAFE5160_CONVST_PULSE();
|
||||||
void tpafe5160_start_conv(void)
|
|
||||||
{
|
|
||||||
TP_CONVST_PULSE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:等待转换完成
|
|
||||||
* 入口参数:timeout_ms - 超时时间 uint32_t > 0
|
* 函数功能:等待转换完<E68DA2>? * 入口参数:timeout_ms - 超时时间 uint32_t > 0
|
||||||
* 返回值:0 - 转换完成,-2 - 超时
|
* 返回值:0 - 转换完成<EFBFBD>?2 - 超时
|
||||||
* 限定条件:已调用 tpafe5160_start_conv()
|
* 限定条件:已调用 tpafe5160_start_conv()
|
||||||
* 函数说明:轮询 BUSY 引脚等待下降沿
|
* 函数说明:轮<EFBFBD>?BUSY 引脚等待下降<EFBFBD>? */
|
||||||
*/
|
int tpafe5160_wait_busy(uint32_t timeout_ms) {
|
||||||
int tpafe5160_wait_busy(uint32_t timeout_ms)
|
|
||||||
{
|
|
||||||
uint32_t tick_start = HAL_GetTick();
|
uint32_t tick_start = HAL_GetTick();
|
||||||
|
|
||||||
/* 等待 BUSY 释放(低电平表示空闲) */
|
/* 等待 BUSY 释放(低电平表示空闲<EFBFBD>?*/
|
||||||
while (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) {
|
while (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) {
|
||||||
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
|
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
|
||||||
return TPAFE5160_BUSY_TIMEOUT;
|
return TPAFE5160_BUSY_TIMEOUT;
|
||||||
@@ -119,29 +114,26 @@ int tpafe5160_wait_busy(uint32_t timeout_ms)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:查询当前是否正在转换
|
* 函数功能:查询当前是否正在转<EFBFBD>? * 入口参数:无
|
||||||
* 入口参数:无
|
* 返回值:1 - 正在转换<E8BDAC>? - 空闲
|
||||||
* 返回值:1 - 正在转换,0 - 空闲
|
|
||||||
* 限定条件:GPIO 已初始化
|
* 限定条件:GPIO 已初始化
|
||||||
* 函数说明:读取 BUSY 引脚电平
|
* 函数说明:读<EFBFBD>?BUSY 引脚电平
|
||||||
*/
|
*/
|
||||||
uint8_t tpafe5160_is_busy(void)
|
uint8_t tpafe5160_is_busy(void) {
|
||||||
{
|
|
||||||
return (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) ? 1 : 0;
|
return (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取全部8通道转换结果
|
* 函数功能:读取全<EFBFBD>?通道转换结果
|
||||||
* 入口参数:buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
|
* 入口参数:buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
|
||||||
* 返回值:0 - 成功,-2 - BUSY 超时
|
* 返回值:0 - 成功<EFBFBD>?2 - BUSY 超时
|
||||||
* 限定条件:GPIO 已初始化
|
* 限定条件:GPIO 已初始化
|
||||||
* 函数说明:1. 启动转换并等待 BUSY 释放
|
* 函数说明<EFBFBD>?. 启动转换并等<EFBFBD>?BUSY 释放
|
||||||
* 2. 连续8次拉低RD读取各通道数据
|
* 2. 连续8次拉低RD读取各通道数据
|
||||||
* 3. 通过 FRSTDATA 验证第一通道
|
* 3. 通过 FRSTDATA 验证第一通道
|
||||||
*/
|
*/
|
||||||
int tpafe5160_read_all(int16_t *buf)
|
int tpafe5160_read_all(int16_t *buf) {
|
||||||
{
|
int ret = 0;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* 启动转换 */
|
/* 启动转换 */
|
||||||
tpafe5160_start_conv();
|
tpafe5160_start_conv();
|
||||||
@@ -149,6 +141,7 @@ int tpafe5160_read_all(int16_t *buf)
|
|||||||
/* 等待转换完成 */
|
/* 等待转换完成 */
|
||||||
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
|
||||||
if (ret != TPAFE5160_OK) {
|
if (ret != TPAFE5160_OK) {
|
||||||
|
DBG_ERROR("read_all BUSY timeout");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,17 +152,15 @@ int tpafe5160_read_all(int16_t *buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读取指定通道的转换结果
|
* 函数功能:读取指定通道的转换结<EFBFBD>? * 入口参数:channel - 通道<E9809A>? uint8_t 0 - 7
|
||||||
* 入口参数:channel - 通道号 uint8_t 0 - 7
|
|
||||||
* value - 输出指针 int16_t* 不为 NULL
|
* value - 输出指针 int16_t* 不为 NULL
|
||||||
* 返回值:0 - 成功,-1 - 通道号无效,-2 - BUSY 超时
|
* 返回值:0 - 成功<EFBFBD>?1 - 通道号无效,-2 - BUSY 超时
|
||||||
* 限定条件:GPIO 已初始化
|
* 限定条件:GPIO 已初始化
|
||||||
* 函数说明:启动转换并等待完成后,连续读取至指定通道
|
* 函数说明:启动转换并等待完成后,连续读取至指定通道
|
||||||
*/
|
*/
|
||||||
int tpafe5160_read_channel(uint8_t channel, int16_t *value)
|
int tpafe5160_read_channel(uint8_t channel, int16_t *value) {
|
||||||
{
|
int ret = 0;
|
||||||
int ret;
|
int16_t buf[TPAFE5160_CH_NUM] = {0};
|
||||||
int16_t buf[TPAFE5160_CH_NUM];
|
|
||||||
|
|
||||||
if (channel >= TPAFE5160_CH_NUM || value == NULL) {
|
if (channel >= TPAFE5160_CH_NUM || value == NULL) {
|
||||||
return TPAFE5160_ERROR;
|
return TPAFE5160_ERROR;
|
||||||
@@ -185,29 +176,23 @@ int tpafe5160_read_channel(uint8_t channel, int16_t *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:原始ADC值转电压值
|
* 函数功能:原始ADC值转电压<EFBFBD>? * 入口参数:raw - ADC原始<E58E9F>? int16_t 有符号补<E58FB7>? * 返回值:电压<E794B5>? float 单位 V
|
||||||
* 入口参数:raw - ADC原始值 int16_t 有符号补码
|
|
||||||
* 返回值:电压值 float 单位 V
|
|
||||||
* 限定条件:无
|
* 限定条件:无
|
||||||
* 函数说明:±5V量程时 LSB = 10V / 65536 ≈ 152.59μV
|
* 函数说明:<EFBFBD>?V量程<EFBFBD>?LSB = 10V / 65536 <EFBFBD>?152.59μV
|
||||||
* ±10V量程时 LSB = 20V / 65536 ≈ 305.18μV
|
* ±10V量程<EFBFBD>?LSB = 20V / 65536 <EFBFBD>?305.18μV
|
||||||
* 硬件 RANGE 引脚接 GND,默认 ±5V 量程
|
* 硬件 RANGE 引脚<EFBFBD>?GND,默<EFBFBD>?±5V 量程
|
||||||
*/
|
*/
|
||||||
float tpafe5160_to_voltage(int16_t raw)
|
float tpafe5160_to_voltage(int16_t raw) {
|
||||||
{
|
/* RANGE <20>?GND <20>?±5V 量程,满量程 10V */
|
||||||
/* RANGE 接 GND → ±5V 量程,满量程 10V */
|
|
||||||
return (float)raw * (10.0f / 65536.0f);
|
return (float)raw * (10.0f / 65536.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:直接读取并行数据总线(不启动转换)
|
* 函数功能:直接读取并行数据总线(不启动转换<EFBFBD>? * 入口参数:无
|
||||||
* 入口参数:无
|
* 返回值:16位原始数<EFBFBD>? uint16_t
|
||||||
* 返回值:16位原始数据 uint16_t
|
* 限定条件:RD 为低<E4B8BA>?CS <20>?RD 已拉<E5B7B2>? * 函数说明:读<EFBC9A>?GPIOG->IDR <20>?6位,对应 DB[15:0]
|
||||||
* 限定条件:RD 为低或 CS 与 RD 已拉低
|
|
||||||
* 函数说明:读取 GPIOG->IDR 低16位,对应 DB[15:0]
|
|
||||||
*/
|
*/
|
||||||
uint16_t tpafe5160_read_bus(void)
|
uint16_t tpafe5160_read_bus(void) {
|
||||||
{
|
|
||||||
return TPAFE5160_READ_BUS();
|
return TPAFE5160_READ_BUS();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,100 +200,82 @@ uint16_t tpafe5160_read_bus(void)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:通过 RD 脉冲连续读取多个通道数据
|
* 函数功能:通过 RD 脉冲连续读取多个通道数据
|
||||||
* 入口参数:count - 要读取的通道数 uint8_t 1 - 8
|
* 入口参数:count - 要读取的通道<EFBFBD>? uint8_t 1 - 8
|
||||||
* buf - 输出缓冲区 int16_t* 不为 NULL
|
* buf - 输出缓冲<EFBFBD>? int16_t* 不为 NULL
|
||||||
* 返回值:无
|
* 返回值:<EFBFBD>? * 限定条件:转换已完成(BUSY 为低),RD 初始为高
|
||||||
* 限定条件:转换已完成(BUSY 为低),RD 初始为高
|
* 函数说明:每<EFBFBD>?RD 下降沿输出一个通道数据,按通道1~8顺序输出
|
||||||
* 函数说明:每次 RD 下降沿输出一个通道数据,按通道1~8顺序输出
|
* DB[15:0] 直接<E79BB4>?GPIOG[15:0],通过 IDR 寄存器一次读<E6ACA1>? *
|
||||||
* DB[15:0] 直接接 GPIOG[15:0],通过 IDR 寄存器一次读取
|
* 时序关键<EFBFBD>?(VDRIVE > 2.7V, 168MHz 主频)<29>? * GPIO 写操<E58699>?~6ns + 端口延迟 ~18ns <20>?RD 引脚实际翻转<E7BFBB>?24ns <20>? * t14 数据建立 <20>?21ns <20>?数据<E695B0>?RD 下降沿后 21ns 内有<E58685>? * 5 <20>?NOP (~30ns) 覆盖建立时间
|
||||||
*
|
|
||||||
* 时序关键点 (VDRIVE > 2.7V, 168MHz 主频):
|
|
||||||
* GPIO 写操作 ~6ns + 端口延迟 ~18ns → RD 引脚实际翻转约 24ns 后
|
|
||||||
* t14 数据建立 ≤ 21ns → 数据在 RD 下降沿后 21ns 内有效
|
|
||||||
* 5 个 NOP (~30ns) 覆盖建立时间
|
|
||||||
*/
|
*/
|
||||||
static void tpafe5160_read_channels(uint8_t count, int16_t *buf)
|
static void tpafe5160_read_channels(uint8_t count, int16_t *buf) {
|
||||||
{
|
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
/* RD 下降沿:ADC 输出当前通道数据到 DB[15:0] */
|
/* RD 下降沿:ADC 输出当前通道数据<EFBFBD>?DB[15:0] */
|
||||||
TP_RD_LOW_DLY();
|
TPAFE5160_RD_LOW_DLY();
|
||||||
|
|
||||||
/* 读取16位并行数据 */
|
/* 读取16位并行数<EFBFBD>?*/
|
||||||
buf[i] = (int16_t)TPAFE5160_READ_BUS();
|
buf[i] = (int16_t)TPAFE5160_READ_BUS();
|
||||||
|
|
||||||
/* RD 上升沿:准备下一通道 */
|
/* RD 上升沿:准备下一通道 */
|
||||||
TP_RD_HIGH_DLY();
|
TPAFE5160_RD_HIGH_DLY();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ======================== 中断模式实现 ======================== */
|
/* ======================== 中断模式实现 ======================== */
|
||||||
|
|
||||||
/* 双缓冲区:ISR 写 s_buf_b,主循环读 s_buf_a */
|
/* 双缓冲区:ISR <EFBFBD>?s_buf_b,主循环<EFBFBD>?s_buf_a */
|
||||||
static int16_t s_buf_a[TPAFE5160_CH_NUM];
|
static int16_t s_buf_a[TPAFE5160_CH_NUM];
|
||||||
static int16_t s_buf_b[TPAFE5160_CH_NUM];
|
static int16_t s_buf_b[TPAFE5160_CH_NUM];
|
||||||
static volatile uint8_t s_ready = 0;
|
static volatile uint8_t s_ready = 0;
|
||||||
static volatile uint8_t s_buf_sel = 0; /* 0 = 写 B / 读 A, 1 = 写 A / 读 B */
|
static volatile uint8_t s_buf_sel = 0; /* 0 = <EFBFBD>?B / <EFBFBD>?A, 1 = <EFBFBD>?A / <EFBFBD>?B */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:使能 BUSY EXTI 中断(运行时重使能用)
|
* 函数功能:使<EFBFBD>?BUSY EXTI 中断(运行时重使能用<EFBFBD>? * 入口参数:无
|
||||||
* 入口参数:无
|
* 返回值:<EFBFBD>? * 限定条件:CubeMX 已完<E5B7B2>?GPIO <20>?NVIC 配置
|
||||||
* 返回值:无
|
* 函数说明:正常启动流程无需调用,仅<EFBFBD>?irq_disable() 后需要重新使能时使用
|
||||||
* 限定条件:CubeMX 已完成 GPIO 和 NVIC 配置
|
|
||||||
* 函数说明:正常启动流程无需调用,仅在 irq_disable() 后需要重新使能时使用
|
|
||||||
*/
|
*/
|
||||||
void tpafe5160_irq_enable(void)
|
void tpafe5160_irq_enable(void) {
|
||||||
{
|
|
||||||
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
|
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:关闭 BUSY EXTI 中断
|
* 函数功能:关<EFBFBD>?BUSY EXTI 中断
|
||||||
* 入口参数:无
|
* 入口参数:无
|
||||||
* 返回值:无
|
* 返回值:<EFBFBD>? * 限定条件:已调用 tpafe5160_irq_enable()
|
||||||
* 限定条件:已调用 tpafe5160_irq_enable()
|
|
||||||
* 函数说明:仅禁用 NVIC 中断,GPIO 配置保持 CubeMX 设定
|
* 函数说明:仅禁用 NVIC 中断,GPIO 配置保持 CubeMX 设定
|
||||||
*/
|
*/
|
||||||
void tpafe5160_irq_disable(void)
|
void tpafe5160_irq_disable(void) {
|
||||||
{
|
|
||||||
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
|
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:启动转换(中断模式)
|
* 函数功能:启动转换(中断模式<EFBFBD>? * 入口参数:无
|
||||||
* 入口参数:无
|
* 返回值:<EFBFBD>? * 限定条件:已调用 tpafe5160_irq_enable()
|
||||||
* 返回值:无
|
* 函数说明:产<EFBFBD>?CONVST 脉冲,转换完成后<E68890>?EXTI 中断自动读取 8 通道数据
|
||||||
* 限定条件:已调用 tpafe5160_irq_enable()
|
|
||||||
* 函数说明:产生 CONVST 脉冲,转换完成后由 EXTI 中断自动读取 8 通道数据
|
|
||||||
*/
|
*/
|
||||||
void tpafe5160_start_conv_irq(void)
|
void tpafe5160_start_conv_irq(void) {
|
||||||
{
|
|
||||||
s_ready = 0;
|
s_ready = 0;
|
||||||
TP_CONVST_PULSE();
|
TPAFE5160_CONVST_PULSE();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:检查是否有新的转换数据
|
* 函数功能:检查是否有新的转换数据
|
||||||
* 入口参数:无
|
* 入口参数:无
|
||||||
* 返回值:1 - 数据就绪,0 - 无新数据
|
* 返回值:1 - 数据就绪<EFBFBD>? - 无新数据
|
||||||
* 限定条件:中断模式已启用
|
* 限定条件:中断模式已启用
|
||||||
* 函数说明:在 EXTI 回调中置位,主循环读取后需调用 tpafe5160_clear_ready() 清除
|
* 函数说明:在 EXTI 回调中置位,主循环读取后需调用 tpafe5160_clear_ready() 清除
|
||||||
*/
|
*/
|
||||||
uint8_t tpafe5160_data_ready(void)
|
uint8_t tpafe5160_data_ready(void) {
|
||||||
{
|
|
||||||
return s_ready;
|
return s_ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:清除数据就绪标志
|
* 函数功能:清除数据就绪标<EFBFBD>? * 入口参数:无
|
||||||
* 入口参数:无
|
* 返回值:<EFBFBD>? * 限定条件:中断模式已启用
|
||||||
* 返回值:无
|
|
||||||
* 限定条件:中断模式已启用
|
|
||||||
* 函数说明:主循环处理完数据后调用
|
* 函数说明:主循环处理完数据后调用
|
||||||
*/
|
*/
|
||||||
void tpafe5160_clear_ready(void)
|
void tpafe5160_clear_ready(void) {
|
||||||
{
|
|
||||||
s_ready = 0;
|
s_ready = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,23 +283,16 @@ void tpafe5160_clear_ready(void)
|
|||||||
* 函数功能:获取数据缓冲区指针
|
* 函数功能:获取数据缓冲区指针
|
||||||
* 入口参数:无
|
* 入口参数:无
|
||||||
* 返回值:int16_t[8] 数据缓冲区的 const 指针
|
* 返回值:int16_t[8] 数据缓冲区的 const 指针
|
||||||
* 限定条件:tpafe5160_data_ready() 返回 1 时调用
|
* 限定条件:tpafe5160_data_ready() 返回 1 时调<EFBFBD>? * 函数说明:双缓冲切换,ISR 写另一个缓冲区,主循环安全读取当前缓冲<E7BC93>? */
|
||||||
* 函数说明:双缓冲切换,ISR 写另一个缓冲区,主循环安全读取当前缓冲区
|
const int16_t* tpafe5160_get_buf(void) {
|
||||||
*/
|
|
||||||
const int16_t* tpafe5160_get_buf(void)
|
|
||||||
{
|
|
||||||
return (s_buf_sel == 0) ? s_buf_a : s_buf_b;
|
return (s_buf_sel == 0) ? s_buf_a : s_buf_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:BUSY 下降沿 EXTI 回调(由 HAL_GPIO_EXTI_IRQHandler 调用)
|
* 函数功能:BUSY 下降<EFBFBD>?EXTI 处理(由 HAL_GPIO_EXTI_Callback 调用<E8B083>? * 入口参数:GPIO_Pin - 触发中断的引脚号
|
||||||
* 入口参数:GPIO_Pin - 触发中断的引脚号
|
* 返回值:<EFBFBD>? * 限定条件:BUSY EXTI 已使<E5B7B2>? * 函数说明:转换完成后自动读取 8 通道数据到缓冲区,耗时<E88097>?0.5µs (168MHz)
|
||||||
* 返回值:无
|
|
||||||
* 限定条件:BUSY EXTI 已使能
|
|
||||||
* 函数说明:转换完成后自动读取 8 通道数据到缓冲区,耗时约 0.5µs (168MHz)
|
|
||||||
*/
|
*/
|
||||||
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
|
void tpafe5160_exti_handler(uint16_t GPIO_Pin) {
|
||||||
{
|
|
||||||
if (GPIO_Pin != TP_BUSY_Pin) {
|
if (GPIO_Pin != TP_BUSY_Pin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -343,9 +303,9 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
|
|||||||
/* 连续读取8个通道,约 0.5µs @ 168MHz */
|
/* 连续读取8个通道,约 0.5µs @ 168MHz */
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
for (i = 0; i < TPAFE5160_CH_NUM; i++) {
|
for (i = 0; i < TPAFE5160_CH_NUM; i++) {
|
||||||
TP_RD_LOW_DLY();
|
TPAFE5160_RD_LOW_DLY();
|
||||||
p_wr[i] = (int16_t)TPAFE5160_READ_BUS();
|
p_wr[i] = (int16_t)TPAFE5160_READ_BUS();
|
||||||
TP_RD_HIGH_DLY();
|
TPAFE5160_RD_HIGH_DLY();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 切换缓冲区并标记就绪 */
|
/* 切换缓冲区并标记就绪 */
|
||||||
|
|||||||
@@ -48,20 +48,20 @@ extern "C" {
|
|||||||
|
|
||||||
/* 过采样率枚举 (OS[2:0] 编码,OS2为MSB, OS0为LSB) */
|
/* 过采样率枚举 (OS[2:0] 编码,OS2为MSB, OS0为LSB) */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TP_OS_NONE = 0, /* 000 — 无过采样,350 kSPS */
|
TPAFE5160_OS_NONE = 0, /* 000 — 无过采样,350 kSPS */
|
||||||
TP_OS_X2 = 1, /* 001 — 2倍过采样,175 kSPS */
|
TPAFE5160_OS_X2 = 1, /* 001 — 2倍过采样,175 kSPS */
|
||||||
TP_OS_X4 = 2, /* 010 — 4倍过采样,87.5 kSPS */
|
TPAFE5160_OS_X4 = 2, /* 010 — 4倍过采样,87.5 kSPS */
|
||||||
TP_OS_X8 = 3, /* 011 — 8倍过采样,43.75 kSPS */
|
TPAFE5160_OS_X8 = 3, /* 011 — 8倍过采样,43.75 kSPS */
|
||||||
TP_OS_X16 = 4, /* 100 — 16倍过采样,21.875 kSPS */
|
TPAFE5160_OS_X16 = 4, /* 100 — 16倍过采样,21.875 kSPS */
|
||||||
TP_OS_X32 = 5, /* 101 — 32倍过采样,10.94 kSPS */
|
TPAFE5160_OS_X32 = 5, /* 101 — 32倍过采样,10.94 kSPS */
|
||||||
TP_OS_X64 = 6, /* 110 — 64倍过采样,5.47 kSPS */
|
TPAFE5160_OS_X64 = 6, /* 110 — 64倍过采样,5.47 kSPS */
|
||||||
TP_OS_HBW = 7 /* 111 — 高带宽模式 (~30kHz),350 kSPS */
|
TPAFE5160_OS_HBW = 7 /* 111 — 高带宽模式 (~30kHz),350 kSPS */
|
||||||
} tpafe5160_os_t;
|
} tpafe5160_os_t;
|
||||||
|
|
||||||
/* 输入量程枚举 */
|
/* 输入量程枚举 */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TP_RANGE_5V = 0, /* ±5V (RANGE = LOW) */
|
TPAFE5160_RANGE_5V = 0, /* ±5V (RANGE = LOW) */
|
||||||
TP_RANGE_10V = 1 /* ±10V (RANGE = HIGH) */
|
TPAFE5160_RANGE_10V = 1 /* ±10V (RANGE = HIGH) */
|
||||||
} tpafe5160_range_t;
|
} tpafe5160_range_t;
|
||||||
|
|
||||||
/* 返回值定义 */
|
/* 返回值定义 */
|
||||||
@@ -217,6 +217,15 @@ void tpafe5160_clear_ready(void);
|
|||||||
*/
|
*/
|
||||||
const int16_t* tpafe5160_get_buf(void);
|
const int16_t* tpafe5160_get_buf(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:EXTI 中断处理(由 HAL_GPIO_EXTI_Callback 调用)
|
||||||
|
* 入口参数:GPIO_Pin - 触发的引脚号
|
||||||
|
* 返回值:无
|
||||||
|
* 限定条件:BUSY EXTI 已使能
|
||||||
|
* 函数说明:BUSY 下降沿时自动读取 8 通道数据到缓冲区
|
||||||
|
*/
|
||||||
|
void tpafe5160_exti_handler(uint16_t GPIO_Pin);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
46
Drivers/BSP/dbg_cfg.h
Normal file
46
Drivers/BSP/dbg_cfg.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Debug Output Configuration
|
||||||
|
* 模块功能:全局调试输出配置,定义各调试级别的编译开关
|
||||||
|
* 参考 FlashDB fdb_cfg.h 设计模式
|
||||||
|
* 使用方法:
|
||||||
|
* - DBG_ENABLE 总开关,定义后启用 DBG_ERROR / DBG_INFO
|
||||||
|
* - DBG_DEBUG_ENABLE 子开关,同时定义 DBG_ENABLE 后启用 DBG_DEBUG(含 __func__:__LINE__)
|
||||||
|
* - 两者都不定义:所有 DBG_* 宏为空操作(release 版本)
|
||||||
|
*
|
||||||
|
* 用户可在本文件中直接控制,也可在编译器预定义中覆盖
|
||||||
|
* 适用平台:所有 BSP 驱动
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
* 2026-07-18 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DBG_CFG_H
|
||||||
|
#define __DBG_CFG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 总开关:取消注释启用所有调试输出
|
||||||
|
*/
|
||||||
|
#define DBG_ENABLE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 调试级别开关:取消注释额外输出 DBG_DEBUG 级别
|
||||||
|
* 仅在 DBG_ENABLE 定义时生效
|
||||||
|
*/
|
||||||
|
/* #define DBG_DEBUG_ENABLE */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 系统时间戳开关:取消注释在所有日志行首追加 [YYYY-MM-DD HH:MM:SS] 时间戳
|
||||||
|
* 依赖 sys_clock_init() 已调用(SD2506 RTC 初始化完成)
|
||||||
|
*/
|
||||||
|
#define APP_TIMESTAMP_ENABLE
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __DBG_CFG_H */
|
||||||
138
Drivers/BSP/dbg_log.h
Normal file
138
Drivers/BSP/dbg_log.h
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:Unified Debug Logging
|
||||||
|
* 模块功能:提供统一的调试输出宏,支持 DEBUG / INFO / ERROR 三级日志控制
|
||||||
|
* 参考 FlashDB FDB_DEBUG/FDB_INFO 设计模式
|
||||||
|
* 使用方法:
|
||||||
|
* 1. 在 dbg_cfg.h 中定义 DBG_ENABLE(总开关)和 DBG_DEBUG_ENABLE(DEBUG 级别)
|
||||||
|
* 2. 每个 .c 文件在包含本文件前定义 DBG_TAG,如:
|
||||||
|
* #define DBG_TAG "[CH395]"
|
||||||
|
* #include "dbg_log.h"
|
||||||
|
* 适用平台:所有 BSP 驱动
|
||||||
|
* 作者:王建锋
|
||||||
|
* 创建日期:2026-07-18
|
||||||
|
* 修改记录:
|
||||||
|
* 2026-07-18 王建锋 创建初始版本
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DBG_LOG_H
|
||||||
|
#define __DBG_LOG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "dbg_cfg.h"
|
||||||
|
|
||||||
|
#ifdef APP_TIMESTAMP_ENABLE
|
||||||
|
#include "sys_clock.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 总开关:DBG_ENABLE
|
||||||
|
* 定义 → 输出 DBG_ERROR / DBG_INFO;若 DBG_DEBUG_ENABLE 也定义则输出 DBG_DEBUG
|
||||||
|
* 不定义 → 所有输出宏为空操作(release 版本)
|
||||||
|
* 在 net_config.h 或编译器预定义中控制
|
||||||
|
*/
|
||||||
|
#ifdef DBG_ENABLE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 可重定向的打印函数,用户可在编译选项或 net_config.h 中预定义
|
||||||
|
* #define DBG_PRINT(...) my_printf(__VA_ARGS__)
|
||||||
|
*/
|
||||||
|
#ifndef DBG_PRINT
|
||||||
|
#define DBG_PRINT(...) printf(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 模块标签默认值,每个 .c 文件应在包含本文件前定义 DBG_TAG
|
||||||
|
* #define DBG_TAG "[CH395]"
|
||||||
|
*/
|
||||||
|
#ifndef DBG_TAG
|
||||||
|
#define DBG_TAG ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef APP_TIMESTAMP_ENABLE
|
||||||
|
/*
|
||||||
|
* 时间戳输出宏 — 在日志行首追加 [YYYY-MM-DD HH:MM:SS] 格式的系统墙钟时间
|
||||||
|
* sys_clock_get_str() 线程安全(内部使用 DWT US 计数,无锁)
|
||||||
|
*/
|
||||||
|
#define DBG_TS() sys_clock_get_str((char[32]){0}, 32)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_ERROR - 错误输出,受 DBG_ENABLE + APP_TIMESTAMP_ENABLE 控制
|
||||||
|
*/
|
||||||
|
#define DBG_ERROR(fmt, ...) do { \
|
||||||
|
char __ts_buf[32]; \
|
||||||
|
(void)__ts_buf; \
|
||||||
|
DBG_PRINT("[%s]" "[ERR]" DBG_TAG " " fmt "\r\n", \
|
||||||
|
sys_clock_get_str(__ts_buf, sizeof(__ts_buf)), ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_INFO - 信息输出,受 DBG_ENABLE + APP_TIMESTAMP_ENABLE 控制
|
||||||
|
*/
|
||||||
|
#define DBG_INFO(fmt, ...) do { \
|
||||||
|
char __ts_buf[32]; \
|
||||||
|
(void)__ts_buf; \
|
||||||
|
DBG_PRINT("[%s]" DBG_TAG " " fmt "\r\n", \
|
||||||
|
sys_clock_get_str(__ts_buf, sizeof(__ts_buf)), ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_DEBUG - 调试输出,需 DBG_ENABLE + DBG_DEBUG_ENABLE + APP_TIMESTAMP_ENABLE 同时定义
|
||||||
|
*/
|
||||||
|
#ifdef DBG_DEBUG_ENABLE
|
||||||
|
#define DBG_DEBUG(fmt, ...) do { \
|
||||||
|
char __ts_buf[32]; \
|
||||||
|
(void)__ts_buf; \
|
||||||
|
DBG_PRINT("[%s]" DBG_TAG "(%s:%d) " fmt "\r\n", \
|
||||||
|
sys_clock_get_str(__ts_buf, sizeof(__ts_buf)), __func__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define DBG_DEBUG(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else /* APP_TIMESTAMP_ENABLE not defined */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_ERROR - 错误输出,受 DBG_ENABLE 控制(无时间戳)
|
||||||
|
*/
|
||||||
|
#define DBG_ERROR(fmt, ...) do { \
|
||||||
|
DBG_PRINT("[ERR]" DBG_TAG " " fmt "\r\n", ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_INFO - 信息输出,受 DBG_ENABLE 控制(无时间戳)
|
||||||
|
*/
|
||||||
|
#define DBG_INFO(fmt, ...) do { \
|
||||||
|
DBG_PRINT(DBG_TAG " " fmt "\r\n", ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DBG_DEBUG - 调试输出,需 DBG_ENABLE + DBG_DEBUG_ENABLE 同时定义(无时间戳)
|
||||||
|
*/
|
||||||
|
#ifdef DBG_DEBUG_ENABLE
|
||||||
|
#define DBG_DEBUG(fmt, ...) do { \
|
||||||
|
DBG_PRINT(DBG_TAG "(%s:%d) " fmt "\r\n", __func__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define DBG_DEBUG(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* APP_TIMESTAMP_ENABLE */
|
||||||
|
|
||||||
|
#else /* DBG_ENABLE not defined — all debug output disabled */
|
||||||
|
|
||||||
|
#define DBG_ERROR(fmt, ...)
|
||||||
|
#define DBG_INFO(fmt, ...)
|
||||||
|
#define DBG_DEBUG(fmt, ...)
|
||||||
|
|
||||||
|
#endif /* DBG_ENABLE */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __DBG_LOG_H */
|
||||||
2157
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h
Normal file
2157
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h
Normal file
File diff suppressed because it is too large
Load Diff
357
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h
Normal file
357
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file stm32f4xx_hal_tim_ex.h
|
||||||
|
* @author MCD Application Team
|
||||||
|
* @brief Header file of TIM HAL Extended module.
|
||||||
|
******************************************************************************
|
||||||
|
* @attention
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 STMicroelectronics.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This software is licensed under terms that can be found in the LICENSE file
|
||||||
|
* in the root directory of this software component.
|
||||||
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||||
|
*
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||||
|
#ifndef STM32F4xx_HAL_TIM_EX_H
|
||||||
|
#define STM32F4xx_HAL_TIM_EX_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Includes ------------------------------------------------------------------*/
|
||||||
|
#include "stm32f4xx_hal_def.h"
|
||||||
|
|
||||||
|
/** @addtogroup STM32F4xx_HAL_Driver
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Exported types ------------------------------------------------------------*/
|
||||||
|
/** @defgroup TIMEx_Exported_Types TIM Extended Exported Types
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief TIM Hall sensor Configuration Structure definition
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t IC1Polarity; /*!< Specifies the active edge of the input signal.
|
||||||
|
This parameter can be a value of @ref TIM_Input_Capture_Polarity */
|
||||||
|
|
||||||
|
uint32_t IC1Prescaler; /*!< Specifies the Input Capture Prescaler.
|
||||||
|
This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
|
||||||
|
|
||||||
|
uint32_t IC1Filter; /*!< Specifies the input capture filter.
|
||||||
|
This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
|
||||||
|
|
||||||
|
uint32_t Commutation_Delay; /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
|
||||||
|
This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
|
||||||
|
} TIM_HallSensor_InitTypeDef;
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
/* End of exported types -----------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Exported constants --------------------------------------------------------*/
|
||||||
|
/** @defgroup TIMEx_Exported_Constants TIM Extended Exported Constants
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup TIMEx_Remap TIM Extended Remapping
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#if defined (TIM2)
|
||||||
|
#if defined(TIM8)
|
||||||
|
#define TIM_TIM2_TIM8_TRGO 0x00000000U /*!< TIM2 ITR1 is connected to TIM8 TRGO */
|
||||||
|
#endif /* TIM8 */
|
||||||
|
#define TIM_TIM2_ETH_PTP TIM_OR_ITR1_RMP_0 /*!< TIM2 ITR1 is connected to PTP trigger output */
|
||||||
|
#define TIM_TIM2_USBFS_SOF TIM_OR_ITR1_RMP_1 /*!< TIM2 ITR1 is connected to OTG FS SOF */
|
||||||
|
#define TIM_TIM2_USBHS_SOF (TIM_OR_ITR1_RMP_1 | TIM_OR_ITR1_RMP_0) /*!< TIM2 ITR1 is connected to OTG HS SOF */
|
||||||
|
#endif /* TIM2 */
|
||||||
|
|
||||||
|
#define TIM_TIM5_GPIO 0x00000000U /*!< TIM5 TI4 is connected to GPIO */
|
||||||
|
#define TIM_TIM5_LSI TIM_OR_TI4_RMP_0 /*!< TIM5 TI4 is connected to LSI */
|
||||||
|
#define TIM_TIM5_LSE TIM_OR_TI4_RMP_1 /*!< TIM5 TI4 is connected to LSE */
|
||||||
|
#define TIM_TIM5_RTC (TIM_OR_TI4_RMP_1 | TIM_OR_TI4_RMP_0) /*!< TIM5 TI4 is connected to the RTC wakeup interrupt */
|
||||||
|
|
||||||
|
#define TIM_TIM11_GPIO 0x00000000U /*!< TIM11 TI1 is connected to GPIO */
|
||||||
|
#define TIM_TIM11_HSE TIM_OR_TI1_RMP_1 /*!< TIM11 TI1 is connected to HSE_RTC clock */
|
||||||
|
#if defined(SPDIFRX)
|
||||||
|
#define TIM_TIM11_SPDIFRX TIM_OR_TI1_RMP_0 /*!< TIM11 TI1 is connected to SPDIFRX_FRAME_SYNC */
|
||||||
|
#endif /* SPDIFRX*/
|
||||||
|
|
||||||
|
#if defined(LPTIM_OR_TIM1_ITR2_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP)
|
||||||
|
#define LPTIM_REMAP_MASK 0x10000000U
|
||||||
|
|
||||||
|
#define TIM_TIM9_TIM3_TRGO LPTIM_REMAP_MASK /*!< TIM9 ITR1 is connected to TIM3 TRGO */
|
||||||
|
#define TIM_TIM9_LPTIM (LPTIM_REMAP_MASK | LPTIM_OR_TIM9_ITR1_RMP) /*!< TIM9 ITR1 is connected to LPTIM1 output */
|
||||||
|
|
||||||
|
#define TIM_TIM5_TIM3_TRGO LPTIM_REMAP_MASK /*!< TIM5 ITR1 is connected to TIM3 TRGO */
|
||||||
|
#define TIM_TIM5_LPTIM (LPTIM_REMAP_MASK | LPTIM_OR_TIM5_ITR1_RMP) /*!< TIM5 ITR1 is connected to LPTIM1 output */
|
||||||
|
|
||||||
|
#define TIM_TIM1_TIM3_TRGO LPTIM_REMAP_MASK /*!< TIM1 ITR2 is connected to TIM3 TRGO */
|
||||||
|
#define TIM_TIM1_LPTIM (LPTIM_REMAP_MASK | LPTIM_OR_TIM1_ITR2_RMP) /*!< TIM1 ITR2 is connected to LPTIM1 output */
|
||||||
|
#endif /* LPTIM_OR_TIM1_ITR2_RMP && LPTIM_OR_TIM5_ITR1_RMP && LPTIM_OR_TIM5_ITR1_RMP */
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
/* End of exported constants -------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Exported macro ------------------------------------------------------------*/
|
||||||
|
/** @defgroup TIMEx_Exported_Macros TIM Extended Exported Macros
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
/* End of exported macro -----------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Private macro -------------------------------------------------------------*/
|
||||||
|
/** @defgroup TIMEx_Private_Macros TIM Extended Private Macros
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#if defined(SPDIFRX)
|
||||||
|
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||||
|
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_TIM8_TRGO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||||
|
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||||
|
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM11_SPDIFRX) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||||
|
#elif defined(TIM2)
|
||||||
|
#if defined(LPTIM_OR_TIM1_ITR2_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP)
|
||||||
|
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||||
|
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_TIM8_TRGO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_ETH_PTP) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||||
|
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||||
|
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM11_HSE))) || \
|
||||||
|
(((INSTANCE) == TIM1) && (((TIM_REMAP) == TIM_TIM1_TIM3_TRGO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM1_LPTIM))) || \
|
||||||
|
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_TIM3_TRGO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LPTIM))) || \
|
||||||
|
(((INSTANCE) == TIM9) && (((TIM_REMAP) == TIM_TIM9_TIM3_TRGO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM9_LPTIM))))
|
||||||
|
#elif defined(TIM8)
|
||||||
|
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||||
|
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_TIM8_TRGO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_ETH_PTP) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||||
|
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||||
|
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||||
|
#else
|
||||||
|
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||||
|
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_ETH_PTP) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||||
|
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||||
|
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||||
|
#endif /* LPTIM_OR_TIM1_ITR2_RMP && LPTIM_OR_TIM5_ITR1_RMP && LPTIM_OR_TIM5_ITR1_RMP */
|
||||||
|
#else
|
||||||
|
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||||
|
((((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||||
|
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||||
|
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||||
|
#endif /* SPDIFRX */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
/* End of private macro ------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Exported functions --------------------------------------------------------*/
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions TIM Extended Exported Functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group1 Extended Timer Hall Sensor functions
|
||||||
|
* @brief Timer Hall Sensor functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Timer Hall Sensor functions **********************************************/
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Init(TIM_HandleTypeDef *htim, const TIM_HallSensor_InitTypeDef *sConfig);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_DeInit(TIM_HandleTypeDef *htim);
|
||||||
|
|
||||||
|
void HAL_TIMEx_HallSensor_MspInit(TIM_HandleTypeDef *htim);
|
||||||
|
void HAL_TIMEx_HallSensor_MspDeInit(TIM_HandleTypeDef *htim);
|
||||||
|
|
||||||
|
/* Blocking mode: Polling */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start(TIM_HandleTypeDef *htim);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop(TIM_HandleTypeDef *htim);
|
||||||
|
/* Non-Blocking mode: Interrupt */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_IT(TIM_HandleTypeDef *htim);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_IT(TIM_HandleTypeDef *htim);
|
||||||
|
/* Non-Blocking mode: DMA */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_DMA(TIM_HandleTypeDef *htim);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group2 Extended Timer Complementary Output Compare functions
|
||||||
|
* @brief Timer Complementary Output Compare functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Timer Complementary Output Compare functions *****************************/
|
||||||
|
/* Blocking mode: Polling */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OCN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OCN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
|
||||||
|
/* Non-Blocking mode: Interrupt */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OCN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
|
||||||
|
/* Non-Blocking mode: DMA */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OCN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
|
||||||
|
uint16_t Length);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group3 Extended Timer Complementary PWM functions
|
||||||
|
* @brief Timer Complementary PWM functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Timer Complementary PWM functions ****************************************/
|
||||||
|
/* Blocking mode: Polling */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
|
||||||
|
/* Non-Blocking mode: Interrupt */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
/* Non-Blocking mode: DMA */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
|
||||||
|
uint16_t Length);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group4 Extended Timer Complementary One Pulse functions
|
||||||
|
* @brief Timer Complementary One Pulse functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Timer Complementary One Pulse functions **********************************/
|
||||||
|
/* Blocking mode: Polling */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||||
|
|
||||||
|
/* Non-Blocking mode: Interrupt */
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group5 Extended Peripheral Control functions
|
||||||
|
* @brief Peripheral Control functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Extended Control functions ************************************************/
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent(TIM_HandleTypeDef *htim, uint32_t InputTrigger,
|
||||||
|
uint32_t CommutationSource);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_IT(TIM_HandleTypeDef *htim, uint32_t InputTrigger,
|
||||||
|
uint32_t CommutationSource);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_DMA(TIM_HandleTypeDef *htim, uint32_t InputTrigger,
|
||||||
|
uint32_t CommutationSource);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim,
|
||||||
|
const TIM_MasterConfigTypeDef *sMasterConfig);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim,
|
||||||
|
const TIM_BreakDeadTimeConfigTypeDef *sBreakDeadTimeConfig);
|
||||||
|
HAL_StatusTypeDef HAL_TIMEx_RemapConfig(TIM_HandleTypeDef *htim, uint32_t Remap);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group6 Extended Callbacks functions
|
||||||
|
* @brief Extended Callbacks functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Extended Callback **********************************************************/
|
||||||
|
void HAL_TIMEx_CommutCallback(TIM_HandleTypeDef *htim);
|
||||||
|
void HAL_TIMEx_CommutHalfCpltCallback(TIM_HandleTypeDef *htim);
|
||||||
|
void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @addtogroup TIMEx_Exported_Functions_Group7 Extended Peripheral State functions
|
||||||
|
* @brief Extended Peripheral State functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* Extended Peripheral State functions ***************************************/
|
||||||
|
HAL_TIM_StateTypeDef HAL_TIMEx_HallSensor_GetState(const TIM_HandleTypeDef *htim);
|
||||||
|
HAL_TIM_ChannelStateTypeDef HAL_TIMEx_GetChannelNState(const TIM_HandleTypeDef *htim, uint32_t ChannelN);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
/* End of exported functions -------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Private functions----------------------------------------------------------*/
|
||||||
|
/** @addtogroup TIMEx_Private_Functions TIM Extended Private Functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
void TIMEx_DMACommutationCplt(DMA_HandleTypeDef *hdma);
|
||||||
|
void TIMEx_DMACommutationHalfCplt(DMA_HandleTypeDef *hdma);
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
/* End of private functions --------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* STM32F4xx_HAL_TIM_EX_H */
|
||||||
7629
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
Normal file
7629
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
Normal file
File diff suppressed because it is too large
Load Diff
2410
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
Normal file
2410
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
Normal file
File diff suppressed because it is too large
Load Diff
170
Inc/FreeRTOSConfig.h
Normal file
170
Inc/FreeRTOSConfig.h
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
/* USER CODE BEGIN Header */
|
||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Portion Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
* Portion Copyright (C) 2019 StMicroelectronics, Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
/* USER CODE END Header */
|
||||||
|
|
||||||
|
#ifndef FREERTOS_CONFIG_H
|
||||||
|
#define FREERTOS_CONFIG_H
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------
|
||||||
|
* Application specific definitions.
|
||||||
|
*
|
||||||
|
* These definitions should be adjusted for your particular hardware and
|
||||||
|
* application requirements.
|
||||||
|
*
|
||||||
|
* These parameters and more are described within the 'configuration' section of the
|
||||||
|
* FreeRTOS API documentation available on the FreeRTOS.org web site.
|
||||||
|
*
|
||||||
|
* See http://www.freertos.org/a00110.html
|
||||||
|
*----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* USER CODE BEGIN Includes */
|
||||||
|
/* Section where include file can be added */
|
||||||
|
/* USER CODE END Includes */
|
||||||
|
|
||||||
|
/* Ensure definitions are only used by the compiler, and not by the assembler. */
|
||||||
|
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
|
||||||
|
#include <stdint.h>
|
||||||
|
extern uint32_t SystemCoreClock;
|
||||||
|
#endif
|
||||||
|
#ifndef CMSIS_device_header
|
||||||
|
#define CMSIS_device_header "stm32f4xx.h"
|
||||||
|
#endif /* CMSIS_device_header */
|
||||||
|
|
||||||
|
#define configENABLE_FPU 1
|
||||||
|
#define configENABLE_MPU 0
|
||||||
|
|
||||||
|
#define configUSE_PREEMPTION 1
|
||||||
|
#define configSUPPORT_STATIC_ALLOCATION 1
|
||||||
|
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||||
|
#define configUSE_IDLE_HOOK 0
|
||||||
|
#define configUSE_TICK_HOOK 0
|
||||||
|
#define configCPU_CLOCK_HZ ( SystemCoreClock )
|
||||||
|
#define configTICK_RATE_HZ ((TickType_t)1000)
|
||||||
|
#define configMAX_PRIORITIES ( 56 )
|
||||||
|
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
|
||||||
|
#define configTOTAL_HEAP_SIZE ((size_t)30720)
|
||||||
|
#define configMAX_TASK_NAME_LEN ( 16 )
|
||||||
|
#define configUSE_TRACE_FACILITY 1
|
||||||
|
#define configUSE_16_BIT_TICKS 0
|
||||||
|
#define configUSE_MUTEXES 1
|
||||||
|
#define configQUEUE_REGISTRY_SIZE 8
|
||||||
|
#define configUSE_RECURSIVE_MUTEXES 1
|
||||||
|
#define configUSE_COUNTING_SEMAPHORES 1
|
||||||
|
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||||
|
/* USER CODE BEGIN MESSAGE_BUFFER_LENGTH_TYPE */
|
||||||
|
/* Defaults to size_t for backward compatibility, but can be changed
|
||||||
|
if lengths will always be less than the number of bytes in a size_t. */
|
||||||
|
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
|
||||||
|
/* USER CODE END MESSAGE_BUFFER_LENGTH_TYPE */
|
||||||
|
|
||||||
|
/* Co-routine definitions. */
|
||||||
|
#define configUSE_CO_ROUTINES 0
|
||||||
|
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
|
||||||
|
|
||||||
|
/* Software timer definitions. */
|
||||||
|
#define configUSE_TIMERS 1
|
||||||
|
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||||
|
#define configTIMER_QUEUE_LENGTH 10
|
||||||
|
#define configTIMER_TASK_STACK_DEPTH 256
|
||||||
|
|
||||||
|
/* CMSIS-RTOS V2 flags */
|
||||||
|
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
|
||||||
|
#define configUSE_OS2_THREAD_ENUMERATE 1
|
||||||
|
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
|
||||||
|
#define configUSE_OS2_THREAD_FLAGS 1
|
||||||
|
#define configUSE_OS2_TIMER 1
|
||||||
|
#define configUSE_OS2_MUTEX 1
|
||||||
|
|
||||||
|
/* Set the following definitions to 1 to include the API function, or zero
|
||||||
|
to exclude the API function. */
|
||||||
|
#define INCLUDE_vTaskPrioritySet 1
|
||||||
|
#define INCLUDE_uxTaskPriorityGet 1
|
||||||
|
#define INCLUDE_vTaskDelete 1
|
||||||
|
#define INCLUDE_vTaskCleanUpResources 0
|
||||||
|
#define INCLUDE_vTaskSuspend 1
|
||||||
|
#define INCLUDE_vTaskDelayUntil 1
|
||||||
|
#define INCLUDE_vTaskDelay 1
|
||||||
|
#define INCLUDE_xTaskGetSchedulerState 1
|
||||||
|
#define INCLUDE_xTimerPendFunctionCall 1
|
||||||
|
#define INCLUDE_xQueueGetMutexHolder 1
|
||||||
|
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||||
|
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||||
|
#define INCLUDE_eTaskGetState 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
|
||||||
|
* by the application thus the correct define need to be enabled below
|
||||||
|
*/
|
||||||
|
#define USE_FreeRTOS_HEAP_4
|
||||||
|
|
||||||
|
/* Cortex-M specific definitions. */
|
||||||
|
#ifdef __NVIC_PRIO_BITS
|
||||||
|
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||||
|
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||||
|
#else
|
||||||
|
#define configPRIO_BITS 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||||
|
function. */
|
||||||
|
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
|
||||||
|
|
||||||
|
/* The highest interrupt priority that can be used by any interrupt service
|
||||||
|
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||||
|
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||||
|
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||||
|
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||||
|
|
||||||
|
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||||
|
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||||
|
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||||
|
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
|
||||||
|
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||||
|
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||||
|
|
||||||
|
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||||
|
header file. */
|
||||||
|
/* USER CODE BEGIN 1 */
|
||||||
|
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
|
||||||
|
/* USER CODE END 1 */
|
||||||
|
|
||||||
|
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||||
|
standard names. */
|
||||||
|
#define vPortSVCHandler SVC_Handler
|
||||||
|
#define xPortPendSVHandler PendSV_Handler
|
||||||
|
|
||||||
|
/* IMPORTANT: After 10.3.1 update, Systick_Handler comes from NVIC (if SYS timebase = systick), otherwise from cmsis_os2.c */
|
||||||
|
|
||||||
|
#define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0
|
||||||
|
|
||||||
|
/* USER CODE BEGIN Defines */
|
||||||
|
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
|
||||||
|
/* USER CODE END Defines */
|
||||||
|
|
||||||
|
#endif /* FREERTOS_CONFIG_H */
|
||||||
@@ -57,6 +57,8 @@ void Error_Handler(void);
|
|||||||
/* USER CODE END EFP */
|
/* USER CODE END EFP */
|
||||||
|
|
||||||
/* Private defines -----------------------------------------------------------*/
|
/* Private defines -----------------------------------------------------------*/
|
||||||
|
#define CH395_INT_Pin GPIO_PIN_5
|
||||||
|
#define CH395_INT_GPIO_Port GPIOE
|
||||||
#define LED1_Pin GPIO_PIN_4
|
#define LED1_Pin GPIO_PIN_4
|
||||||
#define LED1_GPIO_Port GPIOC
|
#define LED1_GPIO_Port GPIOC
|
||||||
#define LED2_Pin GPIO_PIN_5
|
#define LED2_Pin GPIO_PIN_5
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
/* #define HAL_SD_MODULE_ENABLED */
|
/* #define HAL_SD_MODULE_ENABLED */
|
||||||
/* #define HAL_MMC_MODULE_ENABLED */
|
/* #define HAL_MMC_MODULE_ENABLED */
|
||||||
#define HAL_SPI_MODULE_ENABLED
|
#define HAL_SPI_MODULE_ENABLED
|
||||||
/* #define HAL_TIM_MODULE_ENABLED */
|
#define HAL_TIM_MODULE_ENABLED
|
||||||
#define HAL_UART_MODULE_ENABLED
|
#define HAL_UART_MODULE_ENABLED
|
||||||
/* #define HAL_USART_MODULE_ENABLED */
|
/* #define HAL_USART_MODULE_ENABLED */
|
||||||
/* #define HAL_IRDA_MODULE_ENABLED */
|
/* #define HAL_IRDA_MODULE_ENABLED */
|
||||||
|
|||||||
@@ -51,17 +51,21 @@ void HardFault_Handler(void);
|
|||||||
void MemManage_Handler(void);
|
void MemManage_Handler(void);
|
||||||
void BusFault_Handler(void);
|
void BusFault_Handler(void);
|
||||||
void UsageFault_Handler(void);
|
void UsageFault_Handler(void);
|
||||||
void SVC_Handler(void);
|
|
||||||
void DebugMon_Handler(void);
|
void DebugMon_Handler(void);
|
||||||
void PendSV_Handler(void);
|
void DMA1_Stream3_IRQHandler(void);
|
||||||
void SysTick_Handler(void);
|
void DMA1_Stream4_IRQHandler(void);
|
||||||
void EXTI9_5_IRQHandler(void);
|
void EXTI9_5_IRQHandler(void);
|
||||||
|
void SPI1_IRQHandler(void);
|
||||||
|
void SPI2_IRQHandler(void);
|
||||||
void USART1_IRQHandler(void);
|
void USART1_IRQHandler(void);
|
||||||
void USART2_IRQHandler(void);
|
void USART2_IRQHandler(void);
|
||||||
void USART3_IRQHandler(void);
|
void USART3_IRQHandler(void);
|
||||||
void UART4_IRQHandler(void);
|
void UART4_IRQHandler(void);
|
||||||
void UART5_IRQHandler(void);
|
void UART5_IRQHandler(void);
|
||||||
|
void TIM7_IRQHandler(void);
|
||||||
|
void DMA2_Stream0_IRQHandler(void);
|
||||||
void DMA2_Stream2_IRQHandler(void);
|
void DMA2_Stream2_IRQHandler(void);
|
||||||
|
void DMA2_Stream3_IRQHandler(void);
|
||||||
void DMA2_Stream7_IRQHandler(void);
|
void DMA2_Stream7_IRQHandler(void);
|
||||||
/* USER CODE BEGIN EFP */
|
/* USER CODE BEGIN EFP */
|
||||||
|
|
||||||
|
|||||||
43
Lib/FatFs/diskio.h
Normal file
43
Lib/FatFs/diskio.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef _DISKIO_DEFINED
|
||||||
|
#define _DISKIO_DEFINED
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef BYTE DSTATUS;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RES_OK = 0,
|
||||||
|
RES_ERROR,
|
||||||
|
RES_WRPRT,
|
||||||
|
RES_NOTRDY,
|
||||||
|
RES_PARERR
|
||||||
|
} DRESULT;
|
||||||
|
|
||||||
|
DSTATUS disk_initialize(BYTE pdrv);
|
||||||
|
DSTATUS disk_status(BYTE pdrv);
|
||||||
|
DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||||
|
DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||||
|
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buff);
|
||||||
|
|
||||||
|
#define STA_NOINIT 0x01
|
||||||
|
#define STA_NODISK 0x02
|
||||||
|
#define STA_PROTECT 0x04
|
||||||
|
|
||||||
|
#define CTRL_SYNC 0
|
||||||
|
#define GET_SECTOR_COUNT 1
|
||||||
|
#define GET_SECTOR_SIZE 2
|
||||||
|
#define GET_BLOCK_SIZE 3
|
||||||
|
#define CTRL_TRIM 4
|
||||||
|
|
||||||
|
#define CTRL_POWER 5
|
||||||
|
#define CTRL_LOCK 6
|
||||||
|
#define CTRL_EJECT 7
|
||||||
|
#define CTRL_FORMAT 8
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
7084
Lib/FatFs/ff.c
Normal file
7084
Lib/FatFs/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
429
Lib/FatFs/ff.h
Normal file
429
Lib/FatFs/ff.h
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
/*----------------------------------------------------------------------------/
|
||||||
|
/ FatFs - Generic FAT Filesystem module R0.15 /
|
||||||
|
/-----------------------------------------------------------------------------/
|
||||||
|
/
|
||||||
|
/ Copyright (C) 2022, ChaN, all right reserved.
|
||||||
|
/
|
||||||
|
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||||
|
/ source and binary forms, with or without modification, are permitted provided
|
||||||
|
/ that the following condition is met:
|
||||||
|
/
|
||||||
|
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
/ this condition and the following disclaimer.
|
||||||
|
/
|
||||||
|
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||||
|
/ and any warranties related to this software are DISCLAIMED.
|
||||||
|
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||||
|
/ by use of this software.
|
||||||
|
/
|
||||||
|
/----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FF_DEFINED
|
||||||
|
#define FF_DEFINED 80286 /* Revision ID */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ffconf.h" /* FatFs configuration options */
|
||||||
|
|
||||||
|
#if FF_DEFINED != FFCONF_DEF
|
||||||
|
#error Wrong configuration file (ffconf.h).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Integer types used for FatFs API */
|
||||||
|
|
||||||
|
#if defined(_WIN32) /* Windows VC++ (for development only) */
|
||||||
|
#define FF_INTDEF 2
|
||||||
|
#include <windows.h>
|
||||||
|
typedef unsigned __int64 QWORD;
|
||||||
|
#include <float.h>
|
||||||
|
#define isnan(v) _isnan(v)
|
||||||
|
#define isinf(v) (!_finite(v))
|
||||||
|
|
||||||
|
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||||
|
#define FF_INTDEF 2
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||||
|
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||||
|
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||||
|
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||||
|
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||||
|
typedef WORD WCHAR; /* UTF-16 character type */
|
||||||
|
|
||||||
|
#else /* Earlier than C99 */
|
||||||
|
#define FF_INTDEF 1
|
||||||
|
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||||
|
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||||
|
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||||
|
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||||
|
typedef WORD WCHAR; /* UTF-16 character type */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Type of file size and LBA variables */
|
||||||
|
|
||||||
|
#if FF_FS_EXFAT
|
||||||
|
#if FF_INTDEF != 2
|
||||||
|
#error exFAT feature wants C99 or later
|
||||||
|
#endif
|
||||||
|
typedef QWORD FSIZE_t;
|
||||||
|
#if FF_LBA64
|
||||||
|
typedef QWORD LBA_t;
|
||||||
|
#else
|
||||||
|
typedef DWORD LBA_t;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if FF_LBA64
|
||||||
|
#error exFAT needs to be enabled when enable 64-bit LBA
|
||||||
|
#endif
|
||||||
|
typedef DWORD FSIZE_t;
|
||||||
|
typedef DWORD LBA_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Type of path name strings on FatFs API (TCHAR) */
|
||||||
|
|
||||||
|
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||||
|
typedef WCHAR TCHAR;
|
||||||
|
#define _T(x) L ## x
|
||||||
|
#define _TEXT(x) L ## x
|
||||||
|
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||||
|
typedef char TCHAR;
|
||||||
|
#define _T(x) u8 ## x
|
||||||
|
#define _TEXT(x) u8 ## x
|
||||||
|
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||||
|
typedef DWORD TCHAR;
|
||||||
|
#define _T(x) U ## x
|
||||||
|
#define _TEXT(x) U ## x
|
||||||
|
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||||
|
#error Wrong FF_LFN_UNICODE setting
|
||||||
|
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||||
|
typedef char TCHAR;
|
||||||
|
#define _T(x) x
|
||||||
|
#define _TEXT(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Definitions of volume management */
|
||||||
|
|
||||||
|
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||||
|
typedef struct {
|
||||||
|
BYTE pd; /* Physical drive number */
|
||||||
|
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||||
|
} PARTITION;
|
||||||
|
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FF_STR_VOLUME_ID
|
||||||
|
#ifndef FF_VOLUME_STRS
|
||||||
|
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Filesystem object structure (FATFS) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||||
|
BYTE pdrv; /* Volume hosting physical drive */
|
||||||
|
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
|
||||||
|
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||||
|
BYTE wflag; /* win[] status (b0:dirty) */
|
||||||
|
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
|
||||||
|
WORD id; /* Volume mount ID */
|
||||||
|
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||||
|
WORD csize; /* Cluster size [sectors] */
|
||||||
|
#if FF_MAX_SS != FF_MIN_SS
|
||||||
|
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||||
|
#endif
|
||||||
|
#if FF_USE_LFN
|
||||||
|
WCHAR* lfnbuf; /* LFN working buffer */
|
||||||
|
#endif
|
||||||
|
#if FF_FS_EXFAT
|
||||||
|
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||||
|
#endif
|
||||||
|
#if !FF_FS_READONLY
|
||||||
|
DWORD last_clst; /* Last allocated cluster */
|
||||||
|
DWORD free_clst; /* Number of free clusters */
|
||||||
|
#endif
|
||||||
|
#if FF_FS_RPATH
|
||||||
|
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||||
|
#if FF_FS_EXFAT
|
||||||
|
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||||
|
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||||
|
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||||
|
DWORD fsize; /* Number of sectors per FAT */
|
||||||
|
LBA_t volbase; /* Volume base sector */
|
||||||
|
LBA_t fatbase; /* FAT base sector */
|
||||||
|
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
|
||||||
|
LBA_t database; /* Data base sector */
|
||||||
|
#if FF_FS_EXFAT
|
||||||
|
LBA_t bitbase; /* Allocation bitmap base sector */
|
||||||
|
#endif
|
||||||
|
LBA_t winsect; /* Current sector appearing in the win[] */
|
||||||
|
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||||
|
} FATFS;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Object ID and allocation information (FFOBJID) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||||
|
WORD id; /* Hosting volume's mount ID */
|
||||||
|
BYTE attr; /* Object attribute */
|
||||||
|
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||||
|
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||||
|
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||||
|
#if FF_FS_EXFAT
|
||||||
|
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||||
|
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||||
|
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||||
|
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||||
|
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||||
|
#endif
|
||||||
|
#if FF_FS_LOCK
|
||||||
|
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||||
|
#endif
|
||||||
|
} FFOBJID;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File object structure (FIL) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||||
|
BYTE flag; /* File status flags */
|
||||||
|
BYTE err; /* Abort flag (error code) */
|
||||||
|
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||||
|
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||||
|
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||||
|
#if !FF_FS_READONLY
|
||||||
|
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||||
|
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||||
|
#endif
|
||||||
|
#if FF_USE_FASTSEEK
|
||||||
|
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||||
|
#endif
|
||||||
|
#if !FF_FS_TINY
|
||||||
|
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||||
|
#endif
|
||||||
|
} FIL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Directory object structure (DIR) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FFOBJID obj; /* Object identifier */
|
||||||
|
DWORD dptr; /* Current read/write offset */
|
||||||
|
DWORD clust; /* Current cluster */
|
||||||
|
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
||||||
|
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||||
|
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||||
|
#if FF_USE_LFN
|
||||||
|
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||||
|
#endif
|
||||||
|
#if FF_USE_FIND
|
||||||
|
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||||
|
#endif
|
||||||
|
} DIR;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File information structure (FILINFO) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FSIZE_t fsize; /* File size */
|
||||||
|
WORD fdate; /* Modified date */
|
||||||
|
WORD ftime; /* Modified time */
|
||||||
|
BYTE fattrib; /* File attribute */
|
||||||
|
#if FF_USE_LFN
|
||||||
|
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
|
||||||
|
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||||
|
#else
|
||||||
|
TCHAR fname[12 + 1]; /* File name */
|
||||||
|
#endif
|
||||||
|
} FILINFO;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Format parameter structure (MKFS_PARM) */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||||
|
BYTE n_fat; /* Number of FATs */
|
||||||
|
UINT align; /* Data area alignment (sector) */
|
||||||
|
UINT n_root; /* Number of root directory entries */
|
||||||
|
DWORD au_size; /* Cluster size (byte) */
|
||||||
|
} MKFS_PARM;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* File function return code (FRESULT) */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FR_OK = 0, /* (0) Succeeded */
|
||||||
|
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||||
|
FR_INT_ERR, /* (2) Assertion failed */
|
||||||
|
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||||
|
FR_NO_FILE, /* (4) Could not find the file */
|
||||||
|
FR_NO_PATH, /* (5) Could not find the path */
|
||||||
|
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||||
|
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||||
|
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||||
|
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||||
|
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||||
|
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||||
|
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||||
|
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||||
|
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||||
|
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||||
|
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||||
|
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||||
|
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||||
|
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||||
|
} FRESULT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* FatFs Module Application Interface */
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||||
|
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||||
|
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||||
|
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||||
|
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||||
|
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||||
|
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||||
|
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||||
|
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||||
|
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||||
|
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||||
|
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||||
|
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||||
|
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||||
|
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||||
|
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||||
|
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||||
|
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||||
|
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||||
|
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||||
|
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||||
|
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||||
|
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||||
|
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||||
|
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||||
|
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||||
|
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||||
|
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||||
|
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
||||||
|
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||||
|
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||||
|
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||||
|
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||||
|
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||||
|
|
||||||
|
/* Some API fucntions are implemented as macro */
|
||||||
|
|
||||||
|
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||||
|
#define f_error(fp) ((fp)->err)
|
||||||
|
#define f_tell(fp) ((fp)->fptr)
|
||||||
|
#define f_size(fp) ((fp)->obj.objsize)
|
||||||
|
#define f_rewind(fp) f_lseek((fp), 0)
|
||||||
|
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||||
|
#define f_rmdir(path) f_unlink(path)
|
||||||
|
#define f_unmount(path) f_mount(0, path, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* Additional Functions */
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* RTC function (provided by user) */
|
||||||
|
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||||
|
DWORD get_fattime (void); /* Get current time */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* LFN support functions (defined in ffunicode.c) */
|
||||||
|
|
||||||
|
#if FF_USE_LFN >= 1
|
||||||
|
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||||
|
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||||
|
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* O/S dependent functions (samples available in ffsystem.c) */
|
||||||
|
|
||||||
|
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||||
|
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||||
|
void ff_memfree (void* mblock); /* Free memory block */
|
||||||
|
#endif
|
||||||
|
#if FF_FS_REENTRANT /* Sync functions */
|
||||||
|
int ff_mutex_create (int vol); /* Create a sync object */
|
||||||
|
void ff_mutex_delete (int vol); /* Delete a sync object */
|
||||||
|
int ff_mutex_take (int vol); /* Lock sync object */
|
||||||
|
void ff_mutex_give (int vol); /* Unlock sync object */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
/* Flags and Offset Address */
|
||||||
|
/*--------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* File access mode and open method flags (3rd argument of f_open) */
|
||||||
|
#define FA_READ 0x01
|
||||||
|
#define FA_WRITE 0x02
|
||||||
|
#define FA_OPEN_EXISTING 0x00
|
||||||
|
#define FA_CREATE_NEW 0x04
|
||||||
|
#define FA_CREATE_ALWAYS 0x08
|
||||||
|
#define FA_OPEN_ALWAYS 0x10
|
||||||
|
#define FA_OPEN_APPEND 0x30
|
||||||
|
|
||||||
|
/* Fast seek controls (2nd argument of f_lseek) */
|
||||||
|
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||||
|
|
||||||
|
/* Format options (2nd argument of f_mkfs) */
|
||||||
|
#define FM_FAT 0x01
|
||||||
|
#define FM_FAT32 0x02
|
||||||
|
#define FM_EXFAT 0x04
|
||||||
|
#define FM_ANY 0x07
|
||||||
|
#define FM_SFD 0x08
|
||||||
|
|
||||||
|
/* Filesystem type (FATFS.fs_type) */
|
||||||
|
#define FS_FAT12 1
|
||||||
|
#define FS_FAT16 2
|
||||||
|
#define FS_FAT32 3
|
||||||
|
#define FS_EXFAT 4
|
||||||
|
|
||||||
|
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||||
|
#define AM_RDO 0x01 /* Read only */
|
||||||
|
#define AM_HID 0x02 /* Hidden */
|
||||||
|
#define AM_SYS 0x04 /* System */
|
||||||
|
#define AM_DIR 0x10 /* Directory */
|
||||||
|
#define AM_ARC 0x20 /* Archive */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* FF_DEFINED */
|
||||||
41
Lib/FatFs/ffconf.h
Normal file
41
Lib/FatFs/ffconf.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#define FFCONF_DEF 80286
|
||||||
|
|
||||||
|
#define FF_FS_READONLY 0
|
||||||
|
#define FF_FS_MINIMIZE 0
|
||||||
|
#define FF_USE_FIND 0
|
||||||
|
#define FF_USE_MKFS 1
|
||||||
|
#define FF_USE_FASTSEEK 0
|
||||||
|
#define FF_USE_EXPAND 0
|
||||||
|
#define FF_USE_CHMOD 0
|
||||||
|
#define FF_USE_LABEL 0
|
||||||
|
#define FF_USE_FORWARD 0
|
||||||
|
#define FF_USE_STRFUNC 0
|
||||||
|
#define FF_PRINT_LLI 1
|
||||||
|
#define FF_PRINT_FLOAT 1
|
||||||
|
#define FF_STRF_ENCODE 3
|
||||||
|
#define FF_CODE_PAGE 437
|
||||||
|
#define FF_USE_LFN 0
|
||||||
|
#define FF_MAX_LFN 255
|
||||||
|
#define FF_LFN_UNICODE 0
|
||||||
|
#define FF_LFN_BUF 255
|
||||||
|
#define FF_SFN_BUF 12
|
||||||
|
#define FF_FS_RPATH 0
|
||||||
|
#define FF_VOLUMES 1
|
||||||
|
#define FF_STR_VOLUME_ID 0
|
||||||
|
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||||
|
#define FF_MULTI_PARTITION 0
|
||||||
|
#define FF_MIN_SS 512
|
||||||
|
#define FF_MAX_SS 512
|
||||||
|
#define FF_LBA64 0
|
||||||
|
#define FF_MIN_GPT 0x10000000
|
||||||
|
#define FF_USE_TRIM 0
|
||||||
|
#define FF_FS_TINY 0
|
||||||
|
#define FF_FS_EXFAT 0
|
||||||
|
#define FF_FS_NORTC 1
|
||||||
|
#define FF_NORTC_MON 1
|
||||||
|
#define FF_NORTC_MDAY 1
|
||||||
|
#define FF_NORTC_YEAR 2026
|
||||||
|
#define FF_FS_NOFSINFO 0
|
||||||
|
#define FF_FS_LOCK 0
|
||||||
|
#define FF_FS_REENTRANT 0
|
||||||
|
#define FF_FS_TIMEOUT 1000
|
||||||
Submodule Lib/FlashDB deleted from 8236571f6e
50
Lib/dhara/bytes.h
Normal file
50
Lib/dhara/bytes.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DHARA_BYTES_H_
|
||||||
|
#define DHARA_BYTES_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static inline uint16_t dhara_r16(const uint8_t *data)
|
||||||
|
{
|
||||||
|
return ((uint16_t)data[0]) |
|
||||||
|
(((uint16_t)data[1]) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dhara_w16(uint8_t *data, uint16_t v)
|
||||||
|
{
|
||||||
|
data[0] = v;
|
||||||
|
data[1] = v >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t dhara_r32(const uint8_t *data)
|
||||||
|
{
|
||||||
|
return ((uint32_t)data[0]) |
|
||||||
|
(((uint32_t)data[1]) << 8) |
|
||||||
|
(((uint32_t)data[2]) << 16) |
|
||||||
|
(((uint32_t)data[3]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void dhara_w32(uint8_t *data, uint32_t v)
|
||||||
|
{
|
||||||
|
data[0] = v;
|
||||||
|
data[1] = v >> 8;
|
||||||
|
data[2] = v >> 16;
|
||||||
|
data[3] = v >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
45
Lib/dhara/error.h
Normal file
45
Lib/dhara/error.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DHARA_ERROR_H_
|
||||||
|
#define DHARA_ERROR_H_
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DHARA_E_NONE = 0,
|
||||||
|
DHARA_E_BAD_BLOCK,
|
||||||
|
DHARA_E_ECC,
|
||||||
|
DHARA_E_TOO_BAD,
|
||||||
|
DHARA_E_RECOVER,
|
||||||
|
DHARA_E_JOURNAL_FULL,
|
||||||
|
DHARA_E_NOT_FOUND,
|
||||||
|
DHARA_E_MAP_FULL,
|
||||||
|
DHARA_E_CORRUPT_MAP,
|
||||||
|
DHARA_E_MAX
|
||||||
|
} dhara_error_t;
|
||||||
|
|
||||||
|
/* Produce a human-readable error message. This function is kept in a
|
||||||
|
* separate compilation unit and can be omitted to reduce binary size.
|
||||||
|
*/
|
||||||
|
const char *dhara_strerror(dhara_error_t err);
|
||||||
|
|
||||||
|
/* Save an error */
|
||||||
|
static inline void dhara_set_error(dhara_error_t *err, dhara_error_t v)
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
*err = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
884
Lib/dhara/journal.c
Normal file
884
Lib/dhara/journal.c
Normal file
@@ -0,0 +1,884 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "journal.h"
|
||||||
|
#include "bytes.h"
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Metapage binary format
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Does the page buffer contain a valid checkpoint page? */
|
||||||
|
static inline int hdr_has_magic(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
return (buf[0] == 'D') &&
|
||||||
|
(buf[1] == 'h') &&
|
||||||
|
(buf[2] == 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hdr_put_magic(uint8_t *buf)
|
||||||
|
{
|
||||||
|
buf[0] = 'D';
|
||||||
|
buf[1] = 'h';
|
||||||
|
buf[2] = 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* What epoch is this page? */
|
||||||
|
static inline uint8_t hdr_get_epoch(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
return buf[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hdr_set_epoch(uint8_t *buf, uint8_t e)
|
||||||
|
{
|
||||||
|
buf[3] = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dhara_page_t hdr_get_tail(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
return dhara_r32(buf + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hdr_set_tail(uint8_t *buf, dhara_page_t tail)
|
||||||
|
{
|
||||||
|
dhara_w32(buf + 4, tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dhara_page_t hdr_get_bb_current(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
return dhara_r32(buf + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hdr_set_bb_current(uint8_t *buf, dhara_page_t count)
|
||||||
|
{
|
||||||
|
dhara_w32(buf + 8, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dhara_page_t hdr_get_bb_last(const uint8_t *buf)
|
||||||
|
{
|
||||||
|
return dhara_r32(buf + 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hdr_set_bb_last(uint8_t *buf, dhara_page_t count)
|
||||||
|
{
|
||||||
|
dhara_w32(buf + 12, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear user metadata */
|
||||||
|
static inline void hdr_clear_user(uint8_t *buf, uint8_t log2_page_size)
|
||||||
|
{
|
||||||
|
memset(buf + DHARA_HEADER_SIZE + DHARA_COOKIE_SIZE, 0xff,
|
||||||
|
(1 << log2_page_size) - DHARA_HEADER_SIZE - DHARA_COOKIE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain pointers to user data */
|
||||||
|
static inline size_t hdr_user_offset(uint8_t which)
|
||||||
|
{
|
||||||
|
return DHARA_HEADER_SIZE + DHARA_COOKIE_SIZE +
|
||||||
|
which * DHARA_META_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Page geometry helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Is this page index aligned to N bits? */
|
||||||
|
static inline int is_aligned(dhara_page_t p, int n)
|
||||||
|
{
|
||||||
|
return !(p & ((1 << n) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Are these two pages from the same alignment group? */
|
||||||
|
static inline int align_eq(dhara_page_t a, dhara_page_t b,
|
||||||
|
int n)
|
||||||
|
{
|
||||||
|
return !((a ^ b) >> n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* What is the successor of this block? */
|
||||||
|
static dhara_block_t next_block(const struct dhara_nand *n, dhara_block_t blk)
|
||||||
|
{
|
||||||
|
blk++;
|
||||||
|
if (blk >= n->num_blocks)
|
||||||
|
blk = 0;
|
||||||
|
|
||||||
|
return blk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dhara_page_t next_upage(const struct dhara_journal *j,
|
||||||
|
dhara_page_t p)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
if (is_aligned(p + 1, j->log2_ppc))
|
||||||
|
p++;
|
||||||
|
|
||||||
|
if (p >= (j->nand->num_blocks << j->nand->log2_ppb))
|
||||||
|
p = 0;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate a checkpoint period: the largest value of ppc such that
|
||||||
|
* (2**ppc - 1) metadata blocks can fit on a page with one journal
|
||||||
|
* header.
|
||||||
|
*/
|
||||||
|
static int choose_ppc(int log2_page_size, int max)
|
||||||
|
{
|
||||||
|
const int max_meta = (1 << log2_page_size) -
|
||||||
|
DHARA_HEADER_SIZE - DHARA_COOKIE_SIZE;
|
||||||
|
int total_meta = DHARA_META_SIZE;
|
||||||
|
int ppc = 1;
|
||||||
|
|
||||||
|
while (ppc < max) {
|
||||||
|
total_meta <<= 1;
|
||||||
|
total_meta += DHARA_META_SIZE;
|
||||||
|
|
||||||
|
if (total_meta > max_meta)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ppc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ppc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Journal setup/resume
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Clear recovery status */
|
||||||
|
static void clear_recovery(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
j->recover_next = DHARA_PAGE_NONE;
|
||||||
|
j->recover_root = DHARA_PAGE_NONE;
|
||||||
|
j->recover_meta = DHARA_PAGE_NONE;
|
||||||
|
j->flags &= ~(DHARA_JOURNAL_F_BAD_META |
|
||||||
|
DHARA_JOURNAL_F_RECOVERY |
|
||||||
|
DHARA_JOURNAL_F_ENUM_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up an empty journal */
|
||||||
|
static void reset_journal(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
/* We don't yet have a bad block estimate, so make a
|
||||||
|
* conservative guess.
|
||||||
|
*/
|
||||||
|
j->epoch = 0;
|
||||||
|
j->bb_last = j->nand->num_blocks >> 6;
|
||||||
|
j->bb_current = 0;
|
||||||
|
|
||||||
|
j->flags = 0;
|
||||||
|
|
||||||
|
/* Empty journal */
|
||||||
|
j->head = 0;
|
||||||
|
j->tail = 0;
|
||||||
|
j->tail_sync = 0;
|
||||||
|
j->root = DHARA_PAGE_NONE;
|
||||||
|
|
||||||
|
/* No recovery required */
|
||||||
|
clear_recovery(j);
|
||||||
|
|
||||||
|
/* Empty metadata buffer */
|
||||||
|
memset(j->page_buf, 0xff, 1 << j->nand->log2_page_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void roll_stats(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
j->bb_last = j->bb_current;
|
||||||
|
j->bb_current = 0;
|
||||||
|
j->epoch++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dhara_journal_init(struct dhara_journal *j,
|
||||||
|
const struct dhara_nand *n,
|
||||||
|
uint8_t *page_buf)
|
||||||
|
{
|
||||||
|
/* Set fixed parameters */
|
||||||
|
j->nand = n;
|
||||||
|
j->page_buf = page_buf;
|
||||||
|
j->log2_ppc = choose_ppc(n->log2_page_size, n->log2_ppb);
|
||||||
|
|
||||||
|
reset_journal(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the first checkpoint-containing block. If a block contains any
|
||||||
|
* checkpoints at all, then it must contain one in the first checkpoint
|
||||||
|
* location -- otherwise, we would have considered the block eraseable.
|
||||||
|
*/
|
||||||
|
static int find_checkblock(struct dhara_journal *j,
|
||||||
|
dhara_block_t blk, dhara_block_t *where,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; (blk < j->nand->num_blocks) &&
|
||||||
|
(i < DHARA_MAX_RETRIES); i++) {
|
||||||
|
const dhara_page_t p =
|
||||||
|
(blk << j->nand->log2_ppb) |
|
||||||
|
((1 << j->log2_ppc) - 1);
|
||||||
|
|
||||||
|
if (!(dhara_nand_is_bad(j->nand, blk) ||
|
||||||
|
dhara_nand_read(j->nand, p,
|
||||||
|
0, 1 << j->nand->log2_page_size,
|
||||||
|
j->page_buf, err)) &&
|
||||||
|
hdr_has_magic(j->page_buf)) {
|
||||||
|
*where = blk;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dhara_block_t find_last_checkblock(struct dhara_journal *j,
|
||||||
|
dhara_block_t first)
|
||||||
|
{
|
||||||
|
dhara_block_t low = first;
|
||||||
|
dhara_block_t high = j->nand->num_blocks - 1;
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
const dhara_block_t mid = (low + high) >> 1;
|
||||||
|
dhara_block_t found;
|
||||||
|
|
||||||
|
if ((find_checkblock(j, mid, &found, NULL) < 0) ||
|
||||||
|
(hdr_get_epoch(j->page_buf) != j->epoch)) {
|
||||||
|
if (!mid)
|
||||||
|
return first;
|
||||||
|
|
||||||
|
high = mid - 1;
|
||||||
|
} else {
|
||||||
|
dhara_block_t nf;
|
||||||
|
|
||||||
|
if (((found + 1) >= j->nand->num_blocks) ||
|
||||||
|
(find_checkblock(j, found + 1,
|
||||||
|
&nf, NULL) < 0) ||
|
||||||
|
(hdr_get_epoch(j->page_buf) != j->epoch))
|
||||||
|
return found;
|
||||||
|
|
||||||
|
low = nf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test whether a checkpoint group is in a state fit for reprogramming,
|
||||||
|
* but allow for the fact that is_free() might not have any way of
|
||||||
|
* distinguishing between an unprogrammed page, and a page programmed
|
||||||
|
* with all-0xff bytes (but if so, it must be ok to reprogram such a
|
||||||
|
* page).
|
||||||
|
*
|
||||||
|
* We used to test for an unprogrammed checkpoint group by checking to
|
||||||
|
* see if the first user-page had been programmed since last erase (by
|
||||||
|
* testing only the first page with is_free). This works if is_free is
|
||||||
|
* precise, because the pages are written in order.
|
||||||
|
*
|
||||||
|
* If is_free is imprecise, we need to check all pages in the group.
|
||||||
|
* That also works, because the final page in a checkpoint group is
|
||||||
|
* guaranteed to contain non-0xff bytes. Therefore, we return 1 only if
|
||||||
|
* the group is truly unprogrammed, or if it was partially programmed
|
||||||
|
* with some all-0xff user pages (which changes nothing for us).
|
||||||
|
*/
|
||||||
|
static int cp_free(struct dhara_journal *j, dhara_page_t first_user)
|
||||||
|
{
|
||||||
|
const int count = 1 << j->log2_ppc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (!dhara_nand_is_free(j->nand, first_user + i))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dhara_page_t find_last_group(struct dhara_journal *j,
|
||||||
|
dhara_block_t blk)
|
||||||
|
{
|
||||||
|
const int num_groups = 1 << (j->nand->log2_ppb - j->log2_ppc);
|
||||||
|
int low = 0;
|
||||||
|
int high = num_groups - 1;
|
||||||
|
|
||||||
|
/* If a checkpoint group is completely unprogrammed, everything
|
||||||
|
* following it will be completely unprogrammed also.
|
||||||
|
*
|
||||||
|
* Therefore, binary search checkpoint groups until we find the
|
||||||
|
* last programmed one.
|
||||||
|
*/
|
||||||
|
while (low <= high) {
|
||||||
|
int mid = (low + high) >> 1;
|
||||||
|
const dhara_page_t p = (mid << j->log2_ppc) |
|
||||||
|
(blk << j->nand->log2_ppb);
|
||||||
|
|
||||||
|
if (cp_free(j, p)) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else if (((mid + 1) >= num_groups) ||
|
||||||
|
cp_free(j, p + (1 << j->log2_ppc))) {
|
||||||
|
return p;
|
||||||
|
} else {
|
||||||
|
low = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blk << j->nand->log2_ppb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_root(struct dhara_journal *j, dhara_page_t start,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
const dhara_block_t blk = start >> j->nand->log2_ppb;
|
||||||
|
int i = (start & ((1 << j->nand->log2_ppb) - 1)) >> j->log2_ppc;
|
||||||
|
|
||||||
|
while (i >= 0) {
|
||||||
|
const dhara_page_t p = (blk << j->nand->log2_ppb) +
|
||||||
|
((i + 1) << j->log2_ppc) - 1;
|
||||||
|
|
||||||
|
if (!dhara_nand_read(j->nand, p,
|
||||||
|
0, 1 << j->nand->log2_page_size,
|
||||||
|
j->page_buf, err) &&
|
||||||
|
(hdr_has_magic(j->page_buf)) &&
|
||||||
|
(hdr_get_epoch(j->page_buf) == j->epoch)) {
|
||||||
|
j->root = p - 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_head(struct dhara_journal *j, dhara_page_t start,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
j->head = next_upage(j, start);
|
||||||
|
if (!j->head)
|
||||||
|
roll_stats(j);
|
||||||
|
|
||||||
|
/* Starting from the last good checkpoint, find either:
|
||||||
|
*
|
||||||
|
* (a) the next free user-page in the same block
|
||||||
|
* (b) or, the first page of the next block
|
||||||
|
*
|
||||||
|
* The block we end up on might be bad, but that's ok -- we'll
|
||||||
|
* skip it when we go to prepare the next write.
|
||||||
|
*/
|
||||||
|
for (;;) {
|
||||||
|
/* How many free pages trail this checkpoint group? */
|
||||||
|
const unsigned int ppc = 1 << j->log2_ppc;
|
||||||
|
unsigned int n = 0;
|
||||||
|
dhara_page_t first = j->head & ~(dhara_page_t)(ppc - 1);
|
||||||
|
|
||||||
|
while (n < ppc &&
|
||||||
|
dhara_nand_is_free(j->nand, first + ppc - n - 1))
|
||||||
|
n++;
|
||||||
|
|
||||||
|
/* If we have some, then we've found our next free
|
||||||
|
* userpage.
|
||||||
|
*/
|
||||||
|
if (n > 1) {
|
||||||
|
j->head = first + ppc - n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip to the next checkpoint group */
|
||||||
|
j->head = first + ppc;
|
||||||
|
if (j->head >= (j->nand->num_blocks << j->nand->log2_ppb)) {
|
||||||
|
j->head = 0;
|
||||||
|
roll_stats(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we hit the end of the block, we're done */
|
||||||
|
if (is_aligned(j->head, j->nand->log2_ppb)) {
|
||||||
|
/* Make sure we don't chase over the tail */
|
||||||
|
if (align_eq(j->head, j->tail, j->nand->log2_ppb))
|
||||||
|
j->tail = next_block(j->nand,
|
||||||
|
j->tail >> j->nand->log2_ppb) <<
|
||||||
|
j->nand->log2_ppb;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_journal_resume(struct dhara_journal *j, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_block_t first, last;
|
||||||
|
dhara_page_t last_group;
|
||||||
|
|
||||||
|
/* Find the first checkpoint-containing block */
|
||||||
|
if (find_checkblock(j, 0, &first, err) < 0) {
|
||||||
|
reset_journal(j);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the last checkpoint-containing block in this epoch */
|
||||||
|
j->epoch = hdr_get_epoch(j->page_buf);
|
||||||
|
last = find_last_checkblock(j, first);
|
||||||
|
|
||||||
|
/* Find the last programmed checkpoint group in the block */
|
||||||
|
last_group = find_last_group(j, last);
|
||||||
|
|
||||||
|
/* Perform a linear scan to find the last good checkpoint (and
|
||||||
|
* therefore the root).
|
||||||
|
*/
|
||||||
|
if (find_root(j, last_group, err) < 0) {
|
||||||
|
reset_journal(j);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore settings from checkpoint */
|
||||||
|
j->tail = hdr_get_tail(j->page_buf);
|
||||||
|
j->bb_current = hdr_get_bb_current(j->page_buf);
|
||||||
|
j->bb_last = hdr_get_bb_last(j->page_buf);
|
||||||
|
hdr_clear_user(j->page_buf, j->nand->log2_page_size);
|
||||||
|
|
||||||
|
/* Perform another linear scan to find the next free user page */
|
||||||
|
if (find_head(j, last_group, err) < 0) {
|
||||||
|
reset_journal(j);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->flags = 0;
|
||||||
|
j->tail_sync = j->tail;
|
||||||
|
|
||||||
|
clear_recovery(j);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Public interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
dhara_page_t dhara_journal_capacity(const struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
const dhara_block_t max_bad = j->bb_last > j->bb_current ?
|
||||||
|
j->bb_last : j->bb_current;
|
||||||
|
const dhara_block_t good_blocks = j->nand->num_blocks - max_bad - 1;
|
||||||
|
const int log2_cpb = j->nand->log2_ppb - j->log2_ppc;
|
||||||
|
const dhara_page_t good_cps = good_blocks << log2_cpb;
|
||||||
|
|
||||||
|
/* Good checkpoints * (checkpoint period - 1) */
|
||||||
|
return (good_cps << j->log2_ppc) - good_cps;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_page_t dhara_journal_size(const struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
/* Find the number of raw pages, and the number of checkpoints
|
||||||
|
* between the head and the tail. The difference between the two
|
||||||
|
* is the number of user pages (upper limit).
|
||||||
|
*/
|
||||||
|
dhara_page_t num_pages = j->head;
|
||||||
|
dhara_page_t num_cps = j->head >> j->log2_ppc;
|
||||||
|
|
||||||
|
if (j->head < j->tail_sync) {
|
||||||
|
const dhara_page_t total_pages =
|
||||||
|
j->nand->num_blocks << j->nand->log2_ppb;
|
||||||
|
|
||||||
|
num_pages += total_pages;
|
||||||
|
num_cps += total_pages >> j->log2_ppc;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_pages -= j->tail_sync;
|
||||||
|
num_cps -= j->tail_sync >> j->log2_ppc;
|
||||||
|
|
||||||
|
return num_pages - num_cps;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_journal_read_meta(struct dhara_journal *j, dhara_page_t p,
|
||||||
|
uint8_t *buf, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
/* Offset of metadata within the metadata page */
|
||||||
|
const dhara_page_t ppc_mask = (1 << j->log2_ppc) - 1;
|
||||||
|
const size_t offset = hdr_user_offset(p & ppc_mask);
|
||||||
|
|
||||||
|
/* Special case: buffered metadata */
|
||||||
|
if (align_eq(p, j->head, j->log2_ppc)) {
|
||||||
|
memcpy(buf, j->page_buf + offset, DHARA_META_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special case: incomplete metadata dumped at start of
|
||||||
|
* recovery.
|
||||||
|
*/
|
||||||
|
if ((j->recover_meta != DHARA_PAGE_NONE) &&
|
||||||
|
align_eq(p, j->recover_root, j->log2_ppc))
|
||||||
|
return dhara_nand_read(j->nand, j->recover_meta,
|
||||||
|
offset, DHARA_META_SIZE,
|
||||||
|
buf, err);
|
||||||
|
|
||||||
|
/* General case: fetch from metadata page for checkpoint group */
|
||||||
|
return dhara_nand_read(j->nand, p | ppc_mask,
|
||||||
|
offset, DHARA_META_SIZE,
|
||||||
|
buf, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_page_t dhara_journal_peek(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
if (j->head == j->tail)
|
||||||
|
return DHARA_PAGE_NONE;
|
||||||
|
|
||||||
|
if (is_aligned(j->tail, j->nand->log2_ppb)) {
|
||||||
|
dhara_block_t blk = j->tail >> j->nand->log2_ppb;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||||
|
if ((blk == (j->head >> j->nand->log2_ppb)) ||
|
||||||
|
!dhara_nand_is_bad(j->nand, blk)) {
|
||||||
|
j->tail = blk << j->nand->log2_ppb;
|
||||||
|
|
||||||
|
if (j->tail == j->head)
|
||||||
|
j->root = DHARA_PAGE_NONE;
|
||||||
|
|
||||||
|
return j->tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk = next_block(j->nand, blk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return j->tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dhara_page_t wrap(dhara_page_t a, dhara_page_t b)
|
||||||
|
{
|
||||||
|
return a >= b ? (a - b) : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dhara_journal_dequeue(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
if (j->head == j->tail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
j->tail = next_upage(j, j->tail);
|
||||||
|
|
||||||
|
/* If the journal is clean at the time of dequeue, then this
|
||||||
|
* data was always obsolete, and can be reused immediately.
|
||||||
|
*/
|
||||||
|
if (!(j->flags & (DHARA_JOURNAL_F_DIRTY | DHARA_JOURNAL_F_RECOVERY)))
|
||||||
|
j->tail_sync = j->tail;
|
||||||
|
|
||||||
|
const dhara_page_t chip_size = j->nand->num_blocks << j->nand->log2_ppb;
|
||||||
|
const dhara_page_t raw_size = wrap(j->head + chip_size - j->tail,
|
||||||
|
chip_size);
|
||||||
|
const dhara_page_t root_offset = wrap(j->head + chip_size - j->root,
|
||||||
|
chip_size);
|
||||||
|
|
||||||
|
if (root_offset > raw_size)
|
||||||
|
j->root = DHARA_PAGE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dhara_journal_clear(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
j->tail = j->head;
|
||||||
|
j->root = DHARA_PAGE_NONE;
|
||||||
|
j->flags |= DHARA_JOURNAL_F_DIRTY;
|
||||||
|
|
||||||
|
hdr_clear_user(j->page_buf, j->nand->log2_page_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int skip_block(struct dhara_journal *j, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
const dhara_block_t next = next_block(j->nand,
|
||||||
|
j->head >> j->nand->log2_ppb);
|
||||||
|
|
||||||
|
/* We can't roll onto the same block as the tail */
|
||||||
|
if ((j->tail_sync >> j->nand->log2_ppb) == next) {
|
||||||
|
dhara_set_error(err, DHARA_E_JOURNAL_FULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->head = next << j->nand->log2_ppb;
|
||||||
|
if (!j->head)
|
||||||
|
roll_stats(j);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the head pointer is on a ready-to-program page. */
|
||||||
|
static int prepare_head(struct dhara_journal *j, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
const dhara_page_t next = next_upage(j, j->head);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* We can't write if doing so would cause the head pointer to
|
||||||
|
* roll onto the same block as the last-synced tail.
|
||||||
|
*/
|
||||||
|
if (align_eq(next, j->tail_sync, j->nand->log2_ppb) &&
|
||||||
|
!align_eq(next, j->head, j->nand->log2_ppb)) {
|
||||||
|
dhara_set_error(err, DHARA_E_JOURNAL_FULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->flags |= DHARA_JOURNAL_F_DIRTY;
|
||||||
|
if (!is_aligned(j->head, j->nand->log2_ppb))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||||
|
const dhara_block_t blk = j->head >> j->nand->log2_ppb;
|
||||||
|
|
||||||
|
if (!dhara_nand_is_bad(j->nand, blk))
|
||||||
|
return dhara_nand_erase(j->nand, blk, err);
|
||||||
|
|
||||||
|
j->bb_current++;
|
||||||
|
if (skip_block(j, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void restart_recovery(struct dhara_journal *j, dhara_page_t old_head)
|
||||||
|
{
|
||||||
|
/* Mark the current head bad immediately, unless we're also
|
||||||
|
* using it to hold our dumped metadata (it will then be marked
|
||||||
|
* bad at the end of recovery).
|
||||||
|
*/
|
||||||
|
if ((j->recover_meta == DHARA_PAGE_NONE) ||
|
||||||
|
!align_eq(j->recover_meta, old_head, j->nand->log2_ppb))
|
||||||
|
dhara_nand_mark_bad(j->nand, old_head >> j->nand->log2_ppb);
|
||||||
|
else
|
||||||
|
j->flags |= DHARA_JOURNAL_F_BAD_META;
|
||||||
|
|
||||||
|
/* Start recovery again. Reset the source enumeration to
|
||||||
|
* the start of the original bad block, and reset the
|
||||||
|
* destination enumeration to the newly found good
|
||||||
|
* block.
|
||||||
|
*/
|
||||||
|
j->flags &= ~DHARA_JOURNAL_F_ENUM_DONE;
|
||||||
|
j->recover_next =
|
||||||
|
j->recover_root & ~((1 << j->nand->log2_ppb) - 1);
|
||||||
|
|
||||||
|
j->root = j->recover_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_meta(struct dhara_journal *j, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* We've just begun recovery on a new erasable block, but we
|
||||||
|
* have buffered metadata from the failed block.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||||
|
dhara_error_t my_err;
|
||||||
|
|
||||||
|
/* Try to dump metadata on this page */
|
||||||
|
if (!(prepare_head(j, &my_err) ||
|
||||||
|
dhara_nand_prog(j->nand, j->head,
|
||||||
|
j->page_buf, &my_err))) {
|
||||||
|
j->recover_meta = j->head;
|
||||||
|
j->head = next_upage(j, j->head);
|
||||||
|
if (!j->head)
|
||||||
|
roll_stats(j);
|
||||||
|
hdr_clear_user(j->page_buf, j->nand->log2_page_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Report fatal errors */
|
||||||
|
if (my_err != DHARA_E_BAD_BLOCK) {
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->bb_current++;
|
||||||
|
dhara_nand_mark_bad(j->nand, j->head >> j->nand->log2_ppb);
|
||||||
|
|
||||||
|
if (skip_block(j, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recover_from(struct dhara_journal *j,
|
||||||
|
dhara_error_t write_err,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
const dhara_page_t old_head = j->head;
|
||||||
|
|
||||||
|
if (write_err != DHARA_E_BAD_BLOCK) {
|
||||||
|
dhara_set_error(err, write_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance to the next free page */
|
||||||
|
j->bb_current++;
|
||||||
|
if (skip_block(j, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Are we already in the middle of a recovery? */
|
||||||
|
if (dhara_journal_in_recovery(j)) {
|
||||||
|
restart_recovery(j, old_head);
|
||||||
|
dhara_set_error(err, DHARA_E_RECOVER);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Were we block aligned? No recovery required! */
|
||||||
|
if (is_aligned(old_head, j->nand->log2_ppb)) {
|
||||||
|
dhara_nand_mark_bad(j->nand, old_head >> j->nand->log2_ppb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
j->recover_root = j->root;
|
||||||
|
j->recover_next =
|
||||||
|
j->recover_root & ~((1 << j->nand->log2_ppb) - 1);
|
||||||
|
|
||||||
|
/* Are we holding buffered metadata? Dump it first. */
|
||||||
|
if (!is_aligned(old_head, j->log2_ppc) &&
|
||||||
|
dump_meta(j, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
j->flags |= DHARA_JOURNAL_F_RECOVERY;
|
||||||
|
dhara_set_error(err, DHARA_E_RECOVER);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void finish_recovery(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
/* We just recovered the last page. Mark the recovered
|
||||||
|
* block as bad.
|
||||||
|
*/
|
||||||
|
dhara_nand_mark_bad(j->nand,
|
||||||
|
j->recover_root >> j->nand->log2_ppb);
|
||||||
|
|
||||||
|
/* If we had to dump metadata, and the page on which we
|
||||||
|
* did this also went bad, mark it bad too.
|
||||||
|
*/
|
||||||
|
if (j->flags & DHARA_JOURNAL_F_BAD_META)
|
||||||
|
dhara_nand_mark_bad(j->nand,
|
||||||
|
j->recover_meta >> j->nand->log2_ppb);
|
||||||
|
|
||||||
|
/* Was the tail on this page? Skip it forward */
|
||||||
|
clear_recovery(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int push_meta(struct dhara_journal *j, const uint8_t *meta,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
const dhara_page_t old_head = j->head;
|
||||||
|
dhara_error_t my_err;
|
||||||
|
const size_t offset =
|
||||||
|
hdr_user_offset(j->head & ((1 << j->log2_ppc) - 1));
|
||||||
|
|
||||||
|
/* We've just written a user page. Add the metadata to the
|
||||||
|
* buffer.
|
||||||
|
*/
|
||||||
|
if (meta)
|
||||||
|
memcpy(j->page_buf + offset, meta, DHARA_META_SIZE);
|
||||||
|
else
|
||||||
|
memset(j->page_buf + offset, 0xff, DHARA_META_SIZE);
|
||||||
|
|
||||||
|
/* Unless we've filled the buffer, don't do any IO */
|
||||||
|
if (!is_aligned(j->head + 2, j->log2_ppc)) {
|
||||||
|
j->root = j->head;
|
||||||
|
j->head++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't need to check for immediate recover, because that'll
|
||||||
|
* never happen -- we're not block-aligned.
|
||||||
|
*/
|
||||||
|
hdr_put_magic(j->page_buf);
|
||||||
|
hdr_set_epoch(j->page_buf, j->epoch);
|
||||||
|
hdr_set_tail(j->page_buf, j->tail);
|
||||||
|
hdr_set_bb_current(j->page_buf, j->bb_current);
|
||||||
|
hdr_set_bb_last(j->page_buf, j->bb_last);
|
||||||
|
|
||||||
|
if (dhara_nand_prog(j->nand, j->head + 1, j->page_buf, &my_err) < 0)
|
||||||
|
return recover_from(j, my_err, err);
|
||||||
|
|
||||||
|
j->flags &= ~DHARA_JOURNAL_F_DIRTY;
|
||||||
|
|
||||||
|
j->root = old_head;
|
||||||
|
j->head = next_upage(j, j->head);
|
||||||
|
|
||||||
|
if (!j->head)
|
||||||
|
roll_stats(j);
|
||||||
|
|
||||||
|
if (j->flags & DHARA_JOURNAL_F_ENUM_DONE)
|
||||||
|
finish_recovery(j);
|
||||||
|
|
||||||
|
if (!(j->flags & DHARA_JOURNAL_F_RECOVERY))
|
||||||
|
j->tail_sync = j->tail;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_journal_enqueue(struct dhara_journal *j,
|
||||||
|
const uint8_t *data, const uint8_t *meta,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_error_t my_err;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||||
|
if (!(prepare_head(j, &my_err) ||
|
||||||
|
(data && dhara_nand_prog(j->nand, j->head, data,
|
||||||
|
&my_err))))
|
||||||
|
return push_meta(j, meta, err);
|
||||||
|
|
||||||
|
if (recover_from(j, my_err, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_journal_copy(struct dhara_journal *j,
|
||||||
|
dhara_page_t p, const uint8_t *meta,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_error_t my_err;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||||
|
if (!(prepare_head(j, &my_err) ||
|
||||||
|
dhara_nand_copy(j->nand, p, j->head, &my_err)))
|
||||||
|
return push_meta(j, meta, err);
|
||||||
|
|
||||||
|
if (recover_from(j, my_err, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_page_t dhara_journal_next_recoverable(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
const dhara_page_t n = j->recover_next;
|
||||||
|
|
||||||
|
if (!dhara_journal_in_recovery(j))
|
||||||
|
return DHARA_PAGE_NONE;
|
||||||
|
|
||||||
|
if (j->flags & DHARA_JOURNAL_F_ENUM_DONE)
|
||||||
|
return DHARA_PAGE_NONE;
|
||||||
|
|
||||||
|
if (j->recover_next == j->recover_root)
|
||||||
|
j->flags |= DHARA_JOURNAL_F_ENUM_DONE;
|
||||||
|
else
|
||||||
|
j->recover_next = next_upage(j, j->recover_next);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
256
Lib/dhara/journal.h
Normal file
256
Lib/dhara/journal.h
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DHARA_JOURNAL_H_
|
||||||
|
#define DHARA_JOURNAL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "nand.h"
|
||||||
|
|
||||||
|
/* Number of bytes used by the journal checkpoint header. */
|
||||||
|
#define DHARA_HEADER_SIZE 16
|
||||||
|
|
||||||
|
/* Global metadata available for a higher layer. This metadata is
|
||||||
|
* persistent once the journal reaches a checkpoint, and is restored on
|
||||||
|
* startup.
|
||||||
|
*/
|
||||||
|
#define DHARA_COOKIE_SIZE 4
|
||||||
|
|
||||||
|
/* This is the size of the metadata slice which accompanies each written
|
||||||
|
* page. This is independent of the underlying page/OOB size.
|
||||||
|
*/
|
||||||
|
#define DHARA_META_SIZE 132
|
||||||
|
|
||||||
|
/* When a block fails, or garbage is encountered, we try again on the
|
||||||
|
* next block/checkpoint. We can do this up to the given number of
|
||||||
|
* times.
|
||||||
|
*/
|
||||||
|
#define DHARA_MAX_RETRIES 8
|
||||||
|
|
||||||
|
/* This is a page number which can be used to represent "no such page".
|
||||||
|
* It's guaranteed to never be a valid user page.
|
||||||
|
*/
|
||||||
|
#define DHARA_PAGE_NONE ((dhara_page_t)0xffffffff)
|
||||||
|
|
||||||
|
/* State flags */
|
||||||
|
#define DHARA_JOURNAL_F_DIRTY 0x01
|
||||||
|
#define DHARA_JOURNAL_F_BAD_META 0x02
|
||||||
|
#define DHARA_JOURNAL_F_RECOVERY 0x04
|
||||||
|
#define DHARA_JOURNAL_F_ENUM_DONE 0x08
|
||||||
|
|
||||||
|
/* The journal layer presents the NAND pages as a double-ended queue.
|
||||||
|
* Pages, with associated metadata may be pushed onto the end of the
|
||||||
|
* queue, and pages may be popped from the end.
|
||||||
|
*
|
||||||
|
* Block erase, metadata storage are handled automatically. Bad blocks
|
||||||
|
* are handled by relocating data to the next available non-bad page in
|
||||||
|
* the sequence.
|
||||||
|
*
|
||||||
|
* It's up to the user to ensure that the queue doesn't grow beyond the
|
||||||
|
* capacity of the NAND chip, but helper functions are provided to
|
||||||
|
* assist with this. If the head meets the tail, the journal will refuse
|
||||||
|
* to enqueue more pages.
|
||||||
|
*/
|
||||||
|
struct dhara_journal {
|
||||||
|
const struct dhara_nand *nand;
|
||||||
|
uint8_t *page_buf;
|
||||||
|
|
||||||
|
/* In the journal, user data is grouped into checkpoints of
|
||||||
|
* 2**log2_ppc contiguous aligned pages.
|
||||||
|
*
|
||||||
|
* The last page of each checkpoint contains the journal header
|
||||||
|
* and the metadata for the other pages in the period (the user
|
||||||
|
* pages).
|
||||||
|
*/
|
||||||
|
uint8_t log2_ppc;
|
||||||
|
|
||||||
|
/* Epoch counter. This is incremented whenever the journal head
|
||||||
|
* passes the end of the chip and wraps around.
|
||||||
|
*/
|
||||||
|
uint8_t epoch;
|
||||||
|
|
||||||
|
/* General purpose flags field */
|
||||||
|
uint8_t flags;
|
||||||
|
|
||||||
|
/* Bad-block counters. bb_last is our best estimate of the
|
||||||
|
* number of bad blocks in the chip as a whole. bb_current is
|
||||||
|
* the number of bad blocks in all blocks before the current
|
||||||
|
* head.
|
||||||
|
*/
|
||||||
|
dhara_block_t bb_current;
|
||||||
|
dhara_block_t bb_last;
|
||||||
|
|
||||||
|
/* Log head and tail. The tail pointer points to the last user
|
||||||
|
* page in the log, and the head pointer points to the next free
|
||||||
|
* raw page. The root points to the last written user page.
|
||||||
|
*/
|
||||||
|
dhara_page_t tail_sync;
|
||||||
|
dhara_page_t tail;
|
||||||
|
dhara_page_t head;
|
||||||
|
|
||||||
|
/* This points to the last written user page in the journal */
|
||||||
|
dhara_page_t root;
|
||||||
|
|
||||||
|
/* Recovery mode: recover_root points to the last valid user
|
||||||
|
* page in the block requiring recovery. recover_next points to
|
||||||
|
* the next user page needing recovery.
|
||||||
|
*
|
||||||
|
* If we had buffered metadata before recovery started, it will
|
||||||
|
* have been dumped to a free page, indicated by recover_meta.
|
||||||
|
* If this block later goes bad, we will have to defer bad-block
|
||||||
|
* marking until recovery is complete (F_BAD_META).
|
||||||
|
*/
|
||||||
|
dhara_page_t recover_next;
|
||||||
|
dhara_page_t recover_root;
|
||||||
|
dhara_page_t recover_meta;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize a journal. You must supply a pointer to a NAND chip
|
||||||
|
* driver, and a single page buffer. This page buffer will be used
|
||||||
|
* exclusively by the journal, but you are responsible for allocating
|
||||||
|
* it, and freeing it (if necessary) at the end.
|
||||||
|
*
|
||||||
|
* No NAND operations are performed at this point.
|
||||||
|
*/
|
||||||
|
void dhara_journal_init(struct dhara_journal *j,
|
||||||
|
const struct dhara_nand *n,
|
||||||
|
uint8_t *page_buf);
|
||||||
|
|
||||||
|
/* Start up the journal -- search the NAND for the journal head, or
|
||||||
|
* initialize a blank journal if one isn't found. Returns 0 on success
|
||||||
|
* or -1 if a (fatal) error occurs.
|
||||||
|
*
|
||||||
|
* This operation is O(log N), where N is the number of pages in the
|
||||||
|
* NAND chip. All other operations are O(1).
|
||||||
|
*
|
||||||
|
* If this operation fails, the journal will be reset to an empty state.
|
||||||
|
*/
|
||||||
|
int dhara_journal_resume(struct dhara_journal *j, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Obtain an upper bound on the number of user pages storable in the
|
||||||
|
* journal.
|
||||||
|
*/
|
||||||
|
dhara_page_t dhara_journal_capacity(const struct dhara_journal *j);
|
||||||
|
|
||||||
|
/* Obtain an upper bound on the number of user pages consumed by the
|
||||||
|
* journal.
|
||||||
|
*/
|
||||||
|
dhara_page_t dhara_journal_size(const struct dhara_journal *j);
|
||||||
|
|
||||||
|
/* Obtain a pointer to the cookie data */
|
||||||
|
static inline uint8_t *dhara_journal_cookie(const struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
return j->page_buf + DHARA_HEADER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain the locations of the first and last pages in the journal.
|
||||||
|
*/
|
||||||
|
static inline dhara_page_t dhara_journal_root(const struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
return j->root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read metadata associated with a page. This assumes that the page
|
||||||
|
* provided is a valid data page. The actual page data is read via the
|
||||||
|
* normal NAND interface.
|
||||||
|
*/
|
||||||
|
int dhara_journal_read_meta(struct dhara_journal *j, dhara_page_t p,
|
||||||
|
uint8_t *buf, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Advance the tail to the next non-bad block and return the page that's
|
||||||
|
* ready to read. If no page is ready, return DHARA_PAGE_NONE.
|
||||||
|
*/
|
||||||
|
dhara_page_t dhara_journal_peek(struct dhara_journal *j);
|
||||||
|
|
||||||
|
/* Remove the last page from the journal. This doesn't take permanent
|
||||||
|
* effect until the next checkpoint.
|
||||||
|
*/
|
||||||
|
void dhara_journal_dequeue(struct dhara_journal *j);
|
||||||
|
|
||||||
|
/* Remove all pages form the journal. This doesn't take permanent effect
|
||||||
|
* until the next checkpoint.
|
||||||
|
*/
|
||||||
|
void dhara_journal_clear(struct dhara_journal *j);
|
||||||
|
|
||||||
|
/* Append a page to the journal. Both raw page data and metadata must be
|
||||||
|
* specified. The push operation is not persistent until a checkpoint is
|
||||||
|
* reached.
|
||||||
|
*
|
||||||
|
* This operation may fail with the error code E_RECOVER. If this
|
||||||
|
* occurs, the upper layer must complete the assisted recovery procedure
|
||||||
|
* and then try again.
|
||||||
|
*
|
||||||
|
* This operation may be used as part of a recovery. If further errors
|
||||||
|
* occur during recovery, E_RECOVER is returned, and the procedure must
|
||||||
|
* be restarted.
|
||||||
|
*/
|
||||||
|
int dhara_journal_enqueue(struct dhara_journal *j,
|
||||||
|
const uint8_t *data, const uint8_t *meta,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Copy an existing page to the front of the journal. New metadata must
|
||||||
|
* be specified. This operation is not persistent until a checkpoint is
|
||||||
|
* reached.
|
||||||
|
*
|
||||||
|
* This operation may fail with the error code E_RECOVER. If this
|
||||||
|
* occurs, the upper layer must complete the assisted recovery procedure
|
||||||
|
* and then try again.
|
||||||
|
*
|
||||||
|
* This operation may be used as part of a recovery. If further errors
|
||||||
|
* occur during recovery, E_RECOVER is returned, and the procedure must
|
||||||
|
* be restarted.
|
||||||
|
*/
|
||||||
|
int dhara_journal_copy(struct dhara_journal *j,
|
||||||
|
dhara_page_t p, const uint8_t *meta,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Mark the journal dirty. */
|
||||||
|
static inline void dhara_journal_mark_dirty(struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
j->flags |= DHARA_JOURNAL_F_DIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is the journal checkpointed? If true, then all pages enqueued are now
|
||||||
|
* persistent.
|
||||||
|
*/
|
||||||
|
static inline int dhara_journal_is_clean(const struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
return !(j->flags & DHARA_JOURNAL_F_DIRTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If an operation returns E_RECOVER, you must begin the recovery
|
||||||
|
* procedure. You must then:
|
||||||
|
*
|
||||||
|
* - call dhara_journal_next_recoverable() to obtain the next block
|
||||||
|
* to be recovered (if any). If there are no blocks remaining to be
|
||||||
|
* recovered, DHARA_JOURNAL_PAGE_NONE is returned.
|
||||||
|
*
|
||||||
|
* - proceed to the next checkpoint. Once the journal is clean,
|
||||||
|
* recovery will finish automatically.
|
||||||
|
*
|
||||||
|
* If any operation during recovery fails due to a bad block, E_RECOVER
|
||||||
|
* is returned again, and recovery restarts. Do not add new data to the
|
||||||
|
* journal (rewrites of recovered data are fine) until recovery is
|
||||||
|
* complete.
|
||||||
|
*/
|
||||||
|
static inline int dhara_journal_in_recovery(const struct dhara_journal *j)
|
||||||
|
{
|
||||||
|
return j->flags & DHARA_JOURNAL_F_RECOVERY;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_page_t dhara_journal_next_recoverable(struct dhara_journal *j);
|
||||||
|
|
||||||
|
#endif
|
||||||
530
Lib/dhara/map.c
Normal file
530
Lib/dhara/map.c
Normal file
@@ -0,0 +1,530 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "bytes.h"
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
#define DHARA_RADIX_DEPTH (sizeof(dhara_sector_t) << 3)
|
||||||
|
|
||||||
|
static inline dhara_sector_t d_bit(int depth)
|
||||||
|
{
|
||||||
|
return ((dhara_sector_t)1) << (DHARA_RADIX_DEPTH - depth - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Metadata/cookie layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline void ck_set_count(uint8_t *cookie, dhara_sector_t count)
|
||||||
|
{
|
||||||
|
dhara_w32(cookie, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dhara_sector_t ck_get_count(const uint8_t *cookie)
|
||||||
|
{
|
||||||
|
return dhara_r32(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void meta_clear(uint8_t *meta)
|
||||||
|
{
|
||||||
|
memset(meta, 0xff, DHARA_META_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dhara_sector_t meta_get_id(const uint8_t *meta)
|
||||||
|
{
|
||||||
|
return dhara_r32(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void meta_set_id(uint8_t *meta, dhara_sector_t id)
|
||||||
|
{
|
||||||
|
dhara_w32(meta, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dhara_page_t meta_get_alt(const uint8_t *meta, int level)
|
||||||
|
{
|
||||||
|
return dhara_r32(meta + 4 + (level << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void meta_set_alt(uint8_t *meta, int level, dhara_page_t alt)
|
||||||
|
{
|
||||||
|
dhara_w32(meta + 4 + (level << 2), alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* Public interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
void dhara_map_init(struct dhara_map *m, const struct dhara_nand *n,
|
||||||
|
uint8_t *page_buf, uint8_t gc_ratio)
|
||||||
|
{
|
||||||
|
if (!gc_ratio)
|
||||||
|
gc_ratio = 1;
|
||||||
|
|
||||||
|
dhara_journal_init(&m->journal, n, page_buf);
|
||||||
|
m->gc_ratio = gc_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_resume(struct dhara_map *m, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
if (dhara_journal_resume(&m->journal, err) < 0) {
|
||||||
|
m->count = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m->count = ck_get_count(dhara_journal_cookie(&m->journal));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dhara_map_clear(struct dhara_map *m)
|
||||||
|
{
|
||||||
|
if (m->count) {
|
||||||
|
m->count = 0;
|
||||||
|
dhara_journal_clear(&m->journal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_sector_t dhara_map_capacity(const struct dhara_map *m)
|
||||||
|
{
|
||||||
|
const dhara_sector_t cap = dhara_journal_capacity(&m->journal);
|
||||||
|
const dhara_sector_t reserve = cap / (m->gc_ratio + 1);
|
||||||
|
const dhara_sector_t safety_margin =
|
||||||
|
DHARA_MAX_RETRIES << m->journal.nand->log2_ppb;
|
||||||
|
|
||||||
|
if (reserve + safety_margin >= cap)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return cap - reserve - safety_margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trace the path from the root to the given sector, emitting
|
||||||
|
* alt-pointers and alt-full bits in the given metadata buffer. This
|
||||||
|
* also returns the physical page containing the given sector, if it
|
||||||
|
* exists.
|
||||||
|
*
|
||||||
|
* If the page can't be found, a suitable path will be constructed
|
||||||
|
* (containing PAGE_NONE alt-pointers), and DHARA_E_NOT_FOUND will be
|
||||||
|
* returned.
|
||||||
|
*/
|
||||||
|
static int trace_path(struct dhara_map *m, dhara_sector_t target,
|
||||||
|
dhara_page_t *loc, uint8_t *new_meta,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
uint8_t meta[DHARA_META_SIZE];
|
||||||
|
int depth = 0;
|
||||||
|
dhara_page_t p = dhara_journal_root(&m->journal);
|
||||||
|
|
||||||
|
if (new_meta)
|
||||||
|
meta_set_id(new_meta, target);
|
||||||
|
|
||||||
|
if (p == DHARA_PAGE_NONE)
|
||||||
|
goto not_found;
|
||||||
|
|
||||||
|
if (dhara_journal_read_meta(&m->journal, p, meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (depth < DHARA_RADIX_DEPTH) {
|
||||||
|
const dhara_sector_t id = meta_get_id(meta);
|
||||||
|
|
||||||
|
if (id == DHARA_SECTOR_NONE)
|
||||||
|
goto not_found;
|
||||||
|
|
||||||
|
if ((target ^ id) & d_bit(depth)) {
|
||||||
|
if (new_meta)
|
||||||
|
meta_set_alt(new_meta, depth, p);
|
||||||
|
|
||||||
|
p = meta_get_alt(meta, depth);
|
||||||
|
if (p == DHARA_PAGE_NONE) {
|
||||||
|
depth++;
|
||||||
|
goto not_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dhara_journal_read_meta(&m->journal, p,
|
||||||
|
meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (new_meta)
|
||||||
|
meta_set_alt(new_meta, depth,
|
||||||
|
meta_get_alt(meta, depth));
|
||||||
|
}
|
||||||
|
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc)
|
||||||
|
*loc = p;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
not_found:
|
||||||
|
if (new_meta) {
|
||||||
|
while (depth < DHARA_RADIX_DEPTH)
|
||||||
|
meta_set_alt(new_meta, depth++, DHARA_SECTOR_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, DHARA_E_NOT_FOUND);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_find(struct dhara_map *m, dhara_sector_t target,
|
||||||
|
dhara_page_t *loc, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
return trace_path(m, target, loc, NULL, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_read(struct dhara_map *m, dhara_sector_t s,
|
||||||
|
uint8_t *data, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
const struct dhara_nand *n = m->journal.nand;
|
||||||
|
dhara_error_t my_err;
|
||||||
|
dhara_page_t p;
|
||||||
|
|
||||||
|
if (dhara_map_find(m, s, &p, &my_err) < 0) {
|
||||||
|
if (my_err == DHARA_E_NOT_FOUND) {
|
||||||
|
memset(data, 0xff, 1 << n->log2_page_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dhara_nand_read(n, p, 0, 1 << n->log2_page_size, data, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the given page. If it's garbage, do nothing. Otherwise, rewrite
|
||||||
|
* it at the front of the map. Return raw errors from the journal (do
|
||||||
|
* not perform recovery).
|
||||||
|
*/
|
||||||
|
static int raw_gc(struct dhara_map *m, dhara_page_t src,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_sector_t target;
|
||||||
|
dhara_page_t current;
|
||||||
|
dhara_error_t my_err;
|
||||||
|
uint8_t meta[DHARA_META_SIZE];
|
||||||
|
|
||||||
|
if (dhara_journal_read_meta(&m->journal, src, meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Is the page just filler/garbage? */
|
||||||
|
target = meta_get_id(meta);
|
||||||
|
if (target == DHARA_SECTOR_NONE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Find out where the sector once represented by this page
|
||||||
|
* currently resides (if anywhere).
|
||||||
|
*/
|
||||||
|
if (trace_path(m, target, ¤t, meta, &my_err) < 0) {
|
||||||
|
if (my_err == DHARA_E_NOT_FOUND)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is this page still the most current representative? If not,
|
||||||
|
* do nothing.
|
||||||
|
*/
|
||||||
|
if (current != src)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Rewrite it at the front of the journal with updated metadata */
|
||||||
|
ck_set_count(dhara_journal_cookie(&m->journal), m->count);
|
||||||
|
if (dhara_journal_copy(&m->journal, src, meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pad_queue(struct dhara_map *m, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_page_t p = dhara_journal_root(&m->journal);
|
||||||
|
uint8_t root_meta[DHARA_META_SIZE];
|
||||||
|
|
||||||
|
ck_set_count(dhara_journal_cookie(&m->journal), m->count);
|
||||||
|
|
||||||
|
if (p == DHARA_PAGE_NONE)
|
||||||
|
return dhara_journal_enqueue(&m->journal, NULL, NULL, err);
|
||||||
|
|
||||||
|
if (dhara_journal_read_meta(&m->journal, p, root_meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return dhara_journal_copy(&m->journal, p, root_meta, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to recover the journal */
|
||||||
|
static int try_recover(struct dhara_map *m, dhara_error_t cause,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
int restart_count = 0;
|
||||||
|
|
||||||
|
if (cause != DHARA_E_RECOVER) {
|
||||||
|
dhara_set_error(err, cause);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (dhara_journal_in_recovery(&m->journal)) {
|
||||||
|
dhara_page_t p = dhara_journal_next_recoverable(&m->journal);
|
||||||
|
dhara_error_t my_err;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (p == DHARA_PAGE_NONE)
|
||||||
|
ret = pad_queue(m, &my_err);
|
||||||
|
else
|
||||||
|
ret = raw_gc(m, p, &my_err);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (my_err != DHARA_E_RECOVER) {
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restart_count >= DHARA_MAX_RETRIES) {
|
||||||
|
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int auto_gc(struct dhara_map *m, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (dhara_journal_size(&m->journal) < dhara_map_capacity(m))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i <= m->gc_ratio; i++)
|
||||||
|
if (dhara_map_gc(m, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prepare_write(struct dhara_map *m, dhara_sector_t dst,
|
||||||
|
uint8_t *meta, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_error_t my_err;
|
||||||
|
|
||||||
|
if (auto_gc(m, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (trace_path(m, dst, NULL, meta, &my_err) < 0) {
|
||||||
|
if (my_err != DHARA_E_NOT_FOUND) {
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m->count >= dhara_map_capacity(m)) {
|
||||||
|
dhara_set_error(err, DHARA_E_MAP_FULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ck_set_count(dhara_journal_cookie(&m->journal), m->count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_write(struct dhara_map *m, dhara_sector_t dst,
|
||||||
|
const uint8_t *data, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
uint8_t meta[DHARA_META_SIZE];
|
||||||
|
dhara_error_t my_err;
|
||||||
|
const dhara_sector_t old_count = m->count;
|
||||||
|
|
||||||
|
if (prepare_write(m, dst, meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!dhara_journal_enqueue(&m->journal, data, meta, &my_err))
|
||||||
|
break;
|
||||||
|
|
||||||
|
m->count = old_count;
|
||||||
|
|
||||||
|
if (try_recover(m, my_err, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_copy_page(struct dhara_map *m, dhara_page_t src,
|
||||||
|
dhara_sector_t dst, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
uint8_t meta[DHARA_META_SIZE];
|
||||||
|
dhara_error_t my_err;
|
||||||
|
const dhara_sector_t old_count = m->count;
|
||||||
|
|
||||||
|
if (prepare_write(m, dst, meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!dhara_journal_copy(&m->journal, src, meta, &my_err))
|
||||||
|
break;
|
||||||
|
|
||||||
|
m->count = old_count;
|
||||||
|
|
||||||
|
if (try_recover(m, my_err, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_copy_sector(struct dhara_map *m, dhara_sector_t src,
|
||||||
|
dhara_sector_t dst, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_error_t my_err;
|
||||||
|
dhara_page_t p;
|
||||||
|
|
||||||
|
if (dhara_map_find(m, src, &p, &my_err) < 0) {
|
||||||
|
if (my_err == DHARA_E_NOT_FOUND)
|
||||||
|
return dhara_map_trim(m, dst, err);
|
||||||
|
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dhara_map_copy_page(m, p, dst, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int try_delete(struct dhara_map *m, dhara_sector_t s,
|
||||||
|
dhara_error_t *err)
|
||||||
|
{
|
||||||
|
dhara_error_t my_err;
|
||||||
|
uint8_t meta[DHARA_META_SIZE];
|
||||||
|
dhara_page_t alt_page;
|
||||||
|
uint8_t alt_meta[DHARA_META_SIZE];
|
||||||
|
int level = DHARA_RADIX_DEPTH - 1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (trace_path(m, s, NULL, meta, &my_err) < 0) {
|
||||||
|
if (my_err == DHARA_E_NOT_FOUND)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dhara_set_error(err, my_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select any of the closest cousins of this node which are
|
||||||
|
* subtrees of at least the requested order.
|
||||||
|
*/
|
||||||
|
while (level >= 0) {
|
||||||
|
alt_page = meta_get_alt(meta, level);
|
||||||
|
if (alt_page != DHARA_PAGE_NONE)
|
||||||
|
break;
|
||||||
|
level--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Special case: deletion of last sector */
|
||||||
|
if (level < 0) {
|
||||||
|
m->count = 0;
|
||||||
|
dhara_journal_clear(&m->journal);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rewrite the cousin with an up-to-date path which doesn't
|
||||||
|
* point to the original node.
|
||||||
|
*/
|
||||||
|
if (dhara_journal_read_meta(&m->journal, alt_page, alt_meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
meta_set_id(meta, meta_get_id(alt_meta));
|
||||||
|
|
||||||
|
meta_set_alt(meta, level, DHARA_PAGE_NONE);
|
||||||
|
for (i = level + 1; i < DHARA_RADIX_DEPTH; i++)
|
||||||
|
meta_set_alt(meta, i, meta_get_alt(alt_meta, i));
|
||||||
|
|
||||||
|
meta_set_alt(meta, level, DHARA_PAGE_NONE);
|
||||||
|
|
||||||
|
ck_set_count(dhara_journal_cookie(&m->journal), m->count - 1);
|
||||||
|
if (dhara_journal_copy(&m->journal, alt_page, meta, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m->count--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_trim(struct dhara_map *m, dhara_sector_t s, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
dhara_error_t my_err;
|
||||||
|
|
||||||
|
if (auto_gc(m, err) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!try_delete(m, s, &my_err))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (try_recover(m, my_err, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_sync(struct dhara_map *m, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
while (!dhara_journal_is_clean(&m->journal)) {
|
||||||
|
dhara_page_t p = dhara_journal_peek(&m->journal);
|
||||||
|
dhara_error_t my_err;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (p == DHARA_PAGE_NONE) {
|
||||||
|
ret = pad_queue(m, &my_err);
|
||||||
|
} else {
|
||||||
|
ret = raw_gc(m, p, &my_err);
|
||||||
|
if (!ret)
|
||||||
|
dhara_journal_dequeue(&m->journal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret < 0) && (try_recover(m, my_err, err) < 0))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dhara_map_gc(struct dhara_map *m, dhara_error_t *err)
|
||||||
|
{
|
||||||
|
if (!m->count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
dhara_page_t tail = dhara_journal_peek(&m->journal);
|
||||||
|
dhara_error_t my_err;
|
||||||
|
|
||||||
|
if (tail == DHARA_PAGE_NONE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!raw_gc(m, tail, &my_err)) {
|
||||||
|
dhara_journal_dequeue(&m->journal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try_recover(m, my_err, err) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
114
Lib/dhara/map.h
Normal file
114
Lib/dhara/map.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DHARA_MAP_H_
|
||||||
|
#define DHARA_MAP_H_
|
||||||
|
|
||||||
|
#include "journal.h"
|
||||||
|
|
||||||
|
/* The map is a journal indexing format. It maps virtual sectors to
|
||||||
|
* pages of data in flash memory.
|
||||||
|
*/
|
||||||
|
typedef uint32_t dhara_sector_t;
|
||||||
|
|
||||||
|
/* This sector value is reserved */
|
||||||
|
#define DHARA_SECTOR_NONE 0xffffffff
|
||||||
|
|
||||||
|
struct dhara_map {
|
||||||
|
struct dhara_journal journal;
|
||||||
|
|
||||||
|
uint8_t gc_ratio;
|
||||||
|
dhara_sector_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize a map. You need to supply a buffer for page metadata, and
|
||||||
|
* a garbage collection ratio. This is the ratio of garbage collection
|
||||||
|
* operations to real writes when automatic collection is active.
|
||||||
|
*
|
||||||
|
* Smaller values lead to faster and more predictable IO, at the
|
||||||
|
* expense of capacity. You should always initialize the same chip with
|
||||||
|
* the same garbage collection ratio.
|
||||||
|
*/
|
||||||
|
void dhara_map_init(struct dhara_map *m, const struct dhara_nand *n,
|
||||||
|
uint8_t *page_buf, uint8_t gc_ratio);
|
||||||
|
|
||||||
|
/* Recover stored state, if possible. If there is no valid stored state
|
||||||
|
* on the chip, -1 is returned, and an empty map is initialized.
|
||||||
|
*/
|
||||||
|
int dhara_map_resume(struct dhara_map *m, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Clear the map (delete all sectors). */
|
||||||
|
void dhara_map_clear(struct dhara_map *m);
|
||||||
|
|
||||||
|
/* Obtain the maximum capacity of the map. */
|
||||||
|
dhara_sector_t dhara_map_capacity(const struct dhara_map *m);
|
||||||
|
|
||||||
|
/* Obtain the current number of allocated sectors. */
|
||||||
|
static inline dhara_sector_t dhara_map_size(const struct dhara_map *m)
|
||||||
|
{
|
||||||
|
return m->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the physical page which holds the current data for this sector.
|
||||||
|
* Returns 0 on success or -1 if an error occurs. If the sector doesn't
|
||||||
|
* exist, the error is E_NOT_FOUND.
|
||||||
|
*/
|
||||||
|
int dhara_map_find(struct dhara_map *m, dhara_sector_t s,
|
||||||
|
dhara_page_t *loc, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Read from the given logical sector. If the sector is unmapped, a
|
||||||
|
* blank page (0xff) will be returned.
|
||||||
|
*/
|
||||||
|
int dhara_map_read(struct dhara_map *m, dhara_sector_t s,
|
||||||
|
uint8_t *data, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Write data to a logical sector. */
|
||||||
|
int dhara_map_write(struct dhara_map *m, dhara_sector_t s,
|
||||||
|
const uint8_t *data, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Copy any flash page to a logical sector. */
|
||||||
|
int dhara_map_copy_page(struct dhara_map *m, dhara_page_t src,
|
||||||
|
dhara_sector_t dst, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Copy one sector to another. If the source sector is unmapped, the
|
||||||
|
* destination sector will be trimmed.
|
||||||
|
*/
|
||||||
|
int dhara_map_copy_sector(struct dhara_map *m, dhara_sector_t src,
|
||||||
|
dhara_sector_t dst, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Delete a logical sector. You don't necessarily need to do this, but
|
||||||
|
* it's a useful hint if you no longer require the sector's data to be
|
||||||
|
* kept.
|
||||||
|
*
|
||||||
|
* If order is non-zero, it specifies that all sectors in the
|
||||||
|
* (2**order)-aligned group of s are to be deleted.
|
||||||
|
*/
|
||||||
|
int dhara_map_trim(struct dhara_map *m, dhara_sector_t s,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Synchronize the map. Once this returns successfully, all changes to
|
||||||
|
* date are persistent and durable. Conversely, there is no guarantee
|
||||||
|
* that unsynchronized changes will be persistent.
|
||||||
|
*/
|
||||||
|
int dhara_map_sync(struct dhara_map *m, dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Perform one garbage collection step. You can do this whenever you
|
||||||
|
* like, but it's not necessary -- garbage collection happens
|
||||||
|
* automatically and is interleaved with other operations.
|
||||||
|
*/
|
||||||
|
int dhara_map_gc(struct dhara_map *m, dhara_error_t *err);
|
||||||
|
|
||||||
|
#endif
|
||||||
105
Lib/dhara/nand.h
Normal file
105
Lib/dhara/nand.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/* Dhara - NAND flash management layer
|
||||||
|
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DHARA_NAND_H_
|
||||||
|
#define DHARA_NAND_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
/* Each page in a NAND device is indexed, starting at 0. It's required
|
||||||
|
* that there be a power-of-two number of pages in a eraseblock, so you can
|
||||||
|
* view a page number is being a concatenation (in binary) of a block
|
||||||
|
* number and the number of a page within a block.
|
||||||
|
*/
|
||||||
|
typedef uint32_t dhara_page_t;
|
||||||
|
|
||||||
|
/* Blocks are also indexed, starting at 0. */
|
||||||
|
typedef uint32_t dhara_block_t;
|
||||||
|
|
||||||
|
/* Each NAND chip must be represented by one of these structures. It's
|
||||||
|
* intended that this structure be embedded in a larger structure for
|
||||||
|
* context.
|
||||||
|
*
|
||||||
|
* The functions declared below are not implemented -- they must be
|
||||||
|
* provided and satisfy the documented conditions.
|
||||||
|
*/
|
||||||
|
struct dhara_nand {
|
||||||
|
/* Base-2 logarithm of the page size. If your device supports
|
||||||
|
* partial programming, you may want to subdivide the actual
|
||||||
|
* pages into separate ECC-correctable regions and present those
|
||||||
|
* as pages.
|
||||||
|
*/
|
||||||
|
uint8_t log2_page_size;
|
||||||
|
|
||||||
|
/* Base-2 logarithm of the number of pages within an eraseblock */
|
||||||
|
uint8_t log2_ppb;
|
||||||
|
|
||||||
|
/* Total number of eraseblocks */
|
||||||
|
unsigned int num_blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Is the given block bad? */
|
||||||
|
int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b);
|
||||||
|
|
||||||
|
/* Mark bad the given block (or attempt to). No return value is
|
||||||
|
* required, because there's nothing that can be done in response.
|
||||||
|
*/
|
||||||
|
void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b);
|
||||||
|
|
||||||
|
/* Erase the given block. This function should return 0 on success or -1
|
||||||
|
* on failure.
|
||||||
|
*
|
||||||
|
* The status reported by the chip should be checked. If an erase
|
||||||
|
* operation fails, return -1 and set err to E_BAD_BLOCK.
|
||||||
|
*/
|
||||||
|
int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Program the given page. The data pointer is a pointer to an entire
|
||||||
|
* page ((1 << log2_page_size) bytes). The operation status should be
|
||||||
|
* checked. If the operation fails, return -1 and set err to
|
||||||
|
* E_BAD_BLOCK.
|
||||||
|
*
|
||||||
|
* Pages will be programmed sequentially within a block, and will not be
|
||||||
|
* reprogrammed.
|
||||||
|
*/
|
||||||
|
int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p,
|
||||||
|
const uint8_t *data,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Check that the given page is erased */
|
||||||
|
int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p);
|
||||||
|
|
||||||
|
/* Read a portion of a page. ECC must be handled by the NAND
|
||||||
|
* implementation. Returns 0 on sucess or -1 if an error occurs. If an
|
||||||
|
* uncorrectable ECC error occurs, return -1 and set err to E_ECC.
|
||||||
|
*/
|
||||||
|
int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p,
|
||||||
|
size_t offset, size_t length,
|
||||||
|
uint8_t *data,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
/* Read a page from one location and reprogram it in another location.
|
||||||
|
* This might be done using the chip's internal buffers, but it must use
|
||||||
|
* ECC.
|
||||||
|
*/
|
||||||
|
int dhara_nand_copy(const struct dhara_nand *n,
|
||||||
|
dhara_page_t src, dhara_page_t dst,
|
||||||
|
dhara_error_t *err);
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
1
MDK-ARM/_build.cmd
Normal file
1
MDK-ARM/_build.cmd
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@echo off && cd /d "%~dp0" && C:\Keil_v5\UV4\UV4.exe -r "STM32F407-Demo.uvprojx" -t STM32F407-Demo -j0
|
||||||
19
MDK-ARM/build.bat
Normal file
19
MDK-ARM/build.bat
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set UV4=C:\Keil_v5\UV4\UV4.exe
|
||||||
|
set PROJ=%~dp0STM32F407-Demo.uvprojx
|
||||||
|
set LOG=%~dp0build_log.txt
|
||||||
|
set TARGET=STM32F407-Demo
|
||||||
|
|
||||||
|
"%UV4%" -r "%PROJ%" -t%TARGET% -j0 -o "%LOG%"
|
||||||
|
set RET=%ERRORLEVEL%
|
||||||
|
|
||||||
|
if %RET% equ 0 (
|
||||||
|
echo BUILD_OK: 0 Error(s), 0 Warning(s)
|
||||||
|
) else (
|
||||||
|
echo BUILD_FAIL: exit code %RET%
|
||||||
|
if exist "%LOG%" type "%LOG%"
|
||||||
|
)
|
||||||
|
|
||||||
|
exit /b %RET%
|
||||||
76
MDK-ARM/build_log.txt
Normal file
76
MDK-ARM/build_log.txt
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
|
||||||
|
Rebuild target 'STM32F407-Demo'
|
||||||
|
assembling startup_stm32f407xx.s...
|
||||||
|
compiling crc.c...
|
||||||
|
compiling ringbuf.c...
|
||||||
|
compiling stm32f4xx_it.c...
|
||||||
|
compiling app_main.c...
|
||||||
|
compiling dma.c...
|
||||||
|
compiling stm32f4xx_hal_flash_ramfunc.c...
|
||||||
|
compiling i2c.c...
|
||||||
|
compiling sys_clock.c...
|
||||||
|
compiling stm32f4xx_hal_timebase_tim.c...
|
||||||
|
compiling stm32f4xx_hal_gpio.c...
|
||||||
|
compiling stm32f4xx_hal_dma_ex.c...
|
||||||
|
compiling stm32f4xx_hal_dma.c...
|
||||||
|
compiling stm32f4xx_hal_flash_ex.c...
|
||||||
|
compiling stm32f4xx_hal_pwr.c...
|
||||||
|
compiling spi.c...
|
||||||
|
compiling stm32f4xx_hal_msp.c...
|
||||||
|
compiling stm32f4xx_hal_rcc.c...
|
||||||
|
compiling stm32f4xx_hal_flash.c...
|
||||||
|
compiling gpio.c...
|
||||||
|
compiling usart.c...
|
||||||
|
compiling stm32f4xx_hal_tim_ex.c...
|
||||||
|
compiling net_task.c...
|
||||||
|
compiling freertos.c...
|
||||||
|
compiling stm32f4xx_hal_rcc_ex.c...
|
||||||
|
compiling rs485_task.c...
|
||||||
|
compiling main.c...
|
||||||
|
compiling adc_task.c...
|
||||||
|
compiling time_sync.c...
|
||||||
|
compiling lftpd_string.c...
|
||||||
|
compiling croutine.c...
|
||||||
|
compiling stm32f4xx_hal_tim.c...
|
||||||
|
compiling list.c...
|
||||||
|
compiling event_groups.c...
|
||||||
|
compiling queue.c...
|
||||||
|
compiling stream_buffer.c...
|
||||||
|
compiling timers.c...
|
||||||
|
compiling tasks.c...
|
||||||
|
compiling heap_4.c...
|
||||||
|
compiling port.c...
|
||||||
|
compiling stm32f4xx_hal_pwr_ex.c...
|
||||||
|
compiling journal.c...
|
||||||
|
compiling map.c...
|
||||||
|
compiling ff.c...
|
||||||
|
compiling stm32f4xx_hal.c...
|
||||||
|
compiling system_stm32f4xx.c...
|
||||||
|
compiling lftpd_io.c...
|
||||||
|
compiling stm32f4xx_hal_cortex.c...
|
||||||
|
compiling stm32f4xx_hal_i2c_ex.c...
|
||||||
|
compiling stm32f4xx_hal_exti.c...
|
||||||
|
compiling rs485.c...
|
||||||
|
compiling tpafe5160.c...
|
||||||
|
compiling sd2506.c...
|
||||||
|
compiling ch395f.c...
|
||||||
|
compiling nand_ftl.c...
|
||||||
|
compiling stm32f4xx_hal_spi.c...
|
||||||
|
compiling lftpd_inet.c...
|
||||||
|
compiling gd5f2gq5ue.c...
|
||||||
|
compiling net_select.c...
|
||||||
|
compiling stm32f4xx_hal_uart.c...
|
||||||
|
compiling lftpd.c...
|
||||||
|
compiling net_socket.c...
|
||||||
|
compiling stm32f4xx_hal_i2c.c...
|
||||||
|
compiling cmsis_os2.c...
|
||||||
|
compiling ch395f_test_task.c...
|
||||||
|
compiling net_test_task.c...
|
||||||
|
compiling gd5f_test_task.c...
|
||||||
|
compiling storage_test_task.c...
|
||||||
|
compiling sd2506_test_task.c...
|
||||||
|
linking...
|
||||||
|
Program Size: Code=64436 RO-data=3936 RW-data=420 ZI-data=51868
|
||||||
|
FromELF: creating hex file...
|
||||||
|
".\STM32F407-Demo\STM32F407-Demo.axf" - 0 Error(s), 0 Warning(s).
|
||||||
|
Build Time Elapsed: 00:00:14
|
||||||
0
MDK-ARM/err.txt
Normal file
0
MDK-ARM/err.txt
Normal file
0
MDK-ARM/out.txt
Normal file
0
MDK-ARM/out.txt
Normal file
846
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h
vendored
Normal file
846
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h
vendored
Normal file
@@ -0,0 +1,846 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013-2019 ARM Limited. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* $Date: 10. January 2017
|
||||||
|
* $Revision: V2.1.0
|
||||||
|
*
|
||||||
|
* Project: CMSIS-RTOS API
|
||||||
|
* Title: cmsis_os.h FreeRTOS header file
|
||||||
|
*
|
||||||
|
* Version 0.02
|
||||||
|
* Initial Proposal Phase
|
||||||
|
* Version 0.03
|
||||||
|
* osKernelStart added, optional feature: main started as thread
|
||||||
|
* osSemaphores have standard behavior
|
||||||
|
* osTimerCreate does not start the timer, added osTimerStart
|
||||||
|
* osThreadPass is renamed to osThreadYield
|
||||||
|
* Version 1.01
|
||||||
|
* Support for C++ interface
|
||||||
|
* - const attribute removed from the osXxxxDef_t typedefs
|
||||||
|
* - const attribute added to the osXxxxDef macros
|
||||||
|
* Added: osTimerDelete, osMutexDelete, osSemaphoreDelete
|
||||||
|
* Added: osKernelInitialize
|
||||||
|
* Version 1.02
|
||||||
|
* Control functions for short timeouts in microsecond resolution:
|
||||||
|
* Added: osKernelSysTick, osKernelSysTickFrequency, osKernelSysTickMicroSec
|
||||||
|
* Removed: osSignalGet
|
||||||
|
* Version 2.0.0
|
||||||
|
* OS objects creation without macros (dynamic creation and resource allocation):
|
||||||
|
* - added: osXxxxNew functions which replace osXxxxCreate
|
||||||
|
* - added: osXxxxAttr_t structures
|
||||||
|
* - deprecated: osXxxxCreate functions, osXxxxDef_t structures
|
||||||
|
* - deprecated: osXxxxDef and osXxxx macros
|
||||||
|
* osStatus codes simplified and renamed to osStatus_t
|
||||||
|
* osEvent return structure deprecated
|
||||||
|
* Kernel:
|
||||||
|
* - added: osKernelInfo_t and osKernelGetInfo
|
||||||
|
* - added: osKernelState_t and osKernelGetState (replaces osKernelRunning)
|
||||||
|
* - added: osKernelLock, osKernelUnlock
|
||||||
|
* - added: osKernelSuspend, osKernelResume
|
||||||
|
* - added: osKernelGetTickCount, osKernelGetTickFreq
|
||||||
|
* - renamed osKernelSysTick to osKernelGetSysTimerCount
|
||||||
|
* - replaced osKernelSysTickFrequency with osKernelGetSysTimerFreq
|
||||||
|
* - deprecated osKernelSysTickMicroSec
|
||||||
|
* Thread:
|
||||||
|
* - extended number of thread priorities
|
||||||
|
* - renamed osPrioriry to osPrioriry_t
|
||||||
|
* - replaced osThreadCreate with osThreadNew
|
||||||
|
* - added: osThreadGetName
|
||||||
|
* - added: osThreadState_t and osThreadGetState
|
||||||
|
* - added: osThreadGetStackSize, osThreadGetStackSpace
|
||||||
|
* - added: osThreadSuspend, osThreadResume
|
||||||
|
* - added: osThreadJoin, osThreadDetach, osThreadExit
|
||||||
|
* - added: osThreadGetCount, osThreadEnumerate
|
||||||
|
* - added: Thread Flags (moved from Signals)
|
||||||
|
* Signals:
|
||||||
|
* - renamed osSignals to osThreadFlags (moved to Thread Flags)
|
||||||
|
* - changed return value of Set/Clear/Wait functions
|
||||||
|
* - Clear function limited to current running thread
|
||||||
|
* - extended Wait function (options)
|
||||||
|
* - added: osThreadFlagsGet
|
||||||
|
* Event Flags:
|
||||||
|
* - added new independent object for handling Event Flags
|
||||||
|
* Delay and Wait functions:
|
||||||
|
* - added: osDelayUntil
|
||||||
|
* - deprecated: osWait
|
||||||
|
* Timer:
|
||||||
|
* - replaced osTimerCreate with osTimerNew
|
||||||
|
* - added: osTimerGetName, osTimerIsRunning
|
||||||
|
* Mutex:
|
||||||
|
* - extended: attributes (Recursive, Priority Inherit, Robust)
|
||||||
|
* - replaced osMutexCreate with osMutexNew
|
||||||
|
* - renamed osMutexWait to osMutexAcquire
|
||||||
|
* - added: osMutexGetName, osMutexGetOwner
|
||||||
|
* Semaphore:
|
||||||
|
* - extended: maximum and initial token count
|
||||||
|
* - replaced osSemaphoreCreate with osSemaphoreNew
|
||||||
|
* - renamed osSemaphoreWait to osSemaphoreAcquire (changed return value)
|
||||||
|
* - added: osSemaphoreGetName, osSemaphoreGetCount
|
||||||
|
* Memory Pool:
|
||||||
|
* - using osMemoryPool prefix instead of osPool
|
||||||
|
* - replaced osPoolCreate with osMemoryPoolNew
|
||||||
|
* - extended osMemoryPoolAlloc (timeout)
|
||||||
|
* - added: osMemoryPoolGetName
|
||||||
|
* - added: osMemoryPoolGetCapacity, osMemoryPoolGetBlockSize
|
||||||
|
* - added: osMemoryPoolGetCount, osMemoryPoolGetSpace
|
||||||
|
* - added: osMemoryPoolDelete
|
||||||
|
* - deprecated: osPoolCAlloc
|
||||||
|
* Message Queue:
|
||||||
|
* - extended: fixed size message instead of a single 32-bit value
|
||||||
|
* - using osMessageQueue prefix instead of osMessage
|
||||||
|
* - replaced osMessageCreate with osMessageQueueNew
|
||||||
|
* - updated: osMessageQueuePut, osMessageQueueGet
|
||||||
|
* - added: osMessageQueueGetName
|
||||||
|
* - added: osMessageQueueGetCapacity, osMessageQueueGetMsgSize
|
||||||
|
* - added: osMessageQueueGetCount, osMessageQueueGetSpace
|
||||||
|
* - added: osMessageQueueReset, osMessageQueueDelete
|
||||||
|
* Mail Queue:
|
||||||
|
* - deprecated (superseded by extended Message Queue functionality)
|
||||||
|
* Version 2.1.0
|
||||||
|
* Support for critical and uncritical sections (nesting safe):
|
||||||
|
* - updated: osKernelLock, osKernelUnlock
|
||||||
|
* - added: osKernelRestoreLock
|
||||||
|
* Updated Thread and Event Flags:
|
||||||
|
* - changed flags parameter and return type from int32_t to uint32_t
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef CMSIS_OS_H_
|
||||||
|
#define CMSIS_OS_H_
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
#define RTOS_ID_n ((tskKERNEL_VERSION_MAJOR << 16) | (tskKERNEL_VERSION_MINOR))
|
||||||
|
#define RTOS_ID_s ("FreeRTOS " tskKERNEL_VERSION_NUMBER)
|
||||||
|
|
||||||
|
#define osCMSIS 0x20001U ///< API version (main[31:16].sub[15:0])
|
||||||
|
|
||||||
|
#define osCMSIS_FreeRTOS RTOS_ID_n ///< RTOS identification and version (main[31:16].sub[15:0])
|
||||||
|
|
||||||
|
#define osKernelSystemId RTOS_ID_s ///< RTOS identification string
|
||||||
|
|
||||||
|
#define osFeature_MainThread 0 ///< main thread 1=main can be thread, 0=not available
|
||||||
|
#define osFeature_Signals 24U ///< maximum number of Signal Flags available per thread
|
||||||
|
#define osFeature_Semaphore 65535U ///< maximum count for \ref osSemaphoreCreate function
|
||||||
|
#define osFeature_Wait 0 ///< osWait function: 1=available, 0=not available
|
||||||
|
#define osFeature_SysTick 1 ///< osKernelSysTick functions: 1=available, 0=not available
|
||||||
|
#define osFeature_Pool 0 ///< Memory Pools: 1=available, 0=not available
|
||||||
|
#define osFeature_MessageQ 1 ///< Message Queues: 1=available, 0=not available
|
||||||
|
#define osFeature_MailQ 0 ///< Mail Queues: 1=available, 0=not available
|
||||||
|
|
||||||
|
#if defined(__CC_ARM)
|
||||||
|
#define os_InRegs __value_in_regs
|
||||||
|
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
|
||||||
|
#define os_InRegs __attribute__((value_in_regs))
|
||||||
|
#else
|
||||||
|
#define os_InRegs
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "cmsis_os2.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Enumerations, structures, defines ====
|
||||||
|
|
||||||
|
/// Priority values.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef enum {
|
||||||
|
osPriorityIdle = -3, ///< Priority: idle (lowest)
|
||||||
|
osPriorityLow = -2, ///< Priority: low
|
||||||
|
osPriorityBelowNormal = -1, ///< Priority: below normal
|
||||||
|
osPriorityNormal = 0, ///< Priority: normal (default)
|
||||||
|
osPriorityAboveNormal = +1, ///< Priority: above normal
|
||||||
|
osPriorityHigh = +2, ///< Priority: high
|
||||||
|
osPriorityRealtime = +3, ///< Priority: realtime (highest)
|
||||||
|
osPriorityError = 0x84, ///< System cannot determine priority or illegal priority.
|
||||||
|
osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||||
|
} osPriority;
|
||||||
|
#else
|
||||||
|
#define osPriority osPriority_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Entry point of a thread.
|
||||||
|
typedef void (*os_pthread) (void const *argument);
|
||||||
|
|
||||||
|
/// Entry point of a timer call back function.
|
||||||
|
typedef void (*os_ptimer) (void const *argument);
|
||||||
|
|
||||||
|
/// Timer type.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef enum {
|
||||||
|
osTimerOnce = 0, ///< One-shot timer.
|
||||||
|
osTimerPeriodic = 1 ///< Repeating timer.
|
||||||
|
} os_timer_type;
|
||||||
|
#else
|
||||||
|
#define os_timer_type osTimerType_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Timeout value.
|
||||||
|
#define osWaitForever 0xFFFFFFFFU ///< Wait forever timeout value.
|
||||||
|
|
||||||
|
/// Status code values returned by CMSIS-RTOS functions.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef enum {
|
||||||
|
osOK = 0, ///< Function completed; no error or event occurred.
|
||||||
|
osEventSignal = 0x08, ///< Function completed; signal event occurred.
|
||||||
|
osEventMessage = 0x10, ///< Function completed; message event occurred.
|
||||||
|
osEventMail = 0x20, ///< Function completed; mail event occurred.
|
||||||
|
osEventTimeout = 0x40, ///< Function completed; timeout occurred.
|
||||||
|
osErrorParameter = 0x80, ///< Parameter error: a mandatory parameter was missing or specified an incorrect object.
|
||||||
|
osErrorResource = 0x81, ///< Resource not available: a specified resource was not available.
|
||||||
|
osErrorTimeoutResource = 0xC1, ///< Resource not available within given time: a specified resource was not available within the timeout period.
|
||||||
|
osErrorISR = 0x82, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
|
||||||
|
osErrorISRRecursive = 0x83, ///< Function called multiple times from ISR with same object.
|
||||||
|
osErrorPriority = 0x84, ///< System cannot determine priority or thread has illegal priority.
|
||||||
|
osErrorNoMemory = 0x85, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
|
||||||
|
osErrorValue = 0x86, ///< Value of a parameter is out of range.
|
||||||
|
osErrorOS = 0xFF, ///< Unspecified RTOS error: run-time error but no other error message fits.
|
||||||
|
osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||||
|
} osStatus;
|
||||||
|
#else
|
||||||
|
typedef int32_t osStatus;
|
||||||
|
#define osEventSignal (0x08)
|
||||||
|
#define osEventMessage (0x10)
|
||||||
|
#define osEventMail (0x20)
|
||||||
|
#define osEventTimeout (0x40)
|
||||||
|
#define osErrorOS osError
|
||||||
|
#define osErrorTimeoutResource osErrorTimeout
|
||||||
|
#define osErrorISRRecursive (-126)
|
||||||
|
#define osErrorValue (-127)
|
||||||
|
#define osErrorPriority (-128)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// >>> the following data type definitions may be adapted towards a specific RTOS
|
||||||
|
|
||||||
|
/// Thread ID identifies the thread.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef void *osThreadId;
|
||||||
|
#else
|
||||||
|
#define osThreadId osThreadId_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Timer ID identifies the timer.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef void *osTimerId;
|
||||||
|
#else
|
||||||
|
#define osTimerId osTimerId_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Mutex ID identifies the mutex.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef void *osMutexId;
|
||||||
|
#else
|
||||||
|
#define osMutexId osMutexId_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Semaphore ID identifies the semaphore.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef void *osSemaphoreId;
|
||||||
|
#else
|
||||||
|
#define osSemaphoreId osSemaphoreId_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Pool ID identifies the memory pool.
|
||||||
|
typedef void *osPoolId;
|
||||||
|
|
||||||
|
/// Message ID identifies the message queue.
|
||||||
|
typedef void *osMessageQId;
|
||||||
|
|
||||||
|
/// Mail ID identifies the mail queue.
|
||||||
|
typedef void *osMailQId;
|
||||||
|
|
||||||
|
|
||||||
|
/// Thread Definition structure contains startup information of a thread.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_thread_def {
|
||||||
|
os_pthread pthread; ///< start address of thread function
|
||||||
|
osPriority tpriority; ///< initial thread priority
|
||||||
|
uint32_t instances; ///< maximum number of instances of that thread function
|
||||||
|
uint32_t stacksize; ///< stack size requirements in bytes; 0 is default stack size
|
||||||
|
} osThreadDef_t;
|
||||||
|
#else
|
||||||
|
typedef struct os_thread_def {
|
||||||
|
os_pthread pthread; ///< start address of thread function
|
||||||
|
osThreadAttr_t attr; ///< thread attributes
|
||||||
|
} osThreadDef_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Timer Definition structure contains timer parameters.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_timer_def {
|
||||||
|
os_ptimer ptimer; ///< start address of a timer function
|
||||||
|
} osTimerDef_t;
|
||||||
|
#else
|
||||||
|
typedef struct os_timer_def {
|
||||||
|
os_ptimer ptimer; ///< start address of a timer function
|
||||||
|
osTimerAttr_t attr; ///< timer attributes
|
||||||
|
} osTimerDef_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Mutex Definition structure contains setup information for a mutex.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_mutex_def {
|
||||||
|
uint32_t dummy; ///< dummy value
|
||||||
|
} osMutexDef_t;
|
||||||
|
#else
|
||||||
|
#define osMutexDef_t osMutexAttr_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Semaphore Definition structure contains setup information for a semaphore.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_semaphore_def {
|
||||||
|
uint32_t dummy; ///< dummy value
|
||||||
|
} osSemaphoreDef_t;
|
||||||
|
#else
|
||||||
|
#define osSemaphoreDef_t osSemaphoreAttr_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Definition structure for memory block allocation.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_pool_def {
|
||||||
|
uint32_t pool_sz; ///< number of items (elements) in the pool
|
||||||
|
uint32_t item_sz; ///< size of an item
|
||||||
|
void *pool; ///< pointer to memory for pool
|
||||||
|
} osPoolDef_t;
|
||||||
|
#else
|
||||||
|
typedef struct os_pool_def {
|
||||||
|
uint32_t pool_sz; ///< number of items (elements) in the pool
|
||||||
|
uint32_t item_sz; ///< size of an item
|
||||||
|
osMemoryPoolAttr_t attr; ///< memory pool attributes
|
||||||
|
} osPoolDef_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Definition structure for message queue.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_messageQ_def {
|
||||||
|
uint32_t queue_sz; ///< number of elements in the queue
|
||||||
|
void *pool; ///< memory array for messages
|
||||||
|
} osMessageQDef_t;
|
||||||
|
#else
|
||||||
|
typedef struct os_messageQ_def {
|
||||||
|
uint32_t queue_sz; ///< number of elements in the queue
|
||||||
|
osMessageQueueAttr_t attr; ///< message queue attributes
|
||||||
|
} osMessageQDef_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Definition structure for mail queue.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
typedef struct os_mailQ_def {
|
||||||
|
uint32_t queue_sz; ///< number of elements in the queue
|
||||||
|
uint32_t item_sz; ///< size of an item
|
||||||
|
void *pool; ///< memory array for mail
|
||||||
|
} osMailQDef_t;
|
||||||
|
#else
|
||||||
|
typedef struct os_mailQ_def {
|
||||||
|
uint32_t queue_sz; ///< number of elements in the queue
|
||||||
|
uint32_t item_sz; ///< size of an item
|
||||||
|
void *mail; ///< pointer to mail
|
||||||
|
osMemoryPoolAttr_t mp_attr; ///< memory pool attributes
|
||||||
|
osMessageQueueAttr_t mq_attr; ///< message queue attributes
|
||||||
|
} osMailQDef_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// Event structure contains detailed information about an event.
|
||||||
|
typedef struct {
|
||||||
|
osStatus status; ///< status code: event or error information
|
||||||
|
union {
|
||||||
|
uint32_t v; ///< message as 32-bit value
|
||||||
|
void *p; ///< message or mail as void pointer
|
||||||
|
int32_t signals; ///< signal flags
|
||||||
|
} value; ///< event value
|
||||||
|
union {
|
||||||
|
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
|
||||||
|
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
|
||||||
|
} def; ///< event definition
|
||||||
|
} osEvent;
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Kernel Management Functions ====
|
||||||
|
|
||||||
|
/// Initialize the RTOS Kernel for creating objects.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osKernelInitialize (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Start the RTOS Kernel scheduler.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osKernelStart (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Check if the RTOS kernel is already started.
|
||||||
|
/// \return 0 RTOS is not started, 1 RTOS is started.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
int32_t osKernelRunning(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(osFeature_SysTick) && (osFeature_SysTick != 0)) // System Timer available
|
||||||
|
|
||||||
|
/// Get the RTOS kernel system timer counter.
|
||||||
|
/// \return RTOS kernel system timer as 32-bit value
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
uint32_t osKernelSysTick (void);
|
||||||
|
#else
|
||||||
|
#define osKernelSysTick osKernelGetSysTimerCount
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// The RTOS kernel system timer frequency in Hz.
|
||||||
|
/// \note Reflects the system timer setting and is typically defined in a configuration file.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
#define osKernelSysTickFrequency 100000000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Convert a microseconds value to a RTOS kernel system timer value.
|
||||||
|
/// \param microsec time value in microseconds.
|
||||||
|
/// \return time value normalized to the \ref osKernelSysTickFrequency
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
#define osKernelSysTickMicroSec(microsec) (((uint64_t)microsec * (osKernelSysTickFrequency)) / 1000000)
|
||||||
|
#else
|
||||||
|
#define osKernelSysTickMicroSec(microsec) (((uint64_t)microsec * osKernelGetSysTimerFreq()) / 1000000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // System Timer available
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Thread Management Functions ====
|
||||||
|
|
||||||
|
/// Create a Thread Definition with function, priority, and stack requirements.
|
||||||
|
/// \param name name of the thread function.
|
||||||
|
/// \param priority initial priority of the thread function.
|
||||||
|
/// \param instances number of possible thread instances.
|
||||||
|
/// \param stacksz stack size (in bytes) requirements for the thread function.
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osThreadDef(name, priority, instances, stacksz) \
|
||||||
|
extern const osThreadDef_t os_thread_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osThreadDef(name, priority, instances, stacksz) \
|
||||||
|
static uint64_t os_thread_stack##name[(stacksz)?(((stacksz+7)/8)):1]; \
|
||||||
|
static StaticTask_t os_thread_cb_##name; \
|
||||||
|
const osThreadDef_t os_thread_def_##name = \
|
||||||
|
{ (name), \
|
||||||
|
{ NULL, osThreadDetached, \
|
||||||
|
(instances == 1) ? (&os_thread_cb_##name) : NULL,\
|
||||||
|
(instances == 1) ? sizeof(StaticTask_t) : 0U, \
|
||||||
|
((stacksz) && (instances == 1)) ? (&os_thread_stack##name) : NULL, \
|
||||||
|
8*((stacksz+7)/8), \
|
||||||
|
(priority), 0U, 0U } }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Access a Thread definition.
|
||||||
|
/// \param name name of the thread definition object.
|
||||||
|
#define osThread(name) \
|
||||||
|
&os_thread_def_##name
|
||||||
|
|
||||||
|
/// Create a thread and add it to Active Threads and set it to state READY.
|
||||||
|
/// \param[in] thread_def thread definition referenced with \ref osThread.
|
||||||
|
/// \param[in] argument pointer that is passed to the thread function as start argument.
|
||||||
|
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||||
|
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument);
|
||||||
|
|
||||||
|
/// Return the thread ID of the current running thread.
|
||||||
|
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osThreadId osThreadGetId (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Change priority of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||||
|
/// \param[in] priority new priority value for the thread function.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Get current priority of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||||
|
/// \return current priority value of the specified thread.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osPriority osThreadGetPriority (osThreadId thread_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Pass control to next thread that is in state \b READY.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osThreadYield (void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Terminate execution of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osThreadTerminate (osThreadId thread_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Signal Management ====
|
||||||
|
|
||||||
|
/// Set the specified Signal Flags of an active thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||||
|
/// \param[in] signals specifies the signal flags of the thread that should be set.
|
||||||
|
/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters.
|
||||||
|
int32_t osSignalSet (osThreadId thread_id, int32_t signals);
|
||||||
|
|
||||||
|
/// Clear the specified Signal Flags of an active thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||||
|
/// \param[in] signals specifies the signal flags of the thread that shall be cleared.
|
||||||
|
/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters or call from ISR.
|
||||||
|
int32_t osSignalClear (osThreadId thread_id, int32_t signals);
|
||||||
|
|
||||||
|
/// Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread.
|
||||||
|
/// \param[in] signals wait until all specified signal flags set or 0 for any single signal flag.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return event flag information or error code.
|
||||||
|
os_InRegs osEvent osSignalWait (int32_t signals, uint32_t millisec);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Generic Wait Functions ====
|
||||||
|
|
||||||
|
/// Wait for Timeout (Time Delay).
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue "time delay" value
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osDelay (uint32_t millisec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined (osFeature_Wait) && (osFeature_Wait != 0)) // Generic Wait available
|
||||||
|
|
||||||
|
/// Wait for Signal, Message, Mail, or Timeout.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out
|
||||||
|
/// \return event that contains signal, message, or mail information or error code.
|
||||||
|
os_InRegs osEvent osWait (uint32_t millisec);
|
||||||
|
|
||||||
|
#endif // Generic Wait available
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Timer Management Functions ====
|
||||||
|
|
||||||
|
/// Define a Timer object.
|
||||||
|
/// \param name name of the timer object.
|
||||||
|
/// \param function name of the timer call back function.
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osTimerDef(name, function) \
|
||||||
|
extern const osTimerDef_t os_timer_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osTimerDef(name, function) \
|
||||||
|
static StaticTimer_t os_timer_cb_##name; \
|
||||||
|
const osTimerDef_t os_timer_def_##name = \
|
||||||
|
{ (function), { NULL, 0U, (&os_timer_cb_##name), sizeof(StaticTimer_t) } }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Access a Timer definition.
|
||||||
|
/// \param name name of the timer object.
|
||||||
|
#define osTimer(name) \
|
||||||
|
&os_timer_def_##name
|
||||||
|
|
||||||
|
/// Create and Initialize a timer.
|
||||||
|
/// \param[in] timer_def timer object referenced with \ref osTimer.
|
||||||
|
/// \param[in] type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior.
|
||||||
|
/// \param[in] argument argument to the timer call back function.
|
||||||
|
/// \return timer ID for reference by other functions or NULL in case of error.
|
||||||
|
osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument);
|
||||||
|
|
||||||
|
/// Start or restart a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue "time delay" value of the timer.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osTimerStart (osTimerId timer_id, uint32_t millisec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Stop a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerCreate.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osTimerStop (osTimerId timer_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Delete a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerCreate.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osTimerDelete (osTimerId timer_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Mutex Management Functions ====
|
||||||
|
|
||||||
|
/// Define a Mutex.
|
||||||
|
/// \param name name of the mutex object.
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osMutexDef(name) \
|
||||||
|
extern const osMutexDef_t os_mutex_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osMutexDef(name) \
|
||||||
|
static StaticSemaphore_t os_mutex_cb_##name; \
|
||||||
|
const osMutexDef_t os_mutex_def_##name = \
|
||||||
|
{ NULL, osMutexRecursive | osMutexPrioInherit, (&os_mutex_cb_##name), sizeof(StaticSemaphore_t) }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Access a Mutex definition.
|
||||||
|
/// \param name name of the mutex object.
|
||||||
|
#define osMutex(name) \
|
||||||
|
&os_mutex_def_##name
|
||||||
|
|
||||||
|
/// Create and Initialize a Mutex object.
|
||||||
|
/// \param[in] mutex_def mutex definition referenced with \ref osMutex.
|
||||||
|
/// \return mutex ID for reference by other functions or NULL in case of error.
|
||||||
|
osMutexId osMutexCreate (const osMutexDef_t *mutex_def);
|
||||||
|
|
||||||
|
/// Wait until a Mutex becomes available.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec);
|
||||||
|
#else
|
||||||
|
#define osMutexWait osMutexAcquire
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Release a Mutex that was obtained by \ref osMutexWait.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osMutexRelease (osMutexId mutex_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Delete a Mutex object.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osMutexDelete (osMutexId mutex_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Semaphore Management Functions ====
|
||||||
|
|
||||||
|
#if (defined (osFeature_Semaphore) && (osFeature_Semaphore != 0U)) // Semaphore available
|
||||||
|
|
||||||
|
/// Define a Semaphore object.
|
||||||
|
/// \param name name of the semaphore object.
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osSemaphoreDef(name) \
|
||||||
|
extern const osSemaphoreDef_t os_semaphore_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osSemaphoreDef(name) \
|
||||||
|
static StaticSemaphore_t os_semaphore_cb_##name; \
|
||||||
|
const osSemaphoreDef_t os_semaphore_def_##name = \
|
||||||
|
{ NULL, 0U, (&os_semaphore_cb_##name), sizeof(StaticSemaphore_t) }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Access a Semaphore definition.
|
||||||
|
/// \param name name of the semaphore object.
|
||||||
|
#define osSemaphore(name) \
|
||||||
|
&os_semaphore_def_##name
|
||||||
|
|
||||||
|
/// Create and Initialize a Semaphore object.
|
||||||
|
/// \param[in] semaphore_def semaphore definition referenced with \ref osSemaphore.
|
||||||
|
/// \param[in] count maximum and initial number of available tokens.
|
||||||
|
/// \return semaphore ID for reference by other functions or NULL in case of error.
|
||||||
|
osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count);
|
||||||
|
|
||||||
|
/// Wait until a Semaphore token becomes available.
|
||||||
|
/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return number of available tokens, or -1 in case of incorrect parameters.
|
||||||
|
int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec);
|
||||||
|
|
||||||
|
/// Release a Semaphore token.
|
||||||
|
/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Delete a Semaphore object.
|
||||||
|
/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
#if (osCMSIS < 0x20000U)
|
||||||
|
osStatus osSemaphoreDelete (osSemaphoreId semaphore_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // Semaphore available
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Memory Pool Management Functions ====
|
||||||
|
|
||||||
|
#if (defined(osFeature_Pool) && (osFeature_Pool != 0)) // Memory Pool available
|
||||||
|
|
||||||
|
/// \brief Define a Memory Pool.
|
||||||
|
/// \param name name of the memory pool.
|
||||||
|
/// \param no maximum number of blocks (objects) in the memory pool.
|
||||||
|
/// \param type data type of a single block (object).
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osPoolDef(name, no, type) \
|
||||||
|
extern const osPoolDef_t os_pool_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osPoolDef(name, no, type) \
|
||||||
|
const osPoolDef_t os_pool_def_##name = \
|
||||||
|
{ (no), sizeof(type), {NULL} }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Access a Memory Pool definition.
|
||||||
|
/// \param name name of the memory pool
|
||||||
|
#define osPool(name) \
|
||||||
|
&os_pool_def_##name
|
||||||
|
|
||||||
|
/// Create and Initialize a Memory Pool object.
|
||||||
|
/// \param[in] pool_def memory pool definition referenced with \ref osPool.
|
||||||
|
/// \return memory pool ID for reference by other functions or NULL in case of error.
|
||||||
|
osPoolId osPoolCreate (const osPoolDef_t *pool_def);
|
||||||
|
|
||||||
|
/// Allocate a memory block from a Memory Pool.
|
||||||
|
/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate.
|
||||||
|
/// \return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
void *osPoolAlloc (osPoolId pool_id);
|
||||||
|
|
||||||
|
/// Allocate a memory block from a Memory Pool and set memory block to zero.
|
||||||
|
/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate.
|
||||||
|
/// \return address of the allocated memory block or NULL in case of no memory available.
|
||||||
|
void *osPoolCAlloc (osPoolId pool_id);
|
||||||
|
|
||||||
|
/// Return an allocated memory block back to a Memory Pool.
|
||||||
|
/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate.
|
||||||
|
/// \param[in] block address of the allocated memory block to be returned to the memory pool.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus osPoolFree (osPoolId pool_id, void *block);
|
||||||
|
|
||||||
|
#endif // Memory Pool available
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Message Queue Management Functions ====
|
||||||
|
|
||||||
|
#if (defined(osFeature_MessageQ) && (osFeature_MessageQ != 0)) // Message Queue available
|
||||||
|
|
||||||
|
/// \brief Create a Message Queue Definition.
|
||||||
|
/// \param name name of the queue.
|
||||||
|
/// \param queue_sz maximum number of messages in the queue.
|
||||||
|
/// \param type data type of a single message element (for debugger).
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osMessageQDef(name, queue_sz, type) \
|
||||||
|
extern const osMessageQDef_t os_messageQ_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osMessageQDef(name, queue_sz, type) \
|
||||||
|
static StaticQueue_t os_mq_cb_##name; \
|
||||||
|
static uint32_t os_mq_data_##name[(queue_sz) * sizeof(type)]; \
|
||||||
|
const osMessageQDef_t os_messageQ_def_##name = \
|
||||||
|
{ (queue_sz), \
|
||||||
|
{ NULL, 0U, (&os_mq_cb_##name), sizeof(StaticQueue_t), \
|
||||||
|
(&os_mq_data_##name), sizeof(os_mq_data_##name) } }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Access a Message Queue Definition.
|
||||||
|
/// \param name name of the queue
|
||||||
|
#define osMessageQ(name) \
|
||||||
|
&os_messageQ_def_##name
|
||||||
|
|
||||||
|
/// Create and Initialize a Message Queue object.
|
||||||
|
/// \param[in] queue_def message queue definition referenced with \ref osMessageQ.
|
||||||
|
/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL.
|
||||||
|
/// \return message queue ID for reference by other functions or NULL in case of error.
|
||||||
|
osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id);
|
||||||
|
|
||||||
|
/// Put a Message to a Queue.
|
||||||
|
/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate.
|
||||||
|
/// \param[in] info message information.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec);
|
||||||
|
|
||||||
|
/// Get a Message from a Queue or timeout if Queue is empty.
|
||||||
|
/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return event information that includes status code.
|
||||||
|
os_InRegs osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec);
|
||||||
|
|
||||||
|
#endif // Message Queue available
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Mail Queue Management Functions ====
|
||||||
|
|
||||||
|
#if (defined(osFeature_MailQ) && (osFeature_MailQ != 0)) // Mail Queue available
|
||||||
|
|
||||||
|
/// \brief Create a Mail Queue Definition.
|
||||||
|
/// \param name name of the queue.
|
||||||
|
/// \param queue_sz maximum number of mails in the queue.
|
||||||
|
/// \param type data type of a single mail element.
|
||||||
|
#if defined (osObjectsExternal) // object is external
|
||||||
|
#define osMailQDef(name, queue_sz, type) \
|
||||||
|
extern const osMailQDef_t os_mailQ_def_##name
|
||||||
|
#else // define the object
|
||||||
|
#define osMailQDef(name, queue_sz, type) \
|
||||||
|
const osMailQDef_t os_mailQ_def_##name = \
|
||||||
|
{ (queue_sz), sizeof(type), NULL }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief Access a Mail Queue Definition.
|
||||||
|
/// \param name name of the queue
|
||||||
|
#define osMailQ(name) \
|
||||||
|
&os_mailQ_def_##name
|
||||||
|
|
||||||
|
/// Create and Initialize a Mail Queue object.
|
||||||
|
/// \param[in] queue_def mail queue definition referenced with \ref osMailQ.
|
||||||
|
/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL.
|
||||||
|
/// \return mail queue ID for reference by other functions or NULL in case of error.
|
||||||
|
osMailQId osMailCreate (const osMailQDef_t *queue_def, osThreadId thread_id);
|
||||||
|
|
||||||
|
/// Allocate a memory block for mail from a mail memory pool.
|
||||||
|
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out
|
||||||
|
/// \return pointer to memory block that can be filled with mail or NULL in case of error.
|
||||||
|
void *osMailAlloc (osMailQId queue_id, uint32_t millisec);
|
||||||
|
|
||||||
|
/// Allocate a memory block for mail from a mail memory pool and set memory block to zero.
|
||||||
|
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out
|
||||||
|
/// \return pointer to memory block that can be filled with mail or NULL in case of error.
|
||||||
|
void *osMailCAlloc (osMailQId queue_id, uint32_t millisec);
|
||||||
|
|
||||||
|
/// Put a Mail into a Queue.
|
||||||
|
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||||
|
/// \param[in] mail pointer to memory with mail to put into a queue.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus osMailPut (osMailQId queue_id, const void *mail);
|
||||||
|
|
||||||
|
/// Get a Mail from a Queue or timeout if Queue is empty.
|
||||||
|
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||||
|
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return event information that includes status code.
|
||||||
|
os_InRegs osEvent osMailGet (osMailQId queue_id, uint32_t millisec);
|
||||||
|
|
||||||
|
/// Free a memory block by returning it to a mail memory pool.
|
||||||
|
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||||
|
/// \param[in] mail pointer to memory block that was obtained with \ref osMailGet.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus osMailFree (osMailQId queue_id, void *mail);
|
||||||
|
|
||||||
|
#endif // Mail Queue available
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CMSIS_OS_H_
|
||||||
2482
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
vendored
Normal file
2482
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
734
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h
vendored
Normal file
734
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h
vendored
Normal file
@@ -0,0 +1,734 @@
|
|||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
* Portions Copyright © 2017 STMicroelectronics International N.V. All rights reserved.
|
||||||
|
* Portions Copyright (c) 2013-2017 ARM Limited. All rights reserved.
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Name: cmsis_os2.h
|
||||||
|
* Purpose: CMSIS RTOS2 wrapper for FreeRTOS
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef CMSIS_OS2_H_
|
||||||
|
#define CMSIS_OS2_H_
|
||||||
|
|
||||||
|
#ifndef __NO_RETURN
|
||||||
|
#if defined(__CC_ARM)
|
||||||
|
#define __NO_RETURN __declspec(noreturn)
|
||||||
|
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
|
||||||
|
#define __NO_RETURN __attribute__((__noreturn__))
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define __NO_RETURN __attribute__((__noreturn__))
|
||||||
|
#elif defined(__ICCARM__)
|
||||||
|
#define __NO_RETURN __noreturn
|
||||||
|
#else
|
||||||
|
#define __NO_RETURN
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Enumerations, structures, defines ====
|
||||||
|
|
||||||
|
/// Version information.
|
||||||
|
typedef struct {
|
||||||
|
uint32_t api; ///< API version (major.minor.rev: mmnnnrrrr dec).
|
||||||
|
uint32_t kernel; ///< Kernel version (major.minor.rev: mmnnnrrrr dec).
|
||||||
|
} osVersion_t;
|
||||||
|
|
||||||
|
/// Kernel state.
|
||||||
|
typedef enum {
|
||||||
|
osKernelInactive = 0, ///< Inactive.
|
||||||
|
osKernelReady = 1, ///< Ready.
|
||||||
|
osKernelRunning = 2, ///< Running.
|
||||||
|
osKernelLocked = 3, ///< Locked.
|
||||||
|
osKernelSuspended = 4, ///< Suspended.
|
||||||
|
osKernelError = -1, ///< Error.
|
||||||
|
osKernelReserved = 0x7FFFFFFFU ///< Prevents enum down-size compiler optimization.
|
||||||
|
} osKernelState_t;
|
||||||
|
|
||||||
|
/// Thread state.
|
||||||
|
typedef enum {
|
||||||
|
osThreadInactive = 0, ///< Inactive.
|
||||||
|
osThreadReady = 1, ///< Ready.
|
||||||
|
osThreadRunning = 2, ///< Running.
|
||||||
|
osThreadBlocked = 3, ///< Blocked.
|
||||||
|
osThreadTerminated = 4, ///< Terminated.
|
||||||
|
osThreadError = -1, ///< Error.
|
||||||
|
osThreadReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||||
|
} osThreadState_t;
|
||||||
|
|
||||||
|
/// Priority values.
|
||||||
|
typedef enum {
|
||||||
|
osPriorityNone = 0, ///< No priority (not initialized).
|
||||||
|
osPriorityIdle = 1, ///< Reserved for Idle thread.
|
||||||
|
osPriorityLow = 8, ///< Priority: low
|
||||||
|
osPriorityLow1 = 8+1, ///< Priority: low + 1
|
||||||
|
osPriorityLow2 = 8+2, ///< Priority: low + 2
|
||||||
|
osPriorityLow3 = 8+3, ///< Priority: low + 3
|
||||||
|
osPriorityLow4 = 8+4, ///< Priority: low + 4
|
||||||
|
osPriorityLow5 = 8+5, ///< Priority: low + 5
|
||||||
|
osPriorityLow6 = 8+6, ///< Priority: low + 6
|
||||||
|
osPriorityLow7 = 8+7, ///< Priority: low + 7
|
||||||
|
osPriorityBelowNormal = 16, ///< Priority: below normal
|
||||||
|
osPriorityBelowNormal1 = 16+1, ///< Priority: below normal + 1
|
||||||
|
osPriorityBelowNormal2 = 16+2, ///< Priority: below normal + 2
|
||||||
|
osPriorityBelowNormal3 = 16+3, ///< Priority: below normal + 3
|
||||||
|
osPriorityBelowNormal4 = 16+4, ///< Priority: below normal + 4
|
||||||
|
osPriorityBelowNormal5 = 16+5, ///< Priority: below normal + 5
|
||||||
|
osPriorityBelowNormal6 = 16+6, ///< Priority: below normal + 6
|
||||||
|
osPriorityBelowNormal7 = 16+7, ///< Priority: below normal + 7
|
||||||
|
osPriorityNormal = 24, ///< Priority: normal
|
||||||
|
osPriorityNormal1 = 24+1, ///< Priority: normal + 1
|
||||||
|
osPriorityNormal2 = 24+2, ///< Priority: normal + 2
|
||||||
|
osPriorityNormal3 = 24+3, ///< Priority: normal + 3
|
||||||
|
osPriorityNormal4 = 24+4, ///< Priority: normal + 4
|
||||||
|
osPriorityNormal5 = 24+5, ///< Priority: normal + 5
|
||||||
|
osPriorityNormal6 = 24+6, ///< Priority: normal + 6
|
||||||
|
osPriorityNormal7 = 24+7, ///< Priority: normal + 7
|
||||||
|
osPriorityAboveNormal = 32, ///< Priority: above normal
|
||||||
|
osPriorityAboveNormal1 = 32+1, ///< Priority: above normal + 1
|
||||||
|
osPriorityAboveNormal2 = 32+2, ///< Priority: above normal + 2
|
||||||
|
osPriorityAboveNormal3 = 32+3, ///< Priority: above normal + 3
|
||||||
|
osPriorityAboveNormal4 = 32+4, ///< Priority: above normal + 4
|
||||||
|
osPriorityAboveNormal5 = 32+5, ///< Priority: above normal + 5
|
||||||
|
osPriorityAboveNormal6 = 32+6, ///< Priority: above normal + 6
|
||||||
|
osPriorityAboveNormal7 = 32+7, ///< Priority: above normal + 7
|
||||||
|
osPriorityHigh = 40, ///< Priority: high
|
||||||
|
osPriorityHigh1 = 40+1, ///< Priority: high + 1
|
||||||
|
osPriorityHigh2 = 40+2, ///< Priority: high + 2
|
||||||
|
osPriorityHigh3 = 40+3, ///< Priority: high + 3
|
||||||
|
osPriorityHigh4 = 40+4, ///< Priority: high + 4
|
||||||
|
osPriorityHigh5 = 40+5, ///< Priority: high + 5
|
||||||
|
osPriorityHigh6 = 40+6, ///< Priority: high + 6
|
||||||
|
osPriorityHigh7 = 40+7, ///< Priority: high + 7
|
||||||
|
osPriorityRealtime = 48, ///< Priority: realtime
|
||||||
|
osPriorityRealtime1 = 48+1, ///< Priority: realtime + 1
|
||||||
|
osPriorityRealtime2 = 48+2, ///< Priority: realtime + 2
|
||||||
|
osPriorityRealtime3 = 48+3, ///< Priority: realtime + 3
|
||||||
|
osPriorityRealtime4 = 48+4, ///< Priority: realtime + 4
|
||||||
|
osPriorityRealtime5 = 48+5, ///< Priority: realtime + 5
|
||||||
|
osPriorityRealtime6 = 48+6, ///< Priority: realtime + 6
|
||||||
|
osPriorityRealtime7 = 48+7, ///< Priority: realtime + 7
|
||||||
|
osPriorityISR = 56, ///< Reserved for ISR deferred thread.
|
||||||
|
osPriorityError = -1, ///< System cannot determine priority or illegal priority.
|
||||||
|
osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||||
|
} osPriority_t;
|
||||||
|
|
||||||
|
/// Entry point of a thread.
|
||||||
|
typedef void (*osThreadFunc_t) (void *argument);
|
||||||
|
|
||||||
|
/// Timer callback function.
|
||||||
|
typedef void (*osTimerFunc_t) (void *argument);
|
||||||
|
|
||||||
|
/// Timer type.
|
||||||
|
typedef enum {
|
||||||
|
osTimerOnce = 0, ///< One-shot timer.
|
||||||
|
osTimerPeriodic = 1 ///< Repeating timer.
|
||||||
|
} osTimerType_t;
|
||||||
|
|
||||||
|
// Timeout value.
|
||||||
|
#define osWaitForever 0xFFFFFFFFU ///< Wait forever timeout value.
|
||||||
|
|
||||||
|
// Flags options (\ref osThreadFlagsWait and \ref osEventFlagsWait).
|
||||||
|
#define osFlagsWaitAny 0x00000000U ///< Wait for any flag (default).
|
||||||
|
#define osFlagsWaitAll 0x00000001U ///< Wait for all flags.
|
||||||
|
#define osFlagsNoClear 0x00000002U ///< Do not clear flags which have been specified to wait for.
|
||||||
|
|
||||||
|
// Flags errors (returned by osThreadFlagsXxxx and osEventFlagsXxxx).
|
||||||
|
#define osFlagsError 0x80000000U ///< Error indicator.
|
||||||
|
#define osFlagsErrorUnknown 0xFFFFFFFFU ///< osError (-1).
|
||||||
|
#define osFlagsErrorTimeout 0xFFFFFFFEU ///< osErrorTimeout (-2).
|
||||||
|
#define osFlagsErrorResource 0xFFFFFFFDU ///< osErrorResource (-3).
|
||||||
|
#define osFlagsErrorParameter 0xFFFFFFFCU ///< osErrorParameter (-4).
|
||||||
|
#define osFlagsErrorISR 0xFFFFFFFAU ///< osErrorISR (-6).
|
||||||
|
|
||||||
|
// Thread attributes (attr_bits in \ref osThreadAttr_t).
|
||||||
|
#define osThreadDetached 0x00000000U ///< Thread created in detached mode (default)
|
||||||
|
#define osThreadJoinable 0x00000001U ///< Thread created in joinable mode
|
||||||
|
|
||||||
|
// Mutex attributes (attr_bits in \ref osMutexAttr_t).
|
||||||
|
#define osMutexRecursive 0x00000001U ///< Recursive mutex.
|
||||||
|
#define osMutexPrioInherit 0x00000002U ///< Priority inherit protocol.
|
||||||
|
#define osMutexRobust 0x00000008U ///< Robust mutex.
|
||||||
|
|
||||||
|
/// Status code values returned by CMSIS-RTOS functions.
|
||||||
|
typedef enum {
|
||||||
|
osOK = 0, ///< Operation completed successfully.
|
||||||
|
osError = -1, ///< Unspecified RTOS error: run-time error but no other error message fits.
|
||||||
|
osErrorTimeout = -2, ///< Operation not completed within the timeout period.
|
||||||
|
osErrorResource = -3, ///< Resource not available.
|
||||||
|
osErrorParameter = -4, ///< Parameter error.
|
||||||
|
osErrorNoMemory = -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
|
||||||
|
osErrorISR = -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
|
||||||
|
osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||||
|
} osStatus_t;
|
||||||
|
|
||||||
|
|
||||||
|
/// \details Thread ID identifies the thread.
|
||||||
|
typedef void *osThreadId_t;
|
||||||
|
|
||||||
|
/// \details Timer ID identifies the timer.
|
||||||
|
typedef void *osTimerId_t;
|
||||||
|
|
||||||
|
/// \details Event Flags ID identifies the event flags.
|
||||||
|
typedef void *osEventFlagsId_t;
|
||||||
|
|
||||||
|
/// \details Mutex ID identifies the mutex.
|
||||||
|
typedef void *osMutexId_t;
|
||||||
|
|
||||||
|
/// \details Semaphore ID identifies the semaphore.
|
||||||
|
typedef void *osSemaphoreId_t;
|
||||||
|
|
||||||
|
/// \details Memory Pool ID identifies the memory pool.
|
||||||
|
typedef void *osMemoryPoolId_t;
|
||||||
|
|
||||||
|
/// \details Message Queue ID identifies the message queue.
|
||||||
|
typedef void *osMessageQueueId_t;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef TZ_MODULEID_T
|
||||||
|
#define TZ_MODULEID_T
|
||||||
|
/// \details Data type that identifies secure software modules called by a process.
|
||||||
|
typedef uint32_t TZ_ModuleId_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// Attributes structure for thread.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the thread
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
void *stack_mem; ///< memory for stack
|
||||||
|
uint32_t stack_size; ///< size of stack
|
||||||
|
osPriority_t priority; ///< initial thread priority (default: osPriorityNormal)
|
||||||
|
TZ_ModuleId_t tz_module; ///< TrustZone module identifier
|
||||||
|
uint32_t reserved; ///< reserved (must be 0)
|
||||||
|
} osThreadAttr_t;
|
||||||
|
|
||||||
|
/// Attributes structure for timer.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the timer
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
} osTimerAttr_t;
|
||||||
|
|
||||||
|
/// Attributes structure for event flags.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the event flags
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
} osEventFlagsAttr_t;
|
||||||
|
|
||||||
|
/// Attributes structure for mutex.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the mutex
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
} osMutexAttr_t;
|
||||||
|
|
||||||
|
/// Attributes structure for semaphore.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the semaphore
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
} osSemaphoreAttr_t;
|
||||||
|
|
||||||
|
/// Attributes structure for memory pool.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the memory pool
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
void *mp_mem; ///< memory for data storage
|
||||||
|
uint32_t mp_size; ///< size of provided memory for data storage
|
||||||
|
} osMemoryPoolAttr_t;
|
||||||
|
|
||||||
|
/// Attributes structure for message queue.
|
||||||
|
typedef struct {
|
||||||
|
const char *name; ///< name of the message queue
|
||||||
|
uint32_t attr_bits; ///< attribute bits
|
||||||
|
void *cb_mem; ///< memory for control block
|
||||||
|
uint32_t cb_size; ///< size of provided memory for control block
|
||||||
|
void *mq_mem; ///< memory for data storage
|
||||||
|
uint32_t mq_size; ///< size of provided memory for data storage
|
||||||
|
} osMessageQueueAttr_t;
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Kernel Management Functions ====
|
||||||
|
|
||||||
|
/// Initialize the RTOS Kernel.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osKernelInitialize (void);
|
||||||
|
|
||||||
|
/// Get RTOS Kernel Information.
|
||||||
|
/// \param[out] version pointer to buffer for retrieving version information.
|
||||||
|
/// \param[out] id_buf pointer to buffer for retrieving kernel identification string.
|
||||||
|
/// \param[in] id_size size of buffer for kernel identification string.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osKernelGetInfo (osVersion_t *version, char *id_buf, uint32_t id_size);
|
||||||
|
|
||||||
|
/// Get the current RTOS Kernel state.
|
||||||
|
/// \return current RTOS Kernel state.
|
||||||
|
osKernelState_t osKernelGetState (void);
|
||||||
|
|
||||||
|
/// Start the RTOS Kernel scheduler.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osKernelStart (void);
|
||||||
|
|
||||||
|
/// Lock the RTOS Kernel scheduler.
|
||||||
|
/// \return previous lock state (1 - locked, 0 - not locked, error code if negative).
|
||||||
|
int32_t osKernelLock (void);
|
||||||
|
|
||||||
|
/// Unlock the RTOS Kernel scheduler.
|
||||||
|
/// \return previous lock state (1 - locked, 0 - not locked, error code if negative).
|
||||||
|
int32_t osKernelUnlock (void);
|
||||||
|
|
||||||
|
/// Restore the RTOS Kernel scheduler lock state.
|
||||||
|
/// \param[in] lock lock state obtained by \ref osKernelLock or \ref osKernelUnlock.
|
||||||
|
/// \return new lock state (1 - locked, 0 - not locked, error code if negative).
|
||||||
|
int32_t osKernelRestoreLock (int32_t lock);
|
||||||
|
|
||||||
|
/// Suspend the RTOS Kernel scheduler.
|
||||||
|
/// \return time in ticks, for how long the system can sleep or power-down.
|
||||||
|
uint32_t osKernelSuspend (void);
|
||||||
|
|
||||||
|
/// Resume the RTOS Kernel scheduler.
|
||||||
|
/// \param[in] sleep_ticks time in ticks for how long the system was in sleep or power-down mode.
|
||||||
|
void osKernelResume (uint32_t sleep_ticks);
|
||||||
|
|
||||||
|
/// Get the RTOS kernel tick count.
|
||||||
|
/// \return RTOS kernel current tick count.
|
||||||
|
uint32_t osKernelGetTickCount (void);
|
||||||
|
|
||||||
|
/// Get the RTOS kernel tick frequency.
|
||||||
|
/// \return frequency of the kernel tick in hertz, i.e. kernel ticks per second.
|
||||||
|
uint32_t osKernelGetTickFreq (void);
|
||||||
|
|
||||||
|
/// Get the RTOS kernel system timer count.
|
||||||
|
/// \return RTOS kernel current system timer count as 32-bit value.
|
||||||
|
uint32_t osKernelGetSysTimerCount (void);
|
||||||
|
|
||||||
|
/// Get the RTOS kernel system timer frequency.
|
||||||
|
/// \return frequency of the system timer in hertz, i.e. timer ticks per second.
|
||||||
|
uint32_t osKernelGetSysTimerFreq (void);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Thread Management Functions ====
|
||||||
|
|
||||||
|
/// Create a thread and add it to Active Threads.
|
||||||
|
/// \param[in] func thread function.
|
||||||
|
/// \param[in] argument pointer that is passed to the thread function as start argument.
|
||||||
|
/// \param[in] attr thread attributes; NULL: default values.
|
||||||
|
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||||
|
osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osThreadGetName (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Return the thread ID of the current running thread.
|
||||||
|
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||||
|
osThreadId_t osThreadGetId (void);
|
||||||
|
|
||||||
|
/// Get current thread state of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return current thread state of the specified thread.
|
||||||
|
osThreadState_t osThreadGetState (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Get stack size of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return stack size in bytes.
|
||||||
|
uint32_t osThreadGetStackSize (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Get available stack space of a thread based on stack watermark recording during execution.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return remaining stack space in bytes.
|
||||||
|
uint32_t osThreadGetStackSpace (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Change priority of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \param[in] priority new priority value for the thread function.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadSetPriority (osThreadId_t thread_id, osPriority_t priority);
|
||||||
|
|
||||||
|
/// Get current priority of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return current priority value of the specified thread.
|
||||||
|
osPriority_t osThreadGetPriority (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Pass control to next thread that is in state \b READY.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadYield (void);
|
||||||
|
|
||||||
|
/// Suspend execution of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadSuspend (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Resume execution of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadResume (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Detach a thread (thread storage can be reclaimed when thread terminates).
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadDetach (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Wait for specified thread to terminate.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadJoin (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Terminate execution of current running thread.
|
||||||
|
__NO_RETURN void osThreadExit (void);
|
||||||
|
|
||||||
|
/// Terminate execution of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osThreadTerminate (osThreadId_t thread_id);
|
||||||
|
|
||||||
|
/// Get number of active threads.
|
||||||
|
/// \return number of active threads.
|
||||||
|
uint32_t osThreadGetCount (void);
|
||||||
|
|
||||||
|
/// Enumerate active threads.
|
||||||
|
/// \param[out] thread_array pointer to array for retrieving thread IDs.
|
||||||
|
/// \param[in] array_items maximum number of items in array for retrieving thread IDs.
|
||||||
|
/// \return number of enumerated threads.
|
||||||
|
uint32_t osThreadEnumerate (osThreadId_t *thread_array, uint32_t array_items);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Thread Flags Functions ====
|
||||||
|
|
||||||
|
/// Set the specified Thread Flags of a thread.
|
||||||
|
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||||
|
/// \param[in] flags specifies the flags of the thread that shall be set.
|
||||||
|
/// \return thread flags after setting or error code if highest bit set.
|
||||||
|
uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags);
|
||||||
|
|
||||||
|
/// Clear the specified Thread Flags of current running thread.
|
||||||
|
/// \param[in] flags specifies the flags of the thread that shall be cleared.
|
||||||
|
/// \return thread flags before clearing or error code if highest bit set.
|
||||||
|
uint32_t osThreadFlagsClear (uint32_t flags);
|
||||||
|
|
||||||
|
/// Get the current Thread Flags of current running thread.
|
||||||
|
/// \return current thread flags.
|
||||||
|
uint32_t osThreadFlagsGet (void);
|
||||||
|
|
||||||
|
/// Wait for one or more Thread Flags of the current running thread to become signaled.
|
||||||
|
/// \param[in] flags specifies the flags to wait for.
|
||||||
|
/// \param[in] options specifies flags options (osFlagsXxxx).
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return thread flags before clearing or error code if highest bit set.
|
||||||
|
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Generic Wait Functions ====
|
||||||
|
|
||||||
|
/// Wait for Timeout (Time Delay).
|
||||||
|
/// \param[in] ticks \ref CMSIS_RTOS_TimeOutValue "time ticks" value
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osDelay (uint32_t ticks);
|
||||||
|
|
||||||
|
/// Wait until specified time.
|
||||||
|
/// \param[in] ticks absolute time in ticks
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osDelayUntil (uint32_t ticks);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Timer Management Functions ====
|
||||||
|
|
||||||
|
/// Create and Initialize a timer.
|
||||||
|
/// \param[in] func function pointer to callback function.
|
||||||
|
/// \param[in] type \ref osTimerOnce for one-shot or \ref osTimerPeriodic for periodic behavior.
|
||||||
|
/// \param[in] argument argument to the timer callback function.
|
||||||
|
/// \param[in] attr timer attributes; NULL: default values.
|
||||||
|
/// \return timer ID for reference by other functions or NULL in case of error.
|
||||||
|
osTimerId_t osTimerNew (osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osTimerGetName (osTimerId_t timer_id);
|
||||||
|
|
||||||
|
/// Start or restart a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||||
|
/// \param[in] ticks \ref CMSIS_RTOS_TimeOutValue "time ticks" value of the timer.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks);
|
||||||
|
|
||||||
|
/// Stop a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osTimerStop (osTimerId_t timer_id);
|
||||||
|
|
||||||
|
/// Check if a timer is running.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||||
|
/// \return 0 not running, 1 running.
|
||||||
|
uint32_t osTimerIsRunning (osTimerId_t timer_id);
|
||||||
|
|
||||||
|
/// Delete a timer.
|
||||||
|
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osTimerDelete (osTimerId_t timer_id);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Event Flags Management Functions ====
|
||||||
|
|
||||||
|
/// Create and Initialize an Event Flags object.
|
||||||
|
/// \param[in] attr event flags attributes; NULL: default values.
|
||||||
|
/// \return event flags ID for reference by other functions or NULL in case of error.
|
||||||
|
osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of an Event Flags object.
|
||||||
|
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osEventFlagsGetName (osEventFlagsId_t ef_id);
|
||||||
|
|
||||||
|
/// Set the specified Event Flags.
|
||||||
|
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||||
|
/// \param[in] flags specifies the flags that shall be set.
|
||||||
|
/// \return event flags after setting or error code if highest bit set.
|
||||||
|
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);
|
||||||
|
|
||||||
|
/// Clear the specified Event Flags.
|
||||||
|
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||||
|
/// \param[in] flags specifies the flags that shall be cleared.
|
||||||
|
/// \return event flags before clearing or error code if highest bit set.
|
||||||
|
uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags);
|
||||||
|
|
||||||
|
/// Get the current Event Flags.
|
||||||
|
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||||
|
/// \return current event flags.
|
||||||
|
uint32_t osEventFlagsGet (osEventFlagsId_t ef_id);
|
||||||
|
|
||||||
|
/// Wait for one or more Event Flags to become signaled.
|
||||||
|
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||||
|
/// \param[in] flags specifies the flags to wait for.
|
||||||
|
/// \param[in] options specifies flags options (osFlagsXxxx).
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return event flags before clearing or error code if highest bit set.
|
||||||
|
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout);
|
||||||
|
|
||||||
|
/// Delete an Event Flags object.
|
||||||
|
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osEventFlagsDelete (osEventFlagsId_t ef_id);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Mutex Management Functions ====
|
||||||
|
|
||||||
|
/// Create and Initialize a Mutex object.
|
||||||
|
/// \param[in] attr mutex attributes; NULL: default values.
|
||||||
|
/// \return mutex ID for reference by other functions or NULL in case of error.
|
||||||
|
osMutexId_t osMutexNew (const osMutexAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of a Mutex object.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osMutexGetName (osMutexId_t mutex_id);
|
||||||
|
|
||||||
|
/// Acquire a Mutex or timeout if it is locked.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
|
||||||
|
|
||||||
|
/// Release a Mutex that was acquired by \ref osMutexAcquire.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMutexRelease (osMutexId_t mutex_id);
|
||||||
|
|
||||||
|
/// Get Thread which owns a Mutex object.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||||
|
/// \return thread ID of owner thread or NULL when mutex was not acquired.
|
||||||
|
osThreadId_t osMutexGetOwner (osMutexId_t mutex_id);
|
||||||
|
|
||||||
|
/// Delete a Mutex object.
|
||||||
|
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMutexDelete (osMutexId_t mutex_id);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Semaphore Management Functions ====
|
||||||
|
|
||||||
|
/// Create and Initialize a Semaphore object.
|
||||||
|
/// \param[in] max_count maximum number of available tokens.
|
||||||
|
/// \param[in] initial_count initial number of available tokens.
|
||||||
|
/// \param[in] attr semaphore attributes; NULL: default values.
|
||||||
|
/// \return semaphore ID for reference by other functions or NULL in case of error.
|
||||||
|
osSemaphoreId_t osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of a Semaphore object.
|
||||||
|
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osSemaphoreGetName (osSemaphoreId_t semaphore_id);
|
||||||
|
|
||||||
|
/// Acquire a Semaphore token or timeout if no tokens are available.
|
||||||
|
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout);
|
||||||
|
|
||||||
|
/// Release a Semaphore token up to the initial maximum count.
|
||||||
|
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id);
|
||||||
|
|
||||||
|
/// Get current Semaphore token count.
|
||||||
|
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||||
|
/// \return number of tokens available.
|
||||||
|
uint32_t osSemaphoreGetCount (osSemaphoreId_t semaphore_id);
|
||||||
|
|
||||||
|
/// Delete a Semaphore object.
|
||||||
|
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osSemaphoreDelete (osSemaphoreId_t semaphore_id);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Memory Pool Management Functions ====
|
||||||
|
|
||||||
|
/// Create and Initialize a Memory Pool object.
|
||||||
|
/// \param[in] block_count maximum number of memory blocks in memory pool.
|
||||||
|
/// \param[in] block_size memory block size in bytes.
|
||||||
|
/// \param[in] attr memory pool attributes; NULL: default values.
|
||||||
|
/// \return memory pool ID for reference by other functions or NULL in case of error.
|
||||||
|
osMemoryPoolId_t osMemoryPoolNew (uint32_t block_count, uint32_t block_size, const osMemoryPoolAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of a Memory Pool object.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osMemoryPoolGetName (osMemoryPoolId_t mp_id);
|
||||||
|
|
||||||
|
/// Allocate a memory block from a Memory Pool.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return address of the allocated memory block or NULL in case of no memory is available.
|
||||||
|
void *osMemoryPoolAlloc (osMemoryPoolId_t mp_id, uint32_t timeout);
|
||||||
|
|
||||||
|
/// Return an allocated memory block back to a Memory Pool.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \param[in] block address of the allocated memory block to be returned to the memory pool.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMemoryPoolFree (osMemoryPoolId_t mp_id, void *block);
|
||||||
|
|
||||||
|
/// Get maximum number of memory blocks in a Memory Pool.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \return maximum number of memory blocks.
|
||||||
|
uint32_t osMemoryPoolGetCapacity (osMemoryPoolId_t mp_id);
|
||||||
|
|
||||||
|
/// Get memory block size in a Memory Pool.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \return memory block size in bytes.
|
||||||
|
uint32_t osMemoryPoolGetBlockSize (osMemoryPoolId_t mp_id);
|
||||||
|
|
||||||
|
/// Get number of memory blocks used in a Memory Pool.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \return number of memory blocks used.
|
||||||
|
uint32_t osMemoryPoolGetCount (osMemoryPoolId_t mp_id);
|
||||||
|
|
||||||
|
/// Get number of memory blocks available in a Memory Pool.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \return number of memory blocks available.
|
||||||
|
uint32_t osMemoryPoolGetSpace (osMemoryPoolId_t mp_id);
|
||||||
|
|
||||||
|
/// Delete a Memory Pool object.
|
||||||
|
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMemoryPoolDelete (osMemoryPoolId_t mp_id);
|
||||||
|
|
||||||
|
|
||||||
|
// ==== Message Queue Management Functions ====
|
||||||
|
|
||||||
|
/// Create and Initialize a Message Queue object.
|
||||||
|
/// \param[in] msg_count maximum number of messages in queue.
|
||||||
|
/// \param[in] msg_size maximum message size in bytes.
|
||||||
|
/// \param[in] attr message queue attributes; NULL: default values.
|
||||||
|
/// \return message queue ID for reference by other functions or NULL in case of error.
|
||||||
|
osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr);
|
||||||
|
|
||||||
|
/// Get name of a Message Queue object.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return name as NULL terminated string.
|
||||||
|
const char *osMessageQueueGetName (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
/// Put a Message into a Queue or timeout if Queue is full.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \param[in] msg_ptr pointer to buffer with message to put into a queue.
|
||||||
|
/// \param[in] msg_prio message priority.
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
|
||||||
|
|
||||||
|
/// Get a Message from a Queue or timeout if Queue is empty.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \param[out] msg_ptr pointer to buffer for message to get from a queue.
|
||||||
|
/// \param[out] msg_prio pointer to buffer for message priority or NULL.
|
||||||
|
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);
|
||||||
|
|
||||||
|
/// Get maximum number of messages in a Message Queue.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return maximum number of messages.
|
||||||
|
uint32_t osMessageQueueGetCapacity (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
/// Get maximum message size in a Memory Pool.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return maximum message size in bytes.
|
||||||
|
uint32_t osMessageQueueGetMsgSize (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
/// Get number of queued messages in a Message Queue.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return number of queued messages.
|
||||||
|
uint32_t osMessageQueueGetCount (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
/// Get number of available slots for messages in a Message Queue.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return number of available slots for messages.
|
||||||
|
uint32_t osMessageQueueGetSpace (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
/// Reset a Message Queue to initial empty state.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMessageQueueReset (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
/// Delete a Message Queue object.
|
||||||
|
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||||
|
/// \return status code that indicates the execution status of the function.
|
||||||
|
osStatus_t osMessageQueueDelete (osMessageQueueId_t mq_id);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CMSIS_OS2_H_
|
||||||
63
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_mpool.h
vendored
Normal file
63
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_mpool.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
* Copyright (c) 2013-2020 Arm Limited. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Name: freertos_mpool.h
|
||||||
|
* Purpose: CMSIS RTOS2 wrapper for FreeRTOS
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef FREERTOS_MPOOL_H_
|
||||||
|
#define FREERTOS_MPOOL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "semphr.h"
|
||||||
|
|
||||||
|
/* Memory Pool implementation definitions */
|
||||||
|
#define MPOOL_STATUS 0x5EED0000U
|
||||||
|
|
||||||
|
/* Memory Block header */
|
||||||
|
typedef struct {
|
||||||
|
void *next; /* Pointer to next block */
|
||||||
|
} MemPoolBlock_t;
|
||||||
|
|
||||||
|
/* Memory Pool control block */
|
||||||
|
typedef struct MemPoolDef_t {
|
||||||
|
MemPoolBlock_t *head; /* Pointer to head block */
|
||||||
|
SemaphoreHandle_t sem; /* Pool semaphore handle */
|
||||||
|
uint8_t *mem_arr; /* Pool memory array */
|
||||||
|
uint32_t mem_sz; /* Pool memory array size */
|
||||||
|
const char *name; /* Pointer to name string */
|
||||||
|
uint32_t bl_sz; /* Size of a single block */
|
||||||
|
uint32_t bl_cnt; /* Number of blocks */
|
||||||
|
uint32_t n; /* Block allocation index */
|
||||||
|
volatile uint32_t status; /* Object status flags */
|
||||||
|
#if (configSUPPORT_STATIC_ALLOCATION == 1)
|
||||||
|
StaticSemaphore_t mem_sem; /* Semaphore object memory */
|
||||||
|
#endif
|
||||||
|
} MemPool_t;
|
||||||
|
|
||||||
|
/* No need to hide static object type, just align to coding style */
|
||||||
|
#define StaticMemPool_t MemPool_t
|
||||||
|
|
||||||
|
/* Define memory pool control block size */
|
||||||
|
#define MEMPOOL_CB_SIZE (sizeof(StaticMemPool_t))
|
||||||
|
|
||||||
|
/* Define size of the byte array required to create count of blocks of given size */
|
||||||
|
#define MEMPOOL_ARR_SIZE(bl_count, bl_size) (((((bl_size) + (4 - 1)) / 4) * 4)*(bl_count))
|
||||||
|
|
||||||
|
#endif /* FREERTOS_MPOOL_H_ */
|
||||||
310
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_os2.h
vendored
Normal file
310
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_os2.h
vendored
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
* Copyright (c) 2013-2020 Arm Limited. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Name: freertos_os2.h
|
||||||
|
* Purpose: CMSIS RTOS2 wrapper for FreeRTOS
|
||||||
|
*
|
||||||
|
*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef FREERTOS_OS2_H_
|
||||||
|
#define FREERTOS_OS2_H_
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core
|
||||||
|
|
||||||
|
#include CMSIS_device_header
|
||||||
|
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 FreeRTOS image size optimization definitions.
|
||||||
|
|
||||||
|
Note: Definitions configUSE_OS2 can be used to optimize FreeRTOS image size when
|
||||||
|
certain functionality is not required when using CMSIS-RTOS2 API.
|
||||||
|
In general optimization decisions are left to the tool chain but in cases
|
||||||
|
when coding style prevents it to optimize the code following optional
|
||||||
|
definitions can be used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Option to exclude CMSIS-RTOS2 functions osThreadSuspend and osThreadResume from
|
||||||
|
the application image.
|
||||||
|
*/
|
||||||
|
#ifndef configUSE_OS2_THREAD_SUSPEND_RESUME
|
||||||
|
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Option to exclude CMSIS-RTOS2 function osThreadEnumerate from the application image.
|
||||||
|
*/
|
||||||
|
#ifndef configUSE_OS2_THREAD_ENUMERATE
|
||||||
|
#define configUSE_OS2_THREAD_ENUMERATE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Option to disable CMSIS-RTOS2 function osEventFlagsSet and osEventFlagsClear
|
||||||
|
operation from ISR.
|
||||||
|
*/
|
||||||
|
#ifndef configUSE_OS2_EVENTFLAGS_FROM_ISR
|
||||||
|
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Option to exclude CMSIS-RTOS2 Thread Flags API functions from the application image.
|
||||||
|
*/
|
||||||
|
#ifndef configUSE_OS2_THREAD_FLAGS
|
||||||
|
#define configUSE_OS2_THREAD_FLAGS configUSE_TASK_NOTIFICATIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Option to exclude CMSIS-RTOS2 Timer API functions from the application image.
|
||||||
|
*/
|
||||||
|
#ifndef configUSE_OS2_TIMER
|
||||||
|
#define configUSE_OS2_TIMER configUSE_TIMERS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Option to exclude CMSIS-RTOS2 Mutex API functions from the application image.
|
||||||
|
*/
|
||||||
|
#ifndef configUSE_OS2_MUTEX
|
||||||
|
#define configUSE_OS2_MUTEX configUSE_MUTEXES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 FreeRTOS configuration check (FreeRTOSConfig.h).
|
||||||
|
|
||||||
|
Note: CMSIS-RTOS API requires functions included by using following definitions.
|
||||||
|
In case if certain API function is not used compiler will optimize it away.
|
||||||
|
*/
|
||||||
|
#if (INCLUDE_xSemaphoreGetMutexHolder == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osMutexGetOwner uses FreeRTOS function xSemaphoreGetMutexHolder. In case if
|
||||||
|
osMutexGetOwner is not used in the application image, compiler will optimize it away.
|
||||||
|
Set #define INCLUDE_xSemaphoreGetMutexHolder 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_xSemaphoreGetMutexHolder must equal 1 to implement Mutex Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_vTaskDelay == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osDelay uses FreeRTOS function vTaskDelay. In case if
|
||||||
|
osDelay is not used in the application image, compiler will optimize it away.
|
||||||
|
Set #define INCLUDE_vTaskDelay 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_vTaskDelay must equal 1 to implement Generic Wait Functions API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_vTaskDelayUntil == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osDelayUntil uses FreeRTOS function vTaskDelayUntil. In case if
|
||||||
|
osDelayUntil is not used in the application image, compiler will optimize it away.
|
||||||
|
Set #define INCLUDE_vTaskDelayUntil 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_vTaskDelayUntil must equal 1 to implement Generic Wait Functions API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_vTaskDelete == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osThreadTerminate and osThreadExit uses FreeRTOS function
|
||||||
|
vTaskDelete. In case if they are not used in the application image, compiler
|
||||||
|
will optimize them away.
|
||||||
|
Set #define INCLUDE_vTaskDelete 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_vTaskDelete must equal 1 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_xTaskGetCurrentTaskHandle == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 API uses FreeRTOS function xTaskGetCurrentTaskHandle to implement
|
||||||
|
functions osThreadGetId, osThreadFlagsClear and osThreadFlagsGet. In case if these
|
||||||
|
functions are not used in the application image, compiler will optimize them away.
|
||||||
|
Set #define INCLUDE_xTaskGetCurrentTaskHandle 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_xTaskGetCurrentTaskHandle must equal 1 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_xTaskGetSchedulerState == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 API uses FreeRTOS function xTaskGetSchedulerState to implement Kernel
|
||||||
|
tick handling and therefore it is vital that xTaskGetSchedulerState is included into
|
||||||
|
the application image.
|
||||||
|
Set #define INCLUDE_xTaskGetSchedulerState 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_xTaskGetSchedulerState must equal 1 to implement Kernel Information and Control API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_uxTaskGetStackHighWaterMark == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osThreadGetStackSpace uses FreeRTOS function uxTaskGetStackHighWaterMark.
|
||||||
|
In case if osThreadGetStackSpace is not used in the application image, compiler will
|
||||||
|
optimize it away.
|
||||||
|
Set #define INCLUDE_uxTaskGetStackHighWaterMark 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_uxTaskGetStackHighWaterMark must equal 1 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_uxTaskPriorityGet == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osThreadGetPriority uses FreeRTOS function uxTaskPriorityGet. In case if
|
||||||
|
osThreadGetPriority is not used in the application image, compiler will optimize it away.
|
||||||
|
Set #define INCLUDE_uxTaskPriorityGet 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_uxTaskPriorityGet must equal 1 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_vTaskPrioritySet == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osThreadSetPriority uses FreeRTOS function vTaskPrioritySet. In case if
|
||||||
|
osThreadSetPriority is not used in the application image, compiler will optimize it away.
|
||||||
|
Set #define INCLUDE_vTaskPrioritySet 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_vTaskPrioritySet must equal 1 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_eTaskGetState == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 API uses FreeRTOS function vTaskDelayUntil to implement functions osThreadGetState
|
||||||
|
and osThreadTerminate. In case if these functions are not used in the application image,
|
||||||
|
compiler will optimize them away.
|
||||||
|
Set #define INCLUDE_eTaskGetState 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition INCLUDE_eTaskGetState must equal 1 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_vTaskSuspend == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 API uses FreeRTOS functions vTaskSuspend and vTaskResume to implement
|
||||||
|
functions osThreadSuspend and osThreadResume. In case if these functions are not
|
||||||
|
used in the application image, compiler will optimize them away.
|
||||||
|
Set #define INCLUDE_vTaskSuspend 1 to fix this error.
|
||||||
|
|
||||||
|
Alternatively, if the application does not use osThreadSuspend and
|
||||||
|
osThreadResume they can be excluded from the image code by setting:
|
||||||
|
#define configUSE_OS2_THREAD_SUSPEND_RESUME 0 (in FreeRTOSConfig.h)
|
||||||
|
*/
|
||||||
|
#if (configUSE_OS2_THREAD_SUSPEND_RESUME == 1)
|
||||||
|
#error "Definition INCLUDE_vTaskSuspend must equal 1 to implement Kernel Information and Control API."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if (INCLUDE_xTimerPendFunctionCall == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osEventFlagsSet and osEventFlagsClear, when called from
|
||||||
|
the ISR, call FreeRTOS functions xEventGroupSetBitsFromISR and
|
||||||
|
xEventGroupClearBitsFromISR which are only enabled if timers are operational and
|
||||||
|
xTimerPendFunctionCall in enabled.
|
||||||
|
Set #define INCLUDE_xTimerPendFunctionCall 1 and #define configUSE_TIMERS 1
|
||||||
|
to fix this error.
|
||||||
|
|
||||||
|
Alternatively, if the application does not use osEventFlagsSet and osEventFlagsClear
|
||||||
|
from the ISR their operation from ISR can be restricted by setting:
|
||||||
|
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 0 (in FreeRTOSConfig.h)
|
||||||
|
*/
|
||||||
|
#if (configUSE_OS2_EVENTFLAGS_FROM_ISR == 1)
|
||||||
|
#error "Definition INCLUDE_xTimerPendFunctionCall must equal 1 to implement Event Flags API."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (configUSE_TIMERS == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 Timer Management API functions use FreeRTOS timer functions to implement
|
||||||
|
timer management. In case if these functions are not used in the application image,
|
||||||
|
compiler will optimize them away.
|
||||||
|
Set #define configUSE_TIMERS 1 to fix this error.
|
||||||
|
|
||||||
|
Alternatively, if the application does not use timer functions they can be
|
||||||
|
excluded from the image code by setting:
|
||||||
|
#define configUSE_OS2_TIMER 0 (in FreeRTOSConfig.h)
|
||||||
|
*/
|
||||||
|
#if (configUSE_OS2_TIMER == 1)
|
||||||
|
#error "Definition configUSE_TIMERS must equal 1 to implement Timer Management API."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (configUSE_MUTEXES == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 Mutex Management API functions use FreeRTOS mutex functions to implement
|
||||||
|
mutex management. In case if these functions are not used in the application image,
|
||||||
|
compiler will optimize them away.
|
||||||
|
Set #define configUSE_MUTEXES 1 to fix this error.
|
||||||
|
|
||||||
|
Alternatively, if the application does not use mutex functions they can be
|
||||||
|
excluded from the image code by setting:
|
||||||
|
#define configUSE_OS2_MUTEX 0 (in FreeRTOSConfig.h)
|
||||||
|
*/
|
||||||
|
#if (configUSE_OS2_MUTEX == 1)
|
||||||
|
#error "Definition configUSE_MUTEXES must equal 1 to implement Mutex Management API."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (configUSE_COUNTING_SEMAPHORES == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 Memory Pool functions use FreeRTOS function xSemaphoreCreateCounting
|
||||||
|
to implement memory pools. In case if these functions are not used in the application image,
|
||||||
|
compiler will optimize them away.
|
||||||
|
Set #define configUSE_COUNTING_SEMAPHORES 1 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition configUSE_COUNTING_SEMAPHORES must equal 1 to implement Memory Pool API."
|
||||||
|
#endif
|
||||||
|
#if (configUSE_TASK_NOTIFICATIONS == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 Thread Flags API functions use FreeRTOS Task Notification functions to implement
|
||||||
|
thread flag management. In case if these functions are not used in the application image,
|
||||||
|
compiler will optimize them away.
|
||||||
|
Set #define configUSE_TASK_NOTIFICATIONS 1 to fix this error.
|
||||||
|
|
||||||
|
Alternatively, if the application does not use thread flags functions they can be
|
||||||
|
excluded from the image code by setting:
|
||||||
|
#define configUSE_OS2_THREAD_FLAGS 0 (in FreeRTOSConfig.h)
|
||||||
|
*/
|
||||||
|
#if (configUSE_OS2_THREAD_FLAGS == 1)
|
||||||
|
#error "Definition configUSE_TASK_NOTIFICATIONS must equal 1 to implement Thread Flags API."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (configUSE_TRACE_FACILITY == 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 function osThreadEnumerate requires FreeRTOS function uxTaskGetSystemState
|
||||||
|
which is only enabled if configUSE_TRACE_FACILITY == 1.
|
||||||
|
Set #define configUSE_TRACE_FACILITY 1 to fix this error.
|
||||||
|
|
||||||
|
Alternatively, if the application does not use osThreadEnumerate it can be
|
||||||
|
excluded from the image code by setting:
|
||||||
|
#define configUSE_OS2_THREAD_ENUMERATE 0 (in FreeRTOSConfig.h)
|
||||||
|
*/
|
||||||
|
#if (configUSE_OS2_THREAD_ENUMERATE == 1)
|
||||||
|
#error "Definition configUSE_TRACE_FACILITY must equal 1 to implement osThreadEnumerate."
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (configUSE_16_BIT_TICKS == 1)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 wrapper for FreeRTOS relies on 32-bit tick timer which is also optimal on
|
||||||
|
a 32-bit CPU architectures.
|
||||||
|
Set #define configUSE_16_BIT_TICKS 0 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition configUSE_16_BIT_TICKS must be zero to implement CMSIS-RTOS2 API."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (configMAX_PRIORITIES != 56)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 defines 56 different priorities (see osPriority_t) and portable CMSIS-RTOS2
|
||||||
|
implementation should implement the same number of priorities.
|
||||||
|
Set #define configMAX_PRIORITIES 56 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition configMAX_PRIORITIES must equal 56 to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
#if (configUSE_PORT_OPTIMISED_TASK_SELECTION != 0)
|
||||||
|
/*
|
||||||
|
CMSIS-RTOS2 requires handling of 56 different priorities (see osPriority_t) while FreeRTOS port
|
||||||
|
optimised selection for Cortex core only handles 32 different priorities.
|
||||||
|
Set #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 to fix this error.
|
||||||
|
*/
|
||||||
|
#error "Definition configUSE_PORT_OPTIMISED_TASK_SELECTION must be zero to implement Thread Management API."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* FREERTOS_OS2_H_ */
|
||||||
18
Middlewares/Third_Party/FreeRTOS/Source/LICENSE
vendored
Normal file
18
Middlewares/Third_Party/FreeRTOS/Source/LICENSE
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
353
Middlewares/Third_Party/FreeRTOS/Source/croutine.c
vendored
Normal file
353
Middlewares/Third_Party/FreeRTOS/Source/croutine.c
vendored
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "croutine.h"
|
||||||
|
|
||||||
|
/* Remove the whole file is co-routines are not being used. */
|
||||||
|
#if( configUSE_CO_ROUTINES != 0 )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some kernel aware debuggers require data to be viewed to be global, rather
|
||||||
|
* than file scope.
|
||||||
|
*/
|
||||||
|
#ifdef portREMOVE_STATIC_QUALIFIER
|
||||||
|
#define static
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Lists for ready and blocked co-routines. --------------------*/
|
||||||
|
static List_t pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */
|
||||||
|
static List_t xDelayedCoRoutineList1; /*< Delayed co-routines. */
|
||||||
|
static List_t xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */
|
||||||
|
static List_t * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */
|
||||||
|
static List_t * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */
|
||||||
|
static List_t xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */
|
||||||
|
|
||||||
|
/* Other file private variables. --------------------------------*/
|
||||||
|
CRCB_t * pxCurrentCoRoutine = NULL;
|
||||||
|
static UBaseType_t uxTopCoRoutineReadyPriority = 0;
|
||||||
|
static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0;
|
||||||
|
|
||||||
|
/* The initial state of the co-routine when it is created. */
|
||||||
|
#define corINITIAL_STATE ( 0 )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Place the co-routine represented by pxCRCB into the appropriate ready queue
|
||||||
|
* for the priority. It is inserted at the end of the list.
|
||||||
|
*
|
||||||
|
* This macro accesses the co-routine ready lists and therefore must not be
|
||||||
|
* used from within an ISR.
|
||||||
|
*/
|
||||||
|
#define prvAddCoRoutineToReadyQueue( pxCRCB ) \
|
||||||
|
{ \
|
||||||
|
if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \
|
||||||
|
{ \
|
||||||
|
uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \
|
||||||
|
} \
|
||||||
|
vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility to ready all the lists used by the scheduler. This is called
|
||||||
|
* automatically upon the creation of the first co-routine.
|
||||||
|
*/
|
||||||
|
static void prvInitialiseCoRoutineLists( void );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Co-routines that are readied by an interrupt cannot be placed directly into
|
||||||
|
* the ready lists (there is no mutual exclusion). Instead they are placed in
|
||||||
|
* in the pending ready list in order that they can later be moved to the ready
|
||||||
|
* list by the co-routine scheduler.
|
||||||
|
*/
|
||||||
|
static void prvCheckPendingReadyList( void );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macro that looks at the list of co-routines that are currently delayed to
|
||||||
|
* see if any require waking.
|
||||||
|
*
|
||||||
|
* Co-routines are stored in the queue in the order of their wake time -
|
||||||
|
* meaning once one co-routine has been found whose timer has not expired
|
||||||
|
* we need not look any further down the list.
|
||||||
|
*/
|
||||||
|
static void prvCheckDelayedList( void );
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
BaseType_t xReturn;
|
||||||
|
CRCB_t *pxCoRoutine;
|
||||||
|
|
||||||
|
/* Allocate the memory that will store the co-routine control block. */
|
||||||
|
pxCoRoutine = ( CRCB_t * ) pvPortMalloc( sizeof( CRCB_t ) );
|
||||||
|
if( pxCoRoutine )
|
||||||
|
{
|
||||||
|
/* If pxCurrentCoRoutine is NULL then this is the first co-routine to
|
||||||
|
be created and the co-routine data structures need initialising. */
|
||||||
|
if( pxCurrentCoRoutine == NULL )
|
||||||
|
{
|
||||||
|
pxCurrentCoRoutine = pxCoRoutine;
|
||||||
|
prvInitialiseCoRoutineLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the priority is within limits. */
|
||||||
|
if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES )
|
||||||
|
{
|
||||||
|
uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill out the co-routine control block from the function parameters. */
|
||||||
|
pxCoRoutine->uxState = corINITIAL_STATE;
|
||||||
|
pxCoRoutine->uxPriority = uxPriority;
|
||||||
|
pxCoRoutine->uxIndex = uxIndex;
|
||||||
|
pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode;
|
||||||
|
|
||||||
|
/* Initialise all the other co-routine control block parameters. */
|
||||||
|
vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) );
|
||||||
|
vListInitialiseItem( &( pxCoRoutine->xEventListItem ) );
|
||||||
|
|
||||||
|
/* Set the co-routine control block as a link back from the ListItem_t.
|
||||||
|
This is so we can get back to the containing CRCB from a generic item
|
||||||
|
in a list. */
|
||||||
|
listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine );
|
||||||
|
listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine );
|
||||||
|
|
||||||
|
/* Event lists are always in priority order. */
|
||||||
|
listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) );
|
||||||
|
|
||||||
|
/* Now the co-routine has been initialised it can be added to the ready
|
||||||
|
list at the correct priority. */
|
||||||
|
prvAddCoRoutineToReadyQueue( pxCoRoutine );
|
||||||
|
|
||||||
|
xReturn = pdPASS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xReturn;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList )
|
||||||
|
{
|
||||||
|
TickType_t xTimeToWake;
|
||||||
|
|
||||||
|
/* Calculate the time to wake - this may overflow but this is
|
||||||
|
not a problem. */
|
||||||
|
xTimeToWake = xCoRoutineTickCount + xTicksToDelay;
|
||||||
|
|
||||||
|
/* We must remove ourselves from the ready list before adding
|
||||||
|
ourselves to the blocked list as the same list item is used for
|
||||||
|
both lists. */
|
||||||
|
( void ) uxListRemove( ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );
|
||||||
|
|
||||||
|
/* The list item will be inserted in wake time order. */
|
||||||
|
listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake );
|
||||||
|
|
||||||
|
if( xTimeToWake < xCoRoutineTickCount )
|
||||||
|
{
|
||||||
|
/* Wake time has overflowed. Place this item in the
|
||||||
|
overflow list. */
|
||||||
|
vListInsert( ( List_t * ) pxOverflowDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The wake time has not overflowed, so we can use the
|
||||||
|
current block list. */
|
||||||
|
vListInsert( ( List_t * ) pxDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pxEventList )
|
||||||
|
{
|
||||||
|
/* Also add the co-routine to an event list. If this is done then the
|
||||||
|
function must be called with interrupts disabled. */
|
||||||
|
vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvCheckPendingReadyList( void )
|
||||||
|
{
|
||||||
|
/* Are there any co-routines waiting to get moved to the ready list? These
|
||||||
|
are co-routines that have been readied by an ISR. The ISR cannot access
|
||||||
|
the ready lists itself. */
|
||||||
|
while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE )
|
||||||
|
{
|
||||||
|
CRCB_t *pxUnblockedCRCB;
|
||||||
|
|
||||||
|
/* The pending ready list can be accessed by an ISR. */
|
||||||
|
portDISABLE_INTERRUPTS();
|
||||||
|
{
|
||||||
|
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) );
|
||||||
|
( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) );
|
||||||
|
}
|
||||||
|
portENABLE_INTERRUPTS();
|
||||||
|
|
||||||
|
( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) );
|
||||||
|
prvAddCoRoutineToReadyQueue( pxUnblockedCRCB );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvCheckDelayedList( void )
|
||||||
|
{
|
||||||
|
CRCB_t *pxCRCB;
|
||||||
|
|
||||||
|
xPassedTicks = xTaskGetTickCount() - xLastTickCount;
|
||||||
|
while( xPassedTicks )
|
||||||
|
{
|
||||||
|
xCoRoutineTickCount++;
|
||||||
|
xPassedTicks--;
|
||||||
|
|
||||||
|
/* If the tick count has overflowed we need to swap the ready lists. */
|
||||||
|
if( xCoRoutineTickCount == 0 )
|
||||||
|
{
|
||||||
|
List_t * pxTemp;
|
||||||
|
|
||||||
|
/* Tick count has overflowed so we need to swap the delay lists. If there are
|
||||||
|
any items in pxDelayedCoRoutineList here then there is an error! */
|
||||||
|
pxTemp = pxDelayedCoRoutineList;
|
||||||
|
pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList;
|
||||||
|
pxOverflowDelayedCoRoutineList = pxTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See if this tick has made a timeout expire. */
|
||||||
|
while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE )
|
||||||
|
{
|
||||||
|
pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList );
|
||||||
|
|
||||||
|
if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) )
|
||||||
|
{
|
||||||
|
/* Timeout not yet expired. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
portDISABLE_INTERRUPTS();
|
||||||
|
{
|
||||||
|
/* The event could have occurred just before this critical
|
||||||
|
section. If this is the case then the generic list item will
|
||||||
|
have been moved to the pending ready list and the following
|
||||||
|
line is still valid. Also the pvContainer parameter will have
|
||||||
|
been set to NULL so the following lines are also valid. */
|
||||||
|
( void ) uxListRemove( &( pxCRCB->xGenericListItem ) );
|
||||||
|
|
||||||
|
/* Is the co-routine waiting on an event also? */
|
||||||
|
if( pxCRCB->xEventListItem.pxContainer )
|
||||||
|
{
|
||||||
|
( void ) uxListRemove( &( pxCRCB->xEventListItem ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portENABLE_INTERRUPTS();
|
||||||
|
|
||||||
|
prvAddCoRoutineToReadyQueue( pxCRCB );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xLastTickCount = xCoRoutineTickCount;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vCoRoutineSchedule( void )
|
||||||
|
{
|
||||||
|
/* See if any co-routines readied by events need moving to the ready lists. */
|
||||||
|
prvCheckPendingReadyList();
|
||||||
|
|
||||||
|
/* See if any delayed co-routines have timed out. */
|
||||||
|
prvCheckDelayedList();
|
||||||
|
|
||||||
|
/* Find the highest priority queue that contains ready co-routines. */
|
||||||
|
while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) )
|
||||||
|
{
|
||||||
|
if( uxTopCoRoutineReadyPriority == 0 )
|
||||||
|
{
|
||||||
|
/* No more co-routines to check. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
--uxTopCoRoutineReadyPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines
|
||||||
|
of the same priority get an equal share of the processor time. */
|
||||||
|
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) );
|
||||||
|
|
||||||
|
/* Call the co-routine. */
|
||||||
|
( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static void prvInitialiseCoRoutineLists( void )
|
||||||
|
{
|
||||||
|
UBaseType_t uxPriority;
|
||||||
|
|
||||||
|
for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ )
|
||||||
|
{
|
||||||
|
vListInitialise( ( List_t * ) &( pxReadyCoRoutineLists[ uxPriority ] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
vListInitialise( ( List_t * ) &xDelayedCoRoutineList1 );
|
||||||
|
vListInitialise( ( List_t * ) &xDelayedCoRoutineList2 );
|
||||||
|
vListInitialise( ( List_t * ) &xPendingReadyCoRoutineList );
|
||||||
|
|
||||||
|
/* Start with pxDelayedCoRoutineList using list1 and the
|
||||||
|
pxOverflowDelayedCoRoutineList using list2. */
|
||||||
|
pxDelayedCoRoutineList = &xDelayedCoRoutineList1;
|
||||||
|
pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList )
|
||||||
|
{
|
||||||
|
CRCB_t *pxUnblockedCRCB;
|
||||||
|
BaseType_t xReturn;
|
||||||
|
|
||||||
|
/* This function is called from within an interrupt. It can only access
|
||||||
|
event lists and the pending ready list. This function assumes that a
|
||||||
|
check has already been made to ensure pxEventList is not empty. */
|
||||||
|
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
|
||||||
|
( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) );
|
||||||
|
vListInsertEnd( ( List_t * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) );
|
||||||
|
|
||||||
|
if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority )
|
||||||
|
{
|
||||||
|
xReturn = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xReturn = pdFALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configUSE_CO_ROUTINES == 0 */
|
||||||
|
|
||||||
753
Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
vendored
Normal file
753
Middlewares/Third_Party/FreeRTOS/Source/event_groups.c
vendored
Normal file
@@ -0,0 +1,753 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Standard includes. */
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining
|
||||||
|
all the API functions to use the MPU wrappers. That should only be done when
|
||||||
|
task.h is included from an application file. */
|
||||||
|
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
/* FreeRTOS includes. */
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "task.h"
|
||||||
|
#include "timers.h"
|
||||||
|
#include "event_groups.h"
|
||||||
|
|
||||||
|
/* Lint e961, e750 and e9021 are suppressed as a MISRA exception justified
|
||||||
|
because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined
|
||||||
|
for the header files above, but not in this file, in order to generate the
|
||||||
|
correct privileged Vs unprivileged linkage and placement. */
|
||||||
|
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021 See comment above. */
|
||||||
|
|
||||||
|
/* The following bit fields convey control information in a task's event list
|
||||||
|
item value. It is important they don't clash with the
|
||||||
|
taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */
|
||||||
|
#if configUSE_16_BIT_TICKS == 1
|
||||||
|
#define eventCLEAR_EVENTS_ON_EXIT_BIT 0x0100U
|
||||||
|
#define eventUNBLOCKED_DUE_TO_BIT_SET 0x0200U
|
||||||
|
#define eventWAIT_FOR_ALL_BITS 0x0400U
|
||||||
|
#define eventEVENT_BITS_CONTROL_BYTES 0xff00U
|
||||||
|
#else
|
||||||
|
#define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL
|
||||||
|
#define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL
|
||||||
|
#define eventWAIT_FOR_ALL_BITS 0x04000000UL
|
||||||
|
#define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct EventGroupDef_t
|
||||||
|
{
|
||||||
|
EventBits_t uxEventBits;
|
||||||
|
List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */
|
||||||
|
|
||||||
|
#if( configUSE_TRACE_FACILITY == 1 )
|
||||||
|
UBaseType_t uxEventGroupNumber;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
|
||||||
|
uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the event group is statically allocated to ensure no attempt is made to free the memory. */
|
||||||
|
#endif
|
||||||
|
} EventGroup_t;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test the bits set in uxCurrentEventBits to see if the wait condition is met.
|
||||||
|
* The wait condition is defined by xWaitForAllBits. If xWaitForAllBits is
|
||||||
|
* pdTRUE then the wait condition is met if all the bits set in uxBitsToWaitFor
|
||||||
|
* are also set in uxCurrentEventBits. If xWaitForAllBits is pdFALSE then the
|
||||||
|
* wait condition is met if any of the bits set in uxBitsToWait for are also set
|
||||||
|
* in uxCurrentEventBits.
|
||||||
|
*/
|
||||||
|
static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
|
||||||
|
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer )
|
||||||
|
{
|
||||||
|
EventGroup_t *pxEventBits;
|
||||||
|
|
||||||
|
/* A StaticEventGroup_t object must be provided. */
|
||||||
|
configASSERT( pxEventGroupBuffer );
|
||||||
|
|
||||||
|
#if( configASSERT_DEFINED == 1 )
|
||||||
|
{
|
||||||
|
/* Sanity check that the size of the structure used to declare a
|
||||||
|
variable of type StaticEventGroup_t equals the size of the real
|
||||||
|
event group structure. */
|
||||||
|
volatile size_t xSize = sizeof( StaticEventGroup_t );
|
||||||
|
configASSERT( xSize == sizeof( EventGroup_t ) );
|
||||||
|
} /*lint !e529 xSize is referenced if configASSERT() is defined. */
|
||||||
|
#endif /* configASSERT_DEFINED */
|
||||||
|
|
||||||
|
/* The user has provided a statically allocated event group - use it. */
|
||||||
|
pxEventBits = ( EventGroup_t * ) pxEventGroupBuffer; /*lint !e740 !e9087 EventGroup_t and StaticEventGroup_t are deliberately aliased for data hiding purposes and guaranteed to have the same size and alignment requirement - checked by configASSERT(). */
|
||||||
|
|
||||||
|
if( pxEventBits != NULL )
|
||||||
|
{
|
||||||
|
pxEventBits->uxEventBits = 0;
|
||||||
|
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
|
||||||
|
|
||||||
|
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||||
|
{
|
||||||
|
/* Both static and dynamic allocation can be used, so note that
|
||||||
|
this event group was created statically in case the event group
|
||||||
|
is later deleted. */
|
||||||
|
pxEventBits->ucStaticallyAllocated = pdTRUE;
|
||||||
|
}
|
||||||
|
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||||
|
|
||||||
|
traceEVENT_GROUP_CREATE( pxEventBits );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* xEventGroupCreateStatic should only ever be called with
|
||||||
|
pxEventGroupBuffer pointing to a pre-allocated (compile time
|
||||||
|
allocated) StaticEventGroup_t variable. */
|
||||||
|
traceEVENT_GROUP_CREATE_FAILED();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pxEventBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||||
|
|
||||||
|
EventGroupHandle_t xEventGroupCreate( void )
|
||||||
|
{
|
||||||
|
EventGroup_t *pxEventBits;
|
||||||
|
|
||||||
|
/* Allocate the event group. Justification for MISRA deviation as
|
||||||
|
follows: pvPortMalloc() always ensures returned memory blocks are
|
||||||
|
aligned per the requirements of the MCU stack. In this case
|
||||||
|
pvPortMalloc() must return a pointer that is guaranteed to meet the
|
||||||
|
alignment requirements of the EventGroup_t structure - which (if you
|
||||||
|
follow it through) is the alignment requirements of the TickType_t type
|
||||||
|
(EventBits_t being of TickType_t itself). Therefore, whenever the
|
||||||
|
stack alignment requirements are greater than or equal to the
|
||||||
|
TickType_t alignment requirements the cast is safe. In other cases,
|
||||||
|
where the natural word size of the architecture is less than
|
||||||
|
sizeof( TickType_t ), the TickType_t variables will be accessed in two
|
||||||
|
or more reads operations, and the alignment requirements is only that
|
||||||
|
of each individual read. */
|
||||||
|
pxEventBits = ( EventGroup_t * ) pvPortMalloc( sizeof( EventGroup_t ) ); /*lint !e9087 !e9079 see comment above. */
|
||||||
|
|
||||||
|
if( pxEventBits != NULL )
|
||||||
|
{
|
||||||
|
pxEventBits->uxEventBits = 0;
|
||||||
|
vListInitialise( &( pxEventBits->xTasksWaitingForBits ) );
|
||||||
|
|
||||||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
{
|
||||||
|
/* Both static and dynamic allocation can be used, so note this
|
||||||
|
event group was allocated statically in case the event group is
|
||||||
|
later deleted. */
|
||||||
|
pxEventBits->ucStaticallyAllocated = pdFALSE;
|
||||||
|
}
|
||||||
|
#endif /* configSUPPORT_STATIC_ALLOCATION */
|
||||||
|
|
||||||
|
traceEVENT_GROUP_CREATE( pxEventBits );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
traceEVENT_GROUP_CREATE_FAILED(); /*lint !e9063 Else branch only exists to allow tracing and does not generate code if trace macros are not defined. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return pxEventBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait )
|
||||||
|
{
|
||||||
|
EventBits_t uxOriginalBitValue, uxReturn;
|
||||||
|
EventGroup_t *pxEventBits = xEventGroup;
|
||||||
|
BaseType_t xAlreadyYielded;
|
||||||
|
BaseType_t xTimeoutOccurred = pdFALSE;
|
||||||
|
|
||||||
|
configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||||
|
configASSERT( uxBitsToWaitFor != 0 );
|
||||||
|
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
|
||||||
|
{
|
||||||
|
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
uxOriginalBitValue = pxEventBits->uxEventBits;
|
||||||
|
|
||||||
|
( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet );
|
||||||
|
|
||||||
|
if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor )
|
||||||
|
{
|
||||||
|
/* All the rendezvous bits are now set - no need to block. */
|
||||||
|
uxReturn = ( uxOriginalBitValue | uxBitsToSet );
|
||||||
|
|
||||||
|
/* Rendezvous always clear the bits. They will have been cleared
|
||||||
|
already unless this is the only task in the rendezvous. */
|
||||||
|
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||||
|
|
||||||
|
xTicksToWait = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( xTicksToWait != ( TickType_t ) 0 )
|
||||||
|
{
|
||||||
|
traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor );
|
||||||
|
|
||||||
|
/* Store the bits that the calling task is waiting for in the
|
||||||
|
task's event list item so the kernel knows when a match is
|
||||||
|
found. Then enter the blocked state. */
|
||||||
|
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait );
|
||||||
|
|
||||||
|
/* This assignment is obsolete as uxReturn will get set after
|
||||||
|
the task unblocks, but some compilers mistakenly generate a
|
||||||
|
warning about uxReturn being returned without being set if the
|
||||||
|
assignment is omitted. */
|
||||||
|
uxReturn = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The rendezvous bits were not set, but no block time was
|
||||||
|
specified - just return the current event bit value. */
|
||||||
|
uxReturn = pxEventBits->uxEventBits;
|
||||||
|
xTimeoutOccurred = pdTRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xAlreadyYielded = xTaskResumeAll();
|
||||||
|
|
||||||
|
if( xTicksToWait != ( TickType_t ) 0 )
|
||||||
|
{
|
||||||
|
if( xAlreadyYielded == pdFALSE )
|
||||||
|
{
|
||||||
|
portYIELD_WITHIN_API();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The task blocked to wait for its required bits to be set - at this
|
||||||
|
point either the required bits were set or the block time expired. If
|
||||||
|
the required bits were set they will have been stored in the task's
|
||||||
|
event list item, and they should now be retrieved then cleared. */
|
||||||
|
uxReturn = uxTaskResetEventItemValue();
|
||||||
|
|
||||||
|
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
|
||||||
|
{
|
||||||
|
/* The task timed out, just return the current event bit value. */
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
uxReturn = pxEventBits->uxEventBits;
|
||||||
|
|
||||||
|
/* Although the task got here because it timed out before the
|
||||||
|
bits it was waiting for were set, it is possible that since it
|
||||||
|
unblocked another task has set the bits. If this is the case
|
||||||
|
then it needs to clear the bits before exiting. */
|
||||||
|
if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor )
|
||||||
|
{
|
||||||
|
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
|
||||||
|
xTimeoutOccurred = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The task unblocked because the bits were set. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Control bits might be set as the task had blocked should not be
|
||||||
|
returned. */
|
||||||
|
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred );
|
||||||
|
|
||||||
|
/* Prevent compiler warnings when trace macros are not used. */
|
||||||
|
( void ) xTimeoutOccurred;
|
||||||
|
|
||||||
|
return uxReturn;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )
|
||||||
|
{
|
||||||
|
EventGroup_t *pxEventBits = xEventGroup;
|
||||||
|
EventBits_t uxReturn, uxControlBits = 0;
|
||||||
|
BaseType_t xWaitConditionMet, xAlreadyYielded;
|
||||||
|
BaseType_t xTimeoutOccurred = pdFALSE;
|
||||||
|
|
||||||
|
/* Check the user is not attempting to wait on the bits used by the kernel
|
||||||
|
itself, and that at least one bit is being requested. */
|
||||||
|
configASSERT( xEventGroup );
|
||||||
|
configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||||
|
configASSERT( uxBitsToWaitFor != 0 );
|
||||||
|
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
|
||||||
|
{
|
||||||
|
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits;
|
||||||
|
|
||||||
|
/* Check to see if the wait condition is already met or not. */
|
||||||
|
xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits );
|
||||||
|
|
||||||
|
if( xWaitConditionMet != pdFALSE )
|
||||||
|
{
|
||||||
|
/* The wait condition has already been met so there is no need to
|
||||||
|
block. */
|
||||||
|
uxReturn = uxCurrentEventBits;
|
||||||
|
xTicksToWait = ( TickType_t ) 0;
|
||||||
|
|
||||||
|
/* Clear the wait bits if requested to do so. */
|
||||||
|
if( xClearOnExit != pdFALSE )
|
||||||
|
{
|
||||||
|
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( xTicksToWait == ( TickType_t ) 0 )
|
||||||
|
{
|
||||||
|
/* The wait condition has not been met, but no block time was
|
||||||
|
specified, so just return the current value. */
|
||||||
|
uxReturn = uxCurrentEventBits;
|
||||||
|
xTimeoutOccurred = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The task is going to block to wait for its required bits to be
|
||||||
|
set. uxControlBits are used to remember the specified behaviour of
|
||||||
|
this call to xEventGroupWaitBits() - for use when the event bits
|
||||||
|
unblock the task. */
|
||||||
|
if( xClearOnExit != pdFALSE )
|
||||||
|
{
|
||||||
|
uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
if( xWaitForAllBits != pdFALSE )
|
||||||
|
{
|
||||||
|
uxControlBits |= eventWAIT_FOR_ALL_BITS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the bits that the calling task is waiting for in the
|
||||||
|
task's event list item so the kernel knows when a match is
|
||||||
|
found. Then enter the blocked state. */
|
||||||
|
vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait );
|
||||||
|
|
||||||
|
/* This is obsolete as it will get set after the task unblocks, but
|
||||||
|
some compilers mistakenly generate a warning about the variable
|
||||||
|
being returned without being set if it is not done. */
|
||||||
|
uxReturn = 0;
|
||||||
|
|
||||||
|
traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xAlreadyYielded = xTaskResumeAll();
|
||||||
|
|
||||||
|
if( xTicksToWait != ( TickType_t ) 0 )
|
||||||
|
{
|
||||||
|
if( xAlreadyYielded == pdFALSE )
|
||||||
|
{
|
||||||
|
portYIELD_WITHIN_API();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The task blocked to wait for its required bits to be set - at this
|
||||||
|
point either the required bits were set or the block time expired. If
|
||||||
|
the required bits were set they will have been stored in the task's
|
||||||
|
event list item, and they should now be retrieved then cleared. */
|
||||||
|
uxReturn = uxTaskResetEventItemValue();
|
||||||
|
|
||||||
|
if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 )
|
||||||
|
{
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
/* The task timed out, just return the current event bit value. */
|
||||||
|
uxReturn = pxEventBits->uxEventBits;
|
||||||
|
|
||||||
|
/* It is possible that the event bits were updated between this
|
||||||
|
task leaving the Blocked state and running again. */
|
||||||
|
if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE )
|
||||||
|
{
|
||||||
|
if( xClearOnExit != pdFALSE )
|
||||||
|
{
|
||||||
|
pxEventBits->uxEventBits &= ~uxBitsToWaitFor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
xTimeoutOccurred = pdTRUE;
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* The task unblocked because the bits were set. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The task blocked so control bits may have been set. */
|
||||||
|
uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES;
|
||||||
|
}
|
||||||
|
traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred );
|
||||||
|
|
||||||
|
/* Prevent compiler warnings when trace macros are not used. */
|
||||||
|
( void ) xTimeoutOccurred;
|
||||||
|
|
||||||
|
return uxReturn;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
|
||||||
|
{
|
||||||
|
EventGroup_t *pxEventBits = xEventGroup;
|
||||||
|
EventBits_t uxReturn;
|
||||||
|
|
||||||
|
/* Check the user is not attempting to clear the bits used by the kernel
|
||||||
|
itself. */
|
||||||
|
configASSERT( xEventGroup );
|
||||||
|
configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||||
|
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear );
|
||||||
|
|
||||||
|
/* The value returned is the event group value prior to the bits being
|
||||||
|
cleared. */
|
||||||
|
uxReturn = pxEventBits->uxEventBits;
|
||||||
|
|
||||||
|
/* Clear the bits. */
|
||||||
|
pxEventBits->uxEventBits &= ~uxBitsToClear;
|
||||||
|
}
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
|
||||||
|
return uxReturn;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||||
|
|
||||||
|
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
|
||||||
|
{
|
||||||
|
BaseType_t xReturn;
|
||||||
|
|
||||||
|
traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear );
|
||||||
|
xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */
|
||||||
|
|
||||||
|
return xReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup )
|
||||||
|
{
|
||||||
|
UBaseType_t uxSavedInterruptStatus;
|
||||||
|
EventGroup_t const * const pxEventBits = xEventGroup;
|
||||||
|
EventBits_t uxReturn;
|
||||||
|
|
||||||
|
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||||
|
{
|
||||||
|
uxReturn = pxEventBits->uxEventBits;
|
||||||
|
}
|
||||||
|
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
|
||||||
|
|
||||||
|
return uxReturn;
|
||||||
|
} /*lint !e818 EventGroupHandle_t is a typedef used in other functions to so can't be pointer to const. */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )
|
||||||
|
{
|
||||||
|
ListItem_t *pxListItem, *pxNext;
|
||||||
|
ListItem_t const *pxListEnd;
|
||||||
|
List_t const * pxList;
|
||||||
|
EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits;
|
||||||
|
EventGroup_t *pxEventBits = xEventGroup;
|
||||||
|
BaseType_t xMatchFound = pdFALSE;
|
||||||
|
|
||||||
|
/* Check the user is not attempting to set the bits used by the kernel
|
||||||
|
itself. */
|
||||||
|
configASSERT( xEventGroup );
|
||||||
|
configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 );
|
||||||
|
|
||||||
|
pxList = &( pxEventBits->xTasksWaitingForBits );
|
||||||
|
pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet );
|
||||||
|
|
||||||
|
pxListItem = listGET_HEAD_ENTRY( pxList );
|
||||||
|
|
||||||
|
/* Set the bits. */
|
||||||
|
pxEventBits->uxEventBits |= uxBitsToSet;
|
||||||
|
|
||||||
|
/* See if the new bit value should unblock any tasks. */
|
||||||
|
while( pxListItem != pxListEnd )
|
||||||
|
{
|
||||||
|
pxNext = listGET_NEXT( pxListItem );
|
||||||
|
uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem );
|
||||||
|
xMatchFound = pdFALSE;
|
||||||
|
|
||||||
|
/* Split the bits waited for from the control bits. */
|
||||||
|
uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES;
|
||||||
|
uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES;
|
||||||
|
|
||||||
|
if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 )
|
||||||
|
{
|
||||||
|
/* Just looking for single bit being set. */
|
||||||
|
if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 )
|
||||||
|
{
|
||||||
|
xMatchFound = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor )
|
||||||
|
{
|
||||||
|
/* All bits are set. */
|
||||||
|
xMatchFound = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Need all bits to be set, but not all the bits were set. */
|
||||||
|
}
|
||||||
|
|
||||||
|
if( xMatchFound != pdFALSE )
|
||||||
|
{
|
||||||
|
/* The bits match. Should the bits be cleared on exit? */
|
||||||
|
if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 )
|
||||||
|
{
|
||||||
|
uxBitsToClear |= uxBitsWaitedFor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the actual event flag value in the task's event list
|
||||||
|
item before removing the task from the event list. The
|
||||||
|
eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows
|
||||||
|
that is was unblocked due to its required bits matching, rather
|
||||||
|
than because it timed out. */
|
||||||
|
vTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move onto the next list item. Note pxListItem->pxNext is not
|
||||||
|
used here as the list item may have been removed from the event list
|
||||||
|
and inserted into the ready/pending reading list. */
|
||||||
|
pxListItem = pxNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT
|
||||||
|
bit was set in the control word. */
|
||||||
|
pxEventBits->uxEventBits &= ~uxBitsToClear;
|
||||||
|
}
|
||||||
|
( void ) xTaskResumeAll();
|
||||||
|
|
||||||
|
return pxEventBits->uxEventBits;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
void vEventGroupDelete( EventGroupHandle_t xEventGroup )
|
||||||
|
{
|
||||||
|
EventGroup_t *pxEventBits = xEventGroup;
|
||||||
|
const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits );
|
||||||
|
|
||||||
|
vTaskSuspendAll();
|
||||||
|
{
|
||||||
|
traceEVENT_GROUP_DELETE( xEventGroup );
|
||||||
|
|
||||||
|
while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 )
|
||||||
|
{
|
||||||
|
/* Unblock the task, returning 0 as the event list is being deleted
|
||||||
|
and cannot therefore have any bits set. */
|
||||||
|
configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( const ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) );
|
||||||
|
vTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET );
|
||||||
|
}
|
||||||
|
|
||||||
|
#if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) )
|
||||||
|
{
|
||||||
|
/* The event group can only have been allocated dynamically - free
|
||||||
|
it again. */
|
||||||
|
vPortFree( pxEventBits );
|
||||||
|
}
|
||||||
|
#elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) )
|
||||||
|
{
|
||||||
|
/* The event group could have been allocated statically or
|
||||||
|
dynamically, so check before attempting to free the memory. */
|
||||||
|
if( pxEventBits->ucStaticallyAllocated == ( uint8_t ) pdFALSE )
|
||||||
|
{
|
||||||
|
vPortFree( pxEventBits );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
|
||||||
|
}
|
||||||
|
( void ) xTaskResumeAll();
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* For internal use only - execute a 'set bits' command that was pended from
|
||||||
|
an interrupt. */
|
||||||
|
void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet )
|
||||||
|
{
|
||||||
|
( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* For internal use only - execute a 'clear bits' command that was pended from
|
||||||
|
an interrupt. */
|
||||||
|
void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear )
|
||||||
|
{
|
||||||
|
( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); /*lint !e9079 Can't avoid cast to void* as a generic timer callback prototype. Callback casts back to original type so safe. */
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits )
|
||||||
|
{
|
||||||
|
BaseType_t xWaitConditionMet = pdFALSE;
|
||||||
|
|
||||||
|
if( xWaitForAllBits == pdFALSE )
|
||||||
|
{
|
||||||
|
/* Task only has to wait for one bit within uxBitsToWaitFor to be
|
||||||
|
set. Is one already set? */
|
||||||
|
if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 )
|
||||||
|
{
|
||||||
|
xWaitConditionMet = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Task has to wait for all the bits in uxBitsToWaitFor to be set.
|
||||||
|
Are they set already? */
|
||||||
|
if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor )
|
||||||
|
{
|
||||||
|
xWaitConditionMet = pdTRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mtCOVERAGE_TEST_MARKER();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return xWaitConditionMet;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) )
|
||||||
|
|
||||||
|
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )
|
||||||
|
{
|
||||||
|
BaseType_t xReturn;
|
||||||
|
|
||||||
|
traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet );
|
||||||
|
xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); /*lint !e9087 Can't avoid cast to void* as a generic callback function not specific to this use case. Callback casts back to original type so safe. */
|
||||||
|
|
||||||
|
return xReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if (configUSE_TRACE_FACILITY == 1)
|
||||||
|
|
||||||
|
UBaseType_t uxEventGroupGetNumber( void* xEventGroup )
|
||||||
|
{
|
||||||
|
UBaseType_t xReturn;
|
||||||
|
EventGroup_t const *pxEventBits = ( EventGroup_t * ) xEventGroup; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */
|
||||||
|
|
||||||
|
if( xEventGroup == NULL )
|
||||||
|
{
|
||||||
|
xReturn = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xReturn = pxEventBits->uxEventGroupNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configUSE_TRACE_FACILITY */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if ( configUSE_TRACE_FACILITY == 1 )
|
||||||
|
|
||||||
|
void vEventGroupSetNumber( void * xEventGroup, UBaseType_t uxEventGroupNumber )
|
||||||
|
{
|
||||||
|
( ( EventGroup_t * ) xEventGroup )->uxEventGroupNumber = uxEventGroupNumber; /*lint !e9087 !e9079 EventGroupHandle_t is a pointer to an EventGroup_t, but EventGroupHandle_t is kept opaque outside of this file for data hiding purposes. */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configUSE_TRACE_FACILITY */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
1295
Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h
vendored
Normal file
1295
Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
133
Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h
vendored
Normal file
133
Middlewares/Third_Party/FreeRTOS/Source/include/StackMacros.h
vendored
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STACK_MACROS_H
|
||||||
|
#define STACK_MACROS_H
|
||||||
|
|
||||||
|
#ifndef _MSC_VER /* Visual Studio doesn't support #warning. */
|
||||||
|
#warning The name of this file has changed to stack_macros.h. Please update your code accordingly. This source file (which has the original name) will be removed in future released.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the stack overflow hook function if the stack of the task being swapped
|
||||||
|
* out is currently overflowed, or looks like it might have overflowed in the
|
||||||
|
* past.
|
||||||
|
*
|
||||||
|
* Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check
|
||||||
|
* the current stack state only - comparing the current top of stack value to
|
||||||
|
* the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1
|
||||||
|
* will also cause the last few stack bytes to be checked to ensure the value
|
||||||
|
* to which the bytes were set when the task was created have not been
|
||||||
|
* overwritten. Note this second test does not guarantee that an overflowed
|
||||||
|
* stack will always be recognised.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) )
|
||||||
|
|
||||||
|
/* Only the current stack state is to be checked. */
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
{ \
|
||||||
|
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||||
|
if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \
|
||||||
|
{ \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) )
|
||||||
|
|
||||||
|
/* Only the current stack state is to be checked. */
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
{ \
|
||||||
|
\
|
||||||
|
/* Is the currently saved stack pointer within the stack limit? */ \
|
||||||
|
if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \
|
||||||
|
{ \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) )
|
||||||
|
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
{ \
|
||||||
|
const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \
|
||||||
|
const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \
|
||||||
|
\
|
||||||
|
if( ( pulStack[ 0 ] != ulCheckValue ) || \
|
||||||
|
( pulStack[ 1 ] != ulCheckValue ) || \
|
||||||
|
( pulStack[ 2 ] != ulCheckValue ) || \
|
||||||
|
( pulStack[ 3 ] != ulCheckValue ) ) \
|
||||||
|
{ \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) )
|
||||||
|
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW() \
|
||||||
|
{ \
|
||||||
|
int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \
|
||||||
|
static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \
|
||||||
|
tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
pcEndOfStack -= sizeof( ucExpectedStackBytes ); \
|
||||||
|
\
|
||||||
|
/* Has the extremity of the task stack ever been written over? */ \
|
||||||
|
if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \
|
||||||
|
{ \
|
||||||
|
vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Remove stack overflow macro if not being used. */
|
||||||
|
#ifndef taskCHECK_FOR_STACK_OVERFLOW
|
||||||
|
#define taskCHECK_FOR_STACK_OVERFLOW()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* STACK_MACROS_H */
|
||||||
|
|
||||||
414
Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h
vendored
Normal file
414
Middlewares/Third_Party/FreeRTOS/Source/include/atomic.h
vendored
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file atomic.h
|
||||||
|
* @brief FreeRTOS atomic operation support.
|
||||||
|
*
|
||||||
|
* This file implements atomic functions by disabling interrupts globally.
|
||||||
|
* Implementations with architecture specific atomic instructions can be
|
||||||
|
* provided under each compiler directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ATOMIC_H
|
||||||
|
#define ATOMIC_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h must appear in source files before include atomic.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Standard includes. */
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Port specific definitions -- entering/exiting critical section.
|
||||||
|
* Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
|
||||||
|
*
|
||||||
|
* Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
|
||||||
|
* ATOMIC_ENTER_CRITICAL().
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if defined( portSET_INTERRUPT_MASK_FROM_ISR )
|
||||||
|
|
||||||
|
/* Nested interrupt scheme is supported in this port. */
|
||||||
|
#define ATOMIC_ENTER_CRITICAL() \
|
||||||
|
UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
|
||||||
|
|
||||||
|
#define ATOMIC_EXIT_CRITICAL() \
|
||||||
|
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Nested interrupt scheme is NOT supported in this port. */
|
||||||
|
#define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL()
|
||||||
|
#define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL()
|
||||||
|
|
||||||
|
#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Port specific definition -- "always inline".
|
||||||
|
* Inline is compiler specific, and may not always get inlined depending on your
|
||||||
|
* optimization level. Also, inline is considered as performance optimization
|
||||||
|
* for atomic. Thus, if portFORCE_INLINE is not provided by portmacro.h,
|
||||||
|
* instead of resulting error, simply define it away.
|
||||||
|
*/
|
||||||
|
#ifndef portFORCE_INLINE
|
||||||
|
#define portFORCE_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */
|
||||||
|
#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */
|
||||||
|
|
||||||
|
/*----------------------------- Swap && CAS ------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic compare-and-swap
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic compare-and-swap operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param[in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and checked.
|
||||||
|
* @param[in] ulExchange If condition meets, write this value to memory.
|
||||||
|
* @param[in] ulComparand Swap condition.
|
||||||
|
*
|
||||||
|
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
|
||||||
|
*
|
||||||
|
* @note This function only swaps *pulDestination with ulExchange, if previous
|
||||||
|
* *pulDestination value equals ulComparand.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulExchange,
|
||||||
|
uint32_t ulComparand )
|
||||||
|
{
|
||||||
|
uint32_t ulReturnValue;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
if( *pulDestination == ulComparand )
|
||||||
|
{
|
||||||
|
*pulDestination = ulExchange;
|
||||||
|
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulReturnValue;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic swap (pointers)
|
||||||
|
*
|
||||||
|
* @brief Atomically sets the address pointed to by *ppvDestination to the value
|
||||||
|
* of *pvExchange.
|
||||||
|
*
|
||||||
|
* @param[in, out] ppvDestination Pointer to memory location from where a pointer
|
||||||
|
* value is to be loaded and written back to.
|
||||||
|
* @param[in] pvExchange Pointer value to be written to *ppvDestination.
|
||||||
|
*
|
||||||
|
* @return The initial value of *ppvDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE void * Atomic_SwapPointers_p32( void * volatile * ppvDestination,
|
||||||
|
void * pvExchange )
|
||||||
|
{
|
||||||
|
void * pReturnValue;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
pReturnValue = *ppvDestination;
|
||||||
|
*ppvDestination = pvExchange;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return pReturnValue;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic compare-and-swap (pointers)
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic compare-and-swap operation on the specified pointer
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @param[in, out] ppvDestination Pointer to memory location from where a pointer
|
||||||
|
* value is to be loaded and checked.
|
||||||
|
* @param[in] pvExchange If condition meets, write this value to memory.
|
||||||
|
* @param[in] pvComparand Swap condition.
|
||||||
|
*
|
||||||
|
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
|
||||||
|
*
|
||||||
|
* @note This function only swaps *ppvDestination with pvExchange, if previous
|
||||||
|
* *ppvDestination value equals pvComparand.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32( void * volatile * ppvDestination,
|
||||||
|
void * pvExchange,
|
||||||
|
void * pvComparand )
|
||||||
|
{
|
||||||
|
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
if( *ppvDestination == pvComparand )
|
||||||
|
{
|
||||||
|
*ppvDestination = pvExchange;
|
||||||
|
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*----------------------------- Arithmetic ------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic add
|
||||||
|
*
|
||||||
|
* @brief Atomically adds count to the value of the specified pointer points to.
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
* @param[in] ulCount Value to be added to *pulAddend.
|
||||||
|
*
|
||||||
|
* @return previous *pulAddend value.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Add_u32( uint32_t volatile * pulAddend,
|
||||||
|
uint32_t ulCount )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend += ulCount;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic subtract
|
||||||
|
*
|
||||||
|
* @brief Atomically subtracts count from the value of the specified pointer
|
||||||
|
* pointers to.
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
* @param[in] ulCount Value to be subtract from *pulAddend.
|
||||||
|
*
|
||||||
|
* @return previous *pulAddend value.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Subtract_u32( uint32_t volatile * pulAddend,
|
||||||
|
uint32_t ulCount )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend -= ulCount;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic increment
|
||||||
|
*
|
||||||
|
* @brief Atomically increments the value of the specified pointer points to.
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
*
|
||||||
|
* @return *pulAddend value before increment.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pulAddend )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend += 1;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic decrement
|
||||||
|
*
|
||||||
|
* @brief Atomically decrements the value of the specified pointer points to
|
||||||
|
*
|
||||||
|
* @param[in,out] pulAddend Pointer to memory location from where value is to be
|
||||||
|
* loaded and written back to.
|
||||||
|
*
|
||||||
|
* @return *pulAddend value before decrement.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pulAddend )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulAddend;
|
||||||
|
*pulAddend -= 1;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------- Bitwise Logical ------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic OR
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic OR operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be ORed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_OR_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination |= ulValue;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic AND
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic AND operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be ANDed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_AND_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination &= ulValue;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic NAND
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic NAND operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be NANDed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_NAND_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination = ~( ulCurrent & ulValue );
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atomic XOR
|
||||||
|
*
|
||||||
|
* @brief Performs an atomic XOR operation on the specified values.
|
||||||
|
*
|
||||||
|
* @param [in, out] pulDestination Pointer to memory location from where value is
|
||||||
|
* to be loaded and written back to.
|
||||||
|
* @param [in] ulValue Value to be XORed with *pulDestination.
|
||||||
|
*
|
||||||
|
* @return The original value of *pulDestination.
|
||||||
|
*/
|
||||||
|
static portFORCE_INLINE uint32_t Atomic_XOR_u32( uint32_t volatile * pulDestination,
|
||||||
|
uint32_t ulValue )
|
||||||
|
{
|
||||||
|
uint32_t ulCurrent;
|
||||||
|
|
||||||
|
ATOMIC_ENTER_CRITICAL();
|
||||||
|
{
|
||||||
|
ulCurrent = *pulDestination;
|
||||||
|
*pulDestination ^= ulValue;
|
||||||
|
}
|
||||||
|
ATOMIC_EXIT_CRITICAL();
|
||||||
|
|
||||||
|
return ulCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ATOMIC_H */
|
||||||
720
Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h
vendored
Normal file
720
Middlewares/Third_Party/FreeRTOS/Source/include/croutine.h
vendored
Normal file
@@ -0,0 +1,720 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CO_ROUTINE_H
|
||||||
|
#define CO_ROUTINE_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h must appear in source files before include croutine.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Used to hide the implementation of the co-routine control block. The
|
||||||
|
control block structure however has to be included in the header due to
|
||||||
|
the macro implementation of the co-routine functionality. */
|
||||||
|
typedef void * CoRoutineHandle_t;
|
||||||
|
|
||||||
|
/* Defines the prototype to which co-routine functions must conform. */
|
||||||
|
typedef void (*crCOROUTINE_CODE)( CoRoutineHandle_t, UBaseType_t );
|
||||||
|
|
||||||
|
typedef struct corCoRoutineControlBlock
|
||||||
|
{
|
||||||
|
crCOROUTINE_CODE pxCoRoutineFunction;
|
||||||
|
ListItem_t xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */
|
||||||
|
ListItem_t xEventListItem; /*< List item used to place the CRCB in event lists. */
|
||||||
|
UBaseType_t uxPriority; /*< The priority of the co-routine in relation to other co-routines. */
|
||||||
|
UBaseType_t uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */
|
||||||
|
uint16_t uxState; /*< Used internally by the co-routine implementation. */
|
||||||
|
} CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
*<pre>
|
||||||
|
BaseType_t xCoRoutineCreate(
|
||||||
|
crCOROUTINE_CODE pxCoRoutineCode,
|
||||||
|
UBaseType_t uxPriority,
|
||||||
|
UBaseType_t uxIndex
|
||||||
|
);</pre>
|
||||||
|
*
|
||||||
|
* Create a new co-routine and add it to the list of co-routines that are
|
||||||
|
* ready to run.
|
||||||
|
*
|
||||||
|
* @param pxCoRoutineCode Pointer to the co-routine function. Co-routine
|
||||||
|
* functions require special syntax - see the co-routine section of the WEB
|
||||||
|
* documentation for more information.
|
||||||
|
*
|
||||||
|
* @param uxPriority The priority with respect to other co-routines at which
|
||||||
|
* the co-routine will run.
|
||||||
|
*
|
||||||
|
* @param uxIndex Used to distinguish between different co-routines that
|
||||||
|
* execute the same function. See the example below and the co-routine section
|
||||||
|
* of the WEB documentation for further information.
|
||||||
|
*
|
||||||
|
* @return pdPASS if the co-routine was successfully created and added to a ready
|
||||||
|
* list, otherwise an error code defined with ProjDefs.h.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Co-routine to be created.
|
||||||
|
void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
// This may not be necessary for const variables.
|
||||||
|
static const char cLedToFlash[ 2 ] = { 5, 6 };
|
||||||
|
static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
|
||||||
|
|
||||||
|
// Must start every co-routine with a call to crSTART();
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// This co-routine just delays for a fixed period, then toggles
|
||||||
|
// an LED. Two co-routines are created using this function, so
|
||||||
|
// the uxIndex parameter is used to tell the co-routine which
|
||||||
|
// LED to flash and how int32_t to delay. This assumes xQueue has
|
||||||
|
// already been created.
|
||||||
|
vParTestToggleLED( cLedToFlash[ uxIndex ] );
|
||||||
|
crDELAY( xHandle, uxFlashRates[ uxIndex ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must end every co-routine with a call to crEND();
|
||||||
|
crEND();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function that creates two co-routines.
|
||||||
|
void vOtherFunction( void )
|
||||||
|
{
|
||||||
|
uint8_t ucParameterToPass;
|
||||||
|
TaskHandle_t xHandle;
|
||||||
|
|
||||||
|
// Create two co-routines at priority 0. The first is given index 0
|
||||||
|
// so (from the code above) toggles LED 5 every 200 ticks. The second
|
||||||
|
// is given index 1 so toggles LED 6 every 400 ticks.
|
||||||
|
for( uxIndex = 0; uxIndex < 2; uxIndex++ )
|
||||||
|
{
|
||||||
|
xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xCoRoutineCreate xCoRoutineCreate
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
*<pre>
|
||||||
|
void vCoRoutineSchedule( void );</pre>
|
||||||
|
*
|
||||||
|
* Run a co-routine.
|
||||||
|
*
|
||||||
|
* vCoRoutineSchedule() executes the highest priority co-routine that is able
|
||||||
|
* to run. The co-routine will execute until it either blocks, yields or is
|
||||||
|
* preempted by a task. Co-routines execute cooperatively so one
|
||||||
|
* co-routine cannot be preempted by another, but can be preempted by a task.
|
||||||
|
*
|
||||||
|
* If an application comprises of both tasks and co-routines then
|
||||||
|
* vCoRoutineSchedule should be called from the idle task (in an idle task
|
||||||
|
* hook).
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// This idle task hook will schedule a co-routine each time it is called.
|
||||||
|
// The rest of the idle task will execute between co-routine calls.
|
||||||
|
void vApplicationIdleHook( void )
|
||||||
|
{
|
||||||
|
vCoRoutineSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alternatively, if you do not require any other part of the idle task to
|
||||||
|
// execute, the idle task hook can call vCoRoutineSchedule() within an
|
||||||
|
// infinite loop.
|
||||||
|
void vApplicationIdleHook( void )
|
||||||
|
{
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
vCoRoutineSchedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup vCoRoutineSchedule vCoRoutineSchedule
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
void vCoRoutineSchedule( void );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* <pre>
|
||||||
|
crSTART( CoRoutineHandle_t xHandle );</pre>
|
||||||
|
*
|
||||||
|
* This macro MUST always be called at the start of a co-routine function.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Co-routine to be created.
|
||||||
|
void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
static int32_t ulAVariable;
|
||||||
|
|
||||||
|
// Must start every co-routine with a call to crSTART();
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Co-routine functionality goes here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must end every co-routine with a call to crEND();
|
||||||
|
crEND();
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crSTART crSTART
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crSTART( pxCRCB ) switch( ( ( CRCB_t * )( pxCRCB ) )->uxState ) { case 0:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* <pre>
|
||||||
|
crEND();</pre>
|
||||||
|
*
|
||||||
|
* This macro MUST always be called at the end of a co-routine function.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Co-routine to be created.
|
||||||
|
void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
static int32_t ulAVariable;
|
||||||
|
|
||||||
|
// Must start every co-routine with a call to crSTART();
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Co-routine functionality goes here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must end every co-routine with a call to crEND();
|
||||||
|
crEND();
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crSTART crSTART
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crEND() }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These macros are intended for internal use by the co-routine implementation
|
||||||
|
* only. The macros should not be used directly by application writers.
|
||||||
|
*/
|
||||||
|
#define crSET_STATE0( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2):
|
||||||
|
#define crSET_STATE1( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1):
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
*<pre>
|
||||||
|
crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );</pre>
|
||||||
|
*
|
||||||
|
* Delay a co-routine for a fixed period of time.
|
||||||
|
*
|
||||||
|
* crDELAY can only be called from the co-routine function itself - not
|
||||||
|
* from within a function called by the co-routine function. This is because
|
||||||
|
* co-routines do not maintain their own stack.
|
||||||
|
*
|
||||||
|
* @param xHandle The handle of the co-routine to delay. This is the xHandle
|
||||||
|
* parameter of the co-routine function.
|
||||||
|
*
|
||||||
|
* @param xTickToDelay The number of ticks that the co-routine should delay
|
||||||
|
* for. The actual amount of time this equates to is defined by
|
||||||
|
* configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_PERIOD_MS
|
||||||
|
* can be used to convert ticks to milliseconds.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Co-routine to be created.
|
||||||
|
void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
// This may not be necessary for const variables.
|
||||||
|
// We are to delay for 200ms.
|
||||||
|
static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
|
||||||
|
|
||||||
|
// Must start every co-routine with a call to crSTART();
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Delay for 200ms.
|
||||||
|
crDELAY( xHandle, xDelayTime );
|
||||||
|
|
||||||
|
// Do something here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must end every co-routine with a call to crEND();
|
||||||
|
crEND();
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crDELAY crDELAY
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crDELAY( xHandle, xTicksToDelay ) \
|
||||||
|
if( ( xTicksToDelay ) > 0 ) \
|
||||||
|
{ \
|
||||||
|
vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \
|
||||||
|
} \
|
||||||
|
crSET_STATE0( ( xHandle ) );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
crQUEUE_SEND(
|
||||||
|
CoRoutineHandle_t xHandle,
|
||||||
|
QueueHandle_t pxQueue,
|
||||||
|
void *pvItemToQueue,
|
||||||
|
TickType_t xTicksToWait,
|
||||||
|
BaseType_t *pxResult
|
||||||
|
)</pre>
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
|
||||||
|
* equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
|
||||||
|
* xQueueSend() and xQueueReceive() can only be used from tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND can only be called from the co-routine function itself - not
|
||||||
|
* from within a function called by the co-routine function. This is because
|
||||||
|
* co-routines do not maintain their own stack.
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xHandle The handle of the calling co-routine. This is the xHandle
|
||||||
|
* parameter of the co-routine function.
|
||||||
|
*
|
||||||
|
* @param pxQueue The handle of the queue on which the data will be posted.
|
||||||
|
* The handle is obtained as the return value when the queue is created using
|
||||||
|
* the xQueueCreate() API function.
|
||||||
|
*
|
||||||
|
* @param pvItemToQueue A pointer to the data being posted onto the queue.
|
||||||
|
* The number of bytes of each queued item is specified when the queue is
|
||||||
|
* created. This number of bytes is copied from pvItemToQueue into the queue
|
||||||
|
* itself.
|
||||||
|
*
|
||||||
|
* @param xTickToDelay The number of ticks that the co-routine should block
|
||||||
|
* to wait for space to become available on the queue, should space not be
|
||||||
|
* available immediately. The actual amount of time this equates to is defined
|
||||||
|
* by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
|
||||||
|
* portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see example
|
||||||
|
* below).
|
||||||
|
*
|
||||||
|
* @param pxResult The variable pointed to by pxResult will be set to pdPASS if
|
||||||
|
* data was successfully posted onto the queue, otherwise it will be set to an
|
||||||
|
* error defined within ProjDefs.h.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Co-routine function that blocks for a fixed period then posts a number onto
|
||||||
|
// a queue.
|
||||||
|
static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
static BaseType_t xNumberToPost = 0;
|
||||||
|
static BaseType_t xResult;
|
||||||
|
|
||||||
|
// Co-routines must begin with a call to crSTART().
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// This assumes the queue has already been created.
|
||||||
|
crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
|
||||||
|
|
||||||
|
if( xResult != pdPASS )
|
||||||
|
{
|
||||||
|
// The message was not posted!
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the number to be posted onto the queue.
|
||||||
|
xNumberToPost++;
|
||||||
|
|
||||||
|
// Delay for 100 ticks.
|
||||||
|
crDELAY( xHandle, 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Co-routines must end with a call to crEND().
|
||||||
|
crEND();
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crQUEUE_SEND crQUEUE_SEND
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \
|
||||||
|
{ \
|
||||||
|
*( pxResult ) = xQueueCRSend( ( pxQueue) , ( pvItemToQueue) , ( xTicksToWait ) ); \
|
||||||
|
if( *( pxResult ) == errQUEUE_BLOCKED ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE0( ( xHandle ) ); \
|
||||||
|
*pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \
|
||||||
|
} \
|
||||||
|
if( *pxResult == errQUEUE_YIELD ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE1( ( xHandle ) ); \
|
||||||
|
*pxResult = pdPASS; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* <pre>
|
||||||
|
crQUEUE_RECEIVE(
|
||||||
|
CoRoutineHandle_t xHandle,
|
||||||
|
QueueHandle_t pxQueue,
|
||||||
|
void *pvBuffer,
|
||||||
|
TickType_t xTicksToWait,
|
||||||
|
BaseType_t *pxResult
|
||||||
|
)</pre>
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine
|
||||||
|
* equivalent to the xQueueSend() and xQueueReceive() functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas
|
||||||
|
* xQueueSend() and xQueueReceive() can only be used from tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_RECEIVE can only be called from the co-routine function itself - not
|
||||||
|
* from within a function called by the co-routine function. This is because
|
||||||
|
* co-routines do not maintain their own stack.
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xHandle The handle of the calling co-routine. This is the xHandle
|
||||||
|
* parameter of the co-routine function.
|
||||||
|
*
|
||||||
|
* @param pxQueue The handle of the queue from which the data will be received.
|
||||||
|
* The handle is obtained as the return value when the queue is created using
|
||||||
|
* the xQueueCreate() API function.
|
||||||
|
*
|
||||||
|
* @param pvBuffer The buffer into which the received item is to be copied.
|
||||||
|
* The number of bytes of each queued item is specified when the queue is
|
||||||
|
* created. This number of bytes is copied into pvBuffer.
|
||||||
|
*
|
||||||
|
* @param xTickToDelay The number of ticks that the co-routine should block
|
||||||
|
* to wait for data to become available from the queue, should data not be
|
||||||
|
* available immediately. The actual amount of time this equates to is defined
|
||||||
|
* by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant
|
||||||
|
* portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see the
|
||||||
|
* crQUEUE_SEND example).
|
||||||
|
*
|
||||||
|
* @param pxResult The variable pointed to by pxResult will be set to pdPASS if
|
||||||
|
* data was successfully retrieved from the queue, otherwise it will be set to
|
||||||
|
* an error code as defined within ProjDefs.h.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// A co-routine receives the number of an LED to flash from a queue. It
|
||||||
|
// blocks on the queue until the number is received.
|
||||||
|
static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// Variables in co-routines must be declared static if they must maintain value across a blocking call.
|
||||||
|
static BaseType_t xResult;
|
||||||
|
static UBaseType_t uxLEDToFlash;
|
||||||
|
|
||||||
|
// All co-routines must start with a call to crSTART().
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Wait for data to become available on the queue.
|
||||||
|
crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
|
||||||
|
|
||||||
|
if( xResult == pdPASS )
|
||||||
|
{
|
||||||
|
// We received the LED to flash - flash it!
|
||||||
|
vParTestToggleLED( uxLEDToFlash );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
crEND();
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \
|
||||||
|
{ \
|
||||||
|
*( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), ( xTicksToWait ) ); \
|
||||||
|
if( *( pxResult ) == errQUEUE_BLOCKED ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE0( ( xHandle ) ); \
|
||||||
|
*( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), 0 ); \
|
||||||
|
} \
|
||||||
|
if( *( pxResult ) == errQUEUE_YIELD ) \
|
||||||
|
{ \
|
||||||
|
crSET_STATE1( ( xHandle ) ); \
|
||||||
|
*( pxResult ) = pdPASS; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* <pre>
|
||||||
|
crQUEUE_SEND_FROM_ISR(
|
||||||
|
QueueHandle_t pxQueue,
|
||||||
|
void *pvItemToQueue,
|
||||||
|
BaseType_t xCoRoutinePreviouslyWoken
|
||||||
|
)</pre>
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
|
||||||
|
* co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
|
||||||
|
* functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
|
||||||
|
* pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
|
||||||
|
* xQueueReceiveFromISR() can only be used to pass data between a task and and
|
||||||
|
* ISR.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue
|
||||||
|
* that is being used from within a co-routine.
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xQueue The handle to the queue on which the item is to be posted.
|
||||||
|
*
|
||||||
|
* @param pvItemToQueue A pointer to the item that is to be placed on the
|
||||||
|
* queue. The size of the items the queue will hold was defined when the
|
||||||
|
* queue was created, so this many bytes will be copied from pvItemToQueue
|
||||||
|
* into the queue storage area.
|
||||||
|
*
|
||||||
|
* @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto
|
||||||
|
* the same queue multiple times from a single interrupt. The first call
|
||||||
|
* should always pass in pdFALSE. Subsequent calls should pass in
|
||||||
|
* the value returned from the previous call.
|
||||||
|
*
|
||||||
|
* @return pdTRUE if a co-routine was woken by posting onto the queue. This is
|
||||||
|
* used by the ISR to determine if a context switch may be required following
|
||||||
|
* the ISR.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// A co-routine that blocks on a queue waiting for characters to be received.
|
||||||
|
static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
char cRxedChar;
|
||||||
|
BaseType_t xResult;
|
||||||
|
|
||||||
|
// All co-routines must start with a call to crSTART().
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Wait for data to become available on the queue. This assumes the
|
||||||
|
// queue xCommsRxQueue has already been created!
|
||||||
|
crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
|
||||||
|
|
||||||
|
// Was a character received?
|
||||||
|
if( xResult == pdPASS )
|
||||||
|
{
|
||||||
|
// Process the character here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All co-routines must end with a call to crEND().
|
||||||
|
crEND();
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ISR that uses a queue to send characters received on a serial port to
|
||||||
|
// a co-routine.
|
||||||
|
void vUART_ISR( void )
|
||||||
|
{
|
||||||
|
char cRxedChar;
|
||||||
|
BaseType_t xCRWokenByPost = pdFALSE;
|
||||||
|
|
||||||
|
// We loop around reading characters until there are none left in the UART.
|
||||||
|
while( UART_RX_REG_NOT_EMPTY() )
|
||||||
|
{
|
||||||
|
// Obtain the character from the UART.
|
||||||
|
cRxedChar = UART_RX_REG;
|
||||||
|
|
||||||
|
// Post the character onto a queue. xCRWokenByPost will be pdFALSE
|
||||||
|
// the first time around the loop. If the post causes a co-routine
|
||||||
|
// to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
|
||||||
|
// In this manner we can ensure that if more than one co-routine is
|
||||||
|
// blocked on the queue only one is woken by this ISR no matter how
|
||||||
|
// many characters are posted to the queue.
|
||||||
|
xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* croutine. h
|
||||||
|
* <pre>
|
||||||
|
crQUEUE_SEND_FROM_ISR(
|
||||||
|
QueueHandle_t pxQueue,
|
||||||
|
void *pvBuffer,
|
||||||
|
BaseType_t * pxCoRoutineWoken
|
||||||
|
)</pre>
|
||||||
|
*
|
||||||
|
* The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the
|
||||||
|
* co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR()
|
||||||
|
* functions used by tasks.
|
||||||
|
*
|
||||||
|
* crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to
|
||||||
|
* pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and
|
||||||
|
* xQueueReceiveFromISR() can only be used to pass data between a task and and
|
||||||
|
* ISR.
|
||||||
|
*
|
||||||
|
* crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data
|
||||||
|
* from a queue that is being used from within a co-routine (a co-routine
|
||||||
|
* posted to the queue).
|
||||||
|
*
|
||||||
|
* See the co-routine section of the WEB documentation for information on
|
||||||
|
* passing data between tasks and co-routines and between ISR's and
|
||||||
|
* co-routines.
|
||||||
|
*
|
||||||
|
* @param xQueue The handle to the queue on which the item is to be posted.
|
||||||
|
*
|
||||||
|
* @param pvBuffer A pointer to a buffer into which the received item will be
|
||||||
|
* placed. The size of the items the queue will hold was defined when the
|
||||||
|
* queue was created, so this many bytes will be copied from the queue into
|
||||||
|
* pvBuffer.
|
||||||
|
*
|
||||||
|
* @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become
|
||||||
|
* available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a
|
||||||
|
* co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise
|
||||||
|
* *pxCoRoutineWoken will remain unchanged.
|
||||||
|
*
|
||||||
|
* @return pdTRUE an item was successfully received from the queue, otherwise
|
||||||
|
* pdFALSE.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// A co-routine that posts a character to a queue then blocks for a fixed
|
||||||
|
// period. The character is incremented each time.
|
||||||
|
static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
|
||||||
|
{
|
||||||
|
// cChar holds its value while this co-routine is blocked and must therefore
|
||||||
|
// be declared static.
|
||||||
|
static char cCharToTx = 'a';
|
||||||
|
BaseType_t xResult;
|
||||||
|
|
||||||
|
// All co-routines must start with a call to crSTART().
|
||||||
|
crSTART( xHandle );
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Send the next character to the queue.
|
||||||
|
crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
|
||||||
|
|
||||||
|
if( xResult == pdPASS )
|
||||||
|
{
|
||||||
|
// The character was successfully posted to the queue.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Could not post the character to the queue.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the UART Tx interrupt to cause an interrupt in this
|
||||||
|
// hypothetical UART. The interrupt will obtain the character
|
||||||
|
// from the queue and send it.
|
||||||
|
ENABLE_RX_INTERRUPT();
|
||||||
|
|
||||||
|
// Increment to the next character then block for a fixed period.
|
||||||
|
// cCharToTx will maintain its value across the delay as it is
|
||||||
|
// declared static.
|
||||||
|
cCharToTx++;
|
||||||
|
if( cCharToTx > 'x' )
|
||||||
|
{
|
||||||
|
cCharToTx = 'a';
|
||||||
|
}
|
||||||
|
crDELAY( 100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// All co-routines must end with a call to crEND().
|
||||||
|
crEND();
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ISR that uses a queue to receive characters to send on a UART.
|
||||||
|
void vUART_ISR( void )
|
||||||
|
{
|
||||||
|
char cCharToTx;
|
||||||
|
BaseType_t xCRWokenByPost = pdFALSE;
|
||||||
|
|
||||||
|
while( UART_TX_REG_EMPTY() )
|
||||||
|
{
|
||||||
|
// Are there any characters in the queue waiting to be sent?
|
||||||
|
// xCRWokenByPost will automatically be set to pdTRUE if a co-routine
|
||||||
|
// is woken by the post - ensuring that only a single co-routine is
|
||||||
|
// woken no matter how many times we go around this loop.
|
||||||
|
if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
|
||||||
|
{
|
||||||
|
SEND_CHARACTER( cCharToTx );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}</pre>
|
||||||
|
* \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR
|
||||||
|
* \ingroup Tasks
|
||||||
|
*/
|
||||||
|
#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is intended for internal use by the co-routine macros only.
|
||||||
|
* The macro nature of the co-routine implementation requires that the
|
||||||
|
* prototype appears here. The function should not be used by application
|
||||||
|
* writers.
|
||||||
|
*
|
||||||
|
* Removes the current co-routine from its ready list and places it in the
|
||||||
|
* appropriate delayed list.
|
||||||
|
*/
|
||||||
|
void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is intended for internal use by the queue implementation only.
|
||||||
|
* The function should not be used by application writers.
|
||||||
|
*
|
||||||
|
* Removes the highest priority co-routine from the event list and places it in
|
||||||
|
* the pending ready list.
|
||||||
|
*/
|
||||||
|
BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CO_ROUTINE_H */
|
||||||
279
Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h
vendored
Normal file
279
Middlewares/Third_Party/FreeRTOS/Source/include/deprecated_definitions.h
vendored
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DEPRECATED_DEFINITIONS_H
|
||||||
|
#define DEPRECATED_DEFINITIONS_H
|
||||||
|
|
||||||
|
|
||||||
|
/* Each FreeRTOS port has a unique portmacro.h header file. Originally a
|
||||||
|
pre-processor definition was used to ensure the pre-processor found the correct
|
||||||
|
portmacro.h file for the port being used. That scheme was deprecated in favour
|
||||||
|
of setting the compiler's include path such that it found the correct
|
||||||
|
portmacro.h file - removing the need for the constant and allowing the
|
||||||
|
portmacro.h file to be located anywhere in relation to the port being used. The
|
||||||
|
definitions below remain in the code for backward compatibility only. New
|
||||||
|
projects should not use them. */
|
||||||
|
|
||||||
|
#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT
|
||||||
|
#include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h"
|
||||||
|
typedef void ( __interrupt __far *pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT
|
||||||
|
#include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h"
|
||||||
|
typedef void ( __interrupt __far *pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_MEGA_AVR
|
||||||
|
#include "../portable/GCC/ATMega323/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_MEGA_AVR
|
||||||
|
#include "../portable/IAR/ATMega323/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_PIC24_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_DSPIC_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_PIC18F_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC18F/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPLAB_PIC32MX_PORT
|
||||||
|
#include "../../Source/portable/MPLAB/PIC32MX/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _FEDPICC
|
||||||
|
#include "libFreeRTOS/Include/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SDCC_CYGNAL
|
||||||
|
#include "../../Source/portable/SDCC/Cygnal/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARM7
|
||||||
|
#include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARM7_ECLIPSE
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ROWLEY_LPC23xx
|
||||||
|
#include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_MSP430
|
||||||
|
#include "..\..\Source\portable\IAR\MSP430\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_MSP430
|
||||||
|
#include "../../Source/portable/GCC/MSP430F449/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ROWLEY_MSP430
|
||||||
|
#include "../../Source/portable/Rowley/MSP430F449/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ARM7_LPC21xx_KEIL_RVDS
|
||||||
|
#include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAM7_GCC
|
||||||
|
#include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAM7_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SAM9XE_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LPC2000_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\LPC2000\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR71X_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\STR71x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR75X_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\STR75x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR75X_GCC
|
||||||
|
#include "..\..\Source\portable\GCC\STR75x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STR91X_IAR
|
||||||
|
#include "..\..\Source\portable\IAR\STR91x\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_H8S
|
||||||
|
#include "../../Source/portable/GCC/H8S2329/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_AT91FR40008
|
||||||
|
#include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RVDS_ARMCM3_LM3S102
|
||||||
|
#include "../../Source/portable/RVDS/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARMCM3_LM3S102
|
||||||
|
#include "../../Source/portable/GCC/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_ARMCM3
|
||||||
|
#include "../../Source/portable/GCC/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_ARM_CM3
|
||||||
|
#include "../../Source/portable/IAR/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef IAR_ARMCM3_LM
|
||||||
|
#include "../../Source/portable/IAR/ARM_CM3/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HCS12_CODE_WARRIOR
|
||||||
|
#include "../../Source/portable/CodeWarrior/HCS12/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MICROBLAZE_GCC
|
||||||
|
#include "../../Source/portable/GCC/MicroBlaze/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TERN_EE
|
||||||
|
#include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_HCS12
|
||||||
|
#include "../../Source/portable/GCC/HCS12/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_MCF5235
|
||||||
|
#include "../../Source/portable/GCC/MCF5235/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLDFIRE_V2_GCC
|
||||||
|
#include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef COLDFIRE_V2_CODEWARRIOR
|
||||||
|
#include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_PPC405
|
||||||
|
#include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GCC_PPC440
|
||||||
|
#include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _16FX_SOFTUNE
|
||||||
|
#include "..\..\Source\portable\Softune\MB96340\portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BCC_INDUSTRIAL_PC_PORT
|
||||||
|
/* A short file name has to be used in place of the normal
|
||||||
|
FreeRTOSConfig.h when using the Borland compiler. */
|
||||||
|
#include "frconfig.h"
|
||||||
|
#include "..\portable\BCC\16BitDOS\PC\prtmacro.h"
|
||||||
|
typedef void ( __interrupt __far *pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BCC_FLASH_LITE_186_PORT
|
||||||
|
/* A short file name has to be used in place of the normal
|
||||||
|
FreeRTOSConfig.h when using the Borland compiler. */
|
||||||
|
#include "frconfig.h"
|
||||||
|
#include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h"
|
||||||
|
typedef void ( __interrupt __far *pxISR )();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#ifdef __AVR32_AVR32A__
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ICCAVR32__
|
||||||
|
#ifdef __CORE__
|
||||||
|
#if __CORE__ == __AVR32A__
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __91467D
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __96340
|
||||||
|
#include "portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Fx3__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Jx3__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Jx3_L__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Jx2__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_V850ES_Hx2__
|
||||||
|
#include "../../Source/portable/IAR/V850ES/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_78K0R_Kx3__
|
||||||
|
#include "../../Source/portable/IAR/78K0R/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IAR_78K0R_Kx3L__
|
||||||
|
#include "../../Source/portable/IAR/78K0R/portmacro.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* DEPRECATED_DEFINITIONS_H */
|
||||||
|
|
||||||
757
Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h
vendored
Normal file
757
Middlewares/Third_Party/FreeRTOS/Source/include/event_groups.h
vendored
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EVENT_GROUPS_H
|
||||||
|
#define EVENT_GROUPS_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h" must appear in source files before "include event_groups.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* FreeRTOS includes. */
|
||||||
|
#include "timers.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event group is a collection of bits to which an application can assign a
|
||||||
|
* meaning. For example, an application may create an event group to convey
|
||||||
|
* the status of various CAN bus related events in which bit 0 might mean "A CAN
|
||||||
|
* message has been received and is ready for processing", bit 1 might mean "The
|
||||||
|
* application has queued a message that is ready for sending onto the CAN
|
||||||
|
* network", and bit 2 might mean "It is time to send a SYNC message onto the
|
||||||
|
* CAN network" etc. A task can then test the bit values to see which events
|
||||||
|
* are active, and optionally enter the Blocked state to wait for a specified
|
||||||
|
* bit or a group of specified bits to be active. To continue the CAN bus
|
||||||
|
* example, a CAN controlling task can enter the Blocked state (and therefore
|
||||||
|
* not consume any processing time) until either bit 0, bit 1 or bit 2 are
|
||||||
|
* active, at which time the bit that was actually active would inform the task
|
||||||
|
* which action it had to take (process a received message, send a message, or
|
||||||
|
* send a SYNC).
|
||||||
|
*
|
||||||
|
* The event groups implementation contains intelligence to avoid race
|
||||||
|
* conditions that would otherwise occur were an application to use a simple
|
||||||
|
* variable for the same purpose. This is particularly important with respect
|
||||||
|
* to when a bit within an event group is to be cleared, and when bits have to
|
||||||
|
* be set and then tested atomically - as is the case where event groups are
|
||||||
|
* used to create a synchronisation point between multiple tasks (a
|
||||||
|
* 'rendezvous').
|
||||||
|
*
|
||||||
|
* \defgroup EventGroup
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*
|
||||||
|
* Type by which event groups are referenced. For example, a call to
|
||||||
|
* xEventGroupCreate() returns an EventGroupHandle_t variable that can then
|
||||||
|
* be used as a parameter to other event group functions.
|
||||||
|
*
|
||||||
|
* \defgroup EventGroupHandle_t EventGroupHandle_t
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
struct EventGroupDef_t;
|
||||||
|
typedef struct EventGroupDef_t * EventGroupHandle_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The type that holds event bits always matches TickType_t - therefore the
|
||||||
|
* number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1,
|
||||||
|
* 32 bits if set to 0.
|
||||||
|
*
|
||||||
|
* \defgroup EventBits_t EventBits_t
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
typedef TickType_t EventBits_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventGroupHandle_t xEventGroupCreate( void );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Create a new event group.
|
||||||
|
*
|
||||||
|
* Internally, within the FreeRTOS implementation, event groups use a [small]
|
||||||
|
* block of memory, in which the event group's structure is stored. If an event
|
||||||
|
* groups is created using xEventGropuCreate() then the required memory is
|
||||||
|
* automatically dynamically allocated inside the xEventGroupCreate() function.
|
||||||
|
* (see http://www.freertos.org/a00111.html). If an event group is created
|
||||||
|
* using xEventGropuCreateStatic() then the application writer must instead
|
||||||
|
* provide the memory that will get used by the event group.
|
||||||
|
* xEventGroupCreateStatic() therefore allows an event group to be created
|
||||||
|
* without using any dynamic memory allocation.
|
||||||
|
*
|
||||||
|
* Although event groups are not related to ticks, for internal implementation
|
||||||
|
* reasons the number of bits available for use in an event group is dependent
|
||||||
|
* on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If
|
||||||
|
* configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit
|
||||||
|
* 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has
|
||||||
|
* 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store
|
||||||
|
* event bits within an event group.
|
||||||
|
*
|
||||||
|
* @return If the event group was created then a handle to the event group is
|
||||||
|
* returned. If there was insufficient FreeRTOS heap available to create the
|
||||||
|
* event group then NULL is returned. See http://www.freertos.org/a00111.html
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Declare a variable to hold the created event group.
|
||||||
|
EventGroupHandle_t xCreatedEventGroup;
|
||||||
|
|
||||||
|
// Attempt to create the event group.
|
||||||
|
xCreatedEventGroup = xEventGroupCreate();
|
||||||
|
|
||||||
|
// Was the event group created successfully?
|
||||||
|
if( xCreatedEventGroup == NULL )
|
||||||
|
{
|
||||||
|
// The event group was not created because there was insufficient
|
||||||
|
// FreeRTOS heap available.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The event group was created.
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupCreate xEventGroupCreate
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
|
||||||
|
EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventGroupHandle_t xEventGroupCreateStatic( EventGroupHandle_t * pxEventGroupBuffer );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Create a new event group.
|
||||||
|
*
|
||||||
|
* Internally, within the FreeRTOS implementation, event groups use a [small]
|
||||||
|
* block of memory, in which the event group's structure is stored. If an event
|
||||||
|
* groups is created using xEventGropuCreate() then the required memory is
|
||||||
|
* automatically dynamically allocated inside the xEventGroupCreate() function.
|
||||||
|
* (see http://www.freertos.org/a00111.html). If an event group is created
|
||||||
|
* using xEventGropuCreateStatic() then the application writer must instead
|
||||||
|
* provide the memory that will get used by the event group.
|
||||||
|
* xEventGroupCreateStatic() therefore allows an event group to be created
|
||||||
|
* without using any dynamic memory allocation.
|
||||||
|
*
|
||||||
|
* Although event groups are not related to ticks, for internal implementation
|
||||||
|
* reasons the number of bits available for use in an event group is dependent
|
||||||
|
* on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If
|
||||||
|
* configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit
|
||||||
|
* 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has
|
||||||
|
* 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store
|
||||||
|
* event bits within an event group.
|
||||||
|
*
|
||||||
|
* @param pxEventGroupBuffer pxEventGroupBuffer must point to a variable of type
|
||||||
|
* StaticEventGroup_t, which will be then be used to hold the event group's data
|
||||||
|
* structures, removing the need for the memory to be allocated dynamically.
|
||||||
|
*
|
||||||
|
* @return If the event group was created then a handle to the event group is
|
||||||
|
* returned. If pxEventGroupBuffer was NULL then NULL is returned.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// StaticEventGroup_t is a publicly accessible structure that has the same
|
||||||
|
// size and alignment requirements as the real event group structure. It is
|
||||||
|
// provided as a mechanism for applications to know the size of the event
|
||||||
|
// group (which is dependent on the architecture and configuration file
|
||||||
|
// settings) without breaking the strict data hiding policy by exposing the
|
||||||
|
// real event group internals. This StaticEventGroup_t variable is passed
|
||||||
|
// into the xSemaphoreCreateEventGroupStatic() function and is used to store
|
||||||
|
// the event group's data structures
|
||||||
|
StaticEventGroup_t xEventGroupBuffer;
|
||||||
|
|
||||||
|
// Create the event group without dynamically allocating any memory.
|
||||||
|
xEventGroup = xEventGroupCreateStatic( &xEventGroupBuffer );
|
||||||
|
</pre>
|
||||||
|
*/
|
||||||
|
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
EventGroupHandle_t xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToWaitFor,
|
||||||
|
const BaseType_t xClearOnExit,
|
||||||
|
const BaseType_t xWaitForAllBits,
|
||||||
|
const TickType_t xTicksToWait );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* [Potentially] block to wait for one or more bits to be set within a
|
||||||
|
* previously created event group.
|
||||||
|
*
|
||||||
|
* This function cannot be called from an interrupt.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are being tested. The
|
||||||
|
* event group must have previously been created using a call to
|
||||||
|
* xEventGroupCreate().
|
||||||
|
*
|
||||||
|
* @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test
|
||||||
|
* inside the event group. For example, to wait for bit 0 and/or bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x05. To wait for bits 0 and/or bit 1 and/or bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x07. Etc.
|
||||||
|
*
|
||||||
|
* @param xClearOnExit If xClearOnExit is set to pdTRUE then any bits within
|
||||||
|
* uxBitsToWaitFor that are set within the event group will be cleared before
|
||||||
|
* xEventGroupWaitBits() returns if the wait condition was met (if the function
|
||||||
|
* returns for a reason other than a timeout). If xClearOnExit is set to
|
||||||
|
* pdFALSE then the bits set in the event group are not altered when the call to
|
||||||
|
* xEventGroupWaitBits() returns.
|
||||||
|
*
|
||||||
|
* @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then
|
||||||
|
* xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor
|
||||||
|
* are set or the specified block time expires. If xWaitForAllBits is set to
|
||||||
|
* pdFALSE then xEventGroupWaitBits() will return when any one of the bits set
|
||||||
|
* in uxBitsToWaitFor is set or the specified block time expires. The block
|
||||||
|
* time is specified by the xTicksToWait parameter.
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait
|
||||||
|
* for one/all (depending on the xWaitForAllBits value) of the bits specified by
|
||||||
|
* uxBitsToWaitFor to become set.
|
||||||
|
*
|
||||||
|
* @return The value of the event group at the time either the bits being waited
|
||||||
|
* for became set, or the block time expired. Test the return value to know
|
||||||
|
* which bits were set. If xEventGroupWaitBits() returned because its timeout
|
||||||
|
* expired then not all the bits being waited for will be set. If
|
||||||
|
* xEventGroupWaitBits() returned because the bits it was waiting for were set
|
||||||
|
* then the returned value is the event group value before any bits were
|
||||||
|
* automatically cleared in the case that xClearOnExit parameter was set to
|
||||||
|
* pdTRUE.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
#define BIT_0 ( 1 << 0 )
|
||||||
|
#define BIT_4 ( 1 << 4 )
|
||||||
|
|
||||||
|
void aFunction( EventGroupHandle_t xEventGroup )
|
||||||
|
{
|
||||||
|
EventBits_t uxBits;
|
||||||
|
const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
|
||||||
|
|
||||||
|
// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
|
||||||
|
// the event group. Clear the bits before exiting.
|
||||||
|
uxBits = xEventGroupWaitBits(
|
||||||
|
xEventGroup, // The event group being tested.
|
||||||
|
BIT_0 | BIT_4, // The bits within the event group to wait for.
|
||||||
|
pdTRUE, // BIT_0 and BIT_4 should be cleared before returning.
|
||||||
|
pdFALSE, // Don't wait for both bits, either bit will do.
|
||||||
|
xTicksToWait ); // Wait a maximum of 100ms for either bit to be set.
|
||||||
|
|
||||||
|
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||||
|
{
|
||||||
|
// xEventGroupWaitBits() returned because both bits were set.
|
||||||
|
}
|
||||||
|
else if( ( uxBits & BIT_0 ) != 0 )
|
||||||
|
{
|
||||||
|
// xEventGroupWaitBits() returned because just BIT_0 was set.
|
||||||
|
}
|
||||||
|
else if( ( uxBits & BIT_4 ) != 0 )
|
||||||
|
{
|
||||||
|
// xEventGroupWaitBits() returned because just BIT_4 was set.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// xEventGroupWaitBits() returned because xTicksToWait ticks passed
|
||||||
|
// without either BIT_0 or BIT_4 becoming set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupWaitBits xEventGroupWaitBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Clear bits within an event group. This function cannot be called from an
|
||||||
|
* interrupt.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be cleared.
|
||||||
|
*
|
||||||
|
* @param uxBitsToClear A bitwise value that indicates the bit or bits to clear
|
||||||
|
* in the event group. For example, to clear bit 3 only, set uxBitsToClear to
|
||||||
|
* 0x08. To clear bit 3 and bit 0 set uxBitsToClear to 0x09.
|
||||||
|
*
|
||||||
|
* @return The value of the event group before the specified bits were cleared.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
#define BIT_0 ( 1 << 0 )
|
||||||
|
#define BIT_4 ( 1 << 4 )
|
||||||
|
|
||||||
|
void aFunction( EventGroupHandle_t xEventGroup )
|
||||||
|
{
|
||||||
|
EventBits_t uxBits;
|
||||||
|
|
||||||
|
// Clear bit 0 and bit 4 in xEventGroup.
|
||||||
|
uxBits = xEventGroupClearBits(
|
||||||
|
xEventGroup, // The event group being updated.
|
||||||
|
BIT_0 | BIT_4 );// The bits being cleared.
|
||||||
|
|
||||||
|
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||||
|
{
|
||||||
|
// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
|
||||||
|
// called. Both will now be clear (not set).
|
||||||
|
}
|
||||||
|
else if( ( uxBits & BIT_0 ) != 0 )
|
||||||
|
{
|
||||||
|
// Bit 0 was set before xEventGroupClearBits() was called. It will
|
||||||
|
// now be clear.
|
||||||
|
}
|
||||||
|
else if( ( uxBits & BIT_4 ) != 0 )
|
||||||
|
{
|
||||||
|
// Bit 4 was set before xEventGroupClearBits() was called. It will
|
||||||
|
// now be clear.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Neither bit 0 nor bit 4 were set in the first place.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupClearBits xEventGroupClearBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* A version of xEventGroupClearBits() that can be called from an interrupt.
|
||||||
|
*
|
||||||
|
* Setting bits in an event group is not a deterministic operation because there
|
||||||
|
* are an unknown number of tasks that may be waiting for the bit or bits being
|
||||||
|
* set. FreeRTOS does not allow nondeterministic operations to be performed
|
||||||
|
* while interrupts are disabled, so protects event groups that are accessed
|
||||||
|
* from tasks by suspending the scheduler rather than disabling interrupts. As
|
||||||
|
* a result event groups cannot be accessed directly from an interrupt service
|
||||||
|
* routine. Therefore xEventGroupClearBitsFromISR() sends a message to the
|
||||||
|
* timer task to have the clear operation performed in the context of the timer
|
||||||
|
* task.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be cleared.
|
||||||
|
*
|
||||||
|
* @param uxBitsToClear A bitwise value that indicates the bit or bits to clear.
|
||||||
|
* For example, to clear bit 3 only, set uxBitsToClear to 0x08. To clear bit 3
|
||||||
|
* and bit 0 set uxBitsToClear to 0x09.
|
||||||
|
*
|
||||||
|
* @return If the request to execute the function was posted successfully then
|
||||||
|
* pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned
|
||||||
|
* if the timer service queue was full.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
#define BIT_0 ( 1 << 0 )
|
||||||
|
#define BIT_4 ( 1 << 4 )
|
||||||
|
|
||||||
|
// An event group which it is assumed has already been created by a call to
|
||||||
|
// xEventGroupCreate().
|
||||||
|
EventGroupHandle_t xEventGroup;
|
||||||
|
|
||||||
|
void anInterruptHandler( void )
|
||||||
|
{
|
||||||
|
// Clear bit 0 and bit 4 in xEventGroup.
|
||||||
|
xResult = xEventGroupClearBitsFromISR(
|
||||||
|
xEventGroup, // The event group being updated.
|
||||||
|
BIT_0 | BIT_4 ); // The bits being set.
|
||||||
|
|
||||||
|
if( xResult == pdPASS )
|
||||||
|
{
|
||||||
|
// The message was posted successfully.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupClearBitsFromISR xEventGroupClearBitsFromISR
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#if( configUSE_TRACE_FACILITY == 1 )
|
||||||
|
BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION;
|
||||||
|
#else
|
||||||
|
#define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Set bits within an event group.
|
||||||
|
* This function cannot be called from an interrupt. xEventGroupSetBitsFromISR()
|
||||||
|
* is a version that can be called from an interrupt.
|
||||||
|
*
|
||||||
|
* Setting bits in an event group will automatically unblock tasks that are
|
||||||
|
* blocked waiting for the bits.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be set.
|
||||||
|
*
|
||||||
|
* @param uxBitsToSet A bitwise value that indicates the bit or bits to set.
|
||||||
|
* For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3
|
||||||
|
* and bit 0 set uxBitsToSet to 0x09.
|
||||||
|
*
|
||||||
|
* @return The value of the event group at the time the call to
|
||||||
|
* xEventGroupSetBits() returns. There are two reasons why the returned value
|
||||||
|
* might have the bits specified by the uxBitsToSet parameter cleared. First,
|
||||||
|
* if setting a bit results in a task that was waiting for the bit leaving the
|
||||||
|
* blocked state then it is possible the bit will be cleared automatically
|
||||||
|
* (see the xClearBitOnExit parameter of xEventGroupWaitBits()). Second, any
|
||||||
|
* unblocked (or otherwise Ready state) task that has a priority above that of
|
||||||
|
* the task that called xEventGroupSetBits() will execute and may change the
|
||||||
|
* event group value before the call to xEventGroupSetBits() returns.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
#define BIT_0 ( 1 << 0 )
|
||||||
|
#define BIT_4 ( 1 << 4 )
|
||||||
|
|
||||||
|
void aFunction( EventGroupHandle_t xEventGroup )
|
||||||
|
{
|
||||||
|
EventBits_t uxBits;
|
||||||
|
|
||||||
|
// Set bit 0 and bit 4 in xEventGroup.
|
||||||
|
uxBits = xEventGroupSetBits(
|
||||||
|
xEventGroup, // The event group being updated.
|
||||||
|
BIT_0 | BIT_4 );// The bits being set.
|
||||||
|
|
||||||
|
if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
|
||||||
|
{
|
||||||
|
// Both bit 0 and bit 4 remained set when the function returned.
|
||||||
|
}
|
||||||
|
else if( ( uxBits & BIT_0 ) != 0 )
|
||||||
|
{
|
||||||
|
// Bit 0 remained set when the function returned, but bit 4 was
|
||||||
|
// cleared. It might be that bit 4 was cleared automatically as a
|
||||||
|
// task that was waiting for bit 4 was removed from the Blocked
|
||||||
|
// state.
|
||||||
|
}
|
||||||
|
else if( ( uxBits & BIT_4 ) != 0 )
|
||||||
|
{
|
||||||
|
// Bit 4 remained set when the function returned, but bit 0 was
|
||||||
|
// cleared. It might be that bit 0 was cleared automatically as a
|
||||||
|
// task that was waiting for bit 0 was removed from the Blocked
|
||||||
|
// state.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Neither bit 0 nor bit 4 remained set. It might be that a task
|
||||||
|
// was waiting for both of the bits to be set, and the bits were
|
||||||
|
// cleared as the task left the Blocked state.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupSetBits xEventGroupSetBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* A version of xEventGroupSetBits() that can be called from an interrupt.
|
||||||
|
*
|
||||||
|
* Setting bits in an event group is not a deterministic operation because there
|
||||||
|
* are an unknown number of tasks that may be waiting for the bit or bits being
|
||||||
|
* set. FreeRTOS does not allow nondeterministic operations to be performed in
|
||||||
|
* interrupts or from critical sections. Therefore xEventGroupSetBitsFromISR()
|
||||||
|
* sends a message to the timer task to have the set operation performed in the
|
||||||
|
* context of the timer task - where a scheduler lock is used in place of a
|
||||||
|
* critical section.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are to be set.
|
||||||
|
*
|
||||||
|
* @param uxBitsToSet A bitwise value that indicates the bit or bits to set.
|
||||||
|
* For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3
|
||||||
|
* and bit 0 set uxBitsToSet to 0x09.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken As mentioned above, calling this function
|
||||||
|
* will result in a message being sent to the timer daemon task. If the
|
||||||
|
* priority of the timer daemon task is higher than the priority of the
|
||||||
|
* currently running task (the task the interrupt interrupted) then
|
||||||
|
* *pxHigherPriorityTaskWoken will be set to pdTRUE by
|
||||||
|
* xEventGroupSetBitsFromISR(), indicating that a context switch should be
|
||||||
|
* requested before the interrupt exits. For that reason
|
||||||
|
* *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the
|
||||||
|
* example code below.
|
||||||
|
*
|
||||||
|
* @return If the request to execute the function was posted successfully then
|
||||||
|
* pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned
|
||||||
|
* if the timer service queue was full.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
#define BIT_0 ( 1 << 0 )
|
||||||
|
#define BIT_4 ( 1 << 4 )
|
||||||
|
|
||||||
|
// An event group which it is assumed has already been created by a call to
|
||||||
|
// xEventGroupCreate().
|
||||||
|
EventGroupHandle_t xEventGroup;
|
||||||
|
|
||||||
|
void anInterruptHandler( void )
|
||||||
|
{
|
||||||
|
BaseType_t xHigherPriorityTaskWoken, xResult;
|
||||||
|
|
||||||
|
// xHigherPriorityTaskWoken must be initialised to pdFALSE.
|
||||||
|
xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
|
||||||
|
// Set bit 0 and bit 4 in xEventGroup.
|
||||||
|
xResult = xEventGroupSetBitsFromISR(
|
||||||
|
xEventGroup, // The event group being updated.
|
||||||
|
BIT_0 | BIT_4 // The bits being set.
|
||||||
|
&xHigherPriorityTaskWoken );
|
||||||
|
|
||||||
|
// Was the message posted successfully?
|
||||||
|
if( xResult == pdPASS )
|
||||||
|
{
|
||||||
|
// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
|
||||||
|
// switch should be requested. The macro used is port specific and
|
||||||
|
// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() -
|
||||||
|
// refer to the documentation page for the port being used.
|
||||||
|
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#if( configUSE_TRACE_FACILITY == 1 )
|
||||||
|
BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
|
||||||
|
#else
|
||||||
|
#define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
|
||||||
|
const EventBits_t uxBitsToSet,
|
||||||
|
const EventBits_t uxBitsToWaitFor,
|
||||||
|
TickType_t xTicksToWait );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Atomically set bits within an event group, then wait for a combination of
|
||||||
|
* bits to be set within the same event group. This functionality is typically
|
||||||
|
* used to synchronise multiple tasks, where each task has to wait for the other
|
||||||
|
* tasks to reach a synchronisation point before proceeding.
|
||||||
|
*
|
||||||
|
* This function cannot be used from an interrupt.
|
||||||
|
*
|
||||||
|
* The function will return before its block time expires if the bits specified
|
||||||
|
* by the uxBitsToWait parameter are set, or become set within that time. In
|
||||||
|
* this case all the bits specified by uxBitsToWait will be automatically
|
||||||
|
* cleared before the function returns.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group in which the bits are being tested. The
|
||||||
|
* event group must have previously been created using a call to
|
||||||
|
* xEventGroupCreate().
|
||||||
|
*
|
||||||
|
* @param uxBitsToSet The bits to set in the event group before determining
|
||||||
|
* if, and possibly waiting for, all the bits specified by the uxBitsToWait
|
||||||
|
* parameter are set.
|
||||||
|
*
|
||||||
|
* @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test
|
||||||
|
* inside the event group. For example, to wait for bit 0 and bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x05. To wait for bits 0 and bit 1 and bit 2 set
|
||||||
|
* uxBitsToWaitFor to 0x07. Etc.
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait
|
||||||
|
* for all of the bits specified by uxBitsToWaitFor to become set.
|
||||||
|
*
|
||||||
|
* @return The value of the event group at the time either the bits being waited
|
||||||
|
* for became set, or the block time expired. Test the return value to know
|
||||||
|
* which bits were set. If xEventGroupSync() returned because its timeout
|
||||||
|
* expired then not all the bits being waited for will be set. If
|
||||||
|
* xEventGroupSync() returned because all the bits it was waiting for were
|
||||||
|
* set then the returned value is the event group value before any bits were
|
||||||
|
* automatically cleared.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
<pre>
|
||||||
|
// Bits used by the three tasks.
|
||||||
|
#define TASK_0_BIT ( 1 << 0 )
|
||||||
|
#define TASK_1_BIT ( 1 << 1 )
|
||||||
|
#define TASK_2_BIT ( 1 << 2 )
|
||||||
|
|
||||||
|
#define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
|
||||||
|
|
||||||
|
// Use an event group to synchronise three tasks. It is assumed this event
|
||||||
|
// group has already been created elsewhere.
|
||||||
|
EventGroupHandle_t xEventBits;
|
||||||
|
|
||||||
|
void vTask0( void *pvParameters )
|
||||||
|
{
|
||||||
|
EventBits_t uxReturn;
|
||||||
|
TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
|
||||||
|
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Perform task functionality here.
|
||||||
|
|
||||||
|
// Set bit 0 in the event flag to note this task has reached the
|
||||||
|
// sync point. The other two tasks will set the other two bits defined
|
||||||
|
// by ALL_SYNC_BITS. All three tasks have reached the synchronisation
|
||||||
|
// point when all the ALL_SYNC_BITS are set. Wait a maximum of 100ms
|
||||||
|
// for this to happen.
|
||||||
|
uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
|
||||||
|
|
||||||
|
if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
|
||||||
|
{
|
||||||
|
// All three tasks reached the synchronisation point before the call
|
||||||
|
// to xEventGroupSync() timed out.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vTask1( void *pvParameters )
|
||||||
|
{
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Perform task functionality here.
|
||||||
|
|
||||||
|
// Set bit 1 in the event flag to note this task has reached the
|
||||||
|
// synchronisation point. The other two tasks will set the other two
|
||||||
|
// bits defined by ALL_SYNC_BITS. All three tasks have reached the
|
||||||
|
// synchronisation point when all the ALL_SYNC_BITS are set. Wait
|
||||||
|
// indefinitely for this to happen.
|
||||||
|
xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
|
||||||
|
|
||||||
|
// xEventGroupSync() was called with an indefinite block time, so
|
||||||
|
// this task will only reach here if the syncrhonisation was made by all
|
||||||
|
// three tasks, so there is no need to test the return value.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vTask2( void *pvParameters )
|
||||||
|
{
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Perform task functionality here.
|
||||||
|
|
||||||
|
// Set bit 2 in the event flag to note this task has reached the
|
||||||
|
// synchronisation point. The other two tasks will set the other two
|
||||||
|
// bits defined by ALL_SYNC_BITS. All three tasks have reached the
|
||||||
|
// synchronisation point when all the ALL_SYNC_BITS are set. Wait
|
||||||
|
// indefinitely for this to happen.
|
||||||
|
xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
|
||||||
|
|
||||||
|
// xEventGroupSync() was called with an indefinite block time, so
|
||||||
|
// this task will only reach here if the syncrhonisation was made by all
|
||||||
|
// three tasks, so there is no need to test the return value.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
* \defgroup xEventGroupSync xEventGroupSync
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Returns the current value of the bits in an event group. This function
|
||||||
|
* cannot be used from an interrupt.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group being queried.
|
||||||
|
*
|
||||||
|
* @return The event group bits at the time xEventGroupGetBits() was called.
|
||||||
|
*
|
||||||
|
* \defgroup xEventGroupGetBits xEventGroupGetBits
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* A version of xEventGroupGetBits() that can be called from an ISR.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group being queried.
|
||||||
|
*
|
||||||
|
* @return The event group bits at the time xEventGroupGetBitsFromISR() was called.
|
||||||
|
*
|
||||||
|
* \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR
|
||||||
|
* \ingroup EventGroup
|
||||||
|
*/
|
||||||
|
EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* event_groups.h
|
||||||
|
*<pre>
|
||||||
|
void xEventGroupDelete( EventGroupHandle_t xEventGroup );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Delete an event group that was previously created by a call to
|
||||||
|
* xEventGroupCreate(). Tasks that are blocked on the event group will be
|
||||||
|
* unblocked and obtain 0 as the event group's value.
|
||||||
|
*
|
||||||
|
* @param xEventGroup The event group being deleted.
|
||||||
|
*/
|
||||||
|
void vEventGroupDelete( EventGroupHandle_t xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/* For internal use only. */
|
||||||
|
void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) PRIVILEGED_FUNCTION;
|
||||||
|
void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
|
||||||
|
#if (configUSE_TRACE_FACILITY == 1)
|
||||||
|
UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) PRIVILEGED_FUNCTION;
|
||||||
|
void vEventGroupSetNumber( void* xEventGroup, UBaseType_t uxEventGroupNumber ) PRIVILEGED_FUNCTION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* EVENT_GROUPS_H */
|
||||||
|
|
||||||
|
|
||||||
412
Middlewares/Third_Party/FreeRTOS/Source/include/list.h
vendored
Normal file
412
Middlewares/Third_Party/FreeRTOS/Source/include/list.h
vendored
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the list implementation used by the scheduler. While it is tailored
|
||||||
|
* heavily for the schedulers needs, it is also available for use by
|
||||||
|
* application code.
|
||||||
|
*
|
||||||
|
* list_ts can only store pointers to list_item_ts. Each ListItem_t contains a
|
||||||
|
* numeric value (xItemValue). Most of the time the lists are sorted in
|
||||||
|
* descending item value order.
|
||||||
|
*
|
||||||
|
* Lists are created already containing one list item. The value of this
|
||||||
|
* item is the maximum possible that can be stored, it is therefore always at
|
||||||
|
* the end of the list and acts as a marker. The list member pxHead always
|
||||||
|
* points to this marker - even though it is at the tail of the list. This
|
||||||
|
* is because the tail contains a wrap back pointer to the true head of
|
||||||
|
* the list.
|
||||||
|
*
|
||||||
|
* In addition to it's value, each list item contains a pointer to the next
|
||||||
|
* item in the list (pxNext), a pointer to the list it is in (pxContainer)
|
||||||
|
* and a pointer to back to the object that contains it. These later two
|
||||||
|
* pointers are included for efficiency of list manipulation. There is
|
||||||
|
* effectively a two way link between the object containing the list item and
|
||||||
|
* the list item itself.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* \page ListIntroduction List Implementation
|
||||||
|
* \ingroup FreeRTOSIntro
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error FreeRTOS.h must be included before list.h
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The list structure members are modified from within interrupts, and therefore
|
||||||
|
* by rights should be declared volatile. However, they are only modified in a
|
||||||
|
* functionally atomic way (within critical sections of with the scheduler
|
||||||
|
* suspended) and are either passed by reference into a function or indexed via
|
||||||
|
* a volatile variable. Therefore, in all use cases tested so far, the volatile
|
||||||
|
* qualifier can be omitted in order to provide a moderate performance
|
||||||
|
* improvement without adversely affecting functional behaviour. The assembly
|
||||||
|
* instructions generated by the IAR, ARM and GCC compilers when the respective
|
||||||
|
* compiler's options were set for maximum optimisation has been inspected and
|
||||||
|
* deemed to be as intended. That said, as compiler technology advances, and
|
||||||
|
* especially if aggressive cross module optimisation is used (a use case that
|
||||||
|
* has not been exercised to any great extend) then it is feasible that the
|
||||||
|
* volatile qualifier will be needed for correct optimisation. It is expected
|
||||||
|
* that a compiler removing essential code because, without the volatile
|
||||||
|
* qualifier on the list structure members and with aggressive cross module
|
||||||
|
* optimisation, the compiler deemed the code unnecessary will result in
|
||||||
|
* complete and obvious failure of the scheduler. If this is ever experienced
|
||||||
|
* then the volatile qualifier can be inserted in the relevant places within the
|
||||||
|
* list structures by simply defining configLIST_VOLATILE to volatile in
|
||||||
|
* FreeRTOSConfig.h (as per the example at the bottom of this comment block).
|
||||||
|
* If configLIST_VOLATILE is not defined then the preprocessor directives below
|
||||||
|
* will simply #define configLIST_VOLATILE away completely.
|
||||||
|
*
|
||||||
|
* To use volatile list structure members then add the following line to
|
||||||
|
* FreeRTOSConfig.h (without the quotes):
|
||||||
|
* "#define configLIST_VOLATILE volatile"
|
||||||
|
*/
|
||||||
|
#ifndef configLIST_VOLATILE
|
||||||
|
#define configLIST_VOLATILE
|
||||||
|
#endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Macros that can be used to place known values within the list structures,
|
||||||
|
then check that the known values do not get corrupted during the execution of
|
||||||
|
the application. These may catch the list data structures being overwritten in
|
||||||
|
memory. They will not catch data errors caused by incorrect configuration or
|
||||||
|
use of FreeRTOS.*/
|
||||||
|
#if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 )
|
||||||
|
/* Define the macros to do nothing. */
|
||||||
|
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
|
||||||
|
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem )
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList )
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList )
|
||||||
|
#define listTEST_LIST_ITEM_INTEGRITY( pxItem )
|
||||||
|
#define listTEST_LIST_INTEGRITY( pxList )
|
||||||
|
#else
|
||||||
|
/* Define macros that add new members into the list structures. */
|
||||||
|
#define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1;
|
||||||
|
#define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2;
|
||||||
|
#define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1;
|
||||||
|
#define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2;
|
||||||
|
|
||||||
|
/* Define macros that set the new structure members to known values. */
|
||||||
|
#define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
#define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE
|
||||||
|
|
||||||
|
/* Define macros that will assert if one of the structure members does not
|
||||||
|
contain its expected value. */
|
||||||
|
#define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
|
||||||
|
#define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) )
|
||||||
|
#endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definition of the only type of object that a list can contain.
|
||||||
|
*/
|
||||||
|
struct xLIST;
|
||||||
|
struct xLIST_ITEM
|
||||||
|
{
|
||||||
|
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */
|
||||||
|
void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */
|
||||||
|
struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */
|
||||||
|
listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
};
|
||||||
|
typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */
|
||||||
|
|
||||||
|
struct xMINI_LIST_ITEM
|
||||||
|
{
|
||||||
|
listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
configLIST_VOLATILE TickType_t xItemValue;
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
|
||||||
|
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
|
||||||
|
};
|
||||||
|
typedef struct xMINI_LIST_ITEM MiniListItem_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definition of the type of queue used by the scheduler.
|
||||||
|
*/
|
||||||
|
typedef struct xLIST
|
||||||
|
{
|
||||||
|
listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
volatile UBaseType_t uxNumberOfItems;
|
||||||
|
ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */
|
||||||
|
MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */
|
||||||
|
listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */
|
||||||
|
} List_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to set the owner of a list item. The owner of a list item
|
||||||
|
* is the object (usually a TCB) that contains the list item.
|
||||||
|
*
|
||||||
|
* \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to get the owner of a list item. The owner of a list item
|
||||||
|
* is the object (usually a TCB) that contains the list item.
|
||||||
|
*
|
||||||
|
* \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to set the value of the list item. In most cases the value is
|
||||||
|
* used to sort the list in descending order.
|
||||||
|
*
|
||||||
|
* \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to retrieve the value of the list item. The value can
|
||||||
|
* represent anything - for example the priority of a task, or the time at
|
||||||
|
* which a task should be unblocked.
|
||||||
|
*
|
||||||
|
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to retrieve the value of the list item at the head of a given
|
||||||
|
* list.
|
||||||
|
*
|
||||||
|
* \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the list item at the head of the list.
|
||||||
|
*
|
||||||
|
* \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the next list item.
|
||||||
|
*
|
||||||
|
* \page listGET_NEXT listGET_NEXT
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the list item that marks the end of the list
|
||||||
|
*
|
||||||
|
* \page listGET_END_MARKER listGET_END_MARKER
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to determine if a list contains any items. The macro will
|
||||||
|
* only have the value true if the list is empty.
|
||||||
|
*
|
||||||
|
* \page listLIST_IS_EMPTY listLIST_IS_EMPTY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access macro to return the number of items in the list.
|
||||||
|
*/
|
||||||
|
#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access function to obtain the owner of the next entry in a list.
|
||||||
|
*
|
||||||
|
* The list member pxIndex is used to walk through a list. Calling
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list
|
||||||
|
* and returns that entry's pxOwner parameter. Using multiple calls to this
|
||||||
|
* function it is therefore possible to move through every item contained in
|
||||||
|
* a list.
|
||||||
|
*
|
||||||
|
* The pxOwner parameter of a list item is a pointer to the object that owns
|
||||||
|
* the list item. In the scheduler this is normally a task control block.
|
||||||
|
* The pxOwner parameter effectively creates a two way link between the list
|
||||||
|
* item and its owner.
|
||||||
|
*
|
||||||
|
* @param pxTCB pxTCB is set to the address of the owner of the next list item.
|
||||||
|
* @param pxList The list from which the next item owner is to be returned.
|
||||||
|
*
|
||||||
|
* \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \
|
||||||
|
{ \
|
||||||
|
List_t * const pxConstList = ( pxList ); \
|
||||||
|
/* Increment the index to the next item and return the item, ensuring */ \
|
||||||
|
/* we don't return the marker used at the end of the list. */ \
|
||||||
|
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
|
||||||
|
if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
|
||||||
|
{ \
|
||||||
|
( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \
|
||||||
|
} \
|
||||||
|
( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Access function to obtain the owner of the first entry in a list. Lists
|
||||||
|
* are normally sorted in ascending item value order.
|
||||||
|
*
|
||||||
|
* This function returns the pxOwner member of the first item in the list.
|
||||||
|
* The pxOwner parameter of a list item is a pointer to the object that owns
|
||||||
|
* the list item. In the scheduler this is normally a task control block.
|
||||||
|
* The pxOwner parameter effectively creates a two way link between the list
|
||||||
|
* item and its owner.
|
||||||
|
*
|
||||||
|
* @param pxList The list from which the owner of the head item is to be
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if a list item is within a list. The list item maintains a
|
||||||
|
* "container" pointer that points to the list it is in. All this macro does
|
||||||
|
* is check to see if the container and the list match.
|
||||||
|
*
|
||||||
|
* @param pxList The list we want to know if the list item is within.
|
||||||
|
* @param pxListItem The list item we want to know if is in the list.
|
||||||
|
* @return pdTRUE if the list item is in the list, otherwise pdFALSE.
|
||||||
|
*/
|
||||||
|
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the list a list item is contained within (referenced from).
|
||||||
|
*
|
||||||
|
* @param pxListItem The list item being queried.
|
||||||
|
* @return A pointer to the List_t object that references the pxListItem
|
||||||
|
*/
|
||||||
|
#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This provides a crude means of knowing if a list has been initialised, as
|
||||||
|
* pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise()
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be called before a list is used! This initialises all the members
|
||||||
|
* of the list structure and inserts the xListEnd item into the list as a
|
||||||
|
* marker to the back of the list.
|
||||||
|
*
|
||||||
|
* @param pxList Pointer to the list being initialised.
|
||||||
|
*
|
||||||
|
* \page vListInitialise vListInitialise
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInitialise( List_t * const pxList ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must be called before a list item is used. This sets the list container to
|
||||||
|
* null so the item does not think that it is already contained in a list.
|
||||||
|
*
|
||||||
|
* @param pxItem Pointer to the list item being initialised.
|
||||||
|
*
|
||||||
|
* \page vListInitialiseItem vListInitialiseItem
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a list item into a list. The item will be inserted into the list in
|
||||||
|
* a position determined by its item value (descending item value order).
|
||||||
|
*
|
||||||
|
* @param pxList The list into which the item is to be inserted.
|
||||||
|
*
|
||||||
|
* @param pxNewListItem The item that is to be placed in the list.
|
||||||
|
*
|
||||||
|
* \page vListInsert vListInsert
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a list item into a list. The item will be inserted in a position
|
||||||
|
* such that it will be the last item within the list returned by multiple
|
||||||
|
* calls to listGET_OWNER_OF_NEXT_ENTRY.
|
||||||
|
*
|
||||||
|
* The list member pxIndex is used to walk through a list. Calling
|
||||||
|
* listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list.
|
||||||
|
* Placing an item in a list using vListInsertEnd effectively places the item
|
||||||
|
* in the list position pointed to by pxIndex. This means that every other
|
||||||
|
* item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before
|
||||||
|
* the pxIndex parameter again points to the item being inserted.
|
||||||
|
*
|
||||||
|
* @param pxList The list into which the item is to be inserted.
|
||||||
|
*
|
||||||
|
* @param pxNewListItem The list item to be inserted into the list.
|
||||||
|
*
|
||||||
|
* \page vListInsertEnd vListInsertEnd
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove an item from a list. The list item has a pointer to the list that
|
||||||
|
* it is in, so only the list item need be passed into the function.
|
||||||
|
*
|
||||||
|
* @param uxListRemove The item to be removed. The item will remove itself from
|
||||||
|
* the list pointed to by it's pxContainer parameter.
|
||||||
|
*
|
||||||
|
* @return The number of items that remain in the list after the list item has
|
||||||
|
* been removed.
|
||||||
|
*
|
||||||
|
* \page uxListRemove uxListRemove
|
||||||
|
* \ingroup LinkedList
|
||||||
|
*/
|
||||||
|
UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
803
Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h
vendored
Normal file
803
Middlewares/Third_Party/FreeRTOS/Source/include/message_buffer.h
vendored
Normal file
@@ -0,0 +1,803 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Message buffers build functionality on top of FreeRTOS stream buffers.
|
||||||
|
* Whereas stream buffers are used to send a continuous stream of data from one
|
||||||
|
* task or interrupt to another, message buffers are used to send variable
|
||||||
|
* length discrete messages from one task or interrupt to another. Their
|
||||||
|
* implementation is light weight, making them particularly suited for interrupt
|
||||||
|
* to task and core to core communication scenarios.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must place each call to a writing API function
|
||||||
|
* (such as xMessageBufferSend()) inside a critical section and set the send
|
||||||
|
* block time to 0. Likewise, if there are to be multiple different readers
|
||||||
|
* then the application writer must place each call to a reading API function
|
||||||
|
* (such as xMessageBufferRead()) inside a critical section and set the receive
|
||||||
|
* timeout to 0.
|
||||||
|
*
|
||||||
|
* Message buffers hold variable length messages. To enable that, when a
|
||||||
|
* message is written to the message buffer an additional sizeof( size_t ) bytes
|
||||||
|
* are also written to store the message's length (that happens internally, with
|
||||||
|
* the API function). sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||||
|
* architecture, so writing a 10 byte message to a message buffer on a 32-bit
|
||||||
|
* architecture will actually reduce the available space in the message buffer
|
||||||
|
* by 14 bytes (10 byte are used by the message, and 4 bytes to hold the length
|
||||||
|
* of the message).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FREERTOS_MESSAGE_BUFFER_H
|
||||||
|
#define FREERTOS_MESSAGE_BUFFER_H
|
||||||
|
|
||||||
|
#ifndef INC_FREERTOS_H
|
||||||
|
#error "include FreeRTOS.h must appear in source files before include message_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Message buffers are built onto of stream buffers. */
|
||||||
|
#include "stream_buffer.h"
|
||||||
|
|
||||||
|
#if defined( __cplusplus )
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type by which message buffers are referenced. For example, a call to
|
||||||
|
* xMessageBufferCreate() returns an MessageBufferHandle_t variable that can
|
||||||
|
* then be used as a parameter to xMessageBufferSend(), xMessageBufferReceive(),
|
||||||
|
* etc.
|
||||||
|
*/
|
||||||
|
typedef void * MessageBufferHandle_t;
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
MessageBufferHandle_t xMessageBufferCreate( size_t xBufferSizeBytes );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Creates a new message buffer using dynamically allocated memory. See
|
||||||
|
* xMessageBufferCreateStatic() for a version that uses statically allocated
|
||||||
|
* memory (memory that is allocated at compile time).
|
||||||
|
*
|
||||||
|
* configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 or left undefined in
|
||||||
|
* FreeRTOSConfig.h for xMessageBufferCreate() to be available.
|
||||||
|
*
|
||||||
|
* @param xBufferSizeBytes The total number of bytes (not messages) the message
|
||||||
|
* buffer will be able to hold at any one time. When a message is written to
|
||||||
|
* the message buffer an additional sizeof( size_t ) bytes are also written to
|
||||||
|
* store the message's length. sizeof( size_t ) is typically 4 bytes on a
|
||||||
|
* 32-bit architecture, so on most 32-bit architectures a 10 byte message will
|
||||||
|
* take up 14 bytes of message buffer space.
|
||||||
|
*
|
||||||
|
* @return If NULL is returned, then the message buffer cannot be created
|
||||||
|
* because there is insufficient heap memory available for FreeRTOS to allocate
|
||||||
|
* the message buffer data structures and storage area. A non-NULL value being
|
||||||
|
* returned indicates that the message buffer has been created successfully -
|
||||||
|
* the returned value should be stored as the handle to the created message
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
void vAFunction( void )
|
||||||
|
{
|
||||||
|
MessageBufferHandle_t xMessageBuffer;
|
||||||
|
const size_t xMessageBufferSizeBytes = 100;
|
||||||
|
|
||||||
|
// Create a message buffer that can hold 100 bytes. The memory used to hold
|
||||||
|
// both the message buffer structure and the messages themselves is allocated
|
||||||
|
// dynamically. Each message added to the buffer consumes an additional 4
|
||||||
|
// bytes which are used to hold the lengh of the message.
|
||||||
|
xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
|
||||||
|
|
||||||
|
if( xMessageBuffer == NULL )
|
||||||
|
{
|
||||||
|
// There was not enough heap memory space available to create the
|
||||||
|
// message buffer.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The message buffer was created successfully and can now be used.
|
||||||
|
}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
* \defgroup xMessageBufferCreate xMessageBufferCreate
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferCreate( xBufferSizeBytes ) ( MessageBufferHandle_t ) xStreamBufferGenericCreate( xBufferSizeBytes, ( size_t ) 0, pdTRUE )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
MessageBufferHandle_t xMessageBufferCreateStatic( size_t xBufferSizeBytes,
|
||||||
|
uint8_t *pucMessageBufferStorageArea,
|
||||||
|
StaticMessageBuffer_t *pxStaticMessageBuffer );
|
||||||
|
</pre>
|
||||||
|
* Creates a new message buffer using statically allocated memory. See
|
||||||
|
* xMessageBufferCreate() for a version that uses dynamically allocated memory.
|
||||||
|
*
|
||||||
|
* @param xBufferSizeBytes The size, in bytes, of the buffer pointed to by the
|
||||||
|
* pucMessageBufferStorageArea parameter. When a message is written to the
|
||||||
|
* message buffer an additional sizeof( size_t ) bytes are also written to store
|
||||||
|
* the message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||||
|
* architecture, so on most 32-bit architecture a 10 byte message will take up
|
||||||
|
* 14 bytes of message buffer space. The maximum number of bytes that can be
|
||||||
|
* stored in the message buffer is actually (xBufferSizeBytes - 1).
|
||||||
|
*
|
||||||
|
* @param pucMessageBufferStorageArea Must point to a uint8_t array that is at
|
||||||
|
* least xBufferSizeBytes + 1 big. This is the array to which messages are
|
||||||
|
* copied when they are written to the message buffer.
|
||||||
|
*
|
||||||
|
* @param pxStaticMessageBuffer Must point to a variable of type
|
||||||
|
* StaticMessageBuffer_t, which will be used to hold the message buffer's data
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* @return If the message buffer is created successfully then a handle to the
|
||||||
|
* created message buffer is returned. If either pucMessageBufferStorageArea or
|
||||||
|
* pxStaticmessageBuffer are NULL then NULL is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
// Used to dimension the array used to hold the messages. The available space
|
||||||
|
// will actually be one less than this, so 999.
|
||||||
|
#define STORAGE_SIZE_BYTES 1000
|
||||||
|
|
||||||
|
// Defines the memory that will actually hold the messages within the message
|
||||||
|
// buffer.
|
||||||
|
static uint8_t ucStorageBuffer[ STORAGE_SIZE_BYTES ];
|
||||||
|
|
||||||
|
// The variable used to hold the message buffer structure.
|
||||||
|
StaticMessageBuffer_t xMessageBufferStruct;
|
||||||
|
|
||||||
|
void MyFunction( void )
|
||||||
|
{
|
||||||
|
MessageBufferHandle_t xMessageBuffer;
|
||||||
|
|
||||||
|
xMessageBuffer = xMessageBufferCreateStatic( sizeof( ucBufferStorage ),
|
||||||
|
ucBufferStorage,
|
||||||
|
&xMessageBufferStruct );
|
||||||
|
|
||||||
|
// As neither the pucMessageBufferStorageArea or pxStaticMessageBuffer
|
||||||
|
// parameters were NULL, xMessageBuffer will not be NULL, and can be used to
|
||||||
|
// reference the created message buffer in other message buffer API calls.
|
||||||
|
|
||||||
|
// Other code that uses the message buffer can go here.
|
||||||
|
}
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
* \defgroup xMessageBufferCreateStatic xMessageBufferCreateStatic
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferCreateStatic( xBufferSizeBytes, pucMessageBufferStorageArea, pxStaticMessageBuffer ) ( MessageBufferHandle_t ) xStreamBufferGenericCreateStatic( xBufferSizeBytes, 0, pdTRUE, pucMessageBufferStorageArea, pxStaticMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
size_t xMessageBufferSend( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
const void *pvTxData,
|
||||||
|
size_t xDataLengthBytes,
|
||||||
|
TickType_t xTicksToWait );
|
||||||
|
<pre>
|
||||||
|
*
|
||||||
|
* Sends a discrete message to the message buffer. The message can be any
|
||||||
|
* length that fits within the buffer's free space, and is copied into the
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must place each call to a writing API function
|
||||||
|
* (such as xMessageBufferSend()) inside a critical section and set the send
|
||||||
|
* block time to 0. Likewise, if there are to be multiple different readers
|
||||||
|
* then the application writer must place each call to a reading API function
|
||||||
|
* (such as xMessageBufferRead()) inside a critical section and set the receive
|
||||||
|
* block time to 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferSend() to write to a message buffer from a task. Use
|
||||||
|
* xMessageBufferSendFromISR() to write to a message buffer from an interrupt
|
||||||
|
* service routine (ISR).
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer to which a message is
|
||||||
|
* being sent.
|
||||||
|
*
|
||||||
|
* @param pvTxData A pointer to the message that is to be copied into the
|
||||||
|
* message buffer.
|
||||||
|
*
|
||||||
|
* @param xDataLengthBytes The length of the message. That is, the number of
|
||||||
|
* bytes to copy from pvTxData into the message buffer. When a message is
|
||||||
|
* written to the message buffer an additional sizeof( size_t ) bytes are also
|
||||||
|
* written to store the message's length. sizeof( size_t ) is typically 4 bytes
|
||||||
|
* on a 32-bit architecture, so on most 32-bit architecture setting
|
||||||
|
* xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
|
||||||
|
* bytes (20 bytes of message data and 4 bytes to hold the message length).
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time the calling task should remain
|
||||||
|
* in the Blocked state to wait for enough space to become available in the
|
||||||
|
* message buffer, should the message buffer have insufficient space when
|
||||||
|
* xMessageBufferSend() is called. The calling task will never block if
|
||||||
|
* xTicksToWait is zero. The block time is specified in tick periods, so the
|
||||||
|
* absolute time it represents is dependent on the tick frequency. The macro
|
||||||
|
* pdMS_TO_TICKS() can be used to convert a time specified in milliseconds into
|
||||||
|
* a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will cause
|
||||||
|
* the task to wait indefinitely (without timing out), provided
|
||||||
|
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any
|
||||||
|
* CPU time when they are in the Blocked state.
|
||||||
|
*
|
||||||
|
* @return The number of bytes written to the message buffer. If the call to
|
||||||
|
* xMessageBufferSend() times out before there was enough space to write the
|
||||||
|
* message into the message buffer then zero is returned. If the call did not
|
||||||
|
* time out then xDataLengthBytes is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
<pre>
|
||||||
|
void vAFunction( MessageBufferHandle_t xMessageBuffer )
|
||||||
|
{
|
||||||
|
size_t xBytesSent;
|
||||||
|
uint8_t ucArrayToSend[] = { 0, 1, 2, 3 };
|
||||||
|
char *pcStringToSend = "String to send";
|
||||||
|
const TickType_t x100ms = pdMS_TO_TICKS( 100 );
|
||||||
|
|
||||||
|
// Send an array to the message buffer, blocking for a maximum of 100ms to
|
||||||
|
// wait for enough space to be available in the message buffer.
|
||||||
|
xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) ucArrayToSend, sizeof( ucArrayToSend ), x100ms );
|
||||||
|
|
||||||
|
if( xBytesSent != sizeof( ucArrayToSend ) )
|
||||||
|
{
|
||||||
|
// The call to xMessageBufferSend() times out before there was enough
|
||||||
|
// space in the buffer for the data to be written.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the string to the message buffer. Return immediately if there is
|
||||||
|
// not enough space in the buffer.
|
||||||
|
xBytesSent = xMessageBufferSend( xMessageBuffer, ( void * ) pcStringToSend, strlen( pcStringToSend ), 0 );
|
||||||
|
|
||||||
|
if( xBytesSent != strlen( pcStringToSend ) )
|
||||||
|
{
|
||||||
|
// The string could not be added to the message buffer because there was
|
||||||
|
// not enough free space in the buffer.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xMessageBufferSend xMessageBufferSend
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSend( xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait ) xStreamBufferSend( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, xTicksToWait )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
size_t xMessageBufferSendFromISR( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
const void *pvTxData,
|
||||||
|
size_t xDataLengthBytes,
|
||||||
|
BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
<pre>
|
||||||
|
*
|
||||||
|
* Interrupt safe version of the API function that sends a discrete message to
|
||||||
|
* the message buffer. The message can be any length that fits within the
|
||||||
|
* buffer's free space, and is copied into the buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must place each call to a writing API function
|
||||||
|
* (such as xMessageBufferSend()) inside a critical section and set the send
|
||||||
|
* block time to 0. Likewise, if there are to be multiple different readers
|
||||||
|
* then the application writer must place each call to a reading API function
|
||||||
|
* (such as xMessageBufferRead()) inside a critical section and set the receive
|
||||||
|
* block time to 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferSend() to write to a message buffer from a task. Use
|
||||||
|
* xMessageBufferSendFromISR() to write to a message buffer from an interrupt
|
||||||
|
* service routine (ISR).
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer to which a message is
|
||||||
|
* being sent.
|
||||||
|
*
|
||||||
|
* @param pvTxData A pointer to the message that is to be copied into the
|
||||||
|
* message buffer.
|
||||||
|
*
|
||||||
|
* @param xDataLengthBytes The length of the message. That is, the number of
|
||||||
|
* bytes to copy from pvTxData into the message buffer. When a message is
|
||||||
|
* written to the message buffer an additional sizeof( size_t ) bytes are also
|
||||||
|
* written to store the message's length. sizeof( size_t ) is typically 4 bytes
|
||||||
|
* on a 32-bit architecture, so on most 32-bit architecture setting
|
||||||
|
* xDataLengthBytes to 20 will reduce the free space in the message buffer by 24
|
||||||
|
* bytes (20 bytes of message data and 4 bytes to hold the message length).
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken It is possible that a message buffer will
|
||||||
|
* have a task blocked on it waiting for data. Calling
|
||||||
|
* xMessageBufferSendFromISR() can make data available, and so cause a task that
|
||||||
|
* was waiting for data to leave the Blocked state. If calling
|
||||||
|
* xMessageBufferSendFromISR() causes a task to leave the Blocked state, and the
|
||||||
|
* unblocked task has a priority higher than the currently executing task (the
|
||||||
|
* task that was interrupted), then, internally, xMessageBufferSendFromISR()
|
||||||
|
* will set *pxHigherPriorityTaskWoken to pdTRUE. If
|
||||||
|
* xMessageBufferSendFromISR() sets this value to pdTRUE, then normally a
|
||||||
|
* context switch should be performed before the interrupt is exited. This will
|
||||||
|
* ensure that the interrupt returns directly to the highest priority Ready
|
||||||
|
* state task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it
|
||||||
|
* is passed into the function. See the code example below for an example.
|
||||||
|
*
|
||||||
|
* @return The number of bytes actually written to the message buffer. If the
|
||||||
|
* message buffer didn't have enough free space for the message to be stored
|
||||||
|
* then 0 is returned, otherwise xDataLengthBytes is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
<pre>
|
||||||
|
// A message buffer that has already been created.
|
||||||
|
MessageBufferHandle_t xMessageBuffer;
|
||||||
|
|
||||||
|
void vAnInterruptServiceRoutine( void )
|
||||||
|
{
|
||||||
|
size_t xBytesSent;
|
||||||
|
char *pcStringToSend = "String to send";
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
|
||||||
|
|
||||||
|
// Attempt to send the string to the message buffer.
|
||||||
|
xBytesSent = xMessageBufferSendFromISR( xMessageBuffer,
|
||||||
|
( void * ) pcStringToSend,
|
||||||
|
strlen( pcStringToSend ),
|
||||||
|
&xHigherPriorityTaskWoken );
|
||||||
|
|
||||||
|
if( xBytesSent != strlen( pcStringToSend ) )
|
||||||
|
{
|
||||||
|
// The string could not be added to the message buffer because there was
|
||||||
|
// not enough free space in the buffer.
|
||||||
|
}
|
||||||
|
|
||||||
|
// If xHigherPriorityTaskWoken was set to pdTRUE inside
|
||||||
|
// xMessageBufferSendFromISR() then a task that has a priority above the
|
||||||
|
// priority of the currently executing task was unblocked and a context
|
||||||
|
// switch should be performed to ensure the ISR returns to the unblocked
|
||||||
|
// task. In most FreeRTOS ports this is done by simply passing
|
||||||
|
// xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
|
||||||
|
// variables value, and perform the context switch if necessary. Check the
|
||||||
|
// documentation for the port in use for port specific instructions.
|
||||||
|
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xMessageBufferSendFromISR xMessageBufferSendFromISR
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSendFromISR( xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferSendFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvTxData, xDataLengthBytes, pxHigherPriorityTaskWoken )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
size_t xMessageBufferReceive( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
void *pvRxData,
|
||||||
|
size_t xBufferLengthBytes,
|
||||||
|
TickType_t xTicksToWait );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Receives a discrete message from a message buffer. Messages can be of
|
||||||
|
* variable length and are copied out of the buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must place each call to a writing API function
|
||||||
|
* (such as xMessageBufferSend()) inside a critical section and set the send
|
||||||
|
* block time to 0. Likewise, if there are to be multiple different readers
|
||||||
|
* then the application writer must place each call to a reading API function
|
||||||
|
* (such as xMessageBufferRead()) inside a critical section and set the receive
|
||||||
|
* block time to 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferReceive() to read from a message buffer from a task. Use
|
||||||
|
* xMessageBufferReceiveFromISR() to read from a message buffer from an
|
||||||
|
* interrupt service routine (ISR).
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer from which a message
|
||||||
|
* is being received.
|
||||||
|
*
|
||||||
|
* @param pvRxData A pointer to the buffer into which the received message is
|
||||||
|
* to be copied.
|
||||||
|
*
|
||||||
|
* @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData
|
||||||
|
* parameter. This sets the maximum length of the message that can be received.
|
||||||
|
* If xBufferLengthBytes is too small to hold the next message then the message
|
||||||
|
* will be left in the message buffer and 0 will be returned.
|
||||||
|
*
|
||||||
|
* @param xTicksToWait The maximum amount of time the task should remain in the
|
||||||
|
* Blocked state to wait for a message, should the message buffer be empty.
|
||||||
|
* xMessageBufferReceive() will return immediately if xTicksToWait is zero and
|
||||||
|
* the message buffer is empty. The block time is specified in tick periods, so
|
||||||
|
* the absolute time it represents is dependent on the tick frequency. The
|
||||||
|
* macro pdMS_TO_TICKS() can be used to convert a time specified in milliseconds
|
||||||
|
* into a time specified in ticks. Setting xTicksToWait to portMAX_DELAY will
|
||||||
|
* cause the task to wait indefinitely (without timing out), provided
|
||||||
|
* INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h. Tasks do not use any
|
||||||
|
* CPU time when they are in the Blocked state.
|
||||||
|
*
|
||||||
|
* @return The length, in bytes, of the message read from the message buffer, if
|
||||||
|
* any. If xMessageBufferReceive() times out before a message became available
|
||||||
|
* then zero is returned. If the length of the message is greater than
|
||||||
|
* xBufferLengthBytes then the message will be left in the message buffer and
|
||||||
|
* zero is returned.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
<pre>
|
||||||
|
void vAFunction( MessageBuffer_t xMessageBuffer )
|
||||||
|
{
|
||||||
|
uint8_t ucRxData[ 20 ];
|
||||||
|
size_t xReceivedBytes;
|
||||||
|
const TickType_t xBlockTime = pdMS_TO_TICKS( 20 );
|
||||||
|
|
||||||
|
// Receive the next message from the message buffer. Wait in the Blocked
|
||||||
|
// state (so not using any CPU processing time) for a maximum of 100ms for
|
||||||
|
// a message to become available.
|
||||||
|
xReceivedBytes = xMessageBufferReceive( xMessageBuffer,
|
||||||
|
( void * ) ucRxData,
|
||||||
|
sizeof( ucRxData ),
|
||||||
|
xBlockTime );
|
||||||
|
|
||||||
|
if( xReceivedBytes > 0 )
|
||||||
|
{
|
||||||
|
// A ucRxData contains a message that is xReceivedBytes long. Process
|
||||||
|
// the message here....
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xMessageBufferReceive xMessageBufferReceive
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReceive( xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait ) xStreamBufferReceive( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, xTicksToWait )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
size_t xMessageBufferReceiveFromISR( MessageBufferHandle_t xMessageBuffer,
|
||||||
|
void *pvRxData,
|
||||||
|
size_t xBufferLengthBytes,
|
||||||
|
BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* An interrupt safe version of the API function that receives a discrete
|
||||||
|
* message from a message buffer. Messages can be of variable length and are
|
||||||
|
* copied out of the buffer.
|
||||||
|
*
|
||||||
|
* ***NOTE***: Uniquely among FreeRTOS objects, the stream buffer
|
||||||
|
* implementation (so also the message buffer implementation, as message buffers
|
||||||
|
* are built on top of stream buffers) assumes there is only one task or
|
||||||
|
* interrupt that will write to the buffer (the writer), and only one task or
|
||||||
|
* interrupt that will read from the buffer (the reader). It is safe for the
|
||||||
|
* writer and reader to be different tasks or interrupts, but, unlike other
|
||||||
|
* FreeRTOS objects, it is not safe to have multiple different writers or
|
||||||
|
* multiple different readers. If there are to be multiple different writers
|
||||||
|
* then the application writer must place each call to a writing API function
|
||||||
|
* (such as xMessageBufferSend()) inside a critical section and set the send
|
||||||
|
* block time to 0. Likewise, if there are to be multiple different readers
|
||||||
|
* then the application writer must place each call to a reading API function
|
||||||
|
* (such as xMessageBufferRead()) inside a critical section and set the receive
|
||||||
|
* block time to 0.
|
||||||
|
*
|
||||||
|
* Use xMessageBufferReceive() to read from a message buffer from a task. Use
|
||||||
|
* xMessageBufferReceiveFromISR() to read from a message buffer from an
|
||||||
|
* interrupt service routine (ISR).
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer from which a message
|
||||||
|
* is being received.
|
||||||
|
*
|
||||||
|
* @param pvRxData A pointer to the buffer into which the received message is
|
||||||
|
* to be copied.
|
||||||
|
*
|
||||||
|
* @param xBufferLengthBytes The length of the buffer pointed to by the pvRxData
|
||||||
|
* parameter. This sets the maximum length of the message that can be received.
|
||||||
|
* If xBufferLengthBytes is too small to hold the next message then the message
|
||||||
|
* will be left in the message buffer and 0 will be returned.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken It is possible that a message buffer will
|
||||||
|
* have a task blocked on it waiting for space to become available. Calling
|
||||||
|
* xMessageBufferReceiveFromISR() can make space available, and so cause a task
|
||||||
|
* that is waiting for space to leave the Blocked state. If calling
|
||||||
|
* xMessageBufferReceiveFromISR() causes a task to leave the Blocked state, and
|
||||||
|
* the unblocked task has a priority higher than the currently executing task
|
||||||
|
* (the task that was interrupted), then, internally,
|
||||||
|
* xMessageBufferReceiveFromISR() will set *pxHigherPriorityTaskWoken to pdTRUE.
|
||||||
|
* If xMessageBufferReceiveFromISR() sets this value to pdTRUE, then normally a
|
||||||
|
* context switch should be performed before the interrupt is exited. That will
|
||||||
|
* ensure the interrupt returns directly to the highest priority Ready state
|
||||||
|
* task. *pxHigherPriorityTaskWoken should be set to pdFALSE before it is
|
||||||
|
* passed into the function. See the code example below for an example.
|
||||||
|
*
|
||||||
|
* @return The length, in bytes, of the message read from the message buffer, if
|
||||||
|
* any.
|
||||||
|
*
|
||||||
|
* Example use:
|
||||||
|
<pre>
|
||||||
|
// A message buffer that has already been created.
|
||||||
|
MessageBuffer_t xMessageBuffer;
|
||||||
|
|
||||||
|
void vAnInterruptServiceRoutine( void )
|
||||||
|
{
|
||||||
|
uint8_t ucRxData[ 20 ];
|
||||||
|
size_t xReceivedBytes;
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Initialised to pdFALSE.
|
||||||
|
|
||||||
|
// Receive the next message from the message buffer.
|
||||||
|
xReceivedBytes = xMessageBufferReceiveFromISR( xMessageBuffer,
|
||||||
|
( void * ) ucRxData,
|
||||||
|
sizeof( ucRxData ),
|
||||||
|
&xHigherPriorityTaskWoken );
|
||||||
|
|
||||||
|
if( xReceivedBytes > 0 )
|
||||||
|
{
|
||||||
|
// A ucRxData contains a message that is xReceivedBytes long. Process
|
||||||
|
// the message here....
|
||||||
|
}
|
||||||
|
|
||||||
|
// If xHigherPriorityTaskWoken was set to pdTRUE inside
|
||||||
|
// xMessageBufferReceiveFromISR() then a task that has a priority above the
|
||||||
|
// priority of the currently executing task was unblocked and a context
|
||||||
|
// switch should be performed to ensure the ISR returns to the unblocked
|
||||||
|
// task. In most FreeRTOS ports this is done by simply passing
|
||||||
|
// xHigherPriorityTaskWoken into portYIELD_FROM_ISR(), which will test the
|
||||||
|
// variables value, and perform the context switch if necessary. Check the
|
||||||
|
// documentation for the port in use for port specific instructions.
|
||||||
|
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
* \defgroup xMessageBufferReceiveFromISR xMessageBufferReceiveFromISR
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReceiveFromISR( xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken ) xStreamBufferReceiveFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pvRxData, xBufferLengthBytes, pxHigherPriorityTaskWoken )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
void vMessageBufferDelete( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Deletes a message buffer that was previously created using a call to
|
||||||
|
* xMessageBufferCreate() or xMessageBufferCreateStatic(). If the message
|
||||||
|
* buffer was created using dynamic memory (that is, by xMessageBufferCreate()),
|
||||||
|
* then the allocated memory is freed.
|
||||||
|
*
|
||||||
|
* A message buffer handle must not be used after the message buffer has been
|
||||||
|
* deleted.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer to be deleted.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define vMessageBufferDelete( xMessageBuffer ) vStreamBufferDelete( ( StreamBufferHandle_t ) xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
<pre>
|
||||||
|
BaseType_t xMessageBufferIsFull( MessageBufferHandle_t xMessageBuffer ) );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Tests to see if a message buffer is full. A message buffer is full if it
|
||||||
|
* cannot accept any more messages, of any size, until space is made available
|
||||||
|
* by a message being removed from the message buffer.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return If the message buffer referenced by xMessageBuffer is full then
|
||||||
|
* pdTRUE is returned. Otherwise pdFALSE is returned.
|
||||||
|
*/
|
||||||
|
#define xMessageBufferIsFull( xMessageBuffer ) xStreamBufferIsFull( ( StreamBufferHandle_t ) xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
<pre>
|
||||||
|
BaseType_t xMessageBufferIsEmpty( MessageBufferHandle_t xMessageBuffer ) );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Tests to see if a message buffer is empty (does not contain any messages).
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return If the message buffer referenced by xMessageBuffer is empty then
|
||||||
|
* pdTRUE is returned. Otherwise pdFALSE is returned.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define xMessageBufferIsEmpty( xMessageBuffer ) xStreamBufferIsEmpty( ( StreamBufferHandle_t ) xMessageBuffer )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
<pre>
|
||||||
|
BaseType_t xMessageBufferReset( MessageBufferHandle_t xMessageBuffer );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* Resets a message buffer to its initial empty state, discarding any message it
|
||||||
|
* contained.
|
||||||
|
*
|
||||||
|
* A message buffer can only be reset if there are no tasks blocked on it.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being reset.
|
||||||
|
*
|
||||||
|
* @return If the message buffer was reset then pdPASS is returned. If the
|
||||||
|
* message buffer could not be reset because either there was a task blocked on
|
||||||
|
* the message queue to wait for space to become available, or to wait for a
|
||||||
|
* a message to be available, then pdFAIL is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferReset xMessageBufferReset
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReset( xMessageBuffer ) xStreamBufferReset( ( StreamBufferHandle_t ) xMessageBuffer )
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
<pre>
|
||||||
|
size_t xMessageBufferSpaceAvailable( MessageBufferHandle_t xMessageBuffer ) );
|
||||||
|
</pre>
|
||||||
|
* Returns the number of bytes of free space in the message buffer.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return The number of bytes that can be written to the message buffer before
|
||||||
|
* the message buffer would be full. When a message is written to the message
|
||||||
|
* buffer an additional sizeof( size_t ) bytes are also written to store the
|
||||||
|
* message's length. sizeof( size_t ) is typically 4 bytes on a 32-bit
|
||||||
|
* architecture, so if xMessageBufferSpacesAvailable() returns 10, then the size
|
||||||
|
* of the largest message that can be written to the message buffer is 6 bytes.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferSpaceAvailable xMessageBufferSpaceAvailable
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSpaceAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer )
|
||||||
|
#define xMessageBufferSpacesAvailable( xMessageBuffer ) xStreamBufferSpacesAvailable( ( StreamBufferHandle_t ) xMessageBuffer ) /* Corrects typo in original macro name. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
<pre>
|
||||||
|
size_t xMessageBufferNextLengthBytes( MessageBufferHandle_t xMessageBuffer ) );
|
||||||
|
</pre>
|
||||||
|
* Returns the length (in bytes) of the next message in a message buffer.
|
||||||
|
* Useful if xMessageBufferReceive() returned 0 because the size of the buffer
|
||||||
|
* passed into xMessageBufferReceive() was too small to hold the next message.
|
||||||
|
*
|
||||||
|
* @param xMessageBuffer The handle of the message buffer being queried.
|
||||||
|
*
|
||||||
|
* @return The length (in bytes) of the next message in the message buffer, or 0
|
||||||
|
* if the message buffer is empty.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferNextLengthBytes xMessageBufferNextLengthBytes
|
||||||
|
* \ingroup MessageBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferNextLengthBytes( xMessageBuffer ) xStreamBufferNextMessageLengthBytes( ( StreamBufferHandle_t ) xMessageBuffer ) PRIVILEGED_FUNCTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
BaseType_t xMessageBufferSendCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* For advanced users only.
|
||||||
|
*
|
||||||
|
* The sbSEND_COMPLETED() macro is called from within the FreeRTOS APIs when
|
||||||
|
* data is sent to a message buffer or stream buffer. If there was a task that
|
||||||
|
* was blocked on the message or stream buffer waiting for data to arrive then
|
||||||
|
* the sbSEND_COMPLETED() macro sends a notification to the task to remove it
|
||||||
|
* from the Blocked state. xMessageBufferSendCompletedFromISR() does the same
|
||||||
|
* thing. It is provided to enable application writers to implement their own
|
||||||
|
* version of sbSEND_COMPLETED(), and MUST NOT BE USED AT ANY OTHER TIME.
|
||||||
|
*
|
||||||
|
* See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for
|
||||||
|
* additional information.
|
||||||
|
*
|
||||||
|
* @param xStreamBuffer The handle of the stream buffer to which data was
|
||||||
|
* written.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be
|
||||||
|
* initialised to pdFALSE before it is passed into
|
||||||
|
* xMessageBufferSendCompletedFromISR(). If calling
|
||||||
|
* xMessageBufferSendCompletedFromISR() removes a task from the Blocked state,
|
||||||
|
* and the task has a priority above the priority of the currently running task,
|
||||||
|
* then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a
|
||||||
|
* context switch should be performed before exiting the ISR.
|
||||||
|
*
|
||||||
|
* @return If a task was removed from the Blocked state then pdTRUE is returned.
|
||||||
|
* Otherwise pdFALSE is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferSendCompletedFromISR xMessageBufferSendCompletedFromISR
|
||||||
|
* \ingroup StreamBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferSendCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferSendCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* message_buffer.h
|
||||||
|
*
|
||||||
|
<pre>
|
||||||
|
BaseType_t xMessageBufferReceiveCompletedFromISR( MessageBufferHandle_t xStreamBuffer, BaseType_t *pxHigherPriorityTaskWoken );
|
||||||
|
</pre>
|
||||||
|
*
|
||||||
|
* For advanced users only.
|
||||||
|
*
|
||||||
|
* The sbRECEIVE_COMPLETED() macro is called from within the FreeRTOS APIs when
|
||||||
|
* data is read out of a message buffer or stream buffer. If there was a task
|
||||||
|
* that was blocked on the message or stream buffer waiting for data to arrive
|
||||||
|
* then the sbRECEIVE_COMPLETED() macro sends a notification to the task to
|
||||||
|
* remove it from the Blocked state. xMessageBufferReceiveCompletedFromISR()
|
||||||
|
* does the same thing. It is provided to enable application writers to
|
||||||
|
* implement their own version of sbRECEIVE_COMPLETED(), and MUST NOT BE USED AT
|
||||||
|
* ANY OTHER TIME.
|
||||||
|
*
|
||||||
|
* See the example implemented in FreeRTOS/Demo/Minimal/MessageBufferAMP.c for
|
||||||
|
* additional information.
|
||||||
|
*
|
||||||
|
* @param xStreamBuffer The handle of the stream buffer from which data was
|
||||||
|
* read.
|
||||||
|
*
|
||||||
|
* @param pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken should be
|
||||||
|
* initialised to pdFALSE before it is passed into
|
||||||
|
* xMessageBufferReceiveCompletedFromISR(). If calling
|
||||||
|
* xMessageBufferReceiveCompletedFromISR() removes a task from the Blocked state,
|
||||||
|
* and the task has a priority above the priority of the currently running task,
|
||||||
|
* then *pxHigherPriorityTaskWoken will get set to pdTRUE indicating that a
|
||||||
|
* context switch should be performed before exiting the ISR.
|
||||||
|
*
|
||||||
|
* @return If a task was removed from the Blocked state then pdTRUE is returned.
|
||||||
|
* Otherwise pdFALSE is returned.
|
||||||
|
*
|
||||||
|
* \defgroup xMessageBufferReceiveCompletedFromISR xMessageBufferReceiveCompletedFromISR
|
||||||
|
* \ingroup StreamBufferManagement
|
||||||
|
*/
|
||||||
|
#define xMessageBufferReceiveCompletedFromISR( xMessageBuffer, pxHigherPriorityTaskWoken ) xStreamBufferReceiveCompletedFromISR( ( StreamBufferHandle_t ) xMessageBuffer, pxHigherPriorityTaskWoken )
|
||||||
|
|
||||||
|
#if defined( __cplusplus )
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !defined( FREERTOS_MESSAGE_BUFFER_H ) */
|
||||||
160
Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h
vendored
Normal file
160
Middlewares/Third_Party/FreeRTOS/Source/include/mpu_prototypes.h
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the MPU is used the standard (non MPU) API functions are mapped to
|
||||||
|
* equivalents that start "MPU_", the prototypes for which are defined in this
|
||||||
|
* header files. This will cause the application code to call the MPU_ version
|
||||||
|
* which wraps the non-MPU version with privilege promoting then demoting code,
|
||||||
|
* so the kernel code always runs will full privileges.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef MPU_PROTOTYPES_H
|
||||||
|
#define MPU_PROTOTYPES_H
|
||||||
|
|
||||||
|
/* MPU versions of tasks.h API functions. */
|
||||||
|
BaseType_t MPU_xTaskCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskCreateStatic( TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * const pvParameters, UBaseType_t uxPriority, StackType_t * const puxStackBuffer, StaticTask_t * const pxTaskBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskDelete( TaskHandle_t xTaskToDelete ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskDelay( const TickType_t xTicksToDelay ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskAbortDelay( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskPriorityGet( const TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
eTaskState MPU_eTaskGetState( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSuspend( TaskHandle_t xTaskToSuspend ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskResume( TaskHandle_t xTaskToResume ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskStartScheduler( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSuspendAll( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskResumeAll( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TickType_t MPU_xTaskGetTickCount( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
char * MPU_pcTaskGetName( TaskHandle_t xTaskToQuery ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskGetHandle( const char *pcNameToQuery ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
configSTACK_DEPTH_TYPE MPU_uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void * MPU_pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint32_t MPU_ulTaskGetIdleRunTimeCounter( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskList( char * pcWriteBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint32_t MPU_ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskNotifyStateClear( TaskHandle_t xTask ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint32_t MPU_ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskIncrementTick( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTaskMissedYield( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskGetSchedulerState( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* MPU versions of queue.h API functions. */
|
||||||
|
BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxQueueSpacesAvailable( const QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueDelete( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateMutex( const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueUnregisterQueue( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
const char * MPU_pcQueueGetName( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueHandle_t MPU_xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueSetHandle_t MPU_xQueueCreateSet( const UBaseType_t uxEventQueueLength ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) FREERTOS_SYSTEM_CALL;
|
||||||
|
QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxQueueGetQueueNumber( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
uint8_t MPU_ucQueueGetQueueType( QueueHandle_t xQueue ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* MPU versions of timers.h API functions. */
|
||||||
|
TimerHandle_t MPU_xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TimerHandle_t MPU_xTimerCreateStatic( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction, StaticTimer_t *pxTimerBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void * MPU_pvTimerGetTimerID( const TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerIsTimerActive( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TaskHandle_t MPU_xTimerGetTimerDaemonTaskHandle( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
const char * MPU_pcTimerGetName( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxTimerGetReloadMode( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TickType_t MPU_xTimerGetPeriod( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
TickType_t MPU_xTimerGetExpiryTime( TimerHandle_t xTimer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerCreateTimerTask( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* MPU versions of event_group.h API functions. */
|
||||||
|
EventGroupHandle_t MPU_xEventGroupCreate( void ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventGroupHandle_t MPU_xEventGroupCreateStatic( StaticEventGroup_t *pxEventGroupBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) FREERTOS_SYSTEM_CALL;
|
||||||
|
EventBits_t MPU_xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vEventGroupDelete( EventGroupHandle_t xEventGroup ) FREERTOS_SYSTEM_CALL;
|
||||||
|
UBaseType_t MPU_uxEventGroupGetNumber( void* xEventGroup ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
/* MPU versions of message/stream_buffer.h API functions. */
|
||||||
|
size_t MPU_xStreamBufferSend( StreamBufferHandle_t xStreamBuffer, const void *pvTxData, size_t xDataLengthBytes, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferReceive( StreamBufferHandle_t xStreamBuffer, void *pvRxData, size_t xBufferLengthBytes, TickType_t xTicksToWait ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferNextMessageLengthBytes( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
void MPU_vStreamBufferDelete( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferIsFull( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferIsEmpty( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferReset( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferSpacesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
size_t MPU_xStreamBufferBytesAvailable( StreamBufferHandle_t xStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
BaseType_t MPU_xStreamBufferSetTriggerLevel( StreamBufferHandle_t xStreamBuffer, size_t xTriggerLevel ) FREERTOS_SYSTEM_CALL;
|
||||||
|
StreamBufferHandle_t MPU_xStreamBufferGenericCreate( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
StreamBufferHandle_t MPU_xStreamBufferGenericCreateStatic( size_t xBufferSizeBytes, size_t xTriggerLevelBytes, BaseType_t xIsMessageBuffer, uint8_t * const pucStreamBufferStorageArea, StaticStreamBuffer_t * const pxStaticStreamBuffer ) FREERTOS_SYSTEM_CALL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MPU_PROTOTYPES_H */
|
||||||
|
|
||||||
189
Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h
vendored
Normal file
189
Middlewares/Third_Party/FreeRTOS/Source/include/mpu_wrappers.h
vendored
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS Kernel V10.3.1
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* http://www.FreeRTOS.org
|
||||||
|
* http://aws.amazon.com/freertos
|
||||||
|
*
|
||||||
|
* 1 tab == 4 spaces!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPU_WRAPPERS_H
|
||||||
|
#define MPU_WRAPPERS_H
|
||||||
|
|
||||||
|
/* This file redefines API functions to be called through a wrapper macro, but
|
||||||
|
only for ports that are using the MPU. */
|
||||||
|
#ifdef portUSING_MPU_WRAPPERS
|
||||||
|
|
||||||
|
/* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is
|
||||||
|
included from queue.c or task.c to prevent it from having an effect within
|
||||||
|
those files. */
|
||||||
|
#ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Map standard (non MPU) API functions to equivalents that start
|
||||||
|
* "MPU_". This will cause the application code to call the MPU_
|
||||||
|
* version, which wraps the non-MPU version with privilege promoting
|
||||||
|
* then demoting code, so the kernel code always runs will full
|
||||||
|
* privileges.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Map standard tasks.h API functions to the MPU equivalents. */
|
||||||
|
#define xTaskCreate MPU_xTaskCreate
|
||||||
|
#define xTaskCreateStatic MPU_xTaskCreateStatic
|
||||||
|
#define xTaskCreateRestricted MPU_xTaskCreateRestricted
|
||||||
|
#define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions
|
||||||
|
#define vTaskDelete MPU_vTaskDelete
|
||||||
|
#define vTaskDelay MPU_vTaskDelay
|
||||||
|
#define vTaskDelayUntil MPU_vTaskDelayUntil
|
||||||
|
#define xTaskAbortDelay MPU_xTaskAbortDelay
|
||||||
|
#define uxTaskPriorityGet MPU_uxTaskPriorityGet
|
||||||
|
#define eTaskGetState MPU_eTaskGetState
|
||||||
|
#define vTaskGetInfo MPU_vTaskGetInfo
|
||||||
|
#define vTaskPrioritySet MPU_vTaskPrioritySet
|
||||||
|
#define vTaskSuspend MPU_vTaskSuspend
|
||||||
|
#define vTaskResume MPU_vTaskResume
|
||||||
|
#define vTaskSuspendAll MPU_vTaskSuspendAll
|
||||||
|
#define xTaskResumeAll MPU_xTaskResumeAll
|
||||||
|
#define xTaskGetTickCount MPU_xTaskGetTickCount
|
||||||
|
#define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks
|
||||||
|
#define pcTaskGetName MPU_pcTaskGetName
|
||||||
|
#define xTaskGetHandle MPU_xTaskGetHandle
|
||||||
|
#define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark
|
||||||
|
#define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2
|
||||||
|
#define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag
|
||||||
|
#define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag
|
||||||
|
#define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer
|
||||||
|
#define pvTaskGetThreadLocalStoragePointer MPU_pvTaskGetThreadLocalStoragePointer
|
||||||
|
#define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook
|
||||||
|
#define xTaskGetIdleTaskHandle MPU_xTaskGetIdleTaskHandle
|
||||||
|
#define uxTaskGetSystemState MPU_uxTaskGetSystemState
|
||||||
|
#define vTaskList MPU_vTaskList
|
||||||
|
#define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats
|
||||||
|
#define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter
|
||||||
|
#define xTaskGenericNotify MPU_xTaskGenericNotify
|
||||||
|
#define xTaskNotifyWait MPU_xTaskNotifyWait
|
||||||
|
#define ulTaskNotifyTake MPU_ulTaskNotifyTake
|
||||||
|
#define xTaskNotifyStateClear MPU_xTaskNotifyStateClear
|
||||||
|
#define ulTaskNotifyValueClear MPU_ulTaskNotifyValueClear
|
||||||
|
#define xTaskCatchUpTicks MPU_xTaskCatchUpTicks
|
||||||
|
|
||||||
|
#define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle
|
||||||
|
#define vTaskSetTimeOutState MPU_vTaskSetTimeOutState
|
||||||
|
#define xTaskCheckForTimeOut MPU_xTaskCheckForTimeOut
|
||||||
|
#define xTaskGetSchedulerState MPU_xTaskGetSchedulerState
|
||||||
|
|
||||||
|
/* Map standard queue.h API functions to the MPU equivalents. */
|
||||||
|
#define xQueueGenericSend MPU_xQueueGenericSend
|
||||||
|
#define xQueueReceive MPU_xQueueReceive
|
||||||
|
#define xQueuePeek MPU_xQueuePeek
|
||||||
|
#define xQueueSemaphoreTake MPU_xQueueSemaphoreTake
|
||||||
|
#define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting
|
||||||
|
#define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable
|
||||||
|
#define vQueueDelete MPU_vQueueDelete
|
||||||
|
#define xQueueCreateMutex MPU_xQueueCreateMutex
|
||||||
|
#define xQueueCreateMutexStatic MPU_xQueueCreateMutexStatic
|
||||||
|
#define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore
|
||||||
|
#define xQueueCreateCountingSemaphoreStatic MPU_xQueueCreateCountingSemaphoreStatic
|
||||||
|
#define xQueueGetMutexHolder MPU_xQueueGetMutexHolder
|
||||||
|
#define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive
|
||||||
|
#define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive
|
||||||
|
#define xQueueGenericCreate MPU_xQueueGenericCreate
|
||||||
|
#define xQueueGenericCreateStatic MPU_xQueueGenericCreateStatic
|
||||||
|
#define xQueueCreateSet MPU_xQueueCreateSet
|
||||||
|
#define xQueueAddToSet MPU_xQueueAddToSet
|
||||||
|
#define xQueueRemoveFromSet MPU_xQueueRemoveFromSet
|
||||||
|
#define xQueueSelectFromSet MPU_xQueueSelectFromSet
|
||||||
|
#define xQueueGenericReset MPU_xQueueGenericReset
|
||||||
|
|
||||||
|
#if( configQUEUE_REGISTRY_SIZE > 0 )
|
||||||
|
#define vQueueAddToRegistry MPU_vQueueAddToRegistry
|
||||||
|
#define vQueueUnregisterQueue MPU_vQueueUnregisterQueue
|
||||||
|
#define pcQueueGetName MPU_pcQueueGetName
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Map standard timer.h API functions to the MPU equivalents. */
|
||||||
|
#define xTimerCreate MPU_xTimerCreate
|
||||||
|
#define xTimerCreateStatic MPU_xTimerCreateStatic
|
||||||
|
#define pvTimerGetTimerID MPU_pvTimerGetTimerID
|
||||||
|
#define vTimerSetTimerID MPU_vTimerSetTimerID
|
||||||
|
#define xTimerIsTimerActive MPU_xTimerIsTimerActive
|
||||||
|
#define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle
|
||||||
|
#define xTimerPendFunctionCall MPU_xTimerPendFunctionCall
|
||||||
|
#define pcTimerGetName MPU_pcTimerGetName
|
||||||
|
#define vTimerSetReloadMode MPU_vTimerSetReloadMode
|
||||||
|
#define uxTimerGetReloadMode MPU_uxTimerGetReloadMode
|
||||||
|
#define xTimerGetPeriod MPU_xTimerGetPeriod
|
||||||
|
#define xTimerGetExpiryTime MPU_xTimerGetExpiryTime
|
||||||
|
#define xTimerGenericCommand MPU_xTimerGenericCommand
|
||||||
|
|
||||||
|
/* Map standard event_group.h API functions to the MPU equivalents. */
|
||||||
|
#define xEventGroupCreate MPU_xEventGroupCreate
|
||||||
|
#define xEventGroupCreateStatic MPU_xEventGroupCreateStatic
|
||||||
|
#define xEventGroupWaitBits MPU_xEventGroupWaitBits
|
||||||
|
#define xEventGroupClearBits MPU_xEventGroupClearBits
|
||||||
|
#define xEventGroupSetBits MPU_xEventGroupSetBits
|
||||||
|
#define xEventGroupSync MPU_xEventGroupSync
|
||||||
|
#define vEventGroupDelete MPU_vEventGroupDelete
|
||||||
|
|
||||||
|
/* Map standard message/stream_buffer.h API functions to the MPU
|
||||||
|
equivalents. */
|
||||||
|
#define xStreamBufferSend MPU_xStreamBufferSend
|
||||||
|
#define xStreamBufferReceive MPU_xStreamBufferReceive
|
||||||
|
#define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes
|
||||||
|
#define vStreamBufferDelete MPU_vStreamBufferDelete
|
||||||
|
#define xStreamBufferIsFull MPU_xStreamBufferIsFull
|
||||||
|
#define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty
|
||||||
|
#define xStreamBufferReset MPU_xStreamBufferReset
|
||||||
|
#define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable
|
||||||
|
#define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable
|
||||||
|
#define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel
|
||||||
|
#define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate
|
||||||
|
#define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove the privileged function macro, but keep the PRIVILEGED_DATA
|
||||||
|
macro so applications can place data in privileged access sections
|
||||||
|
(useful when using statically allocated objects). */
|
||||||
|
#define PRIVILEGED_FUNCTION
|
||||||
|
#define PRIVILEGED_DATA __attribute__((section("privileged_data")))
|
||||||
|
#define FREERTOS_SYSTEM_CALL
|
||||||
|
|
||||||
|
#else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */
|
||||||
|
|
||||||
|
/* Ensure API functions go in the privileged execution section. */
|
||||||
|
#define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions")))
|
||||||
|
#define PRIVILEGED_DATA __attribute__((section("privileged_data")))
|
||||||
|
#define FREERTOS_SYSTEM_CALL __attribute__((section( "freertos_system_calls")))
|
||||||
|
|
||||||
|
#endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */
|
||||||
|
|
||||||
|
#else /* portUSING_MPU_WRAPPERS */
|
||||||
|
|
||||||
|
#define PRIVILEGED_FUNCTION
|
||||||
|
#define PRIVILEGED_DATA
|
||||||
|
#define FREERTOS_SYSTEM_CALL
|
||||||
|
#define portUSING_MPU_WRAPPERS 0
|
||||||
|
|
||||||
|
#endif /* portUSING_MPU_WRAPPERS */
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* MPU_WRAPPERS_H */
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user