Compare commits

...

29 Commits

Author SHA1 Message Date
78eff31de7 文档结构调整 2026-08-28 22:02:24 +08:00
565f23ea18 时钟测试完毕 2026-08-28 21:59:04 +08:00
02a05a7a63 时间终于同步,存储成功 2026-08-28 21:10:51 +08:00
4e9ac0d9c3 时间同步已经完成 2026-08-28 16:42:44 +08:00
615a9b13e9 FTP 测试通过 2026-08-28 12:22:10 +08:00
cf33171c8f 存储测试通过 2026-08-27 18:22:44 +08:00
88160a5134 网络主要内容测试完成 2026-08-26 13:54:52 +08:00
0a707034cc 删除多连接内容,只支持单连接 2026-08-25 12:41:33 +08:00
d6bc3f6253 修改一些注释 2026-08-25 11:49:31 +08:00
4375873083 CH395F 硬件测试完毕 2026-08-24 16:16:18 +08:00
fc88bc8752 构建测试 2026-08-22 19:53:24 +08:00
a7f367f6a3 修改测试 2026-07-22 11:14:22 +08:00
c646df5c2a FTP可以上传文件 2026-07-22 01:46:29 +08:00
038a2542af 测试程序可以使用 2026-07-21 23:05:20 +08:00
46a74f24f3 FTP可以连接成功 2026-07-21 22:42:39 +08:00
75cf4c9fbf 成功启动 2026-07-21 22:24:47 +08:00
6d404d136e 初始创建FTP服务器 2026-07-21 22:08:11 +08:00
d48be81478 删除FlashDB 2026-07-21 21:48:23 +08:00
66a1e8fd6a 解决一个擦除的bug 2026-07-21 20:18:28 +08:00
f50b9c9e66 修改网络的初始化,异步支持问题 2026-07-21 01:22:09 +08:00
fbe0726991 增加文件系统 2026-07-21 00:30:59 +08:00
e06d849007 修复多连接的问题 2026-07-19 15:27:49 +08:00
9ed4c22022 freeRTOS支持 2026-07-19 13:09:06 +08:00
4eb728583b 通过测试排除了一些问题 2026-07-19 04:32:18 +08:00
1669cd216d 增加调试打印输出 2026-07-19 01:18:33 +08:00
f72d08328f TCP可以正常使用 2026-07-19 00:59:56 +08:00
fe107ea9af 关键芯片都驱动,且测试成功 2026-07-18 16:09:06 +08:00
3466c4c062 网络通,测试flash 2026-07-17 15:16:29 +08:00
c8c7ec0992 ch39f 驱动成功 2026-07-15 20:14:54 +08:00
185 changed files with 111600 additions and 103 deletions

File diff suppressed because one or more lines are too long

View 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.

94
AGENTS.md Normal file
View File

@@ -0,0 +1,94 @@
# STM32F4-Base
> **编译验证:所有代码修改后必须执行 `@build` 验证编译通过0 错误 0 警告),否则不要提交。**
## 构建
- **仅支持 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`)
## 架构
```
Src/main.c → 外设初始化 → app_main_init → gd5f2gq5ue_init → tpafe5160_init
→ osKernelInitialize → MX_FREERTOS_Init → osKernelStart
→ [FreeRTOS tasks: defaultTask, netTask, ftpTask]
```
- **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 0TCP LISTEN
- **新文件需手动添加到 Keil MDK 工程**才能编译
## 关键文件
| 路径 | 说明 |
|---|---|
| `Src/main.c` | 入口CubeMX 生成 + USER CODE 区域 |
| `Src/freertos.c` | FreeRTOS task 创建CubeMX 生成 + USER CODE 区域 |
| `App/task/net_task.c` | netTask 主循环(网络初始化 + 轮询) |
| `Drivers/BSP/NET/` | BSD Socket API 网络抽象层 |
| `Drivers/BSP/CH395F/` | CH395F SPI 驱动 |
| `Drivers/BSP/GD5F2GQ5UE/` | NAND Flash 驱动 + dhara FTL + FatFS diskio |
| `Drivers/BSP/TPAFE5160/` | ADC 并行接口驱动 |
| `Drivers/BSP/SD2506/` | RTC 驱动 |
| `Drivers/BSP/RS485/` | RS-485 通信UART5 + PD0 DE |
## 驱动关键点
详见 `docs/` 目录:
- `CH395F_Trap_Records.md` — CH395F 已知陷阱
- `GD5F2GQ5UE_Trap_Records.md` — GD5F2GQ5UE 已知陷阱
- `CH395F_Test_Guide.md` — 网络测试说明
## 注意
- `Inc/``Src/` 中 CubeMX 生成的文件带有 `USER CODE BEGIN`/`END` 标记,自定义代码写在这些区域之间
- `Drivers/BSP/` 下的驱动文件为纯手工代码,不受 CubeMX 保护
- CH395F 每次 SPI 事务需用 `ch395f_spi_begin()` / `ch395f_spi_end()` 包裹
- CH395F SPI PCB设计问题现在分频系数必须为 8速度相对较低
- FreeRTOS V10.3.1 via CMSIS-RTOS V2HAL 时基用 TIM7非 SysTick
- NVIC 优先级分组 4 位,外设中断优先级 ≥ 5`configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`
## 源码编辑与编码安全(铁律)
本工程源码含大量中文注释,曾因错误的文本重写方式导致整文件乱码(`U+FFFD` 替换符,不可逆)。以下规则必须遵守:
- **绝不用 `Get-Content` / `Set-Content` / `cmd` 文本重写含中文的源码文件**。这类"整文件重写"会按系统默认编码重新解码再写回,多字节中文必被 `U+FFFD` 替换且不可逆。
- **改代码只用 Edit 工具**(精确子串替换,不重新编解码整个文件,安全);批量替换用 Edit 的 `replaceAll`
- 必须脚本级替换时,使用显式且一致的编码(如 Python `open(p, encoding="utf-8")` 同编码回写并先确认工程编码Keil 工程常用 GBK——但**最稳的是别碰**,只用 Edit / git。
- **发现乱码先别急着 `git checkout`**`git checkout` 会丢弃所有未提交工作,使损失扩大。先评估能否用 Edit 仅改坏掉的那几行。
- 验证是否真损坏用字节级检查(统计 `EF BF BD` 出现次数),不要依赖终端/Read 渲染判断。

36
App/app_cfg.h Normal file
View 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 - TIM232-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
View 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
View 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
View File

369
App/sys_clock.c Normal file
View 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 congruence0=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
View 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
View 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
View 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
View 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~5netTask 不创建,
* 由 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

1333
Drivers/BSP/CH395F/ch395f.c Normal file

File diff suppressed because it is too large Load Diff

722
Drivers/BSP/CH395F/ch395f.h Normal file
View File

@@ -0,0 +1,722 @@
/*
* 模块名称CH395F SPI Ethernet Protocol Stack Driver
* 模块功能CH395F 以太网协议栈芯片 SPI 驱动提供网络初始化、Socket 管理、
* 数据收发等功能。命令码参考 CH395 手册 v2.2 Table 5-1
* 适用平台STM32F4 系列SPI2 接口)
* 作者:王建锋
* 创建日期2026-06-06
* 修改记录:
* 2026-06-06 王建锋 创建初始版本,按代码规范重构
*/
#ifndef __CH395F_H
#define __CH395F_H
/*
* 头文件包含区 - 仅包含本模块必需的头文件
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/*
* 宏定义区 - SPI 命令码(来自 CH395 手册 v2.2 Table 5-1
*/
/* 无输入数据,无输出数据 */
#define CH395F_CMD_RESET_ALL 0x05U
#define CH395F_CMD_ENTER_SLEEP 0x03U
/* 无输入数据1 字节输出 */
#define CH395F_CMD_GET_IC_VER 0x01U
#define CH395F_CMD_GET_CMD_STATUS 0x2CU
#define CH395F_CMD_GET_GLOB_INT_STATUS 0x29U
#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_DHCP_STATUS 0x42U
/* 无输入数据6 字节输出 */
#define CH395F_CMD_GET_MAC_ADDR 0x40U
/* 无输入数据20 字节输出 */
#define CH395F_CMD_GET_IP_INF 0x43U
/* 1 字节输入,无输出数据 */
#define CH395F_CMD_SET_PHY 0x20U
#define CH395F_CMD_PING_ENABLE 0x3FU
#define CH395F_CMD_DHCP_ENABLE 0x41U
#define CH395F_CMD_SET_RETRAN_COUNT 0x2AU
/* 1 字节输入1 字节输出 */
#define CH395F_CMD_CHECK_EXIST 0x06U
#define CH395F_CMD_SET_FUN_PARA 0x55U
/* 2 字节输入,无输出数据 */
#define CH395F_CMD_SET_TTL 0x51U
#define CH395F_CMD_SET_ARP 0x44U
/* 3 字节输入,无输出数据 */
#define CH395F_CMD_SET_BAUDRATE 0x02U
#define CH395F_CMD_SET_RETRAN_PERIOD 0x2BU
/* 4 字节输入,无输出数据 */
#define CH395F_CMD_SET_IP_ADDR 0x22U
#define CH395F_CMD_SET_GWIP_ADDR 0x23U
#define CH395F_CMD_SET_MASK_ADDR 0x24U
/* 6 字节输入,无输出数据 */
#define CH395F_CMD_SET_MAC_ADDR 0x21U
/* 初始化命令(仅写入,执行时间较长) */
#define CH395F_CMD_INIT_CH395 0x27U
/* Socket 命令 */
/* 1 字节输入socket 索引),无输出数据 */
#define CH395F_CMD_CLOSE_SOCKET_SN 0x3DU
#define CH395F_CMD_OPEN_SOCKET_SN 0x35U
#define CH395F_CMD_TCP_LISTEN_SN 0x36U
#define CH395F_CMD_TCP_CONNECT_SN 0x37U
#define CH395F_CMD_TCP_DISCONNECT_SN 0x38U
#define CH395F_CMD_CLEAR_RECV_BUF_SN 0x2EU
/* 1 字节输入1 字节输出socket 索引) */
#define CH395F_CMD_GET_INT_STATUS_SN 0x30U
#define CH395F_CMD_GET_SOCKET_STATUS_SN 0x2FU
/* 1 字节输入2 字节输出 */
#define CH395F_CMD_GET_RECV_LEN_SN 0x3BU
/* 2 字节输入,无输出数据 */
#define CH395F_CMD_SET_PROTO_TYPE_SN 0x34U
#define CH395F_CMD_SET_IPRAW_PRO_SN 0x3EU
/* 3 字节输入,无输出数据 */
#define CH395F_CMD_SET_DES_PORT_SN 0x32U
#define CH395F_CMD_SET_SOUR_PORT_SN 0x33U
/* 5 字节输入无输出数据socket + 4 字节 IP */
#define CH395F_CMD_SET_IP_ADDR_SN 0x31U
/* 1 字节输入6 字节输出socket 索引 -> 远程 IP + 端口) */
#define CH395F_CMD_GET_REMOT_IPP_SN 0x2DU
/* 3 字节输入 + 数据输出socket + 长度) */
#define CH395F_CMD_READ_RECV_BUF_SN 0x3CU
/* 3 字节输入 + 数据输入socket + 长度 + 数据) */
#define CH395F_CMD_WRITE_SEND_BUF_SN 0x39U
/*
* TCP MSSCH395F_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.4548 块 × 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 + 44 为命令头开销),有效载荷 = 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_RECV_BUF 0x52U
#define CH395F_CMD_SET_SEND_BUF 0x53U
#define CH395F_CMD_SET_KEEPALIVE_IDLE 0x56U
#define CH395F_CMD_SET_KEEPALIVE_INTVL 0x57U
#define CH395F_CMD_SET_KEEPALIVE_CNT 0x58U
#define CH395F_CMD_SET_KEEPALIVE_SN 0x59U
#define CH395F_CMD_EEPROM_ERASE 0xE9U
#define CH395F_CMD_EEPROM_WRITE 0xEAU
#define CH395F_CMD_EEPROM_READ 0xEBU
#define CH395F_CMD_READ_GPIO_REG 0xECH
#define CH395F_CMD_WRITE_GPIO_REG 0xEDU
/*
* CH395 命令执行状态码CMD_GET_CMD_STATUS 返回值,与芯片手册一致)
*/
#define CH395F_ERR_SUCCESS 0x00U /* 成功 */
#define CH395F_ERR_BUSY 0x10U /* 忙,命令正在执行 */
#define CH395F_ERR_MEM 0x11U /* 内存管理错误 */
#define CH395F_ERR_BUF 0x12U /* 缓冲区错误 */
#define CH395F_ERR_TIMEOUT 0x13U /* 超时 */
#define CH395F_ERR_RTE 0x14U /* 路由错误 */
#define CH395F_ERR_ABRT 0x15U /* 连接中止 */
#define CH395F_ERR_RST 0x16U /* 连接复位 */
#define CH395F_ERR_CLSD 0x17U /* 连接关闭 */
#define CH395F_ERR_CONN 0x18U /* 无连接 */
#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 状态码
*/
#define CH395F_PHY_DISCONN 0x01U /* PHY 断开连接 */
#define CH395F_PHY_10M_FULL 0x02U /* 10M 全双工 */
#define CH395F_PHY_10M_HALF 0x04U /* 10M 半双工 */
#define CH395F_PHY_100M_FULL 0x08U /* 100M 全双工 */
#define CH395F_PHY_100M_HALF 0x10U /* 100M 半双工 */
#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 中断 */
/*
* 协议类型
*/
#define CH395F_PROTO_TYPE_IP_RAW 0x00U
#define CH395F_PROTO_TYPE_MAC_RAW 0x01U
#define CH395F_PROTO_TYPE_UDP 0x02U
#define CH395F_PROTO_TYPE_TCP 0x03U
/*
* Socket 状态
*/
#define CH395F_SOCKET_CLOSED 0x00U
#define CH395F_SOCKET_OPEN 0x05U
/*
* TCP 状态
*/
#define CH395F_TCP_CLOSED 0x00U
#define CH395F_TCP_LISTEN 0x01U
#define CH395F_TCP_SYN_SENT 0x02U
#define CH395F_TCP_SYN_RCVD 0x03U
#define CH395F_TCP_ESTABLISHED 0x04U
#define CH395F_TCP_FIN_WAIT_1 0x05U
#define CH395F_TCP_FIN_WAIT_2 0x06U
#define CH395F_TCP_CLOSE_WAIT 0x07U
#define CH395F_TCP_CLOSING 0x08U
#define CH395F_TCP_LAST_ACK 0x09U
#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_IP_CONFLI 0x02U
#define CH395F_GINT_STAT_PHY_CHANGE 0x04U
#define CH395F_GINT_STAT_DHCP 0x08U
#define CH395F_GINT_STAT_SOCK0 0x10U
#define CH395F_GINT_STAT_SOCK1 0x20U
#define CH395F_GINT_STAT_SOCK2 0x40U
#define CH395F_GINT_STAT_SOCK3 0x80U
/*
* 全局中断状态位CMD_GET_GLOB_INT_STATUS_ALL 2字节版支持Socket 0~7
* 低字节高字节分别定义
*/
#define CH395F_GINT_STAT_SOCK4 0x0100U
#define CH395F_GINT_STAT_SOCK5 0x0200U
#define CH395F_GINT_STAT_SOCK6 0x0400U
#define CH395F_GINT_STAT_SOCK7 0x0800U
#define CH395F_GINT_STAT_DHCPV6 0x8000U
/*
* 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: 超时 */
/*
* 类型定义区 - 驱动返回码
*/
typedef enum {
CH395F_STATUS_OK = 0,
CH395F_STATUS_ERROR = -1,
CH395F_STATUS_TIMEOUT = -2,
CH395F_STATUS_NOT_DETECTED = -3
} ch395f_status_t;
/*
* 函数声明区 - SPI 基础事务层
*/
/*
* 函数功能:向 CH395 写入命令字节
* 入口参数cmd - 命令码 uint8_t 0x00 - 0xFF
* 返回值:命令码回显值 uint8_t
* 限定条件:需在 ch395f_spi_begin() 和 ch395f_spi_end() 之间调用
* 函数说明SPI 模式下命令与数据之间无需间隔
*/
uint8_t ch395f_write_cmd(uint8_t cmd);
/*
* 函数功能:向 CH395 写入数据字节
* 入口参数data - 数据字节 uint8_t 0x00 - 0xFF
* 返回值:数据回显值 uint8_t
* 限定条件:需在 ch395f_spi_begin() 和 ch395f_spi_end() 之间调用
*/
uint8_t ch395f_write_data(uint8_t data);
/*
* 函数功能:从 CH395 读取数据字节
* 返回值:接收到的数据字节 uint8_t
* 限定条件:需在 ch395f_spi_begin() 和 ch395f_spi_end() 之间调用
*/
uint8_t ch395f_read_data(void);
/*
* 函数声明区 - 芯片检测与版本
*/
/*
* 函数功能:检测 CH395 芯片是否存在且通信正常
* 返回值CH395F_STATUS_OK - 检测到芯片CH395F_STATUS_NOT_DETECTED - 未检测到
* 限定条件SPI2 已正确初始化
* 函数说明:发送 CMD_CHECK_EXIST (0x06) 及测试字节 0x57
* 芯片应返回按位取反值 0xA8
*/
ch395f_status_t ch395f_check_exist(void);
/*
* 函数功能:读取 CH395 芯片版本号
* 返回值版本字节bit5:0 为版本号uint8_t
* 限定条件:芯片已通过 ch395f_check_exist() 检测
*/
uint8_t ch395f_get_version(void);
/*
* 函数声明区 - 初始化
*/
/*
* 函数功能:软件复位 CH395
* 返回值CH395F_STATUS_OK
* 限定条件SPI2 已正确初始化
* 函数说明:复位耗时约 15msTE0函数内部已包含延时
*/
ch395f_status_t ch395f_reset(void);
/*
* 函数功能:查询命令执行状态
* 返回值状态字节CH395F_ERR_SUCCESS / CH395F_ERR_BUSY / ...uint8_t
* 限定条件:仅在长执行命令(如初始化)后调用
*/
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 协议栈)
* 返回值CH395F_STATUS_OK - 成功CH395F_STATUS_TIMEOUT - 超时失败
* 限定条件:芯片已通过 ch395f_reset() 复位
* 函数说明:阻塞等待初始化完成或超时(典型 5msTE1
*/
ch395f_status_t ch395f_init(void);
/*
* 函数声明区 - 网络参数配置
*/
/*
* 函数功能:设置 CH395 IP 地址(低字节在前)
* 入口参数p_ip - 4 字节 IP 地址指针 uint8_t*
* 限定条件:指针非空,芯片已初始化
*/
void ch395f_set_ip_addr(uint8_t *p_ip);
/*
* 函数功能:设置网关 IP 地址(低字节在前)
* 入口参数p_gwip - 4 字节网关 IP 指针 uint8_t*
* 限定条件:指针非空,芯片已初始化
*/
void ch395f_set_gwip_addr(uint8_t *p_gwip);
/*
* 函数功能:设置子网掩码(低字节在前)
* 入口参数p_mask - 4 字节子网掩码指针 uint8_t*
* 限定条件:指针非空,芯片已初始化
*/
void ch395f_set_mask_addr(uint8_t *p_mask);
/*
* 函数功能:设置 MAC 地址(低字节在前,存储于 EEPROM
* 入口参数p_mac - 6 字节 MAC 地址指针 uint8_t*
* 限定条件:指针非空,芯片已初始化
* 函数说明:执行耗时约 30usTE5
*/
void ch395f_set_mac_addr(uint8_t *p_mac);
/*
* 函数功能:从 CH395 读取 MAC 地址
* 出口参数p_mac - 6 字节 MAC 地址缓冲区指针 uint8_t*
* 限定条件:指针非空
*/
void ch395f_get_mac_addr(uint8_t *p_mac);
/*
* 函数功能:读取 IP 信息IP + 掩码 + DNS共 20 字节)
* 出口参数p_buf - 20 字节缓冲区指针 uint8_t*
* 限定条件:指针非空
*/
void ch395f_get_ip_inf(uint8_t *p_buf);
/*
* 函数声明区 - PHY 管理
*/
/*
* 函数功能:获取 PHY 连接状态
* 返回值PHY 状态码CH395F_PHY_DISCONN、CH395F_PHY_10M_FULL 等uint8_t
* 限定条件:芯片已初始化
*/
uint8_t ch395f_get_phy_status(void);
/*
* 函数功能:设置 PHY 连接模式
* 入口参数phystat - 连接模式码 uint8_t 0x20 = 自动协商
* 限定条件:芯片已初始化
*/
void ch395f_set_phy(uint8_t phystat);
/*
* 函数功能:使能/关闭 CH395 响应 PING
* 入口参数enable - 1 使能 PING 响应0 关闭
* 限定条件:芯片已初始化
* 函数说明:手册 5.37 CMD_PING_ENABLE默认关闭
*/
void ch395f_ping_enable(uint8_t enable);
/*
* 函数声明区 - DHCP
*/
/*
* 函数功能:启用/禁用 DHCP
* 入口参数enable - 1 启用0 禁用 uint8_t
* 限定条件:芯片已初始化
*/
void ch395f_set_dhcp(uint8_t enable);
/*
* 函数功能:获取 DHCP 状态
* 返回值0 = 成功,非零 = 错误/超时 uint8_t
* 限定条件:芯片已初始化且 DHCP 已启用
*/
uint8_t ch395f_get_dhcp_status(void);
/*
* 函数声明区 - Socket 管理
*/
/*
* 函数功能:设置 Socket 协议类型
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* proto - 协议类型 uint8_t CH395F_PROTO_TYPE_xxx
* 限定条件Socket 未打开
*/
void ch395f_set_proto_type(uint8_t sock, uint8_t proto);
/*
* 函数功能:设置 Socket 目标 IP 地址
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* p_ip - 4 字节目标 IP 指针 uint8_t*
* 限定条件:指针非空
*/
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip);
/*
* 函数功能:设置 Socket 目标端口(小端序)
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* port - 目标端口 uint16_t
* 限定条件:无
*/
void ch395f_set_des_port(uint8_t sock, uint16_t port);
/*
* 函数功能:设置 Socket 源端口(小端序)
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* port - 源端口 uint16_t
* 限定条件:无
*/
void ch395f_set_sour_port(uint8_t sock, uint16_t port);
/*
* 函数功能:打开 Socket
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值命令执行状态码CH395F_ERR_SUCCESS 表示成功) uint8_t
* 限定条件:协议类型、目标 IP、端口已设置
*/
uint8_t ch395f_open_socket(uint8_t sock);
/*
* 函数功能:关闭 Socket
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值命令执行状态码CH395F_ERR_SUCCESS 表示成功) uint8_t
* 限定条件Socket 已打开
*/
uint8_t ch395f_close_socket(uint8_t sock);
/*
* 函数功能:启动 TCP 监听模式
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值命令执行状态码CH395F_ERR_SUCCESS 表示成功) uint8_t
* 限定条件Socket 已打开且协议类型为 TCP
*/
uint8_t ch395f_tcp_listen(uint8_t sock);
/*
* 函数功能:启动 TCP 连接
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值命令执行状态码CH395F_ERR_SUCCESS 表示成功) uint8_t
* 限定条件Socket 已打开且协议类型为 TCP
*/
uint8_t ch395f_tcp_connect(uint8_t sock);
/*
* 函数功能:断开 TCP 连接
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值命令执行状态码CH395F_ERR_SUCCESS 表示成功) uint8_t
* 限定条件TCP 已建立连接
*/
uint8_t ch395f_tcp_disconnect(uint8_t sock);
/*
* 函数声明区 - Socket 数据传输
*/
/*
* 函数功能:向 Socket 发送缓冲区写入数据
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* p_data - 数据指针 uint8_t*
* len - 数据长度 uint16_t
* 限定条件:指针非空,长度大于 0TCP 已连接或 UDP 已打开
*/
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
/*
* 函数功能:从 Socket 接收缓冲区读取数据
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* p_data - 输出缓冲区指针 uint8_t*
* len - 待读取数据长度 uint16_t
* 限定条件:指针非空,长度大于 0接收缓冲区有数据
*/
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len);
/*
* 函数功能:获取 Socket 接收缓冲区数据长度
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值:接收数据长度 uint16_t
* 限定条件Socket 已打开
*/
uint16_t ch395f_get_recv_len(uint8_t sock);
/*
* 函数声明区 - 中断状态
*/
/*
* 函数功能获取全局中断状态2 字节版,支持 Socket 0~7
* 返回值:中断状态 uint16_t低字节=Socket 0~3 + PHY/DHCP高字节=Socket 4~7 + DHCPv6
* 限定条件:芯片已初始化
*/
uint16_t ch395f_get_glob_int_status_all(void);
/*
* 函数功能:获取 Socket 中断状态
* 入口参数sock - Socket 索引 uint8_t 0 - 7
* 返回值Socket 中断状态字节 uint8_t
* 限定条件:芯片已初始化
*/
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_t0=使用默认值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 重传周期(单位 100ms0=默认值=10即1秒 uint8_t
* cnt - ARP 重传次数0=默认值=3255=无限重传) 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
}
#endif
#endif /* __CH395F_H */

View File

@@ -0,0 +1,962 @@
/*
* 模块名称GD5F2GQ5UE SPI NAND Flash 驱动
* 模块功能:提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
* 适用平台STM32F407ZGT6 + SPI1 硬件 SPI
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
* 2026-07-17 王建锋 切换硬件 SPI修正擦除地址参考 NuttX 驱动
* 2026-08-27 补充函数注释(代码规范 V1.0);修正工厂坏块扫描为首页;增加 BBT 持久化
*/
/* 头文件包含区 */
#include <string.h>
#include "gd5f2gq5ue.h"
/*
* 调试输出配置
*/
#define DBG_TAG "[NAND]"
#include "dbg_log.h"
/* 私有宏定义区 */
#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 句柄声明 */
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_page_program(uint32_t page_addr, uint16_t column,
const uint8_t *p_buf, size_t size);
static int gd5f_set_feature(uint8_t addr, uint8_t data);
/* 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 位)
* 入口参数timeout_ms - 超时时间 uint32_t > 0
* 返回值0 - 操作完成,-2 - 超时
* 限定条件SPI 已初始化
* 函数说明:循环读取状态寄存器直到 OIP 位清零或超时
*/
int gd5f_wait_busy(uint32_t timeout_ms) {
uint8_t cmd = GD5F_CMD_GET_FEATURE;
uint8_t addr = GD5F_REG_STATUS;
uint8_t status = 0;
uint32_t tick_start = HAL_GetTick();
while (1) {
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, &status, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
if ((status & GD5F_STATUS_OIP) == 0) {
return GD5F_OK;
}
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
return GD5F_BUSY_TIMEOUT;
}
}
}
/*
* 函数功能:发送写使能命令
* 入口参数:无
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明CS# 拉低后发 06h 命令再拉高 */
int gd5f_write_enable(void) {
uint8_t cmd = GD5F_CMD_WRITE_ENABLE;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return GD5F_OK;
}
/*
* 函数功能:读取状态寄存器
* 出口参数p_status - 状态值输出指针 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发 0Fh + C0h 地址后读 1 字节状态 */
int gd5f_read_status(uint8_t *p_status) {
uint8_t cmd = GD5F_CMD_GET_FEATURE;
uint8_t addr = GD5F_REG_STATUS;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, p_status, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return GD5F_OK;
}
/*
* 函数功能:页读取(将数据从存储阵列加载到内部缓存)
* 入口参数page_addr - 页地址 uint32_t 0 ~ GD5F_TOTAL_BLOCKS*64-1
* 返回值0 - 成功,其它 - 错误
* 限定条件SPI 已初始化
* 函数说明:发 13h + 3字节行地址等待 OIP 清零
*/
int gd5f_page_read(uint32_t page_addr) {
uint8_t cmd[4] = {0};
cmd[0] = GD5F_CMD_PAGE_READ;
cmd[1] = (page_addr >> 16) & 0xFF;
cmd[2] = (page_addr >> 8) & 0xFF;
cmd[3] = page_addr & 0xFF;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
return gd5f_wait_busy(100);
}
/*
* 函数功能:从内部缓存读取数据
* 入口参数column - 列地址(页内偏移) uint16_t 0 - 2047
* size - 读取字节数 size_t > 0
* 出口参数p_buf - 数据输出缓冲区 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件:必须先调用 gd5f_page_read 完成数据加载
* 函数说明:发 0Bh + 2字节列地址 +1字节 dummy 后读取数据 */
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
size_t size) {
uint8_t cmd[4] = {0};
cmd[0] = GD5F_CMD_READ_FROM_CACHE;
cmd[1] = (column >> 8) & 0xFF;
cmd[2] = column & 0xFF;
cmd[3] = 0x00;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, 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();
return GD5F_OK;
}
/*
* 函数功能:页编程(将数据写入指定页)
* 入口参数page_addr - 页地址 uint32_t
* column - 列地址 uint16_t 0 - 2047
* p_buf - 数据缓冲区 const uint8_t*
* size - 写入字节数 size_t > 0
* 返回值0 - 成功,其它 - 错误 * 限定条件:目标区域已擦除
* 函数说明1. 写使能 - 02h 加载数据 - 10h 执行编程 - 等待完成
* 2. 编程完成后检查 P_FAIL 位 */
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
const uint8_t *p_buf, size_t size) {
int ret;
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[1] = (uint8_t)(column >> 8);
cmd[2] = (uint8_t)(column);
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 3, 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();
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[1] = (page_addr >> 16) & 0xFF;
cmd[2] = (page_addr >> 8) & 0xFF;
cmd[3] = page_addr & 0xFF;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
ret = gd5f_wait_busy(1000);
if (ret != GD5F_OK) return ret;
gd5f_read_status(&status);
if (status & GD5F_STATUS_P_FAIL) return GD5F_PROGRAM_FAIL;
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 寄存器
* 入口参数addr - 寄存器地址 uint8_t
* data - 写入数据 uint8_t
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:先写使能,发 1Fh + 地址 + 数据,等待操作完成 */
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
uint8_t cmd[3];
gd5f_write_enable();
cmd[0] = GD5F_CMD_SET_FEATURE;
cmd[1] = addr;
cmd[2] = data;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
gd5f_wait_busy(100);
return GD5F_OK;
}
/*
* 函数功能块擦除擦除128KB块
* 入口参数block_addr - 块编号 uint32_t 0 - 2047
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已调用
* 函数说明1. 写使能 - D8h + 3字节页地址 - 等待完成
* 2. 擦除完成后检查 E_FAIL */
int gd5f_block_erase(uint32_t block_addr) {
int ret = GD5F_OK;
uint8_t cmd[4];
uint8_t status = 0;
uint32_t page_addr = block_addr * GD5F_PAGES_PER_BLOCK;
gd5f_write_enable();
cmd[0] = GD5F_CMD_BLOCK_ERASE;
cmd[1] = (page_addr >> 16) & 0xFF;
cmd[2] = (page_addr >> 8) & 0xFF;
cmd[3] = page_addr & 0xFF;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
ret = gd5f_wait_busy(5000);
if (ret != GD5F_OK) {
return ret;
}
gd5f_read_status(&status);
if (status & GD5F_STATUS_E_FAIL) {
return GD5F_ERASE_FAIL;
}
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);
}
}
/* ======================== 公共函数定义 ======================== */
/*
* 函数功能:初始化 GD5F2GQ5UE
* 入口参数:无
* 返回值0 - 成功,其他 - 错误码
* 限定条件SPI1 和相关 GPIO 已由 CubeMX 初始化完成
* 函数说明1. 发送复位命令并等待完成
* 2. 读取芯片 ID 并校验
* 3. 扫描出厂坏块构建 BBTECC 使能前,读每块首页 spare[0]
* 4. 使能内部 ECC (B0h bit4)
* 5. 解除所有块保护 (A0h = 0x00)
* 6. 定位保留块池并加载持久化 BBTECC 使能后)
*/
int gd5f2gq5ue_init(void) {
int ret = GD5F_OK;
uint8_t mid = 0;
uint8_t did = 0;
GD5F_CS_HIGH();
GD5F_WP_HIGH();
GD5F_HOLD_HIGH();
HAL_Delay(10);
DBG_INFO("Resetting NAND...");
ret = gd5f2gq5ue_reset();
if (ret != GD5F_OK) {
DBG_ERROR("Reset failed: %d", ret);
return ret;
}
HAL_Delay(5);
DBG_INFO("Reading NAND ID...");
ret = gd5f2gq5ue_read_id(&mid, &did);
if (ret != GD5F_OK) {
DBG_ERROR("Read ID failed: %d", ret);
return ret;
}
DBG_INFO("NAND ID: MID=0x%02X, DID=0x%02X", mid, did);
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;
}
DBG_INFO("Scanning bad blocks...");
gd5f_bbt_scan();
DBG_INFO("Enabling ECC...");
gd5f_set_feature(0xB0, 0x10);
gd5f_wait_busy(100);
DBG_INFO("Unlocking block protection...");
gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
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;
}
/*
* 函数功能:读取芯片 IDMID + DID
* 出口参数p_mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
* p_did - 设备 ID 输出指针 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发 9Fh 命令后接 1 字节 dummy + MID + DID
*/
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
uint8_t cmd = GD5F_CMD_READ_ID;
uint8_t id_buf[3] = {0};
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
*p_mid = id_buf[1];
*p_did = id_buf[2];
return GD5F_OK;
}
/*
* 函数功能:从 NAND 读取数据(支持跨页)
* 入口参数offset - 起始字节偏移 long 0 ~ 总容量-1
* size - 读取字节数 size_t > 0
* 出口参数p_buf - 数据缓冲区 uint8_t* 不为 NULL
* 返回值0 - 成功,其它 - 错误
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明自动处理跨页读取offset+size 越界返回错误
*/
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
int ret = GD5F_OK;
if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
return GD5F_ERROR;
}
while (size > 0) {
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
uint16_t column = offset % GD5F_PAGE_SIZE;
size_t bytes = GD5F_PAGE_SIZE - column;
if (bytes > size) {
bytes = size;
}
ret = gd5f_page_read(page_addr);
if (ret != GD5F_OK) {
return ret;
}
ret = gd5f_read_from_cache(column, p_buf, bytes);
if (ret != GD5F_OK) {
return ret;
}
offset += bytes;
p_buf += bytes;
size -= bytes;
}
return GD5F_OK;
}
/*
* 函数功能:向 NAND 写入数据(支持跨页)
* 入口参数offset - 起始字节偏移 long 0 ~ 总容量-1
* p_buf - 数据缓冲区 const uint8_t* 不为 NULL
* size - 写入字节数 size_t > 0
* 返回值0 - 成功,其它 - 错误
* 限定条件gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明自动处理跨页写入offset+size 越界返回错误
*/
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
int ret = GD5F_OK;
if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
return GD5F_ERROR;
}
while (size > 0) {
uint32_t page_addr = offset / GD5F_PAGE_SIZE;
uint16_t column = offset % GD5F_PAGE_SIZE;
size_t bytes = GD5F_PAGE_SIZE - column;
if (bytes > size) {
bytes = size;
}
ret = gd5f_page_program(page_addr, column, p_buf, bytes);
if (ret != GD5F_OK) {
return ret;
}
offset += bytes;
p_buf += bytes;
size -= bytes;
}
return GD5F_OK;
}
/*
* 函数功能:擦除块(按块擦除,最小单位 128KB
* 入口参数offset - 起始字节偏移 long 必须 GD5F_BLOCK_SIZE 对齐
* size - 擦除字节数 size_t 必须 GD5F_BLOCK_SIZE 整数倍
* 返回值0 - 成功,其它 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明:循环擦除 [offset, offset+size) 覆盖的每个块,偏移/长度不对齐返回错误
*/
int gd5f2gq5ue_erase(long offset, size_t size) {
int ret = GD5F_OK;
if (offset % GD5F_BLOCK_SIZE != 0) {
return GD5F_ERROR;
}
if (size % GD5F_BLOCK_SIZE != 0) {
return GD5F_ERROR;
}
while (size > 0) {
uint32_t block_addr = offset / GD5F_BLOCK_SIZE;
ret = gd5f_block_erase(block_addr);
if (ret != GD5F_OK) {
return ret;
}
offset += GD5F_BLOCK_SIZE;
size -= GD5F_BLOCK_SIZE;
}
return GD5F_OK;
}
/*
* 函数功能:复位芯片
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发 FFh 复位命令后等待 5ms
*/
int gd5f2gq5ue_reset(void) {
uint8_t cmd = GD5F_CMD_RESET;
GD5F_CS_LOW();
HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
GD5F_CS_HIGH();
HAL_Delay(5);
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;
}
/*
* 函数功能:加载持久化 BBTOR 进 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();
}

View File

@@ -0,0 +1,238 @@
#ifndef __GD5F2GQ5UE_H
#define __GD5F2GQ5UE_H
/*
* 模块名称GD5F2GQ5UE SPI NAND Flash 驱动
* 模块功能:提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
* 适用平台STM32F407ZGT6 + SPI1 硬件 SPI
* 作者:王建锋
* 创建日期2026-07-16
* 修改记录:
* 2026-07-16 王建锋 创建初始版本
* 2026-07-17 王建锋 切换为硬件 SPI修正擦除地址参考 NuttX 驱动
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include <stdint.h>
/* ======================== 宏定义 ======================== */
/* SPI 命令码 */
#define GD5F_CMD_WRITE_ENABLE 0x06
#define GD5F_CMD_WRITE_DISABLE 0x04
#define GD5F_CMD_GET_FEATURE 0x0F
#define GD5F_CMD_SET_FEATURE 0x1F
#define GD5F_CMD_READ_ID 0x9F
#define GD5F_CMD_PAGE_READ 0x13
#define GD5F_CMD_READ_FROM_CACHE 0x0B
#define GD5F_CMD_PROGRAM_LOAD 0x02
#define GD5F_CMD_PROGRAM_EXEC 0x10
#define GD5F_CMD_BLOCK_ERASE 0xD8
#define GD5F_CMD_RESET 0xFF
/* 寄存器地址 */
#define GD5F_REG_PROTECT 0xA0
#define GD5F_REG_FEATURE 0xB0
#define GD5F_REG_STATUS 0xC0
#define GD5F_REG_DRIVER 0xD0
/* 状态位定义 */
#define GD5F_STATUS_OIP (1 << 0)
#define GD5F_STATUS_WEL (1 << 1)
#define GD5F_STATUS_E_FAIL (1 << 2)
#define GD5F_STATUS_P_FAIL (1 << 3)
#define GD5F_STATUS_ECCS0 (1 << 4)
#define GD5F_STATUS_ECCS1 (1 << 5)
/* Feature 位定义 */
#define GD5F_FEATURE_ECC_EN (1 << 4)
#define GD5F_FEATURE_QE (1 << 0)
/* 芯片参数 */
#define GD5F_PAGE_SIZE 2048
#define GD5F_SPARE_SIZE 64
#define GD5F_TOTAL_PAGE_SIZE 2112
#define GD5F_PAGES_PER_BLOCK 64
#define GD5F_BLOCK_SIZE (GD5F_PAGES_PER_BLOCK * GD5F_PAGE_SIZE)
#define GD5F_TOTAL_BLOCKS 2048
#define GD5F_TOTAL_SIZE (GD5F_TOTAL_BLOCKS * GD5F_BLOCK_SIZE)
/* 坏块表持久化:保留块池数量(从 NAND 顶部向下挑选出厂好块存储 BBT */
#define GD5F_BBT_POOL_COUNT 4
/* 制造商 ID 和设备 ID */
#define GD5F_MANUFACTURER_ID 0xC8
#define GD5F_DEVICE_ID 0x52
/* 返回值定义 */
#define GD5F_OK 0
#define GD5F_ERROR -1
#define GD5F_BUSY_TIMEOUT -2
#define GD5F_ECC_ERROR -3
#define GD5F_PROGRAM_FAIL -4
#define GD5F_ERASE_FAIL -5
#define GD5F_ID_MISMATCH -6
/* 控制引脚宏 */
#define GD5F_CS_LOW() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_RESET)
#define GD5F_CS_HIGH() HAL_GPIO_WritePin(GD_CS_GPIO_Port, GD_CS_Pin, GPIO_PIN_SET)
#define GD5F_WP_HIGH() HAL_GPIO_WritePin(GD_WP_GPIO_Port, GD_WP_Pin, GPIO_PIN_SET)
#define GD5F_HOLD_HIGH() HAL_GPIO_WritePin(GD_HOLD_GPIO_Port, GD_HOLD_Pin, GPIO_PIN_SET)
/* ======================== 函数声明 ======================== */
/*
* 函数功能:初始化 GD5F2GQ5UE读 ID + 使能 ECC + 解除块保护)
* 入口参数:无
* 返回值0 - 成功,其他 - 错误码
* 限定条件SPI1 和相关 GPIO 已由 CubeMX 初始化完成
* 函数说明1. 发送复位命令并等待完成
* 2. 读取芯片 ID 并校验
* 3. 使能内部 ECC (B0h bit4)
* 4. 解除所有块保护 (A0h = 0x00)
*/
int32_t gd5f2gq5ue_init(void);
/*
* 函数功能:读取芯片 IDMID + DID
* 入口参数mid - 制造商 ID 输出指针 uint8_t* 不为 NULL
* did - 设备 ID 输出指针 uint8_t* 不为 NULL
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 9Fh 命令后接收1个 dummy + MID + DID
*/
int32_t gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did);
/*
* 函数功能:从 NAND 读取数据(支持跨页)
* 入口参数offset - 起始字节偏移 int32_t 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 读取字节数 uint32_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明:自动处理跨页读取,每次读取不超过当前页剩余空间
*/
int gd5f2gq5ue_read(long offset, uint8_t *buf, size_t size);
/*
* 函数功能:向 NAND 写入数据(支持跨页)
* 入口参数offset - 起始字节偏移 int32_t 0 - GD5F_TOTAL_SIZE-1
* buf - 数据缓冲区 uint8_t* 不为 NULL
* size - 写入字节数 uint32_t > 0
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用,目标区域已擦除
* 函数说明:自动处理跨页写入,每次写入不超过当前页剩余空间
*/
int gd5f2gq5ue_write(long offset, const uint8_t *buf, size_t size);
/*
* 函数功能:擦除块(按块擦除,最小单位 128KB
* 入口参数offset - 起始字节偏移 int32_t 必须 GD5F_BLOCK_SIZE 对齐
* size - 擦除字节数 uint32_t 必须 GD5F_BLOCK_SIZE 整数倍
* 返回值0 - 成功,其他 - 错误码
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明擦除操作以块为单位offset 和 size 必须块对齐
*/
int gd5f2gq5ue_erase(long offset, size_t size);
/*
* 函数功能:复位芯片
* 入口参数:无
* 返回值0 - 成功
* 限定条件SPI 已初始化
* 函数说明:发送 FFh 复位命令后等待 5ms
*/
int32_t gd5f2gq5ue_reset(void);
/*
* 函数功能:查询块是否坏块
* 入口参数block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
* 返回值0 - 好块1 - 坏块
* 限定条件gd5f2gq5ue_init() 已成功调用
* 函数说明:通过 BBTBad 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
}
#endif
#endif /* __GD5F2GQ5UE_H */

View 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;
}

View 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

View 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.

View 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;
}

View 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);

View 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);
}

View 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);
}
}

View 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);
}

View 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;
}

View 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);

View 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);

View 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, ...)

View 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."

View File

@@ -0,0 +1,5 @@
#pragma once
#define CRLF "\r\n"
char* lftpd_string_trim(char* s);

View 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 */

View 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 */
}
}
/* 让出 CPUFreeRTOS 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;
}
}
/* 让出 CPUFreeRTOS 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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 时置 1net_recv_locked 读空后清零。
* net_select 据此判断可读性,从而无需在应用/测试任务上下文直接访问 CH395F SPI
* (所有 ch395f 事务统一由 netTask 串行执行,避免与 net_poll 竞争,见 Trap 16。 */
uint8_t readable;
/* 单连接模式自动重监听:连接断开后由 net 层自动回到 LISTENBSD 风格监听常驻) */
uint8_t auto_relisten;
/* 统计信息 */
uint32_t recv_bytes; /* 累计接收字节数 */
uint32_t send_bytes; /* 累计发送字节数 */
} net_sock_t;
#ifdef __cplusplus
}
#endif
#endif /* __NET_TYPES_H */

152
Drivers/BSP/RS485/rs485.c Normal file
View File

@@ -0,0 +1,152 @@
/*
* 模块名称RS-485 半双工通信驱动
* 模块功能RS-485 半双工通信驱动实现,封装任意 UART 外设实现 485 方向控制,
* 提供阻塞发送、中断接收IDLE 空闲帧检测)。使用 DWT CYCCNT 提供
* 微秒级延时,确保 RS485 收发器 DE 使能稳定。
* 适用平台STM32F4 系列
* 作者:王建锋
* 创建日期2026-07-18
* 修改记录:
* 2026-07-18 王建锋 创建初始版本
* 2026-07-20 王建锋 修复 DE 时序:添加 DWT 微秒延时替代 for 循环
* 2026-07-20 王建锋 清理中文注释乱码,对齐代码规范
*/
#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_RX(h) HAL_GPIO_WritePin((h)->dir_port, (h)->dir_pin, GPIO_PIN_RESET)
/* ======================== 公共函数实现 ======================== */
/*
* 函数功能:初始化 RS-485 句柄
* 入口参数p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
* p_huart - UART 外设句柄指针(已由 CubeMX 初始化) UART_HandleTypeDef*(非空)
* p_dir_port - 方向控制 GPIO 端口(如 GPIOD GPIO_TypeDef*(非空)
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0 uint16_t
* 返回值:无
* 限定条件UART 和 GPIO 须先由 CubeMX 完成初始化
* 函数说明初始化后默认处于接收状态DE 引脚拉低)
*/
void rs485_init(rs485_handle_t *p_handle,
UART_HandleTypeDef *p_huart,
GPIO_TypeDef *p_dir_port,
uint16_t dir_pin)
{
p_handle->huart = p_huart;
p_handle->dir_port = p_dir_port;
p_handle->dir_pin = dir_pin;
p_handle->rx_size = 0;
RS485_DIR_RX(p_handle);
}
/*
* 函数功能:阻塞方式发送数据
* 入口参数p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
* p_data - 待发送数据缓冲区 uint8_t*(非空)
* len - 待发送字节数 uint16_t 1 - N
* timeout - 发送超时ms传 0 使用 RS485_TIMEOUT_DEFAULT uint32_t 0 / 1 - N
* 返回值HAL_OK - 发送成功 HAL_StatusTypeDef
* HAL_TIMEOUT - 发送超时
* HAL_ERROR - 发送错误
* 限定条件:须在主循环或 FreeRTOS 任务中调用,不可在 UART 中断中调用
* 函数说明:发送前拉高 DE等待 50us 确保 RS485 收发器驱动使能稳定后再启动 UART 发送。
* HAL_UART_Transmit 内部等待 TC发送完成标志确保最后一字节完全移出后
* 再拉低 DE 切回接收状态。
*/
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
const uint8_t *p_data,
uint16_t len,
uint32_t timeout)
{
HAL_StatusTypeDef status = HAL_OK;
if (timeout == 0U) {
timeout = RS485_TIMEOUT_DEFAULT;
}
RS485_DIR_TX(p_handle);
delay_us(50);
status = HAL_UART_Transmit(p_handle->huart,
(uint8_t *)p_data,
len,
timeout);
RS485_DIR_RX(p_handle);
return status;
}
/*
* 函数功能启动中断方式接收IDLE 空闲帧检测)
* 入口参数p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
* p_buf - 接收缓冲区 uint8_t*(非空)
* buf_size - 缓冲区大小 uint16_t
* 返回值HAL_OK - 启动成功 HAL_StatusTypeDef
* HAL_ERROR - 启动失败
* 限定条件UART 须已开启全局中断NVIC 使能)
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收。
* 当 UART 总线空闲超过 1 个字符时间后硬件触发 IDLE 中断,
* HAL 自动调用 HAL_UARTEx_RxEventCallback 并返回已接收字节数。
* 用户须在回调中重新调用本函数重新开启接收。
*/
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
uint8_t *p_buf,
uint16_t buf_size)
{
p_handle->rx_size = 0;
return HAL_UARTEx_ReceiveToIdle_IT(p_handle->huart, p_buf, buf_size);
}
/*
* 函数功能:缓存最近一次接收的字节数
* 入口参数p_handle - RS-485 句柄指针 rs485_handle_t*(非空)
* size - 本次接收到的字节数 uint16_t
* 返回值:无
* 限定条件:须在 HAL_UARTEx_RxEventCallback 中调用
* 函数说明:将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
*/
void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size)
{
p_handle->rx_size = size;
}
/*
* 函数功能:获取最近一次接收的字节数
* 入口参数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 *p_handle)
{
return p_handle->rx_size;
}

110
Drivers/BSP/RS485/rs485.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* 模块名称RS-485 半双工通信驱动
* 模块功能RS-485 半双工通信驱动,封装任意 UART 外设实现 485 方向控制,
* 提供阻塞发送、中断接收IDLE 空闲帧检测、DMA 发送等功能。
* 参考 ST AN3070 应用笔记《IO-Link master: USART communication》
* 及 controllerstech.com RS485 教程
* 适用平台STM32F4 系列
* 作者:王建锋
* 创建日期2026-07-18
* 修改记录:
* 2026-07-18 王建锋 创建初始版本
*/
#ifndef __RS485_H
#define __RS485_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/*
* 常量定义
*/
#define RS485_TIMEOUT_DEFAULT 1000U /* 默认发送超时ms */
/*
* RS-485 句柄结构体
* 将任意 UART + 任意 GPIO 组合绑定为一个 RS-485 半双工通信实例
*/
typedef struct {
UART_HandleTypeDef *huart; /* UART 外设句柄 */
GPIO_TypeDef *dir_port; /* 方向控制 GPIO 端口 */
uint16_t dir_pin; /* 方向控制 GPIO 引脚号 */
volatile uint16_t rx_size; /* 最近一次接收的字节数 */
} rs485_handle_t;
/*
* 公共函数声明
*/
/*
* 函数功能:初始化 RS-485 句柄,绑定 UART 和方向控制引脚
* 入口参数handle - RS-485 句柄指针
* huart - UART 外设句柄指针(已由 CubeMX 初始化)
* dir_port - 方向控制 GPIO 端口(如 GPIOD
* dir_pin - 方向控制 GPIO 引脚号(如 GPIO_PIN_0
* 返回值:无
* 限定条件UART 和 GPIO 须先由 CubeMX 完成初始化
* 函数说明初始化后默认处于接收状态DE 引脚拉低)
*/
void rs485_init(rs485_handle_t *p_handle,
UART_HandleTypeDef *p_huart,
GPIO_TypeDef *p_dir_port,
uint16_t dir_pin);
/*
* 函数功能:阻塞方式发送数据
* 入口参数handle - RS-485 句柄指针
* data - 待发送数据缓冲区
* len - 待发送字节数
* timeout - 发送超时ms传 0 使用 RS485_TIMEOUT_DEFAULT
* 返回值HAL_OK / HAL_TIMEOUT / HAL_ERROR
* 限定条件:在主循环或任务中调用,不可在 UART 中断中调用
* 函数说明:发送前自动拉高 DE发送完成后等待 TC 标志再拉低 DE
* 确保最后一字节完全移出后再切换到接收状态
*/
HAL_StatusTypeDef rs485_transmit(rs485_handle_t *p_handle,
const uint8_t *p_data,
uint16_t len,
uint32_t timeout);
/*
* 函数功能启动中断方式接收IDLE 空闲帧检测)
* 入口参数handle - RS-485 句柄指针
* buf - 接收缓冲区
* buf_size - 缓冲区大小
* 返回值HAL_OK / HAL_ERROR
* 限定条件UART 须已开启全局中断NVIC 中须使能对应 UART 中断
* 函数说明:使用 HAL_UARTEx_ReceiveToIdle_IT 实现变长帧接收,
* 收到完整帧后在 RxEventCallback 中通知用户。
* 用户须在回调中重新调用本函数重新开启接收
*/
HAL_StatusTypeDef rs485_receive_start(rs485_handle_t *p_handle,
uint8_t *p_buf,
uint16_t buf_size);
/*
* 函数功能:将 RS-485 句柄与 HAL 回调关联
* 入口参数handle - RS-485 句柄指针
* 返回值:无
* 限定条件:须在 HAL_UART_RxCpltCallback / HAL_UARTEx_RxEventCallback 中调用
* 函数说明:将 HAL 回调中接收到的数据大小回写到句柄的 rx_size 字段
*/
void rs485_rx_set_size(rs485_handle_t *p_handle, uint16_t size);
/*
* 函数功能:获取最近一次接收的字节数
* 入口参数handle - RS-485 句柄指针
* 返回值:最近一次接收的字节数
* 限定条件:须在 HAL_UARTEx_RxEventCallback 触发后调用
*/
uint16_t rs485_rx_get_size(const rs485_handle_t *p_handle);
#ifdef __cplusplus
}
#endif
#endif /* __RS485_H */

497
Drivers/BSP/SD2506/sd2506.c Normal file
View File

@@ -0,0 +1,497 @@
/*
* 模块名称SD2506API-G RTC Driver
* 模块功能SD2506API-G 高精度温补实时时钟驱动I2C 接口
* 适用平台STM32F407ZGTx + SD2506API-G 实时时钟芯片I2C1: PB6-SCL, PB7-SDA
* 作者:王建锋
* 创建日期2026-07-17
* 修改记录:
* 2026-07-17 王建锋 创建初始版本,参考 SD2506API-G Ver2.0 手册
*/
#include <string.h>
#include <stdio.h>
#include "sd2506.h"
#include "i2c.h"
extern I2C_HandleTypeDef hi2c1;
/* ======================== 内部辅助函数 ======================== */
/*
* 函数功能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);
}
/*
* 函数功能:十进制转 BCD 码
* 入口参数dec - 十进制数 uint8_t 0 - 99 */
uint8_t sd2506_dec_to_bcd(uint8_t dec) {
return ((dec / 10) << 4) | (dec % 10);
}
/*
* 函数功能:写单字节寄存器
* 入口参数reg - 寄存器地址 uint8_t 0x00 - 0x79
* val - 写入数据 uint8_t 0x00 - 0xFF
* 返回值0-成功2-I2C 错误 int
* 限定条件I2C 外设已初始化
* 函数说明:通过 I2C 向指定地址写入 1 字节数据
* 限定条件I2C 外设已初始化
* 函数说明:通过 I2C 向指定地址写入 1 字节
*/
static int sd2506_write_reg(uint8_t reg, uint8_t val) {
if (HAL_I2C_Mem_Write(&hi2c1, SD2506_I2C_ADDR_WRITE, reg,
I2C_MEMADD_SIZE_8BIT, &val, 1,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:读单字节寄存器
* 入口参数reg - 寄存器地址 uint8_t 0x00 - 0x79
* p_val - 读取值指针 uint8_t*(非空)
* 返回值0-成功2-I2C 错误 int
* 限定条件I2C 外设已初始化p_val 为非空指针
* 函数说明:通过 I2C 从指定地址读取 1 字节数据
* 限定条件I2C 外设已初始化val 为非空指针函数说明:通过 I2C 从指定地址读取 1 字节
*/
static int sd2506_read_reg(uint8_t reg, uint8_t *p_val) {
if (HAL_I2C_Mem_Read(&hi2c1, SD2506_I2C_ADDR_READ, reg,
I2C_MEMADD_SIZE_8BIT, p_val, 1,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:写多字节寄存器(连续写)
* 入口参数reg - 起始寄存器地址 uint8_t 0x00 - 0x79
* p_data - 数据缓冲区指针 uint8_t*(非空)
* len - 数据长度 uint8_t 1 - N
* 返回值0-成功2-I2C 错误 int
* 限定条件I2C 外设已初始化p_data 为非空指针
* 函数说明:从 reg 开始连续写入 len 字节数据
* 限定条件I2C 外设已初始化data 为非空指针
*/
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,
I2C_MEMADD_SIZE_8BIT, (uint8_t *)p_data, len,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:读多字节寄存器(连续读)
* 入口参数reg - 起始寄存器地址 uint8_t 0x00 - 0x79
* p_data - 数据缓冲区指针 uint8_t*(非空)
* len - 数据长度 uint8_t 1 - N
* 返回值0-成功2-I2C 错误 int
* 限定条件I2C 外设已初始化p_data 为非空指针
* 函数说明:从 reg 开始连续读取 len 字节数据
* 限定条件I2C 外设已初始化data 为非空指针
*/
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,
I2C_MEMADD_SIZE_8BIT, p_data, len,
SD2506_I2C_TIMEOUT_MS) != HAL_OK) {
return SD2506_I2C_ERROR;
}
return SD2506_OK;
}
/*
* 函数功能:开启写保护(允许写入 00H~71H 寄存器)
* 入口参数:无
* 返回值0 - 成功 2 - I2C 错误
* 限定条件I2C 外设已初始化
* 函数说明:顺序:先写 WRTC1=1, 再写 WRTC2=1 + WRTC3=1
*/
static int sd2506_write_enable(void) {
int ret = 0;
/* 写允许顺序SD2506/SD24xx 手册要求):先置 WRTC1(CTR2 bit7)=1
* 再置 WRTC2(CTR1 bit2) + WRTC3(CTR1 bit7)=1。顺序反了芯片不会解开写保护。 */
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x80U);
if (ret != SD2506_OK) return ret;
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x84U);
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/*
* 函数功能:关闭写保护(禁止写入 00H~71H 寄存器)
* 入口参数:无
* 返回值0 - 成功 2 - I2C 错误
* 限定条件I2C 外设已初始化
* 函数说明:顺序:先写 WRTC2=0 + WRTC3=0, 再写 WRTC1=0
*/
static int sd2506_write_disable(void) {
int ret = 0;
/* 写禁止顺序(手册要求):先清 WRTC2/WRTC3(CTR1),再清 WRTC1(CTR2) */
ret = sd2506_write_reg(SD2506_REG_CTR1, 0x00U);
if (ret != SD2506_OK) return ret;
ret = sd2506_write_reg(SD2506_REG_CTR2, 0x00U);
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/* ======================== 公共 API 实现 ======================== */
/*
* 函数功能:初始化 SD2506API-G RTC
* 入口参数:无
* 返回值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 */
ret = sd2506_read_regs(SD2506_REG_ID_START, a_id, SD2506_ID_SIZE);
if (ret != SD2506_OK) {
return ret;
}
/* 上电重置充电寄存器 18H = 82H (开启充电 5K电阻)
* 手册强烈建议每次上电时重置此寄存器 */
ret = sd2506_write_reg(SD2506_REG_CHARGE, 0x82U);
if (ret != SD2506_OK) {
return ret;
}
/* 配置: 24小时制写允许状态下 0FH 的 WRTC 位必须为 1
* 0FH = 0x20 (bit5=ARST=1, 其它标志位清零)
* 注意: 写入时需注意 0FH 的 WRTC 位必须为 1
* 此处直接写入 0x20 即可 (ARST=1, 的 WRTC 位) */
ret = sd2506_write_reg(SD2506_REG_CTR1, SD2506_CTR1_WRITE_OFF);
if (ret != SD2506_OK) {
return ret;
}
return SD2506_OK;
}
/*
* 函数功能:设置 RTC 时间日期
* 入口参数p_time - 时间结构体指针,包含要设置的时间
* 返回值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 (p_time == NULL) {
return SD2506_ERROR;
}
/* 组装 7 字节时间数据 (BCD 码) */
buf[0] = sd2506_dec_to_bcd(p_time->second); /* 00H: 秒 */
buf[1] = sd2506_dec_to_bcd(p_time->minute); /* 01H: 分 */
buf[2] = sd2506_dec_to_bcd(p_time->hour) | 0x80U; /* 02H: 时(bit7=1, 24小时制) */
buf[3] = sd2506_dec_to_bcd(p_time->week); /* 03H: 星期 */
buf[4] = sd2506_dec_to_bcd(p_time->day); /* 04H: 日 */
buf[5] = sd2506_dec_to_bcd(p_time->month); /* 05H: 月 */
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 06H: 年 */
/* 开启写保护 */
ret = sd2506_write_enable();
if (ret != SD2506_OK) return ret;
/* 一次性写入 7 字节时间数据 (00H~06H)
* 手册要求: 不可单独写某一个时间寄存器,否则可能引起错误进入 */
ret = sd2506_write_regs(SD2506_REG_SEC, buf, 7);
if (ret != SD2506_OK) return ret;
/* 关闭写保护 */
ret = sd2506_write_disable();
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/*
* 函数功能:读取 RTC 时间日期
* 入口参数p_time - 输出时间结构体指针 sd2506_time_t*(非空)
* 返回值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 (p_time == NULL) {
return SD2506_ERROR;
}
/* 一次读取 7 字节时间数据 (00H~06H)
* 手册说明: 读取时所有实时数据被锁存,避免错误 */
ret = sd2506_read_regs(SD2506_REG_SEC, buf, 7);
if (ret != SD2506_OK) {
return ret;
}
p_time->second = sd2506_bcd_to_dec(buf[0] & 0x7FU); /* 00H: 秒 */
p_time->minute = sd2506_bcd_to_dec(buf[1] & 0x7FU); /* 01H: 分 */
p_time->hour = sd2506_bcd_to_dec(buf[2] & 0x7FU); /* 02H: 屏蔽 bit7 (12/24标志) */
p_time->week = sd2506_bcd_to_dec(buf[3] & 0x07U); /* 03H: 星期 */
p_time->day = sd2506_bcd_to_dec(buf[4] & 0x3FU); /* 04H: 日 */
p_time->month = sd2506_bcd_to_dec(buf[5] & 0x1FU); /* 05H: 月 */
p_time->year = sd2506_bcd_to_dec(buf[6]) + 2000U; /* 06H: 年 */
return SD2506_OK;
}
/*
* 函数功能:读取内部温度
* 入口参数p_temp - 输出温度值指针 int8_t*(非空)
* 返回值0-成功2-I2C 错误 int
* 限定条件sd2506_init() 已调用成功
* 函数说明:读取寄存器 0x16bit7 为符号位
* 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 (p_temp == NULL) {
return SD2506_ERROR;
}
ret = sd2506_read_reg(SD2506_REG_TEMP, &val);
if (ret != SD2506_OK) {
return ret;
}
/* bit7 为符号位, 其余为温度值 */
if (val & 0x80U) {
/* 负温度 取补码 */
*p_temp = (int8_t)(val | 0xF0U);
} else {
/* 正温度 */
*p_temp = (int8_t)(val & 0x7FU);
}
return SD2506_OK;
}
/*
* 函数功能:读取电池电压(毫伏)
* 入口参数p_voltage - 输出电压指针 uint16_t*(非空)
* 返回值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 (p_voltage == NULL) {
return SD2506_ERROR;
}
/* 读取 1AH bit7 (BAT8_VAL) 与 1BH (BAT_VL) */
ret = sd2506_read_reg(SD2506_REG_CTR5, &val_high);
if (ret != SD2506_OK) return ret;
ret = sd2506_read_reg(SD2506_REG_BAT_VAL, &val_low);
if (ret != SD2506_OK) return ret;
/* 组合 9 位值 bit8=BAT8_VAL(1AH bit7), bit7~0=BAT_VL(1BH)
* 例: 1AH=80H, 1BH=30H => 电压 = 0x130 = 304 => 3.04V => 3040mV */
uint16_t raw = ((val_high & 0x80U) << 1) | val_low;
*p_voltage = raw * 10; /* 转换为毫伏 (raw 单位 0.01V) */
return SD2506_OK;
}
/*
* 函数功能:读取 8 字节芯片 ID
* 入口参数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_read_regs(SD2506_REG_ID_START, p_id, SD2506_ID_SIZE);
}
/*
* 函数功能:读取 SRAM 数据
* 入口参数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;
}
if (addr + len > SD2506_SRAM_SIZE) {
return SD2506_ERROR;
}
return sd2506_read_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
}
/*
* 函数功能:写入 SRAM 数据
* 入口参数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;
}
if (addr + len > SD2506_SRAM_SIZE) {
return SD2506_ERROR;
}
/* SRAM 无需开写保护即可写入 (写保护仅对 00H~71H 有效, SRAM 为 2CH~71H)
* 但为安全起见, SRAM 写入也走写保护流程 */
int ret = 0;
ret = sd2506_write_enable();
if (ret != SD2506_OK) return ret;
ret = sd2506_write_regs(SD2506_REG_SRAM_START + addr, p_buf, len);
if (ret != SD2506_OK) return ret;
ret = sd2506_write_disable();
return ret;
}
/*
* 函数功能:设置闹钟中断
* 入口参数p_time - 报警时间结构体指针 sd2506_time_t*(非空)
* 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 (p_time == NULL) {
return SD2506_ERROR;
}
/* 组装 8 字节报警数据 (07H~0EH) */
buf[0] = sd2506_dec_to_bcd(p_time->second) & 0x7FU; /* 07H: 秒报 */
buf[1] = sd2506_dec_to_bcd(p_time->minute) & 0x7FU; /* 08H: 分报 */
buf[2] = sd2506_dec_to_bcd(p_time->hour) & 0x3FU; /* 09H: 时报最高位始终为0 */
buf[3] = 0x00U; /* 0AH: 星期报警 */
buf[4] = sd2506_dec_to_bcd(p_time->day) & 0x3FU; /* 0BH: 日报 */
buf[5] = sd2506_dec_to_bcd(p_time->month) & 0x1FU; /* 0CH: 月报 */
buf[6] = sd2506_dec_to_bcd(p_time->year - 2000U); /* 0DH: 年报 */
buf[7] = mask; /* 0EH: 报警允许 */
/* 开启写保护 */
ret = sd2506_write_enable();
if (ret != SD2506_OK) return ret;
/* 写入报警寄存器 (07H~0EH) */
ret = sd2506_write_regs(SD2506_REG_AL_SEC, buf, 8);
if (ret != SD2506_OK) return ret;
/* 使能报警中断: CTR2 中 INTAE=1, INTS1=0, INTS0=1, IM=1 (周期中断) */
ret = sd2506_write_reg(SD2506_REG_CTR2,
SD2506_CTR2_INTAE | SD2506_CTR2_INTS0 | SD2506_CTR2_IM);
if (ret != SD2506_OK) return ret;
/* 关闭写保护 */
ret = sd2506_write_disable();
if (ret != SD2506_OK) return ret;
return SD2506_OK;
}
/*
* 函数功能:清除闹钟中断标志
* 入口参数:无
* 返回值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 */
ret = sd2506_read_reg(SD2506_REG_CTR1, &val);
if (ret != SD2506_OK) {
return ret;
}
return SD2506_OK;
}
/*
* 函数功能:读取控制寄存器 1 (CTR1)
* 入口参数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_read_reg(SD2506_REG_CTR1, p_val);
}

296
Drivers/BSP/SD2506/sd2506.h Normal file
View File

@@ -0,0 +1,296 @@
/*
* 模块名称SD2506API-G RTC 实时时钟驱动
* 模块功能SD2506API-G 高精度温补实时时钟模块 I2C 驱动,提供时间读写、
* 温度读取、电池电压检测、ID 读取等功能
* 适用平台STM32F407ZGT6I2C1 接口 (PB6-SCL, PB7-SDA)
* 作者:王建锋
* 创建日期2026-07-17
* 修改记录:
* 2026-07-17 王建锋 创建初始版本,参考 SD2506API-G Ver2.0 手册
*/
#ifndef __SD2506_H
#define __SD2506_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* ======================== I2C 地址 ======================== */
/*
* SD2506API-G 7位器件地址: 0x32
* HAL库使用8位地址: 写 0x64, 读 0x65
*/
#define SD2506_I2C_ADDR 0x32U
#define SD2506_I2C_ADDR_WRITE 0x64U
#define SD2506_I2C_ADDR_READ 0x65U
/* ======================== 寄存器地址定义 ======================== */
/* 实时时钟寄存器 (00H~06H) */
#define SD2506_REG_SEC 0x00U /* 秒 00~59 BCD */
#define SD2506_REG_MIN 0x01U /* 分 00~59 BCD */
#define SD2506_REG_HOUR 0x02U /* 时 00~23 BCD, bit7=12/24 */
#define SD2506_REG_WEEK 0x03U /* 星期 00~06 BCD */
#define SD2506_REG_DAY 0x04U /* 日 01~31 BCD */
#define SD2506_REG_MON 0x05U /* 月 01~12 BCD */
#define SD2506_REG_YEAR 0x06U /* 年 00~99 BCD */
/* 报警寄存器 (07H~0EH) */
#define SD2506_REG_AL_SEC 0x07U /* 秒报警 */
#define SD2506_REG_AL_MIN 0x08U /* 分报警 */
#define SD2506_REG_AL_HOUR 0x09U /* 时报警 */
#define SD2506_REG_AL_WEEK 0x0AU /* 星期报警 */
#define SD2506_REG_AL_DAY 0x0BU /* 日报警 */
#define SD2506_REG_AL_MON 0x0CU /* 月报警 */
#define SD2506_REG_AL_YEAR 0x0DU /* 年报警 */
#define SD2506_REG_AL_EN 0x0EU /* 报警允许寄存器 */
/* 控制寄存器 */
#define SD2506_REG_CTR1 0x0FU /* 控制寄存器1 */
#define SD2506_REG_CTR2 0x10U /* 控制寄存器2 */
#define SD2506_REG_CTR3 0x11U /* 控制寄存器3 */
/* 倒计时寄存器 (13H~15H) */
#define SD2506_REG_CNT0 0x13U /* 倒计时字节0 (LSB) */
#define SD2506_REG_CNT1 0x14U /* 倒计时字节1 */
#define SD2506_REG_CNT2 0x15U /* 倒计时字节2 (MSB) */
/* 温度寄存器 */
#define SD2506_REG_TEMP 0x16U /* 温度值 (bit7=符号位) */
#define SD2506_REG_AGTC 0x17U /* IIC控制寄存器 AGTC */
#define SD2506_REG_CHARGE 0x18U /* 充电选择寄存器 */
#define SD2506_REG_CTR4 0x19U /* 扩展控制寄存器 CTR4 */
#define SD2506_REG_CTR5 0x1AU /* 控制寄存器 CTR5 */
#define SD2506_REG_BAT_VAL 0x1BU /* 电池电压低8位 */
/* 温度报警及历史 */
#define SD2506_REG_TEMP_AL 0x1CU /* 低温报警温度值 */
#define SD2506_REG_TEMP_AH 0x1DU /* 高温报警温度值 */
#define SD2506_REG_TEMP_HIS_L 0x1EU /* 历史低温 */
#define SD2506_REG_TEMP_HIS_H 0x1FU /* 历史高温 */
/* 用户 SRAM (2CH~71H) */
#define SD2506_REG_SRAM_START 0x2CU
#define SD2506_REG_SRAM_END 0x71U
#define SD2506_SRAM_SIZE 70U /* 70字节 */
/* ID 码 (72H~79H) */
#define SD2506_REG_ID_START 0x72U
#define SD2506_REG_ID_END 0x79U
#define SD2506_ID_SIZE 8U /* 8字节 */
/* ======================== 控制位定义 ======================== */
/* 控制寄存器 1 (0FH) */
#define SD2506_CTR1_ARST (1U << 5) /* 自动复位使能 */
#define SD2506_CTR1_INTAF (1U << 3) /* 报警中断标志 */
#define SD2506_CTR1_INTDF (1U << 2) /* 倒计时中断标志 */
#define SD2506_CTR1_BLF (1U << 0) /* 电池欠压标志 */
/* 控制寄存器 2 (10H) */
#define SD2506_CTR2_INTAE (1U << 5) /* 报警中断允许 */
#define SD2506_CTR2_INTFE (1U << 4) /* 频率中断允许 */
#define SD2506_CTR2_INTDE (1U << 3) /* 倒计时中断允许 */
#define SD2506_CTR2_IM (1U << 2) /* 中断模式: 0=单事件, 1=周期 */
#define SD2506_CTR2_INTS1 (1U << 1) /* INT输出选择 bit1 */
#define SD2506_CTR2_INTS0 (1U << 0) /* INT输出选择 bit0 */
/* 报警允许寄存器 (0EH) */
#define SD2506_AL_EN_EAY (1U << 6) /* 年报警允许 */
#define SD2506_AL_EN_EAMO (1U << 5) /* 月报警允许 */
#define SD2506_AL_EN_EAD (1U << 4) /* 日报警允许 */
#define SD2506_AL_EN_EAW (1U << 3) /* 星期报警允许 */
#define SD2506_AL_EN_EAH (1U << 2) /* 时报警允许 */
#define SD2506_AL_EN_EAMN (1U << 1) /* 分报警允许 */
#define SD2506_AL_EN_EAS (1U << 0) /* 秒报警允许 */
/* 24/12小时制位 */
#define SD2506_HOUR_24 (1U << 7) /* bit7=1 表示24小时制 */
/* ======================== 充电配置 ======================== */
/* 充电寄存器 (18H) 建议上电写入值: 0x82 (开启充电, 断开) */
#define SD2506_CHARGE_EN (1U << 7) /* 充电允许位 */
#define SD2506_CHARGE_2K (0U << 0) /* 2K电阻 */
#define SD2506_CHARGE_5K (1U << 0) /* 5K电阻 */
/* ======================== 写保护配置 ======================== */
/* 写允许时 0FH 寄存器建议值: 0xFF (WRTC1=1, WRTC2=1, WRTC3=1) */
#define SD2506_CTR1_WRITE_ON 0xFFU
/* 写禁止时 0FH 寄存器建议值: 0x7B (WRTC1=0, WRTC2=0, WRTC3=0, ARST=1) */
#define SD2506_CTR1_WRITE_OFF 0x7BU
/* ======================== 返回值定义 ======================== */
#define SD2506_OK 0
#define SD2506_ERROR -1
#define SD2506_I2C_ERROR -2
/* ======================== 超时时间 ======================== */
#define SD2506_I2C_TIMEOUT_MS 100
/* ======================== 数据结构 ======================== */
/*
* RTC 时间日期结构体
* year: 2000~2099
* month: 1~12
* day: 1~31
* hour: 0~23 (24小时制)
* minute: 0~59
* second: 0~59
* week: 0~6 (0=星期天, 1=星期一, ..., 6=星期六)
*/
typedef struct {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t week;
} sd2506_time_t;
/* ======================== 函数声明 ======================== */
/*
* 函数功能:初始化 SD2506API-G RTC
* 入口参数:无
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件CubeMX 已完成 I2C1 初始化
* 函数说明1. 读取芯片 ID 验证通信
* 2. 上电重置充电寄存器 18H=82H
* 3. 配置 24 小时制、开自动复位
*/
int sd2506_init(void);
/*
* 函数功能:设置 RTC 时间日期
* 入口参数time - 时间结构体指针,包含要设置的时间
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明1. 先开写保护
* 2. 一次性写入 7 字节时间数据 (00H~06H)
* 3. 关闭写保护
* 注意:不可单独写某一个时间寄存器
*/
int sd2506_set_time(const sd2506_time_t *p_time);
/*
* 函数功能:读取 RTC 时间日期
* 入口参数time - 时间结构体指针,用于存储读取结果
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明1. 一次读取 7 字节时间数据 (00H~06H)
* 2. BCD 转十进制
* 3. 屏蔽小时 bit7 (12/24标志位)
*/
int sd2506_get_time(sd2506_time_t *p_time);
/*
* 函数功能:读取芯片内部温度
* 入口参数temp - 温度输出指针 (整数部分, 有符号)
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明:读取 16H 寄存器bit7 为符号位,范围 -40~+85
*/
int sd2506_get_temperature(int8_t *p_temp);
/*
* 函数功能:读取电池电压 (毫伏)
* 入口参数voltage - 电压输出指针 (单位: mV)
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明1. 读取 1AH bit7 (BAT8_VAL) 和 1BH (BAT_VL)
* 2. 组合 9 位数据得到电压值 (如 0x135 = 309 = 3.09V)
*/
int sd2506_get_battery_voltage(uint16_t *p_voltage);
/*
* 函数功能:读取芯片 8 字节 ID
* 入口参数id - 8 字节输出缓冲区
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明:读取 72H~79H 共 8 字节唯一 ID
*/
int sd2506_get_id(uint8_t p_id[8]);
/*
* 函数功能:读取用户 SRAM 数据
* 入口参数addr - SRAM 起始地址 (0~69)
* buf - 数据输出缓冲区
* len - 读取长度 (1~70)
* 返回值0 - 成功,-2 - I2C 通信错误,-1 - 参数错误
* 限定条件sd2506_init() 已调用
* 函数说明SRAM 地址范围 0~69对应寄存器 2CH~71H
*/
int sd2506_read_sram(uint8_t addr, uint8_t *p_buf, uint8_t len);
/*
* 函数功能:写入用户 SRAM 数据
* 入口参数addr - SRAM 起始地址 (0~69)
* buf - 数据输入缓冲区
* len - 写入长度 (1~70)
* 返回值0 - 成功,-2 - I2C 通信错误,-1 - 参数错误
* 限定条件sd2506_init() 已调用
* 函数说明SRAM 地址范围 0~69对应寄存器 2CH~71H
* SRAM 无需开写保护即可写入
*/
int sd2506_write_sram(uint8_t addr, const uint8_t *p_buf, uint8_t len);
/*
* 函数功能:设置报警中断
* 入口参数time - 报警时间结构体
* mask - 报警匹配掩码 (SD2506_AL_EN_xxx 位或组合)
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明1. 开写保护
* 2. 写入报警寄存器 (07H~0EH)
* 3. 使能报警中断 INTAE
* 4. 关写保护
*/
int sd2506_set_alarm(const sd2506_time_t *p_time, uint8_t mask);
/*
* 函数功能:清除报警中断标志
* 入口参数:无
* 返回值0 - 成功,-2 - I2C 通信错误
* 限定条件sd2506_init() 已调用
* 函数说明:读取 CTR1 自动清除 INTAF (ARST=1 时)
*/
int sd2506_clear_alarm(void);
/*
* 函数功能:读取当前 0FH 控制寄存器1 状态
* 入口参数val - 输出值指针
* 返回值0 - 成功,-2 - I2C 通信错误
*/
int sd2506_read_ctr1(uint8_t *p_val);
/*
* 函数功能BCD 码转十进制
* 入口参数bcd - BCD 码值
* 返回值:十进制值
* 函数说明:内部辅助函数
*/
uint8_t sd2506_bcd_to_dec(uint8_t bcd);
/*
* 函数功能:十进制转 BCD 码
* 入口参数dec - 十进制值
* 返回值BCD 码值
* 函数说明:内部辅助函数
*/
uint8_t sd2506_dec_to_bcd(uint8_t dec);
#ifdef __cplusplus
}
#endif
#endif /* __SD2506_H */

View File

@@ -0,0 +1,314 @@
/*
* 模块名称TPAFE5160 16<31>?通道同步采样ADC驱动
* 模块功能<EFBC9A>?TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接<E58F96>? * 适用平台STM32F407ZGT6<EFBC8C>?6位数据总线<E680BB>?GPIOG[15:0]
* 作者王建<E78E8B>? * 创建日期<E697A5>?026-07-17
* 修改记录<E8AEB0>? * 2026-07-17 王建<E78E8B>? 创建初始版本<EFBC8C>?AD7606 并行驱动<E9A9B1>?TPAFE5160 手册
* 2026-07-17 王建<E78E8B>? 增加 EXTI 中断读取模式
*/
/* 头文件包含区 */
#include "tpafe5160.h"
#include "main.h"
/*
* 调试输出配置
*/
#define DBG_TAG "[ADC]"
#include "dbg_log.h"
/* ======================== 私有宏定<E5AE8F>?======================== */
/*
* 并行读取时序延时 (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()
/* RD 低脉宽延时GPIO<49>?~6ns) + 5NOP(~30ns) > 22ns */
#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_NOP_5(); \
TPAFE5160_CONVST_HIGH(); \
TPAFE5160_NOP_5(); \
} while (0)
/* ======================== 私有函数声明 ======================== */
static void tpafe5160_read_channels(uint8_t count, int16_t *buf);
/* ======================== 公共函数定义 ======================== */
/*
* 函数功能:初始化 TPAFE5160设置过采样、等待就绪
* 入口参数:无
* 返回值0 - 成功<E68890>?2 - BUSY 超时
* 限定条件CubeMX 已完<E5B7B2>?GPIO 初始<E5889D>? * 函数说明<E8AFB4>?. 设置过采样为无过采样 (000)
* 2. 确保 RD 为高、CONVST 为低
* 3. 等待 BUSY 释放(转换空闲)
*/
int tpafe5160_init(void) {
/* 设置默认过采样:无过采样 (OS[2:0] = 000) */
tpafe5160_set_os(TPAFE5160_OS_NONE);
/* 确保控制引脚处于空闲状<E997B2>?*/
TPAFE5160_RD_HIGH();
TPAFE5160_CONVST_LOW();
/* 等待 BUSY 释放确保上电后无残留转<E79599>?*/
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;
}
/*
* 函数功能设置过采样<E98787>? * 入口参数os - 过采样率枚举<E69E9A>? tpafe5160_os_t
* 返回值<E580BC>? * 限定条件GPIO 已初始化
* 函数说明:通过 OS[2:0] 引脚设置过采样率在下一<E4B88B>?BUSY 下降沿锁存生<E5AD98>? */
void tpafe5160_set_os(tpafe5160_os_t os) {
TPAFE5160_OS0(os & 0x01); /* OS0 = bit0 */
TPAFE5160_OS1((os >> 1) & 0x01); /* OS1 = bit1 */
TPAFE5160_OS2((os >> 2) & 0x01); /* OS2 = bit2 */
}
/*
* 函数功能启动一次转换CONVST 上升沿触发)
* 入口参数:无
* 返回值<E580BC>? * 限定条件GPIO 已初始化
* 函数说明<EFBC9A>?CONVST 脉冲上升沿启动全部8通道同步采样与转<E4B88E>? */
void tpafe5160_start_conv(void) {
TPAFE5160_CONVST_PULSE();
}
/*
* 函数功能等待转换完<E68DA2>? * 入口参数timeout_ms - 超时时间 uint32_t > 0
* 返回值0 - 转换完成<E5AE8C>?2 - 超时
* 限定条件:已调用 tpafe5160_start_conv()
* 函数说明<EFBC9A>?BUSY 引脚等待下降<E4B88B>? */
int tpafe5160_wait_busy(uint32_t timeout_ms) {
uint32_t tick_start = HAL_GetTick();
/* 等待 BUSY 释放低电平表示空闲<E7A9BA>?*/
while (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) {
if ((HAL_GetTick() - tick_start) >= timeout_ms) {
return TPAFE5160_BUSY_TIMEOUT;
}
}
return TPAFE5160_OK;
}
/*
* 函数功能查询当前是否正在转<E59CA8>? * 入口参数:无
* 返回值1 - 正在转换<E8BDAC>? - 空闲
* 限定条件GPIO 已初始化
* 函数说明<EFBC9A>?BUSY 引脚电平
*/
uint8_t tpafe5160_is_busy(void) {
return (TPAFE5160_BUSY_READ() == GPIO_PIN_SET) ? 1 : 0;
}
/*
* 函数功能读取全<E58F96>?通道转换结果
* 入口参数buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
* 返回值0 - 成功<E68890>?2 - BUSY 超时
* 限定条件GPIO 已初始化
* 函数说明<E8AFB4>?. 启动转换并等<E5B9B6>?BUSY 释放
* 2. 连续8次拉低RD读取各通道数据
* 3. 通过 FRSTDATA 验证第一通道
*/
int tpafe5160_read_all(int16_t *buf) {
int ret = 0;
/* 启动转换 */
tpafe5160_start_conv();
/* 等待转换完成 */
ret = tpafe5160_wait_busy(TPAFE5160_CONV_TIMEOUT_MS);
if (ret != TPAFE5160_OK) {
DBG_ERROR("read_all BUSY timeout");
return ret;
}
/* 连续读取8个通道 */
tpafe5160_read_channels(TPAFE5160_CH_NUM, buf);
return TPAFE5160_OK;
}
/*
* 函数功能读取指定通道的转换结<E68DA2>? * 入口参数channel - 通道<E9809A>? uint8_t 0 - 7
* value - 输出指针 int16_t* 不为 NULL
* 返回值0 - 成功<E68890>?1 - 通道号无效,-2 - BUSY 超时
* 限定条件GPIO 已初始化
* 函数说明:启动转换并等待完成后,连续读取至指定通道
*/
int tpafe5160_read_channel(uint8_t channel, int16_t *value) {
int ret = 0;
int16_t buf[TPAFE5160_CH_NUM] = {0};
if (channel >= TPAFE5160_CH_NUM || value == NULL) {
return TPAFE5160_ERROR;
}
ret = tpafe5160_read_all(buf);
if (ret != TPAFE5160_OK) {
return ret;
}
*value = buf[channel];
return TPAFE5160_OK;
}
/*
* 函数功能原始ADC值转电压<E794B5>? * 入口参数raw - ADC原始<E58E9F>? int16_t 有符号补<E58FB7>? * 返回值电压<E794B5>? float 单位 V
* 限定条件:无
* 函数说明<E6988E>?V量程<E9878F>?LSB = 10V / 65536 <20>?152.59μV
* ±10V量程<E9878F>?LSB = 20V / 65536 <20>?305.18μV
* 硬件 RANGE 引脚<E5BC95>?GND<EFBC8C>?±5V 量程
*/
float tpafe5160_to_voltage(int16_t raw) {
/* RANGE <20>?GND <20>?±5V 量程,满量程 10V */
return (float)raw * (10.0f / 65536.0f);
}
/*
* 函数功能直接读取并行数据总线不启动转换<E8BDAC>? * 入口参数:无
* 返回值16位原始数<E5A78B>? uint16_t
* 限定条件RD 为低<E4B8BA>?CS <20>?RD 已拉<E5B7B2>? * 函数说明<EFBC9A>?GPIOG->IDR <20>?6位对应 DB[15:0]
*/
uint16_t tpafe5160_read_bus(void) {
return TPAFE5160_READ_BUS();
}
/* ======================== 私有函数定义 ======================== */
/*
* 函数功能:通过 RD 脉冲连续读取多个通道数据
* 入口参数count - 要读取的通道<E9809A>? uint8_t 1 - 8
* buf - 输出缓冲<E7BC93>? int16_t* 不为 NULL
* 返回值<E580BC>? * 限定条件转换已完成BUSY 为低RD 初始为高
* 函数说明<EFBC9A>?RD 下降沿输出一个通道数据按通道1~8顺序输出
* DB[15:0] 直接<E79BB4>?GPIOG[15:0],通过 IDR 寄存器一次读<E6ACA1>? *
* 时序关键<E585B3>?(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) 覆盖建立时间
*/
static void tpafe5160_read_channels(uint8_t count, int16_t *buf) {
uint8_t i;
for (i = 0; i < count; i++) {
/* RD 下降沿ADC 输出当前通道数据<E695B0>?DB[15:0] */
TPAFE5160_RD_LOW_DLY();
/* 读取16位并行数<E8A18C>?*/
buf[i] = (int16_t)TPAFE5160_READ_BUS();
/* RD 上升沿:准备下一通道 */
TPAFE5160_RD_HIGH_DLY();
}
}
/* ======================== 中断模式实现 ======================== */
/* 双缓冲区ISR <20>?s_buf_b主循环<E5BEAA>?s_buf_a */
static int16_t s_buf_a[TPAFE5160_CH_NUM];
static int16_t s_buf_b[TPAFE5160_CH_NUM];
static volatile uint8_t s_ready = 0;
static volatile uint8_t s_buf_sel = 0; /* 0 = <20>?B / <20>?A, 1 = <20>?A / <20>?B */
/*
* 函数功能使<EFBC9A>?BUSY EXTI 中断运行时重使能用<E883BD>? * 入口参数:无
* 返回值<E580BC>? * 限定条件CubeMX 已完<E5B7B2>?GPIO <20>?NVIC 配置
* 函数说明正常启动流程无需调用<EFBC8C>?irq_disable() 后需要重新使能时使用
*/
void tpafe5160_irq_enable(void) {
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
/*
* 函数功能<EFBC9A>?BUSY EXTI 中断
* 入口参数:无
* 返回值<E580BC>? * 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明:仅禁用 NVIC 中断GPIO 配置保持 CubeMX 设定
*/
void tpafe5160_irq_disable(void) {
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
}
/*
* 函数功能启动转换中断模式<E6A8A1>? * 入口参数:无
* 返回值<E580BC>? * 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明<EFBC9A>?CONVST 脉冲转换完成后<E68890>?EXTI 中断自动读取 8 通道数据
*/
void tpafe5160_start_conv_irq(void) {
s_ready = 0;
TPAFE5160_CONVST_PULSE();
}
/*
* 函数功能:检查是否有新的转换数据
* 入口参数:无
* 返回值1 - 数据就绪<E5B0B1>? - 无新数据
* 限定条件:中断模式已启用
* 函数说明:在 EXTI 回调中置位,主循环读取后需调用 tpafe5160_clear_ready() 清除
*/
uint8_t tpafe5160_data_ready(void) {
return s_ready;
}
/*
* 函数功能清除数据就绪标<E7BBAA>? * 入口参数:无
* 返回值<E580BC>? * 限定条件:中断模式已启用
* 函数说明:主循环处理完数据后调用
*/
void tpafe5160_clear_ready(void) {
s_ready = 0;
}
/*
* 函数功能:获取数据缓冲区指针
* 入口参数:无
* 返回值int16_t[8] 数据缓冲区的 const 指针
* 限定条件tpafe5160_data_ready() 返回 1 时调<E697B6>? * 函数说明双缓冲切换ISR 写另一个缓冲区主循环安全读取当前缓冲<E7BC93>? */
const int16_t* tpafe5160_get_buf(void) {
return (s_buf_sel == 0) ? s_buf_a : s_buf_b;
}
/*
* 函数功能BUSY 下降<E4B88B>?EXTI 处理(由 HAL_GPIO_EXTI_Callback 调用<E8B083>? * 入口参数GPIO_Pin - 触发中断的引脚号
* 返回值<E580BC>? * 限定条件BUSY EXTI 已使<E5B7B2>? * 函数说明:转换完成后自动读取 8 通道数据到缓冲区耗时<E88097>?0.5µs (168MHz)
*/
void tpafe5160_exti_handler(uint16_t GPIO_Pin) {
if (GPIO_Pin != TP_BUSY_Pin) {
return;
}
/* 双缓冲切换ISR 写与主循环读不同的缓冲区 */
int16_t *p_wr = (s_buf_sel == 0) ? s_buf_b : s_buf_a;
/* 连续读取8个通道约 0.5µs @ 168MHz */
uint8_t i;
for (i = 0; i < TPAFE5160_CH_NUM; i++) {
TPAFE5160_RD_LOW_DLY();
p_wr[i] = (int16_t)TPAFE5160_READ_BUS();
TPAFE5160_RD_HIGH_DLY();
}
/* 切换缓冲区并标记就绪 */
s_buf_sel ^= 1;
s_ready = 1;
}

View File

@@ -0,0 +1,233 @@
#ifndef __TPAFE5160_H
#define __TPAFE5160_H
/*
* 模块名称TPAFE5160 16位8通道同步采样ADC驱动
* 模块功能:提供 TPAFE5160 并行接口模式下的初始化、过采样设置、转换启动、数据读取接口
* 适用平台STM32F407ZGT6并行16位数据总线接 GPIOG[15:0]
* 作者:王建锋
* 创建日期2026-07-17
* 修改记录:
* 2026-07-17 王建锋 创建初始版本,参考 AD7606 并行驱动及 TPAFE5160 手册
* 2026-07-17 王建锋 增加 EXTI 中断读取模式
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* ======================== 宏定义 ======================== */
/* 通道数量 */
#define TPAFE5160_CH_NUM 8
/* 控制引脚操作宏 —— RD (PD3) */
#define TPAFE5160_RD_LOW() HAL_GPIO_WritePin(TP_RD_GPIO_Port, TP_RD_Pin, GPIO_PIN_RESET)
#define TPAFE5160_RD_HIGH() HAL_GPIO_WritePin(TP_RD_GPIO_Port, TP_RD_Pin, GPIO_PIN_SET)
/* 控制引脚操作宏 —— CONVST (PD4) */
#define TPAFE5160_CONVST_LOW() HAL_GPIO_WritePin(TP_CONVST_GPIO_Port, TP_CONVST_Pin, GPIO_PIN_RESET)
#define TPAFE5160_CONVST_HIGH() HAL_GPIO_WritePin(TP_CONVST_GPIO_Port, TP_CONVST_Pin, GPIO_PIN_SET)
/* 状态引脚读取宏 */
#define TPAFE5160_BUSY_READ() HAL_GPIO_ReadPin(TP_BUSY_GPIO_Port, TP_BUSY_Pin)
#define TPAFE5160_FRSTDATA_READ() HAL_GPIO_ReadPin(TP_FRSTDATA_GPIO_Port, TP_FRSTDATA_Pin)
/* 过采样引脚操作宏 —— OS0 (PF13), OS1 (PF14), OS2 (PF15) */
#define TPAFE5160_OS0(val) HAL_GPIO_WritePin(TP_OS0_GPIO_Port, TP_OS0_Pin, \
(val) ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define TPAFE5160_OS1(val) HAL_GPIO_WritePin(TP_OS1_GPIO_Port, TP_OS1_Pin, \
(val) ? GPIO_PIN_SET : GPIO_PIN_RESET)
#define TPAFE5160_OS2(val) HAL_GPIO_WritePin(TP_OS2_GPIO_Port, TP_OS2_Pin, \
(val) ? GPIO_PIN_SET : GPIO_PIN_RESET)
/* 并行数据总线读取 —— DB[15:0] 接 GPIOG[15:0]单次读取16位 */
#define TPAFE5160_READ_BUS() ((uint16_t)GPIOG->IDR)
/* 过采样率枚举 (OS[2:0] 编码OS2为MSB, OS0为LSB) */
typedef enum {
TPAFE5160_OS_NONE = 0, /* 000 — 无过采样350 kSPS */
TPAFE5160_OS_X2 = 1, /* 001 — 2倍过采样175 kSPS */
TPAFE5160_OS_X4 = 2, /* 010 — 4倍过采样87.5 kSPS */
TPAFE5160_OS_X8 = 3, /* 011 — 8倍过采样43.75 kSPS */
TPAFE5160_OS_X16 = 4, /* 100 — 16倍过采样21.875 kSPS */
TPAFE5160_OS_X32 = 5, /* 101 — 32倍过采样10.94 kSPS */
TPAFE5160_OS_X64 = 6, /* 110 — 64倍过采样5.47 kSPS */
TPAFE5160_OS_HBW = 7 /* 111 — 高带宽模式 (~30kHz)350 kSPS */
} tpafe5160_os_t;
/* 输入量程枚举 */
typedef enum {
TPAFE5160_RANGE_5V = 0, /* ±5V (RANGE = LOW) */
TPAFE5160_RANGE_10V = 1 /* ±10V (RANGE = HIGH) */
} tpafe5160_range_t;
/* 返回值定义 */
#define TPAFE5160_OK 0
#define TPAFE5160_ERROR -1
#define TPAFE5160_BUSY_TIMEOUT -2
/* 默认超时时间 (ms) */
#define TPAFE5160_CONV_TIMEOUT_MS 10
/* ======================== 函数声明 ======================== */
/*
* 函数功能:初始化 TPAFE5160设置过采样、等待就绪
* 入口参数:无
* 返回值0 - 成功,-2 - BUSY 超时
* 限定条件CubeMX 已完成 GPIO 初始化
* 函数说明1. 设置过采样为无过采样 (000)
* 2. 确保 RD 为高、CONVST 为低
* 3. 等待 BUSY 释放(转换空闲)
*/
int tpafe5160_init(void);
/*
* 函数功能:设置过采样率
* 入口参数os - 过采样率枚举值 tpafe5160_os_t
* 返回值:无
* 限定条件GPIO 已初始化
* 函数说明:通过 OS[2:0] 引脚设置过采样率,在下一次转换时生效
*/
void tpafe5160_set_os(tpafe5160_os_t os);
/*
* 函数功能启动一次转换CONVST 上升沿触发)
* 入口参数:无
* 返回值:无
* 限定条件GPIO 已初始化
* 函数说明:产生 CONVST 脉冲上升沿启动全部8通道同步采样与转换
*/
void tpafe5160_start_conv(void);
/*
* 函数功能:等待转换完成
* 入口参数timeout_ms - 超时时间 uint32_t > 0
* 返回值0 - 转换完成,-2 - 超时
* 限定条件:已调用 tpafe5160_start_conv()
* 函数说明:轮询 BUSY 引脚等待下降沿
*/
int tpafe5160_wait_busy(uint32_t timeout_ms);
/*
* 函数功能:查询当前是否正在转换
* 入口参数:无
* 返回值1 - 正在转换0 - 空闲
* 限定条件GPIO 已初始化
* 函数说明:读取 BUSY 引脚电平
*/
uint8_t tpafe5160_is_busy(void);
/*
* 函数功能读取全部8通道转换结果
* 入口参数buf - 8个int16_t的输出缓冲区 int16_t* 不为 NULL
* 返回值0 - 成功,-2 - BUSY 超时
* 限定条件GPIO 已初始化
* 函数说明1. 启动转换并等待 BUSY 释放
* 2. 连续8次拉低RD读取各通道数据
* 3. 通过 FRSTDATA 验证第一通道
*/
int tpafe5160_read_all(int16_t *buf);
/*
* 函数功能:读取指定通道的转换结果
* 入口参数channel - 通道号 uint8_t 0 - 7
* value - 输出指针 int16_t* 不为 NULL
* 返回值0 - 成功,-1 - 通道号无效,-2 - BUSY 超时
* 限定条件GPIO 已初始化
* 函数说明:启动转换并等待完成后,连续读取至指定通道
*/
int tpafe5160_read_channel(uint8_t channel, int16_t *value);
/*
* 函数功能原始ADC值转电压值
* 入口参数raw - ADC原始值 int16_t 有符号补码
* 返回值:电压值 float 单位 V
* 限定条件:无
* 函数说明±5V量程时 LSB=152.59μV±10V量程时 LSB=305.18μV
* 默认使用 ±5V 量程 (RANGE 接 GND)
*/
float tpafe5160_to_voltage(int16_t raw);
/*
* 函数功能:直接读取并行数据总线(不启动转换)
* 入口参数:无
* 返回值16位原始数据 uint16_t
* 限定条件RD 为低或CS与RD已拉低
* 函数说明用于读取当前总线上的数据需自行控制RD时序
*/
uint16_t tpafe5160_read_bus(void);
/* ======================== 中断模式 API ======================== */
/*
* 函数功能:使能 BUSY EXTI 中断(运行时重使能用)
* 入口参数:无
* 返回值:无
* 限定条件CubeMX 已完成 GPIO 和 NVIC 配置
* 函数说明:正常启动流程无需调用,仅在 irq_disable() 后需要重新使能时使用
*/
void tpafe5160_irq_enable(void);
/*
* 函数功能:关闭 BUSY EXTI 中断
* 入口参数:无
* 返回值:无
* 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明:仅禁用 NVIC 中断GPIO 配置保持 CubeMX 设定
*/
void tpafe5160_irq_disable(void);
/*
* 函数功能:启动转换(中断模式)
* 入口参数:无
* 返回值:无
* 限定条件:已调用 tpafe5160_irq_enable()
* 函数说明:产生 CONVST 脉冲,转换完成后由 EXTI 中断自动读取 8 通道数据
*/
void tpafe5160_start_conv_irq(void);
/*
* 函数功能:检查是否有新的转换数据
* 入口参数:无
* 返回值1 - 数据就绪0 - 无新数据
* 限定条件:中断模式已启用
* 函数说明:在 EXTI 回调中置位,主循环读取后需调用 tpafe5160_clear_ready() 清除
*/
uint8_t tpafe5160_data_ready(void);
/*
* 函数功能:清除数据就绪标志
* 入口参数:无
* 返回值:无
* 限定条件:中断模式已启用
* 函数说明:主循环处理完数据后调用
*/
void tpafe5160_clear_ready(void);
/*
* 函数功能:获取数据缓冲区指针
* 入口参数:无
* 返回值int16_t[8] 数据缓冲区的 const 指针
* 限定条件tpafe5160_data_ready() 返回 1 时调用
* 函数说明:缓冲区由 EXTI 回调写入,主循环只读
*/
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
}
#endif
#endif /* __TPAFE5160_H */

46
Drivers/BSP/dbg_cfg.h Normal file
View 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
View File

@@ -0,0 +1,138 @@
/*
* 模块名称Unified Debug Logging
* 模块功能:提供统一的调试输出宏,支持 DEBUG / INFO / ERROR 三级日志控制
* 参考 FlashDB FDB_DEBUG/FDB_INFO 设计模式
* 使用方法:
* 1. 在 dbg_cfg.h 中定义 DBG_ENABLE总开关和 DBG_DEBUG_ENABLEDEBUG 级别)
* 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 */

View File

@@ -0,0 +1,741 @@
/**
******************************************************************************
* @file stm32f4xx_hal_i2c.h
* @author MCD Application Team
* @brief Header file of I2C HAL 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_I2C_H
#define __STM32F4xx_HAL_I2C_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal_def.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @addtogroup I2C
* @{
*/
/* Exported types ------------------------------------------------------------*/
/** @defgroup I2C_Exported_Types I2C Exported Types
* @{
*/
/** @defgroup I2C_Configuration_Structure_definition I2C Configuration Structure definition
* @brief I2C Configuration Structure definition
* @{
*/
typedef struct
{
uint32_t ClockSpeed; /*!< Specifies the clock frequency.
This parameter must be set to a value lower than 400kHz */
uint32_t DutyCycle; /*!< Specifies the I2C fast mode duty cycle.
This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */
uint32_t OwnAddress1; /*!< Specifies the first device own address.
This parameter can be a 7-bit or 10-bit address. */
uint32_t AddressingMode; /*!< Specifies if 7-bit or 10-bit addressing mode is selected.
This parameter can be a value of @ref I2C_addressing_mode */
uint32_t DualAddressMode; /*!< Specifies if dual addressing mode is selected.
This parameter can be a value of @ref I2C_dual_addressing_mode */
uint32_t OwnAddress2; /*!< Specifies the second device own address if dual addressing mode is selected
This parameter can be a 7-bit address. */
uint32_t GeneralCallMode; /*!< Specifies if general call mode is selected.
This parameter can be a value of @ref I2C_general_call_addressing_mode */
uint32_t NoStretchMode; /*!< Specifies if nostretch mode is selected.
This parameter can be a value of @ref I2C_nostretch_mode */
} I2C_InitTypeDef;
/**
* @}
*/
/** @defgroup HAL_state_structure_definition HAL state structure definition
* @brief HAL State structure definition
* @note HAL I2C State value coding follow below described bitmap :
* b7-b6 Error information
* 00 : No Error
* 01 : Abort (Abort user request on going)
* 10 : Timeout
* 11 : Error
* b5 Peripheral initialization status
* 0 : Reset (Peripheral not initialized)
* 1 : Init done (Peripheral initialized and ready to use. HAL I2C Init function called)
* b4 (not used)
* x : Should be set to 0
* b3
* 0 : Ready or Busy (No Listen mode ongoing)
* 1 : Listen (Peripheral in Address Listen Mode)
* b2 Intrinsic process state
* 0 : Ready
* 1 : Busy (Peripheral busy with some configuration or internal operations)
* b1 Rx state
* 0 : Ready (no Rx operation ongoing)
* 1 : Busy (Rx operation ongoing)
* b0 Tx state
* 0 : Ready (no Tx operation ongoing)
* 1 : Busy (Tx operation ongoing)
* @{
*/
typedef enum
{
HAL_I2C_STATE_RESET = 0x00U, /*!< Peripheral is not yet Initialized */
HAL_I2C_STATE_READY = 0x20U, /*!< Peripheral Initialized and ready for use */
HAL_I2C_STATE_BUSY = 0x24U, /*!< An internal process is ongoing */
HAL_I2C_STATE_BUSY_TX = 0x21U, /*!< Data Transmission process is ongoing */
HAL_I2C_STATE_BUSY_RX = 0x22U, /*!< Data Reception process is ongoing */
HAL_I2C_STATE_LISTEN = 0x28U, /*!< Address Listen Mode is ongoing */
HAL_I2C_STATE_BUSY_TX_LISTEN = 0x29U, /*!< Address Listen Mode and Data Transmission
process is ongoing */
HAL_I2C_STATE_BUSY_RX_LISTEN = 0x2AU, /*!< Address Listen Mode and Data Reception
process is ongoing */
HAL_I2C_STATE_ABORT = 0x60U, /*!< Abort user request ongoing */
HAL_I2C_STATE_TIMEOUT = 0xA0U, /*!< Timeout state */
HAL_I2C_STATE_ERROR = 0xE0U /*!< Error */
} HAL_I2C_StateTypeDef;
/**
* @}
*/
/** @defgroup HAL_mode_structure_definition HAL mode structure definition
* @brief HAL Mode structure definition
* @note HAL I2C Mode value coding follow below described bitmap :\n
* b7 (not used)\n
* x : Should be set to 0\n
* b6\n
* 0 : None\n
* 1 : Memory (HAL I2C communication is in Memory Mode)\n
* b5\n
* 0 : None\n
* 1 : Slave (HAL I2C communication is in Slave Mode)\n
* b4\n
* 0 : None\n
* 1 : Master (HAL I2C communication is in Master Mode)\n
* b3-b2-b1-b0 (not used)\n
* xxxx : Should be set to 0000
* @{
*/
typedef enum
{
HAL_I2C_MODE_NONE = 0x00U, /*!< No I2C communication on going */
HAL_I2C_MODE_MASTER = 0x10U, /*!< I2C communication is in Master Mode */
HAL_I2C_MODE_SLAVE = 0x20U, /*!< I2C communication is in Slave Mode */
HAL_I2C_MODE_MEM = 0x40U /*!< I2C communication is in Memory Mode */
} HAL_I2C_ModeTypeDef;
/**
* @}
*/
/** @defgroup I2C_Error_Code_definition I2C Error Code definition
* @brief I2C Error Code definition
* @{
*/
#define HAL_I2C_ERROR_NONE 0x00000000U /*!< No error */
#define HAL_I2C_ERROR_BERR 0x00000001U /*!< BERR error */
#define HAL_I2C_ERROR_ARLO 0x00000002U /*!< ARLO error */
#define HAL_I2C_ERROR_AF 0x00000004U /*!< AF error */
#define HAL_I2C_ERROR_OVR 0x00000008U /*!< OVR error */
#define HAL_I2C_ERROR_DMA 0x00000010U /*!< DMA transfer error */
#define HAL_I2C_ERROR_TIMEOUT 0x00000020U /*!< Timeout Error */
#define HAL_I2C_ERROR_SIZE 0x00000040U /*!< Size Management error */
#define HAL_I2C_ERROR_DMA_PARAM 0x00000080U /*!< DMA Parameter Error */
#define HAL_I2C_WRONG_START 0x00000200U /*!< Wrong start Error */
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
#define HAL_I2C_ERROR_INVALID_CALLBACK 0x00000100U /*!< Invalid Callback error */
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
/**
* @}
*/
/** @defgroup I2C_handle_Structure_definition I2C handle Structure definition
* @brief I2C handle Structure definition
* @{
*/
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
typedef struct __I2C_HandleTypeDef
#else
typedef struct
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
{
I2C_TypeDef *Instance; /*!< I2C registers base address */
I2C_InitTypeDef Init; /*!< I2C communication parameters */
uint8_t *pBuffPtr; /*!< Pointer to I2C transfer buffer */
uint16_t XferSize; /*!< I2C transfer size */
__IO uint16_t XferCount; /*!< I2C transfer counter */
__IO uint32_t XferOptions; /*!< I2C transfer options */
__IO uint32_t PreviousState; /*!< I2C communication Previous state and mode
context for internal usage */
DMA_HandleTypeDef *hdmatx; /*!< I2C Tx DMA handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< I2C Rx DMA handle parameters */
HAL_LockTypeDef Lock; /*!< I2C locking object */
__IO HAL_I2C_StateTypeDef State; /*!< I2C communication state */
__IO HAL_I2C_ModeTypeDef Mode; /*!< I2C communication mode */
__IO uint32_t ErrorCode; /*!< I2C Error code */
__IO uint32_t Devaddress; /*!< I2C Target device address */
__IO uint32_t Memaddress; /*!< I2C Target memory address */
__IO uint32_t MemaddSize; /*!< I2C Target memory address size */
__IO uint32_t EventCount; /*!< I2C Event counter */
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
void (* MasterTxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Master Tx Transfer completed callback */
void (* MasterRxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Master Rx Transfer completed callback */
void (* SlaveTxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Slave Tx Transfer completed callback */
void (* SlaveRxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Slave Rx Transfer completed callback */
void (* ListenCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Listen Complete callback */
void (* MemTxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Memory Tx Transfer completed callback */
void (* MemRxCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Memory Rx Transfer completed callback */
void (* ErrorCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Error callback */
void (* AbortCpltCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Abort callback */
void (* AddrCallback)(struct __I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode); /*!< I2C Slave Address Match callback */
void (* MspInitCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Msp Init callback */
void (* MspDeInitCallback)(struct __I2C_HandleTypeDef *hi2c); /*!< I2C Msp DeInit callback */
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
} I2C_HandleTypeDef;
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
/**
* @brief HAL I2C Callback ID enumeration definition
*/
typedef enum
{
HAL_I2C_MASTER_TX_COMPLETE_CB_ID = 0x00U, /*!< I2C Master Tx Transfer completed callback ID */
HAL_I2C_MASTER_RX_COMPLETE_CB_ID = 0x01U, /*!< I2C Master Rx Transfer completed callback ID */
HAL_I2C_SLAVE_TX_COMPLETE_CB_ID = 0x02U, /*!< I2C Slave Tx Transfer completed callback ID */
HAL_I2C_SLAVE_RX_COMPLETE_CB_ID = 0x03U, /*!< I2C Slave Rx Transfer completed callback ID */
HAL_I2C_LISTEN_COMPLETE_CB_ID = 0x04U, /*!< I2C Listen Complete callback ID */
HAL_I2C_MEM_TX_COMPLETE_CB_ID = 0x05U, /*!< I2C Memory Tx Transfer callback ID */
HAL_I2C_MEM_RX_COMPLETE_CB_ID = 0x06U, /*!< I2C Memory Rx Transfer completed callback ID */
HAL_I2C_ERROR_CB_ID = 0x07U, /*!< I2C Error callback ID */
HAL_I2C_ABORT_CB_ID = 0x08U, /*!< I2C Abort callback ID */
HAL_I2C_MSPINIT_CB_ID = 0x09U, /*!< I2C Msp Init callback ID */
HAL_I2C_MSPDEINIT_CB_ID = 0x0AU /*!< I2C Msp DeInit callback ID */
} HAL_I2C_CallbackIDTypeDef;
/**
* @brief HAL I2C Callback pointer definition
*/
typedef void (*pI2C_CallbackTypeDef)(I2C_HandleTypeDef *hi2c); /*!< pointer to an I2C callback function */
typedef void (*pI2C_AddrCallbackTypeDef)(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode); /*!< pointer to an I2C Address Match callback function */
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
/**
* @}
*/
/**
* @}
*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup I2C_Exported_Constants I2C Exported Constants
* @{
*/
/** @defgroup I2C_duty_cycle_in_fast_mode I2C duty cycle in fast mode
* @{
*/
#define I2C_DUTYCYCLE_2 0x00000000U
#define I2C_DUTYCYCLE_16_9 I2C_CCR_DUTY
/**
* @}
*/
/** @defgroup I2C_addressing_mode I2C addressing mode
* @{
*/
#define I2C_ADDRESSINGMODE_7BIT 0x00004000U
#define I2C_ADDRESSINGMODE_10BIT (I2C_OAR1_ADDMODE | 0x00004000U)
/**
* @}
*/
/** @defgroup I2C_dual_addressing_mode I2C dual addressing mode
* @{
*/
#define I2C_DUALADDRESS_DISABLE 0x00000000U
#define I2C_DUALADDRESS_ENABLE I2C_OAR2_ENDUAL
/**
* @}
*/
/** @defgroup I2C_general_call_addressing_mode I2C general call addressing mode
* @{
*/
#define I2C_GENERALCALL_DISABLE 0x00000000U
#define I2C_GENERALCALL_ENABLE I2C_CR1_ENGC
/**
* @}
*/
/** @defgroup I2C_nostretch_mode I2C nostretch mode
* @{
*/
#define I2C_NOSTRETCH_DISABLE 0x00000000U
#define I2C_NOSTRETCH_ENABLE I2C_CR1_NOSTRETCH
/**
* @}
*/
/** @defgroup I2C_Memory_Address_Size I2C Memory Address Size
* @{
*/
#define I2C_MEMADD_SIZE_8BIT 0x00000001U
#define I2C_MEMADD_SIZE_16BIT 0x00000010U
/**
* @}
*/
/** @defgroup I2C_XferDirection_definition I2C XferDirection definition
* @{
*/
#define I2C_DIRECTION_RECEIVE 0x00000000U
#define I2C_DIRECTION_TRANSMIT 0x00000001U
/**
* @}
*/
/** @defgroup I2C_XferOptions_definition I2C XferOptions definition
* @{
*/
#define I2C_FIRST_FRAME 0x00000001U
#define I2C_FIRST_AND_NEXT_FRAME 0x00000002U
#define I2C_NEXT_FRAME 0x00000004U
#define I2C_FIRST_AND_LAST_FRAME 0x00000008U
#define I2C_LAST_FRAME_NO_STOP 0x00000010U
#define I2C_LAST_FRAME 0x00000020U
/* List of XferOptions in usage of :
* 1- Restart condition in all use cases (direction change or not)
*/
#define I2C_OTHER_FRAME (0x00AA0000U)
#define I2C_OTHER_AND_LAST_FRAME (0xAA000000U)
/**
* @}
*/
/** @defgroup I2C_Interrupt_configuration_definition I2C Interrupt configuration definition
* @brief I2C Interrupt definition
* Elements values convention: 0xXXXXXXXX
* - XXXXXXXX : Interrupt control mask
* @{
*/
#define I2C_IT_BUF I2C_CR2_ITBUFEN
#define I2C_IT_EVT I2C_CR2_ITEVTEN
#define I2C_IT_ERR I2C_CR2_ITERREN
/**
* @}
*/
/** @defgroup I2C_Flag_definition I2C Flag definition
* @{
*/
#define I2C_FLAG_OVR 0x00010800U
#define I2C_FLAG_AF 0x00010400U
#define I2C_FLAG_ARLO 0x00010200U
#define I2C_FLAG_BERR 0x00010100U
#define I2C_FLAG_TXE 0x00010080U
#define I2C_FLAG_RXNE 0x00010040U
#define I2C_FLAG_STOPF 0x00010010U
#define I2C_FLAG_ADD10 0x00010008U
#define I2C_FLAG_BTF 0x00010004U
#define I2C_FLAG_ADDR 0x00010002U
#define I2C_FLAG_SB 0x00010001U
#define I2C_FLAG_DUALF 0x00100080U
#define I2C_FLAG_GENCALL 0x00100010U
#define I2C_FLAG_TRA 0x00100004U
#define I2C_FLAG_BUSY 0x00100002U
#define I2C_FLAG_MSL 0x00100001U
/**
* @}
*/
/**
* @}
*/
/* Exported macros -----------------------------------------------------------*/
/** @defgroup I2C_Exported_Macros I2C Exported Macros
* @{
*/
/** @brief Reset I2C handle state.
* @param __HANDLE__ specifies the I2C Handle.
* @retval None
*/
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
#define __HAL_I2C_RESET_HANDLE_STATE(__HANDLE__) do{ \
(__HANDLE__)->State = HAL_I2C_STATE_RESET; \
(__HANDLE__)->MspInitCallback = NULL; \
(__HANDLE__)->MspDeInitCallback = NULL; \
} while(0)
#else
#define __HAL_I2C_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = HAL_I2C_STATE_RESET)
#endif
/** @brief Enable or disable the specified I2C interrupts.
* @param __HANDLE__ specifies the I2C Handle.
* @param __INTERRUPT__ specifies the interrupt source to enable or disable.
* This parameter can be one of the following values:
* @arg I2C_IT_BUF: Buffer interrupt enable
* @arg I2C_IT_EVT: Event interrupt enable
* @arg I2C_IT_ERR: Error interrupt enable
* @retval None
*/
#define __HAL_I2C_ENABLE_IT(__HANDLE__, __INTERRUPT__) SET_BIT((__HANDLE__)->Instance->CR2,(__INTERRUPT__))
#define __HAL_I2C_DISABLE_IT(__HANDLE__, __INTERRUPT__) CLEAR_BIT((__HANDLE__)->Instance->CR2, (__INTERRUPT__))
/** @brief Checks if the specified I2C interrupt source is enabled or disabled.
* @param __HANDLE__ specifies the I2C Handle.
* @param __INTERRUPT__ specifies the I2C interrupt source to check.
* This parameter can be one of the following values:
* @arg I2C_IT_BUF: Buffer interrupt enable
* @arg I2C_IT_EVT: Event interrupt enable
* @arg I2C_IT_ERR: Error interrupt enable
* @retval The new state of __INTERRUPT__ (TRUE or FALSE).
*/
#define __HAL_I2C_GET_IT_SOURCE(__HANDLE__, __INTERRUPT__) ((((__HANDLE__)->Instance->CR2 & (__INTERRUPT__)) == (__INTERRUPT__)) ? SET : RESET)
/** @brief Checks whether the specified I2C flag is set or not.
* @param __HANDLE__ specifies the I2C Handle.
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg I2C_FLAG_OVR: Overrun/Underrun flag
* @arg I2C_FLAG_AF: Acknowledge failure flag
* @arg I2C_FLAG_ARLO: Arbitration lost flag
* @arg I2C_FLAG_BERR: Bus error flag
* @arg I2C_FLAG_TXE: Data register empty flag
* @arg I2C_FLAG_RXNE: Data register not empty flag
* @arg I2C_FLAG_STOPF: Stop detection flag
* @arg I2C_FLAG_ADD10: 10-bit header sent flag
* @arg I2C_FLAG_BTF: Byte transfer finished flag
* @arg I2C_FLAG_ADDR: Address sent flag
* Address matched flag
* @arg I2C_FLAG_SB: Start bit flag
* @arg I2C_FLAG_DUALF: Dual flag
* @arg I2C_FLAG_GENCALL: General call header flag
* @arg I2C_FLAG_TRA: Transmitter/Receiver flag
* @arg I2C_FLAG_BUSY: Bus busy flag
* @arg I2C_FLAG_MSL: Master/Slave flag
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_I2C_GET_FLAG(__HANDLE__, __FLAG__) ((((uint8_t)((__FLAG__) >> 16U)) == 0x01U) ? \
(((((__HANDLE__)->Instance->SR1) & ((__FLAG__) & I2C_FLAG_MASK)) == ((__FLAG__) & I2C_FLAG_MASK)) ? SET : RESET) : \
(((((__HANDLE__)->Instance->SR2) & ((__FLAG__) & I2C_FLAG_MASK)) == ((__FLAG__) & I2C_FLAG_MASK)) ? SET : RESET))
/** @brief Clears the I2C pending flags which are cleared by writing 0 in a specific bit.
* @param __HANDLE__ specifies the I2C Handle.
* @param __FLAG__ specifies the flag to clear.
* This parameter can be any combination of the following values:
* @arg I2C_FLAG_OVR: Overrun/Underrun flag (Slave mode)
* @arg I2C_FLAG_AF: Acknowledge failure flag
* @arg I2C_FLAG_ARLO: Arbitration lost flag (Master mode)
* @arg I2C_FLAG_BERR: Bus error flag
* @retval None
*/
#define __HAL_I2C_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR1 = ~((__FLAG__) & I2C_FLAG_MASK))
/** @brief Clears the I2C ADDR pending flag.
* @param __HANDLE__ specifies the I2C Handle.
* This parameter can be I2C where x: 1, 2, or 3 to select the I2C peripheral.
* @retval None
*/
#define __HAL_I2C_CLEAR_ADDRFLAG(__HANDLE__) \
do{ \
__IO uint32_t tmpreg = 0x00U; \
tmpreg = (__HANDLE__)->Instance->SR1; \
tmpreg = (__HANDLE__)->Instance->SR2; \
UNUSED(tmpreg); \
} while(0)
/** @brief Clears the I2C STOPF pending flag.
* @param __HANDLE__ specifies the I2C Handle.
* @retval None
*/
#define __HAL_I2C_CLEAR_STOPFLAG(__HANDLE__) \
do{ \
__IO uint32_t tmpreg = 0x00U; \
tmpreg = (__HANDLE__)->Instance->SR1; \
SET_BIT((__HANDLE__)->Instance->CR1, I2C_CR1_PE); \
UNUSED(tmpreg); \
} while(0)
/** @brief Enable the specified I2C peripheral.
* @param __HANDLE__ specifies the I2C Handle.
* @retval None
*/
#define __HAL_I2C_ENABLE(__HANDLE__) SET_BIT((__HANDLE__)->Instance->CR1, I2C_CR1_PE)
/** @brief Disable the specified I2C peripheral.
* @param __HANDLE__ specifies the I2C Handle.
* @retval None
*/
#define __HAL_I2C_DISABLE(__HANDLE__) CLEAR_BIT((__HANDLE__)->Instance->CR1, I2C_CR1_PE)
/**
* @}
*/
/* Include I2C HAL Extension module */
#include "stm32f4xx_hal_i2c_ex.h"
/* Exported functions --------------------------------------------------------*/
/** @addtogroup I2C_Exported_Functions
* @{
*/
/** @addtogroup I2C_Exported_Functions_Group1 Initialization and de-initialization functions
* @{
*/
/* Initialization and de-initialization functions******************************/
HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_DeInit(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MspDeInit(I2C_HandleTypeDef *hi2c);
/* Callbacks Register/UnRegister functions ***********************************/
#if (USE_HAL_I2C_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef HAL_I2C_RegisterCallback(I2C_HandleTypeDef *hi2c, HAL_I2C_CallbackIDTypeDef CallbackID, pI2C_CallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_I2C_UnRegisterCallback(I2C_HandleTypeDef *hi2c, HAL_I2C_CallbackIDTypeDef CallbackID);
HAL_StatusTypeDef HAL_I2C_RegisterAddrCallback(I2C_HandleTypeDef *hi2c, pI2C_AddrCallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_I2C_UnRegisterAddrCallback(I2C_HandleTypeDef *hi2c);
#endif /* USE_HAL_I2C_REGISTER_CALLBACKS */
/**
* @}
*/
/** @addtogroup I2C_Exported_Functions_Group2 Input and Output operation functions
* @{
*/
/* IO operation functions ****************************************************/
/******* Blocking mode: Polling */
HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Slave_Receive(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout);
/******* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_DisableListen_IT(I2C_HandleTypeDef *hi2c);
HAL_StatusTypeDef HAL_I2C_Master_Abort_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress);
/******* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Master_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
HAL_StatusTypeDef HAL_I2C_Slave_Seq_Receive_DMA(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t Size, uint32_t XferOptions);
/**
* @}
*/
/** @addtogroup I2C_IRQ_Handler_and_Callbacks IRQ Handler and Callbacks
* @{
*/
/******* I2C IRQHandler and Callbacks used in non blocking modes (Interrupt and DMA) */
void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c);
void HAL_I2C_ER_IRQHandler(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode);
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c);
void HAL_I2C_AbortCpltCallback(I2C_HandleTypeDef *hi2c);
/**
* @}
*/
/** @addtogroup I2C_Exported_Functions_Group3 Peripheral State, Mode and Error functions
* @{
*/
/* Peripheral State, Mode and Error functions *********************************/
HAL_I2C_StateTypeDef HAL_I2C_GetState(I2C_HandleTypeDef *hi2c);
HAL_I2C_ModeTypeDef HAL_I2C_GetMode(I2C_HandleTypeDef *hi2c);
uint32_t HAL_I2C_GetError(I2C_HandleTypeDef *hi2c);
/**
* @}
*/
/**
* @}
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @defgroup I2C_Private_Constants I2C Private Constants
* @{
*/
#define I2C_FLAG_MASK 0x0000FFFFU
#define I2C_MIN_PCLK_FREQ_STANDARD 2000000U /*!< 2 MHz */
#define I2C_MIN_PCLK_FREQ_FAST 4000000U /*!< 4 MHz */
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @defgroup I2C_Private_Macros I2C Private Macros
* @{
*/
#define I2C_MIN_PCLK_FREQ(__PCLK__, __SPEED__) (((__SPEED__) <= 100000U) ? ((__PCLK__) < I2C_MIN_PCLK_FREQ_STANDARD) : ((__PCLK__) < I2C_MIN_PCLK_FREQ_FAST))
#define I2C_CCR_CALCULATION(__PCLK__, __SPEED__, __COEFF__) (((((__PCLK__) - 1U)/((__SPEED__) * (__COEFF__))) + 1U) & I2C_CCR_CCR)
#define I2C_FREQRANGE(__PCLK__) ((__PCLK__)/1000000U)
#define I2C_RISE_TIME(__FREQRANGE__, __SPEED__) (((__SPEED__) <= 100000U) ? ((__FREQRANGE__) + 1U) : ((((__FREQRANGE__) * 300U) / 1000U) + 1U))
#define I2C_SPEED_STANDARD(__PCLK__, __SPEED__) ((I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 2U) < 4U)? 4U:I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 2U))
#define I2C_SPEED_FAST(__PCLK__, __SPEED__, __DUTYCYCLE__) (((__DUTYCYCLE__) == I2C_DUTYCYCLE_2)? I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 3U) : (I2C_CCR_CALCULATION((__PCLK__), (__SPEED__), 25U) | I2C_DUTYCYCLE_16_9))
#define I2C_SPEED(__PCLK__, __SPEED__, __DUTYCYCLE__) (((__SPEED__) <= 100000U)? (I2C_SPEED_STANDARD((__PCLK__), (__SPEED__))) : \
((I2C_SPEED_FAST((__PCLK__), (__SPEED__), (__DUTYCYCLE__)) & I2C_CCR_CCR) == 0U)? 1U : \
((I2C_SPEED_FAST((__PCLK__), (__SPEED__), (__DUTYCYCLE__))) | I2C_CCR_FS))
#define I2C_7BIT_ADD_WRITE(__ADDRESS__) ((uint8_t)((__ADDRESS__) & (uint8_t)(~I2C_OAR1_ADD0)))
#define I2C_7BIT_ADD_READ(__ADDRESS__) ((uint8_t)((__ADDRESS__) | I2C_OAR1_ADD0))
#define I2C_10BIT_ADDRESS(__ADDRESS__) ((uint8_t)((uint16_t)((__ADDRESS__) & (uint16_t)0x00FF)))
#define I2C_10BIT_HEADER_WRITE(__ADDRESS__) ((uint8_t)((uint16_t)((uint16_t)(((uint16_t)((__ADDRESS__) & (uint16_t)0x0300)) >> 7) | (uint16_t)0x00F0)))
#define I2C_10BIT_HEADER_READ(__ADDRESS__) ((uint8_t)((uint16_t)((uint16_t)(((uint16_t)((__ADDRESS__) & (uint16_t)0x0300)) >> 7) | (uint16_t)(0x00F1))))
#define I2C_MEM_ADD_MSB(__ADDRESS__) ((uint8_t)((uint16_t)(((uint16_t)((__ADDRESS__) & (uint16_t)0xFF00)) >> 8)))
#define I2C_MEM_ADD_LSB(__ADDRESS__) ((uint8_t)((uint16_t)((__ADDRESS__) & (uint16_t)0x00FF)))
/** @defgroup I2C_IS_RTC_Definitions I2C Private macros to check input parameters
* @{
*/
#define IS_I2C_DUTY_CYCLE(CYCLE) (((CYCLE) == I2C_DUTYCYCLE_2) || \
((CYCLE) == I2C_DUTYCYCLE_16_9))
#define IS_I2C_ADDRESSING_MODE(ADDRESS) (((ADDRESS) == I2C_ADDRESSINGMODE_7BIT) || \
((ADDRESS) == I2C_ADDRESSINGMODE_10BIT))
#define IS_I2C_DUAL_ADDRESS(ADDRESS) (((ADDRESS) == I2C_DUALADDRESS_DISABLE) || \
((ADDRESS) == I2C_DUALADDRESS_ENABLE))
#define IS_I2C_GENERAL_CALL(CALL) (((CALL) == I2C_GENERALCALL_DISABLE) || \
((CALL) == I2C_GENERALCALL_ENABLE))
#define IS_I2C_NO_STRETCH(STRETCH) (((STRETCH) == I2C_NOSTRETCH_DISABLE) || \
((STRETCH) == I2C_NOSTRETCH_ENABLE))
#define IS_I2C_MEMADD_SIZE(SIZE) (((SIZE) == I2C_MEMADD_SIZE_8BIT) || \
((SIZE) == I2C_MEMADD_SIZE_16BIT))
#define IS_I2C_CLOCK_SPEED(SPEED) (((SPEED) > 0U) && ((SPEED) <= 400000U))
#define IS_I2C_OWN_ADDRESS1(ADDRESS1) (((ADDRESS1) & 0xFFFFFC00U) == 0U)
#define IS_I2C_OWN_ADDRESS2(ADDRESS2) (((ADDRESS2) & 0xFFFFFF01U) == 0U)
#define IS_I2C_TRANSFER_OPTIONS_REQUEST(REQUEST) (((REQUEST) == I2C_FIRST_FRAME) || \
((REQUEST) == I2C_FIRST_AND_NEXT_FRAME) || \
((REQUEST) == I2C_NEXT_FRAME) || \
((REQUEST) == I2C_FIRST_AND_LAST_FRAME) || \
((REQUEST) == I2C_LAST_FRAME) || \
((REQUEST) == I2C_LAST_FRAME_NO_STOP) || \
IS_I2C_TRANSFER_OTHER_OPTIONS_REQUEST(REQUEST))
#define IS_I2C_TRANSFER_OTHER_OPTIONS_REQUEST(REQUEST) (((REQUEST) == I2C_OTHER_FRAME) || \
((REQUEST) == I2C_OTHER_AND_LAST_FRAME))
#define I2C_CHECK_FLAG(__ISR__, __FLAG__) ((((__ISR__) & ((__FLAG__) & I2C_FLAG_MASK)) == ((__FLAG__) & I2C_FLAG_MASK)) ? SET : RESET)
#define I2C_CHECK_IT_SOURCE(__CR1__, __IT__) ((((__CR1__) & (__IT__)) == (__IT__)) ? SET : RESET)
/**
* @}
*/
/**
* @}
*/
/* Private functions ---------------------------------------------------------*/
/** @defgroup I2C_Private_Functions I2C Private Functions
* @{
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_HAL_I2C_H */

View File

@@ -0,0 +1,115 @@
/**
******************************************************************************
* @file stm32f4xx_hal_i2c_ex.h
* @author MCD Application Team
* @brief Header file of I2C HAL Extension 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_I2C_EX_H
#define __STM32F4xx_HAL_I2C_EX_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(I2C_FLTR_ANOFF)&&defined(I2C_FLTR_DNF)
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal_def.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @addtogroup I2CEx
* @{
*/
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup I2CEx_Exported_Constants I2C Exported Constants
* @{
*/
/** @defgroup I2CEx_Analog_Filter I2C Analog Filter
* @{
*/
#define I2C_ANALOGFILTER_ENABLE 0x00000000U
#define I2C_ANALOGFILTER_DISABLE I2C_FLTR_ANOFF
/**
* @}
*/
/**
* @}
*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup I2CEx_Exported_Functions
* @{
*/
/** @addtogroup I2CEx_Exported_Functions_Group1
* @{
*/
/* Peripheral Control functions ************************************************/
HAL_StatusTypeDef HAL_I2CEx_ConfigAnalogFilter(I2C_HandleTypeDef *hi2c, uint32_t AnalogFilter);
HAL_StatusTypeDef HAL_I2CEx_ConfigDigitalFilter(I2C_HandleTypeDef *hi2c, uint32_t DigitalFilter);
/**
* @}
*/
/**
* @}
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @defgroup I2CEx_Private_Constants I2C Private Constants
* @{
*/
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @defgroup I2CEx_Private_Macros I2C Private Macros
* @{
*/
#define IS_I2C_ANALOG_FILTER(FILTER) (((FILTER) == I2C_ANALOGFILTER_ENABLE) || \
((FILTER) == I2C_ANALOGFILTER_DISABLE))
#define IS_I2C_DIGITAL_FILTER(FILTER) ((FILTER) <= 0x0000000FU)
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_HAL_I2C_EX_H */

View File

@@ -0,0 +1,733 @@
/**
******************************************************************************
* @file stm32f4xx_hal_spi.h
* @author MCD Application Team
* @brief Header file of SPI HAL 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_SPI_H
#define STM32F4xx_HAL_SPI_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal_def.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @addtogroup SPI
* @{
*/
/* Exported types ------------------------------------------------------------*/
/** @defgroup SPI_Exported_Types SPI Exported Types
* @{
*/
/**
* @brief SPI Configuration Structure definition
*/
typedef struct
{
uint32_t Mode; /*!< Specifies the SPI operating mode.
This parameter can be a value of @ref SPI_Mode */
uint32_t Direction; /*!< Specifies the SPI bidirectional mode state.
This parameter can be a value of @ref SPI_Direction */
uint32_t DataSize; /*!< Specifies the SPI data size.
This parameter can be a value of @ref SPI_Data_Size */
uint32_t CLKPolarity; /*!< Specifies the serial clock steady state.
This parameter can be a value of @ref SPI_Clock_Polarity */
uint32_t CLKPhase; /*!< Specifies the clock active edge for the bit capture.
This parameter can be a value of @ref SPI_Clock_Phase */
uint32_t NSS; /*!< Specifies whether the NSS signal is managed by
hardware (NSS pin) or by software using the SSI bit.
This parameter can be a value of @ref SPI_Slave_Select_management */
uint32_t BaudRatePrescaler; /*!< Specifies the Baud Rate prescaler value which will be
used to configure the transmit and receive SCK clock.
This parameter can be a value of @ref SPI_BaudRate_Prescaler
@note The communication clock is derived from the master
clock. The slave clock does not need to be set. */
uint32_t FirstBit; /*!< Specifies whether data transfers start from MSB or LSB bit.
This parameter can be a value of @ref SPI_MSB_LSB_transmission */
uint32_t TIMode; /*!< Specifies if the TI mode is enabled or not.
This parameter can be a value of @ref SPI_TI_mode */
uint32_t CRCCalculation; /*!< Specifies if the CRC calculation is enabled or not.
This parameter can be a value of @ref SPI_CRC_Calculation */
uint32_t CRCPolynomial; /*!< Specifies the polynomial used for the CRC calculation.
This parameter must be an odd number between Min_Data = 1 and Max_Data = 65535 */
} SPI_InitTypeDef;
/**
* @brief HAL SPI State structure definition
*/
typedef enum
{
HAL_SPI_STATE_RESET = 0x00U, /*!< Peripheral not Initialized */
HAL_SPI_STATE_READY = 0x01U, /*!< Peripheral Initialized and ready for use */
HAL_SPI_STATE_BUSY = 0x02U, /*!< an internal process is ongoing */
HAL_SPI_STATE_BUSY_TX = 0x03U, /*!< Data Transmission process is ongoing */
HAL_SPI_STATE_BUSY_RX = 0x04U, /*!< Data Reception process is ongoing */
HAL_SPI_STATE_BUSY_TX_RX = 0x05U, /*!< Data Transmission and Reception process is ongoing */
HAL_SPI_STATE_ERROR = 0x06U, /*!< SPI error state */
HAL_SPI_STATE_ABORT = 0x07U /*!< SPI abort is ongoing */
} HAL_SPI_StateTypeDef;
/**
* @brief SPI handle Structure definition
*/
typedef struct __SPI_HandleTypeDef
{
SPI_TypeDef *Instance; /*!< SPI registers base address */
SPI_InitTypeDef Init; /*!< SPI communication parameters */
const uint8_t *pTxBuffPtr; /*!< Pointer to SPI Tx transfer Buffer */
uint16_t TxXferSize; /*!< SPI Tx Transfer size */
__IO uint16_t TxXferCount; /*!< SPI Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to SPI Rx transfer Buffer */
uint16_t RxXferSize; /*!< SPI Rx Transfer size */
__IO uint16_t RxXferCount; /*!< SPI Rx Transfer Counter */
void (*RxISR)(struct __SPI_HandleTypeDef *hspi); /*!< function pointer on Rx ISR */
void (*TxISR)(struct __SPI_HandleTypeDef *hspi); /*!< function pointer on Tx ISR */
DMA_HandleTypeDef *hdmatx; /*!< SPI Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< SPI Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_SPI_StateTypeDef State; /*!< SPI communication state */
__IO uint32_t ErrorCode; /*!< SPI Error code */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
void (* TxCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Tx Completed callback */
void (* RxCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Rx Completed callback */
void (* TxRxCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI TxRx Completed callback */
void (* TxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Tx Half Completed callback */
void (* RxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Rx Half Completed callback */
void (* TxRxHalfCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI TxRx Half Completed callback */
void (* ErrorCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Error callback */
void (* AbortCpltCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Abort callback */
void (* MspInitCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Msp Init callback */
void (* MspDeInitCallback)(struct __SPI_HandleTypeDef *hspi); /*!< SPI Msp DeInit callback */
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
} SPI_HandleTypeDef;
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
/**
* @brief HAL SPI Callback ID enumeration definition
*/
typedef enum
{
HAL_SPI_TX_COMPLETE_CB_ID = 0x00U, /*!< SPI Tx Completed callback ID */
HAL_SPI_RX_COMPLETE_CB_ID = 0x01U, /*!< SPI Rx Completed callback ID */
HAL_SPI_TX_RX_COMPLETE_CB_ID = 0x02U, /*!< SPI TxRx Completed callback ID */
HAL_SPI_TX_HALF_COMPLETE_CB_ID = 0x03U, /*!< SPI Tx Half Completed callback ID */
HAL_SPI_RX_HALF_COMPLETE_CB_ID = 0x04U, /*!< SPI Rx Half Completed callback ID */
HAL_SPI_TX_RX_HALF_COMPLETE_CB_ID = 0x05U, /*!< SPI TxRx Half Completed callback ID */
HAL_SPI_ERROR_CB_ID = 0x06U, /*!< SPI Error callback ID */
HAL_SPI_ABORT_CB_ID = 0x07U, /*!< SPI Abort callback ID */
HAL_SPI_MSPINIT_CB_ID = 0x08U, /*!< SPI Msp Init callback ID */
HAL_SPI_MSPDEINIT_CB_ID = 0x09U /*!< SPI Msp DeInit callback ID */
} HAL_SPI_CallbackIDTypeDef;
/**
* @brief HAL SPI Callback pointer definition
*/
typedef void (*pSPI_CallbackTypeDef)(SPI_HandleTypeDef *hspi); /*!< pointer to an SPI callback function */
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
/**
* @}
*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup SPI_Exported_Constants SPI Exported Constants
* @{
*/
/** @defgroup SPI_Error_Code SPI Error Code
* @{
*/
#define HAL_SPI_ERROR_NONE (0x00000000U) /*!< No error */
#define HAL_SPI_ERROR_MODF (0x00000001U) /*!< MODF error */
#define HAL_SPI_ERROR_CRC (0x00000002U) /*!< CRC error */
#define HAL_SPI_ERROR_OVR (0x00000004U) /*!< OVR error */
#define HAL_SPI_ERROR_FRE (0x00000008U) /*!< FRE error */
#define HAL_SPI_ERROR_DMA (0x00000010U) /*!< DMA transfer error */
#define HAL_SPI_ERROR_FLAG (0x00000020U) /*!< Error on RXNE/TXE/BSY Flag */
#define HAL_SPI_ERROR_ABORT (0x00000040U) /*!< Error during SPI Abort procedure */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
#define HAL_SPI_ERROR_INVALID_CALLBACK (0x00000080U) /*!< Invalid Callback error */
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
/**
* @}
*/
/** @defgroup SPI_Mode SPI Mode
* @{
*/
#define SPI_MODE_SLAVE (0x00000000U)
#define SPI_MODE_MASTER (SPI_CR1_MSTR | SPI_CR1_SSI)
/**
* @}
*/
/** @defgroup SPI_Direction SPI Direction Mode
* @{
*/
#define SPI_DIRECTION_2LINES (0x00000000U)
#define SPI_DIRECTION_2LINES_RXONLY SPI_CR1_RXONLY
#define SPI_DIRECTION_1LINE SPI_CR1_BIDIMODE
/**
* @}
*/
/** @defgroup SPI_Data_Size SPI Data Size
* @{
*/
#define SPI_DATASIZE_8BIT (0x00000000U)
#define SPI_DATASIZE_16BIT SPI_CR1_DFF
/**
* @}
*/
/** @defgroup SPI_Clock_Polarity SPI Clock Polarity
* @{
*/
#define SPI_POLARITY_LOW (0x00000000U)
#define SPI_POLARITY_HIGH SPI_CR1_CPOL
/**
* @}
*/
/** @defgroup SPI_Clock_Phase SPI Clock Phase
* @{
*/
#define SPI_PHASE_1EDGE (0x00000000U)
#define SPI_PHASE_2EDGE SPI_CR1_CPHA
/**
* @}
*/
/** @defgroup SPI_Slave_Select_management SPI Slave Select Management
* @{
*/
#define SPI_NSS_SOFT SPI_CR1_SSM
#define SPI_NSS_HARD_INPUT (0x00000000U)
#define SPI_NSS_HARD_OUTPUT (SPI_CR2_SSOE << 16U)
/**
* @}
*/
/** @defgroup SPI_BaudRate_Prescaler SPI BaudRate Prescaler
* @{
*/
#define SPI_BAUDRATEPRESCALER_2 (0x00000000U)
#define SPI_BAUDRATEPRESCALER_4 (SPI_CR1_BR_0)
#define SPI_BAUDRATEPRESCALER_8 (SPI_CR1_BR_1)
#define SPI_BAUDRATEPRESCALER_16 (SPI_CR1_BR_1 | SPI_CR1_BR_0)
#define SPI_BAUDRATEPRESCALER_32 (SPI_CR1_BR_2)
#define SPI_BAUDRATEPRESCALER_64 (SPI_CR1_BR_2 | SPI_CR1_BR_0)
#define SPI_BAUDRATEPRESCALER_128 (SPI_CR1_BR_2 | SPI_CR1_BR_1)
#define SPI_BAUDRATEPRESCALER_256 (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
/**
* @}
*/
/** @defgroup SPI_MSB_LSB_transmission SPI MSB LSB Transmission
* @{
*/
#define SPI_FIRSTBIT_MSB (0x00000000U)
#define SPI_FIRSTBIT_LSB SPI_CR1_LSBFIRST
/**
* @}
*/
/** @defgroup SPI_TI_mode SPI TI Mode
* @{
*/
#define SPI_TIMODE_DISABLE (0x00000000U)
#define SPI_TIMODE_ENABLE SPI_CR2_FRF
/**
* @}
*/
/** @defgroup SPI_CRC_Calculation SPI CRC Calculation
* @{
*/
#define SPI_CRCCALCULATION_DISABLE (0x00000000U)
#define SPI_CRCCALCULATION_ENABLE SPI_CR1_CRCEN
/**
* @}
*/
/** @defgroup SPI_Interrupt_definition SPI Interrupt Definition
* @{
*/
#define SPI_IT_TXE SPI_CR2_TXEIE
#define SPI_IT_RXNE SPI_CR2_RXNEIE
#define SPI_IT_ERR SPI_CR2_ERRIE
/**
* @}
*/
/** @defgroup SPI_Flags_definition SPI Flags Definition
* @{
*/
#define SPI_FLAG_RXNE SPI_SR_RXNE /* SPI status flag: Rx buffer not empty flag */
#define SPI_FLAG_TXE SPI_SR_TXE /* SPI status flag: Tx buffer empty flag */
#define SPI_FLAG_BSY SPI_SR_BSY /* SPI status flag: Busy flag */
#define SPI_FLAG_CRCERR SPI_SR_CRCERR /* SPI Error flag: CRC error flag */
#define SPI_FLAG_MODF SPI_SR_MODF /* SPI Error flag: Mode fault flag */
#define SPI_FLAG_OVR SPI_SR_OVR /* SPI Error flag: Overrun flag */
#define SPI_FLAG_FRE SPI_SR_FRE /* SPI Error flag: TI mode frame format error flag */
#define SPI_FLAG_MASK (SPI_SR_RXNE | SPI_SR_TXE | SPI_SR_BSY | SPI_SR_CRCERR\
| SPI_SR_MODF | SPI_SR_OVR | SPI_SR_FRE)
/**
* @}
*/
/**
* @}
*/
/* Exported macros -----------------------------------------------------------*/
/** @defgroup SPI_Exported_Macros SPI Exported Macros
* @{
*/
/** @brief Reset SPI handle state.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
#define __HAL_SPI_RESET_HANDLE_STATE(__HANDLE__) \
do{ \
(__HANDLE__)->State = HAL_SPI_STATE_RESET; \
(__HANDLE__)->MspInitCallback = NULL; \
(__HANDLE__)->MspDeInitCallback = NULL; \
} while(0)
#else
#define __HAL_SPI_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = HAL_SPI_STATE_RESET)
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
/** @brief Enable the specified SPI interrupts.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @param __INTERRUPT__ specifies the interrupt source to enable.
* This parameter can be one of the following values:
* @arg SPI_IT_TXE: Tx buffer empty interrupt enable
* @arg SPI_IT_RXNE: RX buffer not empty interrupt enable
* @arg SPI_IT_ERR: Error interrupt enable
* @retval None
*/
#define __HAL_SPI_ENABLE_IT(__HANDLE__, __INTERRUPT__) SET_BIT((__HANDLE__)->Instance->CR2, (__INTERRUPT__))
/** @brief Disable the specified SPI interrupts.
* @param __HANDLE__ specifies the SPI handle.
* This parameter can be SPIx where x: 1, 2, or 3 to select the SPI peripheral.
* @param __INTERRUPT__ specifies the interrupt source to disable.
* This parameter can be one of the following values:
* @arg SPI_IT_TXE: Tx buffer empty interrupt enable
* @arg SPI_IT_RXNE: RX buffer not empty interrupt enable
* @arg SPI_IT_ERR: Error interrupt enable
* @retval None
*/
#define __HAL_SPI_DISABLE_IT(__HANDLE__, __INTERRUPT__) CLEAR_BIT((__HANDLE__)->Instance->CR2, (__INTERRUPT__))
/** @brief Check whether the specified SPI interrupt source is enabled or not.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @param __INTERRUPT__ specifies the SPI interrupt source to check.
* This parameter can be one of the following values:
* @arg SPI_IT_TXE: Tx buffer empty interrupt enable
* @arg SPI_IT_RXNE: RX buffer not empty interrupt enable
* @arg SPI_IT_ERR: Error interrupt enable
* @retval The new state of __IT__ (TRUE or FALSE).
*/
#define __HAL_SPI_GET_IT_SOURCE(__HANDLE__, __INTERRUPT__) ((((__HANDLE__)->Instance->CR2\
& (__INTERRUPT__)) == (__INTERRUPT__)) ? SET : RESET)
/** @brief Check whether the specified SPI flag is set or not.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg SPI_FLAG_RXNE: Receive buffer not empty flag
* @arg SPI_FLAG_TXE: Transmit buffer empty flag
* @arg SPI_FLAG_CRCERR: CRC error flag
* @arg SPI_FLAG_MODF: Mode fault flag
* @arg SPI_FLAG_OVR: Overrun flag
* @arg SPI_FLAG_BSY: Busy flag
* @arg SPI_FLAG_FRE: Frame format error flag
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_SPI_GET_FLAG(__HANDLE__, __FLAG__) ((((__HANDLE__)->Instance->SR) & (__FLAG__)) == (__FLAG__))
/** @brief Clear the SPI CRCERR pending flag.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define __HAL_SPI_CLEAR_CRCERRFLAG(__HANDLE__) ((__HANDLE__)->Instance->SR = (uint16_t)(~SPI_FLAG_CRCERR))
/** @brief Clear the SPI MODF pending flag.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define __HAL_SPI_CLEAR_MODFFLAG(__HANDLE__) \
do{ \
__IO uint32_t tmpreg_modf = 0x00U; \
tmpreg_modf = (__HANDLE__)->Instance->SR; \
CLEAR_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_SPE); \
UNUSED(tmpreg_modf); \
} while(0U)
/** @brief Clear the SPI OVR pending flag.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define __HAL_SPI_CLEAR_OVRFLAG(__HANDLE__) \
do{ \
__IO uint32_t tmpreg_ovr = 0x00U; \
tmpreg_ovr = (__HANDLE__)->Instance->DR; \
tmpreg_ovr = (__HANDLE__)->Instance->SR; \
UNUSED(tmpreg_ovr); \
} while(0U)
/** @brief Clear the SPI FRE pending flag.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define __HAL_SPI_CLEAR_FREFLAG(__HANDLE__) \
do{ \
__IO uint32_t tmpreg_fre = 0x00U; \
tmpreg_fre = (__HANDLE__)->Instance->SR; \
UNUSED(tmpreg_fre); \
} while(0U)
/** @brief Enable the SPI peripheral.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define __HAL_SPI_ENABLE(__HANDLE__) SET_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_SPE)
/** @brief Disable the SPI peripheral.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define __HAL_SPI_DISABLE(__HANDLE__) CLEAR_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_SPE)
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @defgroup SPI_Private_Macros SPI Private Macros
* @{
*/
/** @brief Set the SPI transmit-only mode.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define SPI_1LINE_TX(__HANDLE__) SET_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_BIDIOE)
/** @brief Set the SPI receive-only mode.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define SPI_1LINE_RX(__HANDLE__) CLEAR_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_BIDIOE)
/** @brief Reset the CRC calculation of the SPI.
* @param __HANDLE__ specifies the SPI Handle.
* This parameter can be SPI where x: 1, 2, or 3 to select the SPI peripheral.
* @retval None
*/
#define SPI_RESET_CRC(__HANDLE__) \
do{ \
CLEAR_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_CRCEN); \
SET_BIT((__HANDLE__)->Instance->CR1, SPI_CR1_CRCEN); \
} while(0U)
/** @brief Check whether the specified SPI flag is set or not.
* @param __SR__ copy of SPI SR register.
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg SPI_FLAG_RXNE: Receive buffer not empty flag
* @arg SPI_FLAG_TXE: Transmit buffer empty flag
* @arg SPI_FLAG_CRCERR: CRC error flag
* @arg SPI_FLAG_MODF: Mode fault flag
* @arg SPI_FLAG_OVR: Overrun flag
* @arg SPI_FLAG_BSY: Busy flag
* @arg SPI_FLAG_FRE: Frame format error flag
* @retval SET or RESET.
*/
#define SPI_CHECK_FLAG(__SR__, __FLAG__) ((((__SR__) & ((__FLAG__) & SPI_FLAG_MASK)) == \
((__FLAG__) & SPI_FLAG_MASK)) ? SET : RESET)
/** @brief Check whether the specified SPI Interrupt is set or not.
* @param __CR2__ copy of SPI CR2 register.
* @param __INTERRUPT__ specifies the SPI interrupt source to check.
* This parameter can be one of the following values:
* @arg SPI_IT_TXE: Tx buffer empty interrupt enable
* @arg SPI_IT_RXNE: RX buffer not empty interrupt enable
* @arg SPI_IT_ERR: Error interrupt enable
* @retval SET or RESET.
*/
#define SPI_CHECK_IT_SOURCE(__CR2__, __INTERRUPT__) ((((__CR2__) & (__INTERRUPT__)) == \
(__INTERRUPT__)) ? SET : RESET)
/** @brief Checks if SPI Mode parameter is in allowed range.
* @param __MODE__ specifies the SPI Mode.
* This parameter can be a value of @ref SPI_Mode
* @retval None
*/
#define IS_SPI_MODE(__MODE__) (((__MODE__) == SPI_MODE_SLAVE) || \
((__MODE__) == SPI_MODE_MASTER))
/** @brief Checks if SPI Direction Mode parameter is in allowed range.
* @param __MODE__ specifies the SPI Direction Mode.
* This parameter can be a value of @ref SPI_Direction
* @retval None
*/
#define IS_SPI_DIRECTION(__MODE__) (((__MODE__) == SPI_DIRECTION_2LINES) || \
((__MODE__) == SPI_DIRECTION_2LINES_RXONLY) || \
((__MODE__) == SPI_DIRECTION_1LINE))
/** @brief Checks if SPI Direction Mode parameter is 2 lines.
* @param __MODE__ specifies the SPI Direction Mode.
* @retval None
*/
#define IS_SPI_DIRECTION_2LINES(__MODE__) ((__MODE__) == SPI_DIRECTION_2LINES)
/** @brief Checks if SPI Direction Mode parameter is 1 or 2 lines.
* @param __MODE__ specifies the SPI Direction Mode.
* @retval None
*/
#define IS_SPI_DIRECTION_2LINES_OR_1LINE(__MODE__) (((__MODE__) == SPI_DIRECTION_2LINES) || \
((__MODE__) == SPI_DIRECTION_1LINE))
/** @brief Checks if SPI Data Size parameter is in allowed range.
* @param __DATASIZE__ specifies the SPI Data Size.
* This parameter can be a value of @ref SPI_Data_Size
* @retval None
*/
#define IS_SPI_DATASIZE(__DATASIZE__) (((__DATASIZE__) == SPI_DATASIZE_16BIT) || \
((__DATASIZE__) == SPI_DATASIZE_8BIT))
/** @brief Checks if SPI Serial clock steady state parameter is in allowed range.
* @param __CPOL__ specifies the SPI serial clock steady state.
* This parameter can be a value of @ref SPI_Clock_Polarity
* @retval None
*/
#define IS_SPI_CPOL(__CPOL__) (((__CPOL__) == SPI_POLARITY_LOW) || \
((__CPOL__) == SPI_POLARITY_HIGH))
/** @brief Checks if SPI Clock Phase parameter is in allowed range.
* @param __CPHA__ specifies the SPI Clock Phase.
* This parameter can be a value of @ref SPI_Clock_Phase
* @retval None
*/
#define IS_SPI_CPHA(__CPHA__) (((__CPHA__) == SPI_PHASE_1EDGE) || \
((__CPHA__) == SPI_PHASE_2EDGE))
/** @brief Checks if SPI Slave Select parameter is in allowed range.
* @param __NSS__ specifies the SPI Slave Select management parameter.
* This parameter can be a value of @ref SPI_Slave_Select_management
* @retval None
*/
#define IS_SPI_NSS(__NSS__) (((__NSS__) == SPI_NSS_SOFT) || \
((__NSS__) == SPI_NSS_HARD_INPUT) || \
((__NSS__) == SPI_NSS_HARD_OUTPUT))
/** @brief Checks if SPI Baudrate prescaler parameter is in allowed range.
* @param __PRESCALER__ specifies the SPI Baudrate prescaler.
* This parameter can be a value of @ref SPI_BaudRate_Prescaler
* @retval None
*/
#define IS_SPI_BAUDRATE_PRESCALER(__PRESCALER__) (((__PRESCALER__) == SPI_BAUDRATEPRESCALER_2) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_4) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_8) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_16) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_32) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_64) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_128) || \
((__PRESCALER__) == SPI_BAUDRATEPRESCALER_256))
/** @brief Checks if SPI MSB LSB transmission parameter is in allowed range.
* @param __BIT__ specifies the SPI MSB LSB transmission (whether data transfer starts from MSB or LSB bit).
* This parameter can be a value of @ref SPI_MSB_LSB_transmission
* @retval None
*/
#define IS_SPI_FIRST_BIT(__BIT__) (((__BIT__) == SPI_FIRSTBIT_MSB) || \
((__BIT__) == SPI_FIRSTBIT_LSB))
/** @brief Checks if SPI TI mode parameter is in allowed range.
* @param __MODE__ specifies the SPI TI mode.
* This parameter can be a value of @ref SPI_TI_mode
* @retval None
*/
#define IS_SPI_TIMODE(__MODE__) (((__MODE__) == SPI_TIMODE_DISABLE) || \
((__MODE__) == SPI_TIMODE_ENABLE))
/** @brief Checks if SPI CRC calculation enabled state is in allowed range.
* @param __CALCULATION__ specifies the SPI CRC calculation enable state.
* This parameter can be a value of @ref SPI_CRC_Calculation
* @retval None
*/
#define IS_SPI_CRC_CALCULATION(__CALCULATION__) (((__CALCULATION__) == SPI_CRCCALCULATION_DISABLE) || \
((__CALCULATION__) == SPI_CRCCALCULATION_ENABLE))
/** @brief Checks if SPI polynomial value to be used for the CRC calculation, is in allowed range.
* @param __POLYNOMIAL__ specifies the SPI polynomial value to be used for the CRC calculation.
* This parameter must be a number between Min_Data = 0 and Max_Data = 65535
* @retval None
*/
#define IS_SPI_CRC_POLYNOMIAL(__POLYNOMIAL__) (((__POLYNOMIAL__) >= 0x1U) && \
((__POLYNOMIAL__) <= 0xFFFFU) && \
(((__POLYNOMIAL__)&0x1U) != 0U))
/** @brief Checks if DMA handle is valid.
* @param __HANDLE__ specifies a DMA Handle.
* @retval None
*/
#define IS_SPI_DMA_HANDLE(__HANDLE__) ((__HANDLE__) != NULL)
/**
* @}
*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup SPI_Exported_Functions
* @{
*/
/** @addtogroup SPI_Exported_Functions_Group1
* @{
*/
/* Initialization/de-initialization functions ********************************/
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DeInit(SPI_HandleTypeDef *hspi);
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi);
void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi);
/* Callbacks Register/UnRegister functions ***********************************/
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
HAL_StatusTypeDef HAL_SPI_RegisterCallback(SPI_HandleTypeDef *hspi, HAL_SPI_CallbackIDTypeDef CallbackID,
pSPI_CallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_SPI_UnRegisterCallback(SPI_HandleTypeDef *hspi, HAL_SPI_CallbackIDTypeDef CallbackID);
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
/**
* @}
*/
/** @addtogroup SPI_Exported_Functions_Group2
* @{
*/
/* I/O operation functions ***************************************************/
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, const uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, const uint8_t *pTxData, uint8_t *pRxData,
uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, const uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, const uint8_t *pTxData, uint8_t *pRxData,
uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, const uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, const uint8_t *pTxData, uint8_t *pRxData,
uint16_t Size);
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);
/* Transfer Abort functions */
HAL_StatusTypeDef HAL_SPI_Abort(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_Abort_IT(SPI_HandleTypeDef *hspi);
void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi);
/**
* @}
*/
/** @addtogroup SPI_Exported_Functions_Group3
* @{
*/
/* Peripheral State and Error functions ***************************************/
HAL_SPI_StateTypeDef HAL_SPI_GetState(const SPI_HandleTypeDef *hspi);
uint32_t HAL_SPI_GetError(const SPI_HandleTypeDef *hspi);
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* STM32F4xx_HAL_SPI_H */

File diff suppressed because it is too large Load Diff

View 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 */

View File

@@ -0,0 +1,909 @@
/**
******************************************************************************
* @file stm32f4xx_hal_uart.h
* @author MCD Application Team
* @brief Header file of UART HAL 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_UART_H
#define __STM32F4xx_HAL_UART_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal_def.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @addtogroup UART
* @{
*/
/* Exported types ------------------------------------------------------------*/
/** @defgroup UART_Exported_Types UART Exported Types
* @{
*/
/**
* @brief UART Init Structure definition
*/
typedef struct
{
uint32_t BaudRate; /*!< This member configures the UART communication baud rate.
The baud rate is computed using the following formula:
- IntegerDivider = ((PCLKx) / (8 * (OVR8+1) * (huart->Init.BaudRate)))
- FractionalDivider = ((IntegerDivider - ((uint32_t) IntegerDivider)) * 8 * (OVR8+1)) + 0.5
Where OVR8 is the "oversampling by 8 mode" configuration bit in the CR1 register. */
uint32_t WordLength; /*!< Specifies the number of data bits transmitted or received in a frame.
This parameter can be a value of @ref UART_Word_Length */
uint32_t StopBits; /*!< Specifies the number of stop bits transmitted.
This parameter can be a value of @ref UART_Stop_Bits */
uint32_t Parity; /*!< Specifies the parity mode.
This parameter can be a value of @ref UART_Parity
@note When parity is enabled, the computed parity is inserted
at the MSB position of the transmitted data (9th bit when
the word length is set to 9 data bits; 8th bit when the
word length is set to 8 data bits). */
uint32_t Mode; /*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
This parameter can be a value of @ref UART_Mode */
uint32_t HwFlowCtl; /*!< Specifies whether the hardware flow control mode is enabled or disabled.
This parameter can be a value of @ref UART_Hardware_Flow_Control */
uint32_t OverSampling; /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to fPCLK/8).
This parameter can be a value of @ref UART_Over_Sampling */
} UART_InitTypeDef;
/**
* @brief HAL UART State structures definition
* @note HAL UART State value is a combination of 2 different substates: gState and RxState.
* - gState contains UART state information related to global Handle management
* and also information related to Tx operations.
* gState value coding follow below described bitmap :
* b7-b6 Error information
* 00 : No Error
* 01 : (Not Used)
* 10 : Timeout
* 11 : Error
* b5 Peripheral initialization status
* 0 : Reset (Peripheral not initialized)
* 1 : Init done (Peripheral initialized. HAL UART Init function already called)
* b4-b3 (not used)
* xx : Should be set to 00
* b2 Intrinsic process state
* 0 : Ready
* 1 : Busy (Peripheral busy with some configuration or internal operations)
* b1 (not used)
* x : Should be set to 0
* b0 Tx state
* 0 : Ready (no Tx operation ongoing)
* 1 : Busy (Tx operation ongoing)
* - RxState contains information related to Rx operations.
* RxState value coding follow below described bitmap :
* b7-b6 (not used)
* xx : Should be set to 00
* b5 Peripheral initialization status
* 0 : Reset (Peripheral not initialized)
* 1 : Init done (Peripheral initialized)
* b4-b2 (not used)
* xxx : Should be set to 000
* b1 Rx state
* 0 : Ready (no Rx operation ongoing)
* 1 : Busy (Rx operation ongoing)
* b0 (not used)
* x : Should be set to 0.
*/
typedef enum
{
HAL_UART_STATE_RESET = 0x00U, /*!< Peripheral is not yet Initialized
Value is allowed for gState and RxState */
HAL_UART_STATE_READY = 0x20U, /*!< Peripheral Initialized and ready for use
Value is allowed for gState and RxState */
HAL_UART_STATE_BUSY = 0x24U, /*!< an internal process is ongoing
Value is allowed for gState only */
HAL_UART_STATE_BUSY_TX = 0x21U, /*!< Data Transmission process is ongoing
Value is allowed for gState only */
HAL_UART_STATE_BUSY_RX = 0x22U, /*!< Data Reception process is ongoing
Value is allowed for RxState only */
HAL_UART_STATE_BUSY_TX_RX = 0x23U, /*!< Data Transmission and Reception process is ongoing
Not to be used for neither gState nor RxState.
Value is result of combination (Or) between gState and RxState values */
HAL_UART_STATE_TIMEOUT = 0xA0U, /*!< Timeout state
Value is allowed for gState only */
HAL_UART_STATE_ERROR = 0xE0U /*!< Error
Value is allowed for gState only */
} HAL_UART_StateTypeDef;
/**
* @brief HAL UART Reception type definition
* @note HAL UART Reception type value aims to identify which type of Reception is ongoing.
* This parameter can be a value of @ref UART_Reception_Type_Values :
* HAL_UART_RECEPTION_STANDARD = 0x00U,
* HAL_UART_RECEPTION_TOIDLE = 0x01U,
*/
typedef uint32_t HAL_UART_RxTypeTypeDef;
/**
* @brief HAL UART Rx Event type definition
* @note HAL UART Rx Event type value aims to identify which type of Event has occurred
* leading to call of the RxEvent callback.
* This parameter can be a value of @ref UART_RxEvent_Type_Values :
* HAL_UART_RXEVENT_TC = 0x00U,
* HAL_UART_RXEVENT_HT = 0x01U,
* HAL_UART_RXEVENT_IDLE = 0x02U,
*/
typedef uint32_t HAL_UART_RxEventTypeTypeDef;
/**
* @brief UART handle Structure definition
*/
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
const uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
uint16_t TxXferSize; /*!< UART Tx Transfer size */
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
__IO HAL_UART_RxTypeTypeDef ReceptionType; /*!< Type of ongoing reception */
__IO HAL_UART_RxEventTypeTypeDef RxEventType; /*!< Type of Rx Event */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO uint32_t ErrorCode; /*!< UART Error code */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Half Complete Callback */
void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Complete Callback */
void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Half Complete Callback */
void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */
void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Error Callback */
void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Complete Callback */
void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Receive Complete Callback */
void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Wakeup Callback */
void (* RxEventCallback)(struct __UART_HandleTypeDef *huart, uint16_t Pos); /*!< UART Reception Event Callback */
void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp Init callback */
void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp DeInit callback */
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
} UART_HandleTypeDef;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/**
* @brief HAL UART Callback ID enumeration definition
*/
typedef enum
{
HAL_UART_TX_HALFCOMPLETE_CB_ID = 0x00U, /*!< UART Tx Half Complete Callback ID */
HAL_UART_TX_COMPLETE_CB_ID = 0x01U, /*!< UART Tx Complete Callback ID */
HAL_UART_RX_HALFCOMPLETE_CB_ID = 0x02U, /*!< UART Rx Half Complete Callback ID */
HAL_UART_RX_COMPLETE_CB_ID = 0x03U, /*!< UART Rx Complete Callback ID */
HAL_UART_ERROR_CB_ID = 0x04U, /*!< UART Error Callback ID */
HAL_UART_ABORT_COMPLETE_CB_ID = 0x05U, /*!< UART Abort Complete Callback ID */
HAL_UART_ABORT_TRANSMIT_COMPLETE_CB_ID = 0x06U, /*!< UART Abort Transmit Complete Callback ID */
HAL_UART_ABORT_RECEIVE_COMPLETE_CB_ID = 0x07U, /*!< UART Abort Receive Complete Callback ID */
HAL_UART_WAKEUP_CB_ID = 0x08U, /*!< UART Wakeup Callback ID */
HAL_UART_MSPINIT_CB_ID = 0x0BU, /*!< UART MspInit callback ID */
HAL_UART_MSPDEINIT_CB_ID = 0x0CU /*!< UART MspDeInit callback ID */
} HAL_UART_CallbackIDTypeDef;
/**
* @brief HAL UART Callback pointer definition
*/
typedef void (*pUART_CallbackTypeDef)(UART_HandleTypeDef *huart); /*!< pointer to an UART callback function */
typedef void (*pUART_RxEventCallbackTypeDef)(struct __UART_HandleTypeDef *huart, uint16_t Pos); /*!< pointer to a UART Rx Event specific callback function */
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
/**
* @}
*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup UART_Exported_Constants UART Exported Constants
* @{
*/
/** @defgroup UART_Error_Code UART Error Code
* @{
*/
#define HAL_UART_ERROR_NONE 0x00000000U /*!< No error */
#define HAL_UART_ERROR_PE 0x00000001U /*!< Parity error */
#define HAL_UART_ERROR_NE 0x00000002U /*!< Noise error */
#define HAL_UART_ERROR_FE 0x00000004U /*!< Frame error */
#define HAL_UART_ERROR_ORE 0x00000008U /*!< Overrun error */
#define HAL_UART_ERROR_DMA 0x00000010U /*!< DMA transfer error */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
#define HAL_UART_ERROR_INVALID_CALLBACK 0x00000020U /*!< Invalid Callback error */
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
/**
* @}
*/
/** @defgroup UART_Word_Length UART Word Length
* @{
*/
#define UART_WORDLENGTH_8B 0x00000000U
#define UART_WORDLENGTH_9B ((uint32_t)USART_CR1_M)
/**
* @}
*/
/** @defgroup UART_Stop_Bits UART Number of Stop Bits
* @{
*/
#define UART_STOPBITS_1 0x00000000U
#define UART_STOPBITS_2 ((uint32_t)USART_CR2_STOP_1)
/**
* @}
*/
/** @defgroup UART_Parity UART Parity
* @{
*/
#define UART_PARITY_NONE 0x00000000U
#define UART_PARITY_EVEN ((uint32_t)USART_CR1_PCE)
#define UART_PARITY_ODD ((uint32_t)(USART_CR1_PCE | USART_CR1_PS))
/**
* @}
*/
/** @defgroup UART_Hardware_Flow_Control UART Hardware Flow Control
* @{
*/
#define UART_HWCONTROL_NONE 0x00000000U
#define UART_HWCONTROL_RTS ((uint32_t)USART_CR3_RTSE)
#define UART_HWCONTROL_CTS ((uint32_t)USART_CR3_CTSE)
#define UART_HWCONTROL_RTS_CTS ((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE))
/**
* @}
*/
/** @defgroup UART_Mode UART Transfer Mode
* @{
*/
#define UART_MODE_RX ((uint32_t)USART_CR1_RE)
#define UART_MODE_TX ((uint32_t)USART_CR1_TE)
#define UART_MODE_TX_RX ((uint32_t)(USART_CR1_TE | USART_CR1_RE))
/**
* @}
*/
/** @defgroup UART_State UART State
* @{
*/
#define UART_STATE_DISABLE 0x00000000U
#define UART_STATE_ENABLE ((uint32_t)USART_CR1_UE)
/**
* @}
*/
/** @defgroup UART_Over_Sampling UART Over Sampling
* @{
*/
#define UART_OVERSAMPLING_16 0x00000000U
#define UART_OVERSAMPLING_8 ((uint32_t)USART_CR1_OVER8)
/**
* @}
*/
/** @defgroup UART_LIN_Break_Detection_Length UART LIN Break Detection Length
* @{
*/
#define UART_LINBREAKDETECTLENGTH_10B 0x00000000U
#define UART_LINBREAKDETECTLENGTH_11B ((uint32_t)USART_CR2_LBDL)
/**
* @}
*/
/** @defgroup UART_WakeUp_functions UART Wakeup Functions
* @{
*/
#define UART_WAKEUPMETHOD_IDLELINE 0x00000000U
#define UART_WAKEUPMETHOD_ADDRESSMARK ((uint32_t)USART_CR1_WAKE)
/**
* @}
*/
/** @defgroup UART_Flags UART FLags
* Elements values convention: 0xXXXX
* - 0xXXXX : Flag mask in the SR register
* @{
*/
#define UART_FLAG_CTS ((uint32_t)USART_SR_CTS)
#define UART_FLAG_LBD ((uint32_t)USART_SR_LBD)
#define UART_FLAG_TXE ((uint32_t)USART_SR_TXE)
#define UART_FLAG_TC ((uint32_t)USART_SR_TC)
#define UART_FLAG_RXNE ((uint32_t)USART_SR_RXNE)
#define UART_FLAG_IDLE ((uint32_t)USART_SR_IDLE)
#define UART_FLAG_ORE ((uint32_t)USART_SR_ORE)
#define UART_FLAG_NE ((uint32_t)USART_SR_NE)
#define UART_FLAG_FE ((uint32_t)USART_SR_FE)
#define UART_FLAG_PE ((uint32_t)USART_SR_PE)
/**
* @}
*/
/** @defgroup UART_Interrupt_definition UART Interrupt Definitions
* Elements values convention: 0xY000XXXX
* - XXXX : Interrupt mask (16 bits) in the Y register
* - Y : Interrupt source register (2bits)
* - 0001: CR1 register
* - 0010: CR2 register
* - 0011: CR3 register
* @{
*/
#define UART_IT_PE ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_PEIE))
#define UART_IT_TXE ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_TXEIE))
#define UART_IT_TC ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_TCIE))
#define UART_IT_RXNE ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_RXNEIE))
#define UART_IT_IDLE ((uint32_t)(UART_CR1_REG_INDEX << 28U | USART_CR1_IDLEIE))
#define UART_IT_LBD ((uint32_t)(UART_CR2_REG_INDEX << 28U | USART_CR2_LBDIE))
#define UART_IT_CTS ((uint32_t)(UART_CR3_REG_INDEX << 28U | USART_CR3_CTSIE))
#define UART_IT_ERR ((uint32_t)(UART_CR3_REG_INDEX << 28U | USART_CR3_EIE))
/**
* @}
*/
/** @defgroup UART_Reception_Type_Values UART Reception type values
* @{
*/
#define HAL_UART_RECEPTION_STANDARD (0x00000000U) /*!< Standard reception */
#define HAL_UART_RECEPTION_TOIDLE (0x00000001U) /*!< Reception till completion or IDLE event */
/**
* @}
*/
/** @defgroup UART_RxEvent_Type_Values UART RxEvent type values
* @{
*/
#define HAL_UART_RXEVENT_TC (0x00000000U) /*!< RxEvent linked to Transfer Complete event */
#define HAL_UART_RXEVENT_HT (0x00000001U) /*!< RxEvent linked to Half Transfer event */
#define HAL_UART_RXEVENT_IDLE (0x00000002U)
/**
* @}
*/
/**
* @}
*/
/* Exported macro ------------------------------------------------------------*/
/** @defgroup UART_Exported_Macros UART Exported Macros
* @{
*/
/** @brief Reset UART handle gstate & RxState
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @retval None
*/
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
#define __HAL_UART_RESET_HANDLE_STATE(__HANDLE__) do{ \
(__HANDLE__)->gState = HAL_UART_STATE_RESET; \
(__HANDLE__)->RxState = HAL_UART_STATE_RESET; \
(__HANDLE__)->MspInitCallback = NULL; \
(__HANDLE__)->MspDeInitCallback = NULL; \
} while(0U)
#else
#define __HAL_UART_RESET_HANDLE_STATE(__HANDLE__) do{ \
(__HANDLE__)->gState = HAL_UART_STATE_RESET; \
(__HANDLE__)->RxState = HAL_UART_STATE_RESET; \
} while(0U)
#endif /*USE_HAL_UART_REGISTER_CALLBACKS */
/** @brief Flushes the UART DR register
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
*/
#define __HAL_UART_FLUSH_DRREGISTER(__HANDLE__) ((__HANDLE__)->Instance->DR)
/** @brief Checks whether the specified UART flag is set or not.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
* @arg UART_FLAG_LBD: LIN Break detection flag
* @arg UART_FLAG_TXE: Transmit data register empty flag
* @arg UART_FLAG_TC: Transmission Complete flag
* @arg UART_FLAG_RXNE: Receive data register not empty flag
* @arg UART_FLAG_IDLE: Idle Line detection flag
* @arg UART_FLAG_ORE: Overrun Error flag
* @arg UART_FLAG_NE: Noise Error flag
* @arg UART_FLAG_FE: Framing Error flag
* @arg UART_FLAG_PE: Parity Error flag
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))
/** @brief Clears the specified UART pending flag.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __FLAG__ specifies the flag to check.
* This parameter can be any combination of the following values:
* @arg UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5).
* @arg UART_FLAG_LBD: LIN Break detection flag.
* @arg UART_FLAG_TC: Transmission Complete flag.
* @arg UART_FLAG_RXNE: Receive data register not empty flag.
*
* @note PE (Parity error), FE (Framing error), NE (Noise error), ORE (Overrun
* error) and IDLE (Idle line detected) flags are cleared by software
* sequence: a read operation to USART_SR register followed by a read
* operation to USART_DR register.
* @note RXNE flag can be also cleared by a read to the USART_DR register.
* @note TC flag can be also cleared by software sequence: a read operation to
* USART_SR register followed by a write operation to USART_DR register.
* @note TXE flag is cleared only by a write to the USART_DR register.
*
* @retval None
*/
#define __HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR = ~(__FLAG__))
/** @brief Clears the UART PE pending flag.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @retval None
*/
#define __HAL_UART_CLEAR_PEFLAG(__HANDLE__) \
do{ \
__IO uint32_t tmpreg = 0x00U; \
tmpreg = (__HANDLE__)->Instance->SR; \
tmpreg = (__HANDLE__)->Instance->DR; \
UNUSED(tmpreg); \
} while(0U)
/** @brief Clears the UART FE pending flag.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @retval None
*/
#define __HAL_UART_CLEAR_FEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
/** @brief Clears the UART NE pending flag.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @retval None
*/
#define __HAL_UART_CLEAR_NEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
/** @brief Clears the UART ORE pending flag.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @retval None
*/
#define __HAL_UART_CLEAR_OREFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
/** @brief Clears the UART IDLE pending flag.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @retval None
*/
#define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
/** @brief Enable the specified UART interrupt.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __INTERRUPT__ specifies the UART interrupt source to enable.
* This parameter can be one of the following values:
* @arg UART_IT_CTS: CTS change interrupt
* @arg UART_IT_LBD: LIN Break detection interrupt
* @arg UART_IT_TXE: Transmit Data Register empty interrupt
* @arg UART_IT_TC: Transmission complete interrupt
* @arg UART_IT_RXNE: Receive Data register not empty interrupt
* @arg UART_IT_IDLE: Idle line detection interrupt
* @arg UART_IT_PE: Parity Error interrupt
* @arg UART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
* @retval None
*/
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) & UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) & UART_IT_MASK)): \
((__HANDLE__)->Instance->CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
/** @brief Disable the specified UART interrupt.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __INTERRUPT__ specifies the UART interrupt source to disable.
* This parameter can be one of the following values:
* @arg UART_IT_CTS: CTS change interrupt
* @arg UART_IT_LBD: LIN Break detection interrupt
* @arg UART_IT_TXE: Transmit Data Register empty interrupt
* @arg UART_IT_TC: Transmission complete interrupt
* @arg UART_IT_RXNE: Receive Data register not empty interrupt
* @arg UART_IT_IDLE: Idle line detection interrupt
* @arg UART_IT_PE: Parity Error interrupt
* @arg UART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
* @retval None
*/
#define __HAL_UART_DISABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U) == UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 &= ~((__INTERRUPT__) & UART_IT_MASK)): \
(((__INTERRUPT__) >> 28U) == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 &= ~((__INTERRUPT__) & UART_IT_MASK)): \
((__HANDLE__)->Instance->CR3 &= ~ ((__INTERRUPT__) & UART_IT_MASK)))
/** @brief Checks whether the specified UART interrupt source is enabled or not.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __IT__ specifies the UART interrupt source to check.
* This parameter can be one of the following values:
* @arg UART_IT_CTS: CTS change interrupt (not available for UART4 and UART5)
* @arg UART_IT_LBD: LIN Break detection interrupt
* @arg UART_IT_TXE: Transmit Data Register empty interrupt
* @arg UART_IT_TC: Transmission complete interrupt
* @arg UART_IT_RXNE: Receive Data register not empty interrupt
* @arg UART_IT_IDLE: Idle line detection interrupt
* @arg UART_IT_ERR: Error interrupt
* @retval The new state of __IT__ (TRUE or FALSE).
*/
#define __HAL_UART_GET_IT_SOURCE(__HANDLE__, __IT__) (((((__IT__) >> 28U) == UART_CR1_REG_INDEX)? (__HANDLE__)->Instance->CR1:(((((uint32_t)(__IT__)) >> 28U) == UART_CR2_REG_INDEX)? \
(__HANDLE__)->Instance->CR2 : (__HANDLE__)->Instance->CR3)) & (((uint32_t)(__IT__)) & UART_IT_MASK))
/** @brief Enable CTS flow control
* @note This macro allows to enable CTS hardware flow control for a given UART instance,
* without need to call HAL_UART_Init() function.
* As involving direct access to UART registers, usage of this macro should be fully endorsed by user.
* @note As macro is expected to be used for modifying CTS Hw flow control feature activation, without need
* for USART instance Deinit/Init, following conditions for macro call should be fulfilled :
* - UART instance should have already been initialised (through call of HAL_UART_Init() )
* - macro could only be called when corresponding UART instance is disabled (i.e __HAL_UART_DISABLE(__HANDLE__))
* and should be followed by an Enable macro (i.e __HAL_UART_ENABLE(__HANDLE__)).
* @param __HANDLE__ specifies the UART Handle.
* The Handle Instance can be any USARTx (supporting the HW Flow control feature).
* It is used to select the USART peripheral (USART availability and x value depending on device).
* @retval None
*/
#define __HAL_UART_HWCONTROL_CTS_ENABLE(__HANDLE__) \
do{ \
ATOMIC_SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_CTSE); \
(__HANDLE__)->Init.HwFlowCtl |= USART_CR3_CTSE; \
} while(0U)
/** @brief Disable CTS flow control
* @note This macro allows to disable CTS hardware flow control for a given UART instance,
* without need to call HAL_UART_Init() function.
* As involving direct access to UART registers, usage of this macro should be fully endorsed by user.
* @note As macro is expected to be used for modifying CTS Hw flow control feature activation, without need
* for USART instance Deinit/Init, following conditions for macro call should be fulfilled :
* - UART instance should have already been initialised (through call of HAL_UART_Init() )
* - macro could only be called when corresponding UART instance is disabled (i.e __HAL_UART_DISABLE(__HANDLE__))
* and should be followed by an Enable macro (i.e __HAL_UART_ENABLE(__HANDLE__)).
* @param __HANDLE__ specifies the UART Handle.
* The Handle Instance can be any USARTx (supporting the HW Flow control feature).
* It is used to select the USART peripheral (USART availability and x value depending on device).
* @retval None
*/
#define __HAL_UART_HWCONTROL_CTS_DISABLE(__HANDLE__) \
do{ \
ATOMIC_CLEAR_BIT((__HANDLE__)->Instance->CR3, USART_CR3_CTSE); \
(__HANDLE__)->Init.HwFlowCtl &= ~(USART_CR3_CTSE); \
} while(0U)
/** @brief Enable RTS flow control
* This macro allows to enable RTS hardware flow control for a given UART instance,
* without need to call HAL_UART_Init() function.
* As involving direct access to UART registers, usage of this macro should be fully endorsed by user.
* @note As macro is expected to be used for modifying RTS Hw flow control feature activation, without need
* for USART instance Deinit/Init, following conditions for macro call should be fulfilled :
* - UART instance should have already been initialised (through call of HAL_UART_Init() )
* - macro could only be called when corresponding UART instance is disabled (i.e __HAL_UART_DISABLE(__HANDLE__))
* and should be followed by an Enable macro (i.e __HAL_UART_ENABLE(__HANDLE__)).
* @param __HANDLE__ specifies the UART Handle.
* The Handle Instance can be any USARTx (supporting the HW Flow control feature).
* It is used to select the USART peripheral (USART availability and x value depending on device).
* @retval None
*/
#define __HAL_UART_HWCONTROL_RTS_ENABLE(__HANDLE__) \
do{ \
ATOMIC_SET_BIT((__HANDLE__)->Instance->CR3, USART_CR3_RTSE); \
(__HANDLE__)->Init.HwFlowCtl |= USART_CR3_RTSE; \
} while(0U)
/** @brief Disable RTS flow control
* This macro allows to disable RTS hardware flow control for a given UART instance,
* without need to call HAL_UART_Init() function.
* As involving direct access to UART registers, usage of this macro should be fully endorsed by user.
* @note As macro is expected to be used for modifying RTS Hw flow control feature activation, without need
* for USART instance Deinit/Init, following conditions for macro call should be fulfilled :
* - UART instance should have already been initialised (through call of HAL_UART_Init() )
* - macro could only be called when corresponding UART instance is disabled (i.e __HAL_UART_DISABLE(__HANDLE__))
* and should be followed by an Enable macro (i.e __HAL_UART_ENABLE(__HANDLE__)).
* @param __HANDLE__ specifies the UART Handle.
* The Handle Instance can be any USARTx (supporting the HW Flow control feature).
* It is used to select the USART peripheral (USART availability and x value depending on device).
* @retval None
*/
#define __HAL_UART_HWCONTROL_RTS_DISABLE(__HANDLE__) \
do{ \
ATOMIC_CLEAR_BIT((__HANDLE__)->Instance->CR3, USART_CR3_RTSE);\
(__HANDLE__)->Init.HwFlowCtl &= ~(USART_CR3_RTSE); \
} while(0U)
/** @brief Macro to enable the UART's one bit sample method
* @param __HANDLE__ specifies the UART Handle.
* @retval None
*/
#define __HAL_UART_ONE_BIT_SAMPLE_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR3|= USART_CR3_ONEBIT)
/** @brief Macro to disable the UART's one bit sample method
* @param __HANDLE__ specifies the UART Handle.
* @retval None
*/
#define __HAL_UART_ONE_BIT_SAMPLE_DISABLE(__HANDLE__) ((__HANDLE__)->Instance->CR3\
&= (uint16_t)~((uint16_t)USART_CR3_ONEBIT))
/** @brief Enable UART
* @param __HANDLE__ specifies the UART Handle.
* @retval None
*/
#define __HAL_UART_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR1 |= USART_CR1_UE)
/** @brief Disable UART
* @param __HANDLE__ specifies the UART Handle.
* @retval None
*/
#define __HAL_UART_DISABLE(__HANDLE__) ((__HANDLE__)->Instance->CR1 &= ~USART_CR1_UE)
/**
* @}
*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup UART_Exported_Functions
* @{
*/
/** @addtogroup UART_Exported_Functions_Group1 Initialization and de-initialization functions
* @{
*/
/* Initialization/de-initialization functions **********************************/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_Init(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_LIN_Init(UART_HandleTypeDef *huart, uint32_t BreakDetectLength);
HAL_StatusTypeDef HAL_MultiProcessor_Init(UART_HandleTypeDef *huart, uint8_t Address, uint32_t WakeUpMethod);
HAL_StatusTypeDef HAL_UART_DeInit(UART_HandleTypeDef *huart);
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
void HAL_UART_MspDeInit(UART_HandleTypeDef *huart);
/* Callbacks Register/UnRegister functions ***********************************/
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef HAL_UART_RegisterCallback(UART_HandleTypeDef *huart, HAL_UART_CallbackIDTypeDef CallbackID,
pUART_CallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_UART_UnRegisterCallback(UART_HandleTypeDef *huart, HAL_UART_CallbackIDTypeDef CallbackID);
HAL_StatusTypeDef HAL_UART_RegisterRxEventCallback(UART_HandleTypeDef *huart, pUART_RxEventCallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_UART_UnRegisterRxEventCallback(UART_HandleTypeDef *huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
/**
* @}
*/
/** @addtogroup UART_Exported_Functions_Group2 IO operation functions
* @{
*/
/* IO operation functions *******************************************************/
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint16_t *RxLen,
uint32_t Timeout);
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_UART_RxEventTypeTypeDef HAL_UARTEx_GetRxEventType(UART_HandleTypeDef *huart);
/* Transfer Abort functions */
HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_Abort_IT(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortTransmit_IT(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_UART_AbortReceive_IT(UART_HandleTypeDef *huart);
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart);
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size);
/**
* @}
*/
/** @addtogroup UART_Exported_Functions_Group3
* @{
*/
/* Peripheral Control functions ************************************************/
HAL_StatusTypeDef HAL_LIN_SendBreak(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_MultiProcessor_EnterMuteMode(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_MultiProcessor_ExitMuteMode(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_EnableTransmitter(UART_HandleTypeDef *huart);
HAL_StatusTypeDef HAL_HalfDuplex_EnableReceiver(UART_HandleTypeDef *huart);
/**
* @}
*/
/** @addtogroup UART_Exported_Functions_Group4
* @{
*/
/* Peripheral State functions **************************************************/
HAL_UART_StateTypeDef HAL_UART_GetState(const UART_HandleTypeDef *huart);
uint32_t HAL_UART_GetError(const UART_HandleTypeDef *huart);
/**
* @}
*/
/**
* @}
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @defgroup UART_Private_Constants UART Private Constants
* @{
*/
/** @brief UART interruptions flag mask
*
*/
#define UART_IT_MASK 0x0000FFFFU
#define UART_CR1_REG_INDEX 1U
#define UART_CR2_REG_INDEX 2U
#define UART_CR3_REG_INDEX 3U
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @defgroup UART_Private_Macros UART Private Macros
* @{
*/
#define IS_UART_WORD_LENGTH(LENGTH) (((LENGTH) == UART_WORDLENGTH_8B) || \
((LENGTH) == UART_WORDLENGTH_9B))
#define IS_UART_LIN_WORD_LENGTH(LENGTH) (((LENGTH) == UART_WORDLENGTH_8B))
#define IS_UART_STOPBITS(STOPBITS) (((STOPBITS) == UART_STOPBITS_1) || \
((STOPBITS) == UART_STOPBITS_2))
#define IS_UART_PARITY(PARITY) (((PARITY) == UART_PARITY_NONE) || \
((PARITY) == UART_PARITY_EVEN) || \
((PARITY) == UART_PARITY_ODD))
#define IS_UART_HARDWARE_FLOW_CONTROL(CONTROL)\
(((CONTROL) == UART_HWCONTROL_NONE) || \
((CONTROL) == UART_HWCONTROL_RTS) || \
((CONTROL) == UART_HWCONTROL_CTS) || \
((CONTROL) == UART_HWCONTROL_RTS_CTS))
#define IS_UART_MODE(MODE) ((((MODE) & 0x0000FFF3U) == 0x00U) && ((MODE) != 0x00U))
#define IS_UART_STATE(STATE) (((STATE) == UART_STATE_DISABLE) || \
((STATE) == UART_STATE_ENABLE))
#define IS_UART_OVERSAMPLING(SAMPLING) (((SAMPLING) == UART_OVERSAMPLING_16) || \
((SAMPLING) == UART_OVERSAMPLING_8))
#define IS_UART_LIN_OVERSAMPLING(SAMPLING) (((SAMPLING) == UART_OVERSAMPLING_16))
#define IS_UART_LIN_BREAK_DETECT_LENGTH(LENGTH) (((LENGTH) == UART_LINBREAKDETECTLENGTH_10B) || \
((LENGTH) == UART_LINBREAKDETECTLENGTH_11B))
#define IS_UART_WAKEUPMETHOD(WAKEUP) (((WAKEUP) == UART_WAKEUPMETHOD_IDLELINE) || \
((WAKEUP) == UART_WAKEUPMETHOD_ADDRESSMARK))
#define IS_UART_BAUDRATE(BAUDRATE) ((BAUDRATE) <= 10500000U)
#define IS_UART_ADDRESS(ADDRESS) ((ADDRESS) <= 0x0FU)
#define UART_DIV_SAMPLING16(_PCLK_, _BAUD_) ((uint32_t)((((uint64_t)(_PCLK_))*25U)/(4U*((uint64_t)(_BAUD_)))))
#define UART_DIVMANT_SAMPLING16(_PCLK_, _BAUD_) (UART_DIV_SAMPLING16((_PCLK_), (_BAUD_))/100U)
#define UART_DIVFRAQ_SAMPLING16(_PCLK_, _BAUD_) ((((UART_DIV_SAMPLING16((_PCLK_), (_BAUD_)) - (UART_DIVMANT_SAMPLING16((_PCLK_), (_BAUD_)) * 100U)) * 16U)\
+ 50U) / 100U)
/* UART BRR = mantissa + overflow + fraction
= (UART DIVMANT << 4) + (UART DIVFRAQ & 0xF0) + (UART DIVFRAQ & 0x0FU) */
#define UART_BRR_SAMPLING16(_PCLK_, _BAUD_) ((UART_DIVMANT_SAMPLING16((_PCLK_), (_BAUD_)) << 4U) + \
(UART_DIVFRAQ_SAMPLING16((_PCLK_), (_BAUD_)) & 0xF0U) + \
(UART_DIVFRAQ_SAMPLING16((_PCLK_), (_BAUD_)) & 0x0FU))
#define UART_DIV_SAMPLING8(_PCLK_, _BAUD_) ((uint32_t)((((uint64_t)(_PCLK_))*25U)/(2U*((uint64_t)(_BAUD_)))))
#define UART_DIVMANT_SAMPLING8(_PCLK_, _BAUD_) (UART_DIV_SAMPLING8((_PCLK_), (_BAUD_))/100U)
#define UART_DIVFRAQ_SAMPLING8(_PCLK_, _BAUD_) ((((UART_DIV_SAMPLING8((_PCLK_), (_BAUD_)) - (UART_DIVMANT_SAMPLING8((_PCLK_), (_BAUD_)) * 100U)) * 8U)\
+ 50U) / 100U)
/* UART BRR = mantissa + overflow + fraction
= (UART DIVMANT << 4) + ((UART DIVFRAQ & 0xF8) << 1) + (UART DIVFRAQ & 0x07U) */
#define UART_BRR_SAMPLING8(_PCLK_, _BAUD_) ((UART_DIVMANT_SAMPLING8((_PCLK_), (_BAUD_)) << 4U) + \
((UART_DIVFRAQ_SAMPLING8((_PCLK_), (_BAUD_)) & 0xF8U) << 1U) + \
(UART_DIVFRAQ_SAMPLING8((_PCLK_), (_BAUD_)) & 0x07U))
/**
* @}
*/
/* Private functions ---------------------------------------------------------*/
/** @defgroup UART_Private_Functions UART Private Functions
* @{
*/
HAL_StatusTypeDef UART_Start_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef UART_Start_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_HAL_UART_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,182 @@
/**
******************************************************************************
* @file stm32f4xx_hal_i2c_ex.c
* @author MCD Application Team
* @brief I2C Extension HAL module driver.
* This file provides firmware functions to manage the following
* functionalities of I2C extension peripheral:
* + Extension features functions
*
******************************************************************************
* @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.
*
******************************************************************************
@verbatim
==============================================================================
##### I2C peripheral extension features #####
==============================================================================
[..] Comparing to other previous devices, the I2C interface for STM32F427xx/437xx/
429xx/439xx devices contains the following additional features :
(+) Possibility to disable or enable Analog Noise Filter
(+) Use of a configured Digital Noise Filter
##### How to use this driver #####
==============================================================================
[..] This driver provides functions to configure Noise Filter
(#) Configure I2C Analog noise filter using the function HAL_I2C_AnalogFilter_Config()
(#) Configure I2C Digital noise filter using the function HAL_I2C_DigitalFilter_Config()
@endverbatim
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @defgroup I2CEx I2CEx
* @brief I2C HAL module driver
* @{
*/
#ifdef HAL_I2C_MODULE_ENABLED
#if defined(I2C_FLTR_ANOFF)&&defined(I2C_FLTR_DNF)
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @defgroup I2CEx_Exported_Functions I2C Exported Functions
* @{
*/
/** @defgroup I2CEx_Exported_Functions_Group1 Extension features functions
* @brief Extension features functions
*
@verbatim
===============================================================================
##### Extension features functions #####
===============================================================================
[..] This section provides functions allowing to:
(+) Configure Noise Filters
@endverbatim
* @{
*/
/**
* @brief Configures I2C Analog noise filter.
* @param hi2c pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2Cx peripheral.
* @param AnalogFilter new state of the Analog filter.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2CEx_ConfigAnalogFilter(I2C_HandleTypeDef *hi2c, uint32_t AnalogFilter)
{
/* Check the parameters */
assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance));
assert_param(IS_I2C_ANALOG_FILTER(AnalogFilter));
if (hi2c->State == HAL_I2C_STATE_READY)
{
hi2c->State = HAL_I2C_STATE_BUSY;
/* Disable the selected I2C peripheral */
__HAL_I2C_DISABLE(hi2c);
/* Reset I2Cx ANOFF bit */
hi2c->Instance->FLTR &= ~(I2C_FLTR_ANOFF);
/* Disable the analog filter */
hi2c->Instance->FLTR |= AnalogFilter;
__HAL_I2C_ENABLE(hi2c);
hi2c->State = HAL_I2C_STATE_READY;
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
/**
* @brief Configures I2C Digital noise filter.
* @param hi2c pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2Cx peripheral.
* @param DigitalFilter Coefficient of digital noise filter between 0x00 and 0x0F.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2CEx_ConfigDigitalFilter(I2C_HandleTypeDef *hi2c, uint32_t DigitalFilter)
{
uint16_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_I2C_ALL_INSTANCE(hi2c->Instance));
assert_param(IS_I2C_DIGITAL_FILTER(DigitalFilter));
if (hi2c->State == HAL_I2C_STATE_READY)
{
hi2c->State = HAL_I2C_STATE_BUSY;
/* Disable the selected I2C peripheral */
__HAL_I2C_DISABLE(hi2c);
/* Get the old register value */
tmpreg = hi2c->Instance->FLTR;
/* Reset I2Cx DNF bit [3:0] */
tmpreg &= ~(I2C_FLTR_DNF);
/* Set I2Cx DNF coefficient */
tmpreg |= DigitalFilter;
/* Store the new register value */
hi2c->Instance->FLTR = tmpreg;
__HAL_I2C_ENABLE(hi2c);
hi2c->State = HAL_I2C_STATE_READY;
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
/**
* @}
*/
/**
* @}
*/
#endif
#endif /* HAL_I2C_MODULE_ENABLED */
/**
* @}
*/
/**
* @}
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

170
Inc/FreeRTOSConfig.h Normal file
View 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 */

52
Inc/dma.h Normal file
View File

@@ -0,0 +1,52 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file dma.h
* @brief This file contains all the function prototypes for
* the dma.c file
******************************************************************************
* @attention
*
* Copyright (c) 2026 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.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __DMA_H__
#define __DMA_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* DMA memory to memory transfer handles -------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_DMA_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __DMA_H__ */

52
Inc/i2c.h Normal file
View File

@@ -0,0 +1,52 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file i2c.h
* @brief This file contains all the function prototypes for
* the i2c.c file
******************************************************************************
* @attention
*
* Copyright (c) 2026 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.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __I2C_H__
#define __I2C_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern I2C_HandleTypeDef hi2c1;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_I2C1_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __I2C_H__ */

View File

@@ -57,10 +57,109 @@ void Error_Handler(void);
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define CH395_INT_Pin GPIO_PIN_5
#define CH395_INT_GPIO_Port GPIOE
#define LED1_Pin GPIO_PIN_4
#define LED1_GPIO_Port GPIOC
#define LED2_Pin GPIO_PIN_5
#define LED2_GPIO_Port GPIOC
#define LED3_Pin GPIO_PIN_1
#define LED3_GPIO_Port GPIOB
#define LED4_Pin GPIO_PIN_2
#define LED4_GPIO_Port GPIOB
#define LED5_Pin GPIO_PIN_11
#define LED5_GPIO_Port GPIOF
#define LED6_Pin GPIO_PIN_12
#define LED6_GPIO_Port GPIOF
#define TP_OS0_Pin GPIO_PIN_13
#define TP_OS0_GPIO_Port GPIOF
#define TP_OS1_Pin GPIO_PIN_14
#define TP_OS1_GPIO_Port GPIOF
#define TP_OS2_Pin GPIO_PIN_15
#define TP_OS2_GPIO_Port GPIOF
#define DB0_Pin GPIO_PIN_0
#define DB0_GPIO_Port GPIOG
#define DB1_Pin GPIO_PIN_1
#define DB1_GPIO_Port GPIOG
#define ST_TX2_Pin GPIO_PIN_10
#define ST_TX2_GPIO_Port GPIOB
#define ST_RX2_Pin GPIO_PIN_11
#define ST_RX2_GPIO_Port GPIOB
#define CH395_SCS_Pin GPIO_PIN_12
#define CH395_SCS_GPIO_Port GPIOB
#define CH395F_SCK_Pin GPIO_PIN_13
#define CH395F_SCK_GPIO_Port GPIOB
#define CH395F_SDO_Pin GPIO_PIN_14
#define CH395F_SDO_GPIO_Port GPIOB
#define CH395F_SDOB15_Pin GPIO_PIN_15
#define CH395F_SDOB15_GPIO_Port GPIOB
#define DB2_Pin GPIO_PIN_2
#define DB2_GPIO_Port GPIOG
#define DB3_Pin GPIO_PIN_3
#define DB3_GPIO_Port GPIOG
#define DB4_Pin GPIO_PIN_4
#define DB4_GPIO_Port GPIOG
#define DB5_Pin GPIO_PIN_5
#define DB5_GPIO_Port GPIOG
#define DB6_Pin GPIO_PIN_6
#define DB6_GPIO_Port GPIOG
#define DB7_Pin GPIO_PIN_7
#define DB7_GPIO_Port GPIOG
#define DB8_Pin GPIO_PIN_8
#define DB8_GPIO_Port GPIOG
#define ST_TX3_Pin GPIO_PIN_10
#define ST_TX3_GPIO_Port GPIOC
#define ST_RX3_Pin GPIO_PIN_11
#define ST_RX3_GPIO_Port GPIOC
#define ST_TX4_Pin GPIO_PIN_12
#define ST_TX4_GPIO_Port GPIOC
#define ST_DIR4_Pin GPIO_PIN_0
#define ST_DIR4_GPIO_Port GPIOD
#define TP_FRSTDATA_Pin GPIO_PIN_1
#define TP_FRSTDATA_GPIO_Port GPIOD
#define ST_RX4_Pin GPIO_PIN_2
#define ST_RX4_GPIO_Port GPIOD
#define TP_RD_Pin GPIO_PIN_3
#define TP_RD_GPIO_Port GPIOD
#define TP_CONVST_Pin GPIO_PIN_4
#define TP_CONVST_GPIO_Port GPIOD
#define ST_TX1_Pin GPIO_PIN_5
#define ST_TX1_GPIO_Port GPIOD
#define ST_RX1_Pin GPIO_PIN_6
#define ST_RX1_GPIO_Port GPIOD
#define TP_BUSY_Pin GPIO_PIN_7
#define TP_BUSY_GPIO_Port GPIOD
#define TP_BUSY_EXTI_IRQn EXTI9_5_IRQn
#define DB9_Pin GPIO_PIN_9
#define DB9_GPIO_Port GPIOG
#define DB10_Pin GPIO_PIN_10
#define DB10_GPIO_Port GPIOG
#define DB11_Pin GPIO_PIN_11
#define DB11_GPIO_Port GPIOG
#define DB12_Pin GPIO_PIN_12
#define DB12_GPIO_Port GPIOG
#define DB13_Pin GPIO_PIN_13
#define DB13_GPIO_Port GPIOG
#define DB14_Pin GPIO_PIN_14
#define DB14_GPIO_Port GPIOG
#define DB15_Pin GPIO_PIN_15
#define DB15_GPIO_Port GPIOG
#define GD_SCLK_Pin GPIO_PIN_3
#define GD_SCLK_GPIO_Port GPIOB
#define GD_SO_Pin GPIO_PIN_4
#define GD_SO_GPIO_Port GPIOB
#define GD_SI_Pin GPIO_PIN_5
#define GD_SI_GPIO_Port GPIOB
#define SD_SCL_Pin GPIO_PIN_6
#define SD_SCL_GPIO_Port GPIOB
#define SD_SDA_Pin GPIO_PIN_7
#define SD_SDA_GPIO_Port GPIOB
#define GD_WP_Pin GPIO_PIN_8
#define GD_WP_GPIO_Port GPIOB
#define GD_CS_Pin GPIO_PIN_0
#define GD_CS_GPIO_Port GPIOE
#define GD_HOLD_Pin GPIO_PIN_1
#define GD_HOLD_GPIO_Port GPIOE
/* USER CODE BEGIN Private defines */

55
Inc/spi.h Normal file
View File

@@ -0,0 +1,55 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file spi.h
* @brief This file contains all the function prototypes for
* the spi.c file
******************************************************************************
* @attention
*
* Copyright (c) 2026 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.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __SPI_H__
#define __SPI_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern SPI_HandleTypeDef hspi1;
extern SPI_HandleTypeDef hspi2;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_SPI1_Init(void);
void MX_SPI2_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __SPI_H__ */

View File

@@ -53,7 +53,7 @@
/* #define HAL_SRAM_MODULE_ENABLED */
/* #define HAL_SDRAM_MODULE_ENABLED */
/* #define HAL_HASH_MODULE_ENABLED */
/* #define HAL_I2C_MODULE_ENABLED */
#define HAL_I2C_MODULE_ENABLED
/* #define HAL_I2S_MODULE_ENABLED */
/* #define HAL_IWDG_MODULE_ENABLED */
/* #define HAL_LTDC_MODULE_ENABLED */
@@ -62,9 +62,9 @@
/* #define HAL_SAI_MODULE_ENABLED */
/* #define HAL_SD_MODULE_ENABLED */
/* #define HAL_MMC_MODULE_ENABLED */
/* #define HAL_SPI_MODULE_ENABLED */
/* #define HAL_TIM_MODULE_ENABLED */
/* #define HAL_UART_MODULE_ENABLED */
#define HAL_SPI_MODULE_ENABLED
#define HAL_TIM_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
/* #define HAL_USART_MODULE_ENABLED */
/* #define HAL_IRDA_MODULE_ENABLED */
/* #define HAL_SMARTCARD_MODULE_ENABLED */

View File

@@ -51,10 +51,22 @@ void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void DMA1_Stream3_IRQHandler(void);
void DMA1_Stream4_IRQHandler(void);
void EXTI9_5_IRQHandler(void);
void SPI1_IRQHandler(void);
void SPI2_IRQHandler(void);
void USART1_IRQHandler(void);
void USART2_IRQHandler(void);
void USART3_IRQHandler(void);
void UART4_IRQHandler(void);
void UART5_IRQHandler(void);
void TIM7_IRQHandler(void);
void DMA2_Stream0_IRQHandler(void);
void DMA2_Stream2_IRQHandler(void);
void DMA2_Stream3_IRQHandler(void);
void DMA2_Stream7_IRQHandler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */

64
Inc/usart.h Normal file
View File

@@ -0,0 +1,64 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.h
* @brief This file contains all the function prototypes for
* the usart.c file
******************************************************************************
* @attention
*
* Copyright (c) 2026 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.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart5;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart3;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_UART4_Init(void);
void MX_UART5_Init(void);
void MX_USART1_UART_Init(void);
void MX_USART2_UART_Init(void);
void MX_USART3_UART_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __USART_H__ */

43
Lib/FatFs/diskio.h Normal file
View 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

File diff suppressed because it is too large Load Diff

429
Lib/FatFs/ff.h Normal file
View 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
View 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

50
Lib/dhara/bytes.h Normal file
View 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
View 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
View 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
View 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
View 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, &current, 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
View 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
View 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
View 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
View 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
View 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
View File

0
MDK-ARM/out.txt Normal file
View File

View 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_

File diff suppressed because it is too large Load Diff

View 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_

View 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_ */

View 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_ */

View 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.

View 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 */

Some files were not shown because too many files have changed in this diff Show More