Compare commits
29 Commits
9943b410f4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 78eff31de7 | |||
| 565f23ea18 | |||
| 02a05a7a63 | |||
| 4e9ac0d9c3 | |||
| 615a9b13e9 | |||
| cf33171c8f | |||
| 88160a5134 | |||
| 0a707034cc | |||
| d6bc3f6253 | |||
| 4375873083 | |||
| fc88bc8752 | |||
| a7f367f6a3 | |||
| c646df5c2a | |||
| 038a2542af | |||
| 46a74f24f3 | |||
| 75cf4c9fbf | |||
| 6d404d136e | |||
| d48be81478 | |||
| 66a1e8fd6a | |||
| f50b9c9e66 | |||
| fbe0726991 | |||
| e06d849007 | |||
| 9ed4c22022 | |||
| 4eb728583b | |||
| 1669cd216d | |||
| f72d08328f | |||
| fe107ea9af | |||
| 3466c4c062 | |||
| c8c7ec0992 |
18
.mxproject
18
.mxproject
File diff suppressed because one or more lines are too long
14
.opencode/command/build.md
Normal file
14
.opencode/command/build.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
description: Build the STM32F4-Base Keil MDK project. Use ONLY when the user asks to build, compile, or verify the project.
|
||||
---
|
||||
|
||||
Run the Keil MDK-ARM build from the project root:
|
||||
|
||||
```
|
||||
cmd /c "cd /d "D:\Code\DTU 程序\STM32F4-Base\MDK-ARM" && build.bat"
|
||||
```
|
||||
|
||||
Check the last 10 lines of `MDK-ARM/build_log.txt` for errors/warnings.
|
||||
Return code 0 = success (0 errors, 0 warnings).
|
||||
Return code 1 = warnings-only (also failure per project rules).
|
||||
Return code 2+ = compile errors.
|
||||
94
AGENTS.md
Normal file
94
AGENTS.md
Normal 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 0(TCP 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 V2,HAL 时基用 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
36
App/app_cfg.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 模块名称:Application Configuration
|
||||
* 模块功能:应用层全局配置宏,控制功能模块的编译开关
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __APP_CFG_H
|
||||
#define __APP_CFG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 系统时钟源选择
|
||||
* 0 - DWT CYCCNT(默认,0 外设开销)
|
||||
* 1 - TIM2(32-bit 定时器,独立于 DWT)
|
||||
*/
|
||||
#define APP_SYS_TIME_SRC 0
|
||||
|
||||
/*
|
||||
* 功能模块使能开关
|
||||
*/
|
||||
#define APP_NET_ENABLE
|
||||
#define APP_ADC_ENABLE
|
||||
#define APP_RS485_ENABLE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __APP_CFG_H */
|
||||
42
App/app_main.c
Normal file
42
App/app_main.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 模块名称:Application Main Entry
|
||||
* 模块功能:应用初始化流程编排、各模块启动、主循环
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include "app_main.h"
|
||||
#include "app_cfg.h"
|
||||
|
||||
#define DBG_TAG "[APP]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/*
|
||||
* 函数功能:应用层初始化
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功,-1 - 失败
|
||||
* 限定条件:CubeMX 外设初始化已完成
|
||||
* 函数说明:在 CubeMX 外设初始化之后、FreeRTOS 调度器启动之前调用
|
||||
*/
|
||||
int app_main_init(void)
|
||||
{
|
||||
DBG_INFO("App init OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:应用主循环
|
||||
* 入口参数:无
|
||||
* 返 回 值:无
|
||||
* 限定条件:FreeRTOS 调度器启动后不再返回
|
||||
* 函数说明:空闲任务可调用的应用层处理入口
|
||||
*/
|
||||
void app_main_run(void)
|
||||
{
|
||||
for (;;) {
|
||||
/* 应用层空闲处理 */
|
||||
}
|
||||
}
|
||||
27
App/app_main.h
Normal file
27
App/app_main.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 模块名称:Application Main Entry
|
||||
* 模块功能:应用层入口,由 main.c 调用完成所有应用初始化与启动
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __APP_MAIN_H
|
||||
#define __APP_MAIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int app_main_init(void);
|
||||
void app_main_run(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __APP_MAIN_H */
|
||||
0
App/protocol/.gitkeep
Normal file
0
App/protocol/.gitkeep
Normal file
369
App/sys_clock.c
Normal file
369
App/sys_clock.c
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* 模块名称:System Clock — Wall Clock Time Maintenance
|
||||
* 模块功能:实现系统墙钟时间维护,包括 RTC 初始读取、HAL Tick 漂移维持
|
||||
* 适用平台:STM32F407ZGTx (Cortex-M4 FPU, 168MHz)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include "sys_clock.h"
|
||||
#include "main.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DBG_TAG "[CLK]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* ============================================================
|
||||
* RTC 校时(开发期一次性)
|
||||
* 使能 RTC_DEFAULT_INIT_ENABLE 后:
|
||||
* RTC_DEFAULT_INIT_FORCE=1:每次启动都写入默认时间(首次/覆盖校时用)
|
||||
* RTC_DEFAULT_INIT_FORCE=0:仅当 RTC 时间无效(越界)时写入
|
||||
* 校时完成后建议将 FORCE 置 0(或注释 ENABLE),避免每次开机覆盖用户设的时间。
|
||||
* 修改日期/时间时同步更新下列宏。
|
||||
* ============================================================ */
|
||||
#define RTC_DEFAULT_INIT_ENABLE 1
|
||||
#define RTC_DEFAULT_INIT_FORCE 0
|
||||
#define RTC_DEFAULT_YEAR 2026
|
||||
#define RTC_DEFAULT_MONTH 8
|
||||
#define RTC_DEFAULT_DAY 24
|
||||
#define RTC_DEFAULT_HOUR 0
|
||||
#define RTC_DEFAULT_MINUTE 0
|
||||
#define RTC_DEFAULT_SECOND 0
|
||||
|
||||
/* ============================================================
|
||||
* 内部状态:系统墙钟
|
||||
* ============================================================ */
|
||||
|
||||
static volatile uint32_t s_base_tick = 0; /* HAL_GetTick() 基准 */
|
||||
static volatile uint32_t s_base_unix_secs = UINT32_MAX;
|
||||
|
||||
/* ============================================================
|
||||
* 内部辅助函数:Unix time ↔ sd2506_time_t 转换
|
||||
* ============================================================ */
|
||||
|
||||
/*
|
||||
* 函数功能:根据公历日期计算星期几
|
||||
* 入口参数:y - 年份 uint16_t 1970~2099
|
||||
* m - 月份 uint8_t 1~12
|
||||
* d - 日 uint8_t 1~31
|
||||
* 返回值:星期编号 uint8_t 0=Sunday, 1=Monday, ... 6=Saturday
|
||||
* 限定条件:输入为公历合法日期
|
||||
* 函数说明:1. 采用 Zeller 公式(变种),结果 0 表示周日
|
||||
* 2. 与 unix_to_sd2506() 内星期算法保持一致
|
||||
*/
|
||||
static uint8_t rtc_calc_week(uint16_t y, uint8_t m, uint8_t d)
|
||||
{
|
||||
uint32_t wy = y;
|
||||
uint32_t wm = m;
|
||||
|
||||
if (wm <= 2) {
|
||||
wy--;
|
||||
wm += 12u;
|
||||
}
|
||||
/* Zeller 公式:世纪项 + 月项 + 日项,模 7 得星期(0=周日) */
|
||||
return (uint8_t)(((uint32_t)(wy % 100u + wy / 100u / 4u - wy / 100u / 15u) +
|
||||
(uint32_t)((26u * (wm + 1u)) / 10u) + (uint32_t)(d % 100u)) % 7u);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:将 Unix 时间戳(自 1970-01-01 00:00:00 起的秒数)转换为 SD2506 RTC 时间结构
|
||||
* 入口参数:unix_secs - Unix 时间戳 uint32_t 0~0x7FFFFFFF(~2038)
|
||||
* p_sd_time - 输出时间结构指针 sd2506_time_t* 不得为 NULL
|
||||
* 出口参数:p_sd_time - 填充年/月/日/时/分/秒/星期
|
||||
* 返回值:无
|
||||
* 限定条件:p_sd_time 必须指向有效缓冲区
|
||||
* 函数说明:1. 先取模拆分时分秒,再按累计天数推算年月日
|
||||
* 2. 星期由 Zeller 公式(与 rtc_calc_week 同算法)得出
|
||||
* 3. SD2506 年份字段为相对值(0~99,+2000),故回写时减 2000
|
||||
*/
|
||||
static void unix_to_sd2506(uint32_t unix_secs, sd2506_time_t *p_sd_time)
|
||||
{
|
||||
uint32_t y, m, d;
|
||||
uint8_t h, mi, s;
|
||||
int is_leap;
|
||||
|
||||
/* 时分秒 */
|
||||
{
|
||||
uint64_t total = (uint64_t)unix_secs;
|
||||
s = (uint8_t)(total % 60uLL);
|
||||
total /= 60uLL;
|
||||
mi = (uint8_t)(total % 60uLL);
|
||||
total /= 60uLL;
|
||||
h = (uint8_t)(total % 24uL);
|
||||
}
|
||||
|
||||
/* 年月日 */
|
||||
{
|
||||
uint64_t day_count = ((uint64_t)unix_secs / 86400uL);
|
||||
|
||||
y = 1970u;
|
||||
while (day_count >= 365uLL) {
|
||||
uint64_t diy = 365uLL;
|
||||
if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) {
|
||||
diy = 366uLL;
|
||||
}
|
||||
if (day_count < diy) break;
|
||||
day_count -= diy;
|
||||
y++;
|
||||
}
|
||||
|
||||
is_leap = ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0));
|
||||
uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
if (is_leap) mdays[1] = 29;
|
||||
|
||||
m = 0;
|
||||
while (m < 12u) {
|
||||
if (day_count < (uint64_t)mdays[m]) break;
|
||||
day_count -= mdays[m];
|
||||
m++;
|
||||
}
|
||||
d = (uint32_t)(day_count + 1uL);
|
||||
|
||||
/* 星期(Zeller's congruence,0=Sunday) */
|
||||
uint64_t wy = y;
|
||||
uint64_t wm = m;
|
||||
if (wm <= 2) {
|
||||
wy--;
|
||||
wm += 12uL;
|
||||
}
|
||||
/* Zeller 公式同 rtc_calc_week():世纪项 + 月项 + 日项,模 7 得星期(0=周日) */
|
||||
uint8_t week = ((uint32_t)(wy % 100uLL + wy / 100uLL / 4uLL - wy / 100uLL / 15uLL) +
|
||||
(uint32_t)(((uint64_t)26 * (wm + 1uL)) / 10uLL) +
|
||||
(uint32_t)(d % 100uLL)) % 7uLL;
|
||||
|
||||
/* sd2506_time_t.year 采用完整年份(如 2026),与 sd2506_set_time
|
||||
* 中 year-2000、sd2506_get_time 中 +2000 的约定保持一致;
|
||||
* 此处绝不能再减 2000,否则写 RTC 时会被二次减 2000 导致年份错乱。 */
|
||||
p_sd_time->year = (uint16_t)y;
|
||||
p_sd_time->month = m + 1uL; /* 月份从 0 → 1 */
|
||||
p_sd_time->day = d;
|
||||
p_sd_time->hour = h;
|
||||
p_sd_time->minute = mi;
|
||||
p_sd_time->second = s;
|
||||
p_sd_time->week = week;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 系统墙钟时间维护
|
||||
* ============================================================ */
|
||||
|
||||
/*
|
||||
* 函数功能:初始化系统墙钟时间基准
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功(始终返回 0)
|
||||
* 限定条件:CubeMX 外设初始化完成、SD2506 RTC 驱动已初始化
|
||||
* 函数说明:1. 从 SD2506 读取初始时间,必要时按编译期默认宏校时(RTC_DEFAULT_INIT_*)
|
||||
* 2. 将 RTC 时间换算为 Unix epoch 秒数
|
||||
* 3. 记录当前 HAL Tick 与 Unix 秒数为基准,供 sys_clock_get() 后续推算
|
||||
* 4. 注意:sd2506_get_time() 返回的 year 已是完整年份(驱动内 +2000),
|
||||
* 换算时不得再加 2000,否则年份虚增导致 Unix 秒数超 uint32 回绕
|
||||
*/
|
||||
int sys_clock_init(void)
|
||||
{
|
||||
sd2506_time_t sd_time;
|
||||
uint32_t year, month, day, hour, minute, second;
|
||||
int64_t unix_secs = 0;
|
||||
int i;
|
||||
|
||||
/* ============================================================
|
||||
* Step 1:从 SD2506 硬件 RTC 读取初始时间
|
||||
* ============================================================
|
||||
* 注意:sd2506_get_time() 返回的 year 已是完整年份(如 2026,
|
||||
* 驱动内部已 +2000)。此处【禁止】再 +2000,否则年份虚增到 4026,
|
||||
* 换算出的 Unix 秒数远超 uint32_t 上限被截断回绕,时间彻底错乱。 */
|
||||
sd2506_get_time(&sd_time);
|
||||
|
||||
/* ============================================================
|
||||
* Step 1.1:开发期校时(可选,由 RTC_DEFAULT_INIT_* 宏控制)
|
||||
* ============================================================
|
||||
* 作用:RTC 出厂/掉电异常导致时间非法时,写入一个可用初始值;
|
||||
* FORCE=1 时每次启动都强制写入,便于首次烧录或硬件时钟丢失。
|
||||
* 判定"时间无效":年份越界、月/日越界(仅粗判,不细校 2 月 29 日等)。 */
|
||||
#if defined(RTC_DEFAULT_INIT_ENABLE)
|
||||
{
|
||||
int rtc_invalid = (sd_time.year < 2000 || sd_time.year > 2099 ||
|
||||
sd_time.month < 1 || sd_time.month > 12 ||
|
||||
sd_time.day < 1 || sd_time.day > 31);
|
||||
#if (RTC_DEFAULT_INIT_FORCE == 1)
|
||||
rtc_invalid = 1; /* 强制每次启动校时:会覆盖用户/网络已设的正确时间,仅限开发期 */
|
||||
#endif
|
||||
if (rtc_invalid) {
|
||||
sd2506_time_t init_t;
|
||||
/* 用编译期默认宏构造初始时间;week 由 Zeller 公式(rtc_calc_week)推算 */
|
||||
init_t.year = RTC_DEFAULT_YEAR;
|
||||
init_t.month = RTC_DEFAULT_MONTH;
|
||||
init_t.day = RTC_DEFAULT_DAY;
|
||||
init_t.hour = RTC_DEFAULT_HOUR;
|
||||
init_t.minute = RTC_DEFAULT_MINUTE;
|
||||
init_t.second = RTC_DEFAULT_SECOND;
|
||||
init_t.week = rtc_calc_week(init_t.year, init_t.month, init_t.day);
|
||||
|
||||
/* 写回 SD2506:断电后该时间被保留,下次上电即为有效值 */
|
||||
if (sd2506_set_time(&init_t) == SD2506_OK) {
|
||||
DBG_INFO("sys_clock_init: RTC set %04d-%02d-%02d %02d:%02d:%02d",
|
||||
init_t.year, init_t.month, init_t.day,
|
||||
init_t.hour, init_t.minute, init_t.second);
|
||||
sd2506_get_time(&sd_time); /* 重新读取校准后的值,确保后续换算用真实 RTC 数据 */
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_init: RTC default write FAIL");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 取出各时间字段(year 已是完整年份,直接用于下文 Unix 换算) */
|
||||
year = (uint32_t)sd_time.year;
|
||||
month = (uint32_t)sd_time.month;
|
||||
day = (uint32_t)sd_time.day;
|
||||
hour = (uint32_t)sd_time.hour;
|
||||
minute = (uint32_t)sd_time.minute;
|
||||
second = (uint32_t)sd_time.second;
|
||||
|
||||
/* ============================================================
|
||||
* Step 2:将 RTC 日历时间换算为 Unix epoch 秒数
|
||||
* ============================================================
|
||||
* Unix 时间定义:自 1970-01-01 00:00:00 起经过的秒数。
|
||||
* 算法:总天数(1970~去年整年 + 今年到上月 + 本月已过天)× 86400
|
||||
* + 当天时分秒。
|
||||
* 用 int64_t / uint64_t 累加避免中间溢出;最终结果(受 uint32_t 限制)
|
||||
* 有效范围约 1970-01-01 ~ 2038-01-19,超出将回绕(见文档第 12 节)。 */
|
||||
{
|
||||
uint64_t total_days = 0;
|
||||
|
||||
/* 2.1 累加 1970 至(今年-1)的整年天数:闰年 366、平年 365
|
||||
* 闰年规则:能被 4 整除但不能被 100 整除,或能被 400 整除 */
|
||||
for (i = 1970; i < year; i++) {
|
||||
if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0)) {
|
||||
total_days += 366uLL;
|
||||
} else {
|
||||
total_days += 365uLL;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2.2 判断今年是否闰年,构造各月天数表(2 月特判) */
|
||||
int is_leap = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
|
||||
uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
if (is_leap) mdays[1] = 29; /* 闰年 2 月 29 天 */
|
||||
|
||||
/* 2.3 累加今年 1 月至(本月-1)月的天数 */
|
||||
for (i = 0; i < month - 1; i++) {
|
||||
total_days += mdays[i];
|
||||
}
|
||||
/* 2.4 加上本月已过天数(day-1:从今天 0 点起算) */
|
||||
total_days += day - 1uL;
|
||||
|
||||
/* 2.5 总天数×86400(一天秒数)+ 当日时分秒 = 绝对 Unix 秒数 */
|
||||
unix_secs = (int64_t)(total_days * 86400LL) + hour * 3600LL + minute * 60LL + second;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Step 3:建立"墙钟基准"(基准法核心)
|
||||
* ============================================================
|
||||
* 记录当前 HAL Tick 与对应的 Unix 秒数。此后 sys_clock_get() 仅用
|
||||
* now_unix = s_base_unix_secs + (HAL_GetTick() - s_base_tick)/1000
|
||||
* 推算当前时间,运行期不再访问 I2C RTC,零阻塞、无累积误差。 */
|
||||
s_base_tick = HAL_GetTick();
|
||||
s_base_unix_secs = (uint32_t)unix_secs;
|
||||
|
||||
DBG_INFO("sys_clock_init: RTC initial time %04d-%02d-%02d %02d:%02d:%02d",
|
||||
year, month, day, hour, minute, second);
|
||||
|
||||
DBG_INFO("sys_clock_init: wall clock base = unix %lu, base_tick=%lu",
|
||||
(unsigned long)s_base_unix_secs, (unsigned long)s_base_tick);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取当前系统墙钟时间(Unix 时间戳)
|
||||
* 入口参数:无
|
||||
* 返回值:当前 Unix 秒数 uint32_t 未初始化时返回 UINT32_MAX
|
||||
* 限定条件:sys_clock_init() 已成功调用
|
||||
* 函数说明:1. 以基准 Tick 与基准 Unix 秒数为起点,按当前 HAL Tick 与 1000ms
|
||||
* 的整除差推算流逝秒数(避免每毫秒累加,减少漂移与溢出风险)
|
||||
* 2. 若推算结果为负(基准未建立),返回 UINT32_MAX 表示无效
|
||||
*/
|
||||
uint32_t sys_clock_get(void)
|
||||
{
|
||||
uint32_t now_tick = HAL_GetTick();
|
||||
int64_t unix_secs = (int64_t)s_base_unix_secs + (int64_t)((uint32_t)(now_tick - s_base_tick) / 1000uL);
|
||||
|
||||
if (unix_secs < 0) {
|
||||
return UINT32_MAX; /* 未初始化 */
|
||||
}
|
||||
|
||||
return (uint32_t)unix_secs;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取当前墙钟时间的可读字符串(YYYY-MM-DD HH:MM:SS)
|
||||
* 入口参数:buf - 输出字符串缓冲区 char* 不得为 NULL
|
||||
* buf_size - 缓冲区大小 size_t 须 ≥ 20(含结尾 '\0')
|
||||
* 出口参数:buf - 填充格式化时间字符串
|
||||
* 返回值:buf 指针(便于链式调用)
|
||||
* 限定条件:buf 指向至少 buf_size 字节的有效缓冲区;sys_clock_init() 已调用
|
||||
* 函数说明:1. 内部先取 Unix 秒数,未初始化时输出占位串 "----/--/-- --:--:--"
|
||||
* 2. 通过 unix_to_sd2506() 转换为 SD2506 结构后格式化
|
||||
* 3. 使用 snprintf 防止缓冲区溢出
|
||||
*/
|
||||
char *sys_clock_get_str(char *buf, size_t buf_size)
|
||||
{
|
||||
uint32_t unix_secs = sys_clock_get();
|
||||
if (unix_secs == UINT32_MAX) {
|
||||
snprintf(buf, buf_size, "----/--/-- --:--:--");
|
||||
return buf;
|
||||
}
|
||||
|
||||
sd2506_time_t sd;
|
||||
unix_to_sd2506(unix_secs, &sd);
|
||||
|
||||
snprintf(buf, buf_size, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||
sd.year, sd.month, sd.day,
|
||||
sd.hour, sd.minute, sd.second);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:设置(更新)系统墙钟时间基准,并可写回 SD2506 RTC
|
||||
* 入口参数:unix_secs - 新的 Unix 时间戳 uint32_t 0~0x7FFFFFFF
|
||||
* sd_time - 对应的 RTC 时间结构指针 sd2506_time_t* 可为 NULL(仅更新基准)
|
||||
* 出口参数:无
|
||||
* 返回值:0 - 成功(始终返回 0)
|
||||
* 限定条件:unix_secs 为合法 Unix 时间戳
|
||||
* 函数说明:1. 重置基准 Tick 与基准 Unix 秒数,使后续 sys_clock_get() 以此为准
|
||||
* 2. 若 sd_time 非 NULL,将其写回 SD2506 RTC,实现断电保持
|
||||
*/
|
||||
int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time)
|
||||
{
|
||||
/* Step 1: 更新墙钟基准 */
|
||||
s_base_tick = HAL_GetTick();
|
||||
s_base_unix_secs = unix_secs;
|
||||
|
||||
DBG_INFO("sys_clock_set: wall clock updated to %lu", (unsigned long)unix_secs);
|
||||
|
||||
/* Step 2: 写回 SD2506 RTC(若提供了 sd_time) */
|
||||
if (sd_time != NULL) {
|
||||
if (sd2506_set_time(sd_time) == SD2506_OK) {
|
||||
DBG_INFO("sys_clock_set: SD2506 RTC written");
|
||||
} else {
|
||||
DBG_ERROR("sys_clock_set: SD2506 RTC write FAIL");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_clock_set_unix(uint32_t unix_secs)
|
||||
{
|
||||
sd2506_time_t sd;
|
||||
|
||||
/* 内部静态函数:将 Unix 秒数拆分为 SD2506 时间结构 */
|
||||
unix_to_sd2506(unix_secs, &sd);
|
||||
|
||||
/* 更新运行期基准并写回 RTC,实现掉电保持 */
|
||||
return sys_clock_set(unix_secs, &sd);
|
||||
}
|
||||
75
App/sys_clock.h
Normal file
75
App/sys_clock.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 模块名称:System Clock — Wall Clock Time Maintenance
|
||||
* 模块功能:基于 HAL Tick + SD2506 RTC 的系统墙钟时间维护
|
||||
* 启动时从 SD2506 RTC 读取初始时间,运行期间以 HAL Tick 计数维持
|
||||
* 适用平台:STM32F407ZGTx (Cortex-M4 FPU, 168MHz)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __SYS_CLOCK_H
|
||||
#define __SYS_CLOCK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sd2506.h"
|
||||
|
||||
/*
|
||||
* 函数功能:系统时钟初始化(从 SD2506 RTC 读取初始时间)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功, -1 - 失败
|
||||
* 限定条件:sd2506_init() 已调用
|
||||
* 函数说明:读取 SD2506 RTC 当前值转换为 Unix timestamp,设为系统墙钟初始时间
|
||||
*/
|
||||
int sys_clock_init(void);
|
||||
|
||||
/*
|
||||
* 函数功能:获取当前系统墙钟时间(Unix epoch seconds)
|
||||
* 入口参数:无
|
||||
* 返回值:Unix time (秒),失败返回 UINT32_MAX
|
||||
* 限定条件:sys_clock_init() 已调用
|
||||
* 函数说明:基于 HAL Tick 计数 + 上次校准时刻的 Unix 秒数计算当前时间,
|
||||
* 精度为毫秒级
|
||||
*/
|
||||
uint32_t sys_clock_get(void);
|
||||
|
||||
/*
|
||||
* 函数功能:格式化系统墙钟时间为字符串
|
||||
* 入口参数:buf - 输出缓冲区(至少 20 字节)
|
||||
* buf_size - 缓冲区大小
|
||||
* 返回值:buf 指针,失败返回 NULL
|
||||
* 限定条件:sys_clock_init() 已调用
|
||||
* 函数说明:格式 "YYYY-MM-DD HH:MM:SS"
|
||||
*/
|
||||
char *sys_clock_get_str(char *buf, size_t buf_size);
|
||||
|
||||
/*
|
||||
* 函数功能:设置系统墙钟时间
|
||||
* 入口参数:unix_secs - Unix epoch time (秒)
|
||||
* sd_time - SD2506 RTC 时间结构体(可选,非 NULL 时同时写入 RTC)
|
||||
* 返回值:0 - 成功, -1 - 失败
|
||||
* 限定条件:sys_clock_init() 已调用
|
||||
* 函数说明:更新系统墙钟基准 + 写回 SD2506 RTC 保持长期准确性
|
||||
*/
|
||||
int sys_clock_set(uint32_t unix_secs, const sd2506_time_t *sd_time);
|
||||
|
||||
/*
|
||||
* 函数功能:以 Unix 时间戳设置系统墙钟并写回 SD2506 RTC
|
||||
* 入口参数:unix_secs - Unix epoch time (秒,本地时间) uint32_t 0~0x7FFFFFFF
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:sys_clock_init() 已调用
|
||||
* 函数说明:内部将 Unix 秒数转换为 sd2506_time_t 后调用 sys_clock_set(),
|
||||
* 便于时间同步模块(如 PC 推送)直接以时间戳校时
|
||||
*/
|
||||
int sys_clock_set_unix(uint32_t unix_secs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SYS_CLOCK_H */
|
||||
45
App/task/adc_task.c
Normal file
45
App/task/adc_task.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 模块名称:ADC Sampling Task
|
||||
* 模块功能:TPAFE5160 8 通道同步采样,数据缓存与通知
|
||||
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include "adc_task.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "tpafe5160.h"
|
||||
|
||||
#define DBG_TAG "[ADC_TASK]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/*
|
||||
* 函数功能:ADC 任务初始化
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功,-1 - 失败
|
||||
* 限定条件:TPAFE5160 硬件已上电
|
||||
* 函数说明:配置采样参数,准备数据缓冲区
|
||||
*/
|
||||
int adc_task_init(void)
|
||||
{
|
||||
DBG_INFO("adc_task_init");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:ADC 任务主函数
|
||||
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||||
* 返 回 值:无
|
||||
* 限定条件:adc_task_init() 已成功调用
|
||||
* 函数说明:定时触发 TPAFE5160 同步采样并处理数据
|
||||
*/
|
||||
void adc_task_func(void const *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
for (;;) {
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
25
App/task/adc_task.h
Normal file
25
App/task/adc_task.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 模块名称:ADC Sampling Task
|
||||
* 模块功能:TPAFE5160 8 通道同步采样,数据缓存与通知
|
||||
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __ADC_TASK_H
|
||||
#define __ADC_TASK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int adc_task_init(void);
|
||||
void adc_task_func(void const *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ADC_TASK_H */
|
||||
114
App/task/net_task.c
Normal file
114
App/task/net_task.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 模块名称:Network Application Task
|
||||
* 模块功能:CH395F 协议栈初始化 + 轮询 + TCP/UDP 业务处理
|
||||
* 与 net_socket.h 配合,在 FreeRTOS 独立任务中运行
|
||||
* 适用平台:STM32F407ZGTx + CH395F
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-07-19 王建锋 migrate from freertos.c StartNetTask
|
||||
* v1.2 2026-07-21 王建锋 网络初始化移入本任务(net_init + TCP 监听)
|
||||
* v1.3 2026-07-22 王建锋 单连接模式重构,增加大文件/边界/KeepAlive 测试
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "net_task.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "net_socket.h"
|
||||
#include "net_select.h"
|
||||
#include "ch395f.h"
|
||||
|
||||
#define DBG_TAG "[NET_TASK]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 网络初始化完成标志(供 ch395f_test_task 等外部任务等待) */
|
||||
volatile uint8_t g_net_ready = 0;
|
||||
|
||||
/*
|
||||
* 函数功能:CH395F 协议栈一次性初始化(两种构建模式共用)
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功,-1 - 失败
|
||||
* 函数说明:net_init → MAC 打印 → PHY 强制 100M 全双工 → 置 g_net_ready。
|
||||
* NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)netTask 不创建,
|
||||
* 由 ch395fTestTask 调用后独占 CH395F。
|
||||
*/
|
||||
int net_stack_bring_up(void)
|
||||
{
|
||||
DBG_INFO("=== Network Init ===");
|
||||
|
||||
if (net_init("192.168.1.100", "255.255.255.0", "192.168.1.1") != 0) {
|
||||
DBG_ERROR("net init: FAIL");
|
||||
return -1;
|
||||
}
|
||||
DBG_INFO("net init: OK");
|
||||
|
||||
uint8_t mac[6];
|
||||
ch395f_get_mac_addr(mac);
|
||||
DBG_INFO("MAC: %02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
|
||||
uint8_t ipinf[20];
|
||||
ch395f_get_ip_inf(ipinf);
|
||||
DBG_INFO("IP_INF: IP=%d.%d.%d.%d GW=%d.%d.%d.%d MASK=%d.%d.%d.%d",
|
||||
ipinf[0], ipinf[1], ipinf[2], ipinf[3],
|
||||
ipinf[4], ipinf[5], ipinf[6], ipinf[7],
|
||||
ipinf[8], ipinf[9], ipinf[10], ipinf[11]);
|
||||
|
||||
ch395f_set_phy(CH395F_PHY_100M_FULL);
|
||||
HAL_Delay(3000);
|
||||
uint8_t phy = ch395f_get_phy_status();
|
||||
DBG_INFO("PHY: %s",
|
||||
(phy == CH395F_PHY_100M_FULL) ? "100M FULL" :
|
||||
(phy == CH395F_PHY_100M_HALF) ? "100M HALF" :
|
||||
(phy == CH395F_PHY_10M_FULL) ? "10M FULL" :
|
||||
(phy == CH395F_PHY_10M_HALF) ? "10M HALF" :
|
||||
(phy == CH395F_PHY_DISCONN) ? "DISCONNECT" : "UNKNOWN");
|
||||
|
||||
g_net_ready = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:网络任务初始化
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功,-1 - 失败
|
||||
* 限定条件:CH395F 协议栈已初始化
|
||||
* 函数说明:创建 TCP Server 监听 Socket,注册回调等
|
||||
*/
|
||||
int net_task_init(void)
|
||||
{
|
||||
DBG_INFO("net_task_init");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:网络任务主函数
|
||||
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||||
* 返 回 值:无
|
||||
* 函数说明:启动后先调用 net_stack_bring_up() 初始化 CH395F 协议栈,
|
||||
* 再进入轮询循环处理网络事件。
|
||||
* 仅 NET 层测试模式(阶段 6~10)会创建本任务;
|
||||
* 硬件层模式下 netTask 不创建,CH395F 由 ch395fTestTask 独占。
|
||||
*/
|
||||
void net_task_func(void const *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
osDelay(2000);
|
||||
|
||||
if (net_stack_bring_up() != 0) {
|
||||
for (;;) {
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
DBG_INFO("netTask: started (single-socket mode)");
|
||||
|
||||
/* ==================== 主轮询循环 ==================== */
|
||||
for (;;) {
|
||||
net_poll();
|
||||
net_process_messages();
|
||||
osDelay(1);
|
||||
}
|
||||
}
|
||||
31
App/task/net_task.h
Normal file
31
App/task/net_task.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 模块名称:Network Application Task
|
||||
* 模块功能:网络协议栈初始化、轮询、TCP/UDP 业务处理
|
||||
* 适用平台:STM32F407ZGTx + CH395F
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-08-24 王建锋 提取 net_stack_bring_up() 供硬件层/NET 层两种测试模式共用
|
||||
*/
|
||||
|
||||
#ifndef __NET_TASK_H
|
||||
#define __NET_TASK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* CH395F 协议栈一次性初始化(net_init + MAC 打印 + PHY 配置 + g_net_ready 置位)。
|
||||
* 返回 0 成功 / -1 失败。NET 层模式由 netTask 调用;硬件层模式(阶段 1~5)
|
||||
* netTask 不创建,由 ch395fTestTask 调用后独占 CH395F。 */
|
||||
int net_stack_bring_up(void);
|
||||
|
||||
int net_task_init(void);
|
||||
void net_task_func(void const *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NET_TASK_H */
|
||||
45
App/task/rs485_task.c
Normal file
45
App/task/rs485_task.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 模块名称:RS485 Communication Task
|
||||
* 模块功能:RS-485 轮询收发、协议帧解析与响应
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include "rs485_task.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "rs485.h"
|
||||
|
||||
#define DBG_TAG "[RS485_TASK]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/*
|
||||
* 函数功能:RS485 任务初始化
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 - 成功,-1 - 失败
|
||||
* 限定条件:UART 和 GPIO 已由 CubeMX 初始化
|
||||
* 函数说明:配置 RS-485 通信参数,注册接收回调
|
||||
*/
|
||||
int rs485_task_init(void)
|
||||
{
|
||||
DBG_INFO("rs485_task_init");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:RS485 任务主函数
|
||||
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||||
* 返 回 值:无
|
||||
* 限定条件:rs485_task_init() 已成功调用
|
||||
* 函数说明:轮询接收缓冲区并解析协议帧
|
||||
*/
|
||||
void rs485_task_func(void const *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
for (;;) {
|
||||
osDelay(100);
|
||||
}
|
||||
}
|
||||
25
App/task/rs485_task.h
Normal file
25
App/task/rs485_task.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 模块名称:RS485 Communication Task
|
||||
* 模块功能:RS-485 数据收发与协议解析
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __RS485_TASK_H
|
||||
#define __RS485_TASK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int rs485_task_init(void);
|
||||
void rs485_task_func(void const *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RS485_TASK_H */
|
||||
138
App/time_sync.c
Normal file
138
App/time_sync.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 模块名称:Time Sync — PC 本地时间推送接收(TCP)
|
||||
* 模块功能:在独立任务 timeSyncTask 中以 TCP Server 方式监听端口,接收 PC 推送的本地时间包,
|
||||
* 调用 sys_clock_set_unix() 更新系统墙钟并写回 SD2506 RTC
|
||||
* 适用平台:STM32F407ZGTx + CH395F
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-08-27 王建锋 创建初始版本(UDP 推送,netTask 内轮询)
|
||||
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
|
||||
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
|
||||
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "time_sync.h"
|
||||
#include "net_socket.h"
|
||||
#include "sys_clock.h"
|
||||
|
||||
#define DBG_TAG "[TIME_SYNC]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 监听端口:PC 推送端(TCP 客户端)需连到本端口 */
|
||||
#define TIME_SYNC_PORT 8888
|
||||
|
||||
/*
|
||||
* 函数功能:创建并监听 TCP 时间同步服务端口
|
||||
* 入口参数:无
|
||||
* 返回值:监听 Socket 描述符(>=0),失败返回 -1
|
||||
* 限定条件:net_init() 已成功;可在非 netTask 上下文调用
|
||||
* 函数说明:net_socket/net_bind/net_listen 均为线程安全 API,内部经消息队列由 netTask 串行处理。
|
||||
* 单连接模式下监听 Socket 在收到连接后会转为数据通道,连接关闭后即被释放,
|
||||
* 因此每次调用都会新建监听 Socket(与 lftpd 同模式),失败自动重试。
|
||||
*/
|
||||
static int time_sync_open_listener(void)
|
||||
{
|
||||
int retry;
|
||||
int s = -1;
|
||||
|
||||
for (retry = 0; retry < 5; retry++) {
|
||||
s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||
if (s < 0) {
|
||||
DBG_ERROR("time_sync: net_socket FAIL");
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
continue;
|
||||
}
|
||||
|
||||
struct net_sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = NET_AF_INET;
|
||||
addr.sin_port = net_htons(TIME_SYNC_PORT);
|
||||
addr.sin_addr.s_addr = NET_INADDR_ANY;
|
||||
|
||||
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
DBG_ERROR("time_sync: bind port %d FAIL", TIME_SYNC_PORT);
|
||||
net_close(s);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (net_listen(s) != 0) {
|
||||
DBG_ERROR("time_sync: listen FAIL");
|
||||
net_close(s);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
continue;
|
||||
}
|
||||
|
||||
DBG_INFO("time_sync: TCP listen on :%d (sock=%d)", TIME_SYNC_PORT, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:处理一个已建立的时间同步连接
|
||||
* 入口参数:conn_sock - 已建立的连接 Socket 描述符(单连接模式下即监听 Socket 自身)
|
||||
* 返回值:无
|
||||
* 函数说明:阻塞读取 PC 发来的时间包("TIME"+uint32 大端 本地时间),解析后写回 RTC,然后关闭连接
|
||||
*/
|
||||
static void time_sync_handle_conn(int conn_sock)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
int len;
|
||||
|
||||
len = net_recv(conn_sock, buf, sizeof(buf), 0);
|
||||
if (len >= 8) {
|
||||
if (buf[0] == 'T' && buf[1] == 'I' && buf[2] == 'M' && buf[3] == 'E') {
|
||||
uint32_t epoch = ((uint32_t)buf[4] << 24) |
|
||||
((uint32_t)buf[5] << 16) |
|
||||
((uint32_t)buf[6] << 8) |
|
||||
((uint32_t)buf[7]);
|
||||
sys_clock_set_unix(epoch);
|
||||
DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
|
||||
}
|
||||
}
|
||||
|
||||
net_close(conn_sock);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
|
||||
* 入口参数:argument - 未使用
|
||||
* 返回值:无
|
||||
* 限定条件:net_init() 已由 netTask 完成;netTask 正在处理消息队列
|
||||
* 函数说明:等待网络栈就绪后,循环:建监听 -> 等连接 -> 处理 -> 关闭 -> 重建监听。
|
||||
* 全部 socket 操作经消息队列由 netTask 串行执行,与本任务/其它网络任务并发安全。
|
||||
*/
|
||||
void timeSyncTask(void *argument)
|
||||
{
|
||||
(void)argument;
|
||||
|
||||
/* 等网络栈起来(与 StartFtpTask 的 7s 延迟同风格,确保 netTask 已开始处理消息) */
|
||||
vTaskDelay(pdMS_TO_TICKS(8000));
|
||||
|
||||
for (;;) {
|
||||
int ls = time_sync_open_listener();
|
||||
if (ls < 0) {
|
||||
DBG_ERROR("time_sync: open listener FAIL, retry later");
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 等待 PC 连接:net_accept 在无连接时立即返回 -1,循环重试同一监听 Socket */
|
||||
int conn = -1;
|
||||
do {
|
||||
conn = net_accept(ls, NULL, NULL);
|
||||
if (conn < 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
} while (conn < 0);
|
||||
|
||||
time_sync_handle_conn(conn);
|
||||
/* conn(单连接模式下即 ls)已被 net_close 释放,下次循环重建监听 */
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
40
App/time_sync.h
Normal file
40
App/time_sync.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 模块名称:Time Sync — PC 本地时间推送接收(TCP)
|
||||
* 模块功能:以 TCP Server 方式监听 TIME_SYNC_PORT,接收 PC 推送的本地时间包,
|
||||
* 更新系统墙钟并写回 SD2506 RTC
|
||||
* 适用平台:STM32F407ZGTx + CH395F
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-08-27 王建锋 创建初始版本(UDP 推送)
|
||||
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask
|
||||
*/
|
||||
|
||||
#ifndef __TIME_SYNC_H
|
||||
#define __TIME_SYNC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 监听端口:PC 推送端(TCP 客户端)需连到本端口 */
|
||||
#define TIME_SYNC_PORT 8888
|
||||
|
||||
/*
|
||||
* 函数功能:时间同步任务主体(独立 FreeRTOS 任务)
|
||||
* 入口参数:argument - FreeRTOS 传入参数(未使用)
|
||||
* 返回值:无
|
||||
* 限定条件:net_init() 已由 netTask 完成;网络消息队列由 netTask 处理
|
||||
* 函数说明:以 TCP Server 方式监听 TIME_SYNC_PORT,接收 PC 推送的本地时间包,
|
||||
* 经消息队列(由 netTask 串行执行)完成 socket 操作,更新系统墙钟。
|
||||
* 该任务与 FTP 等其它网络任务并发安全。
|
||||
*/
|
||||
void timeSyncTask(void *argument);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TIME_SYNC_H */
|
||||
122
App/util/crc.c
Normal file
122
App/util/crc.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 模块名称:CRC Utility
|
||||
* 模块功能:CRC-16 Modbus (poly=0x8005, init=0xFFFF, refin=true, refout=true, xorout=0x0000)
|
||||
* CRC-8 (poly=0x07, init=0x00, refin=false, refout=false, xorout=0x00)
|
||||
* 适用平台:通用嵌入式平台
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include "crc.h"
|
||||
|
||||
static const uint16_t s_crc16_table[256] = {
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
||||
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
||||
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
||||
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
||||
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
||||
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
||||
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
||||
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
||||
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
||||
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
||||
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
||||
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
||||
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
||||
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
||||
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
||||
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
||||
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
||||
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
||||
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
||||
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
||||
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
||||
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
||||
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
||||
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
||||
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
||||
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
||||
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
|
||||
};
|
||||
|
||||
static const uint8_t s_crc8_table[256] = {
|
||||
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
|
||||
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
||||
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
|
||||
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
|
||||
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
|
||||
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
|
||||
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
|
||||
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
|
||||
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
|
||||
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
|
||||
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
|
||||
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
|
||||
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
|
||||
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
|
||||
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
|
||||
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
|
||||
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
|
||||
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
|
||||
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
|
||||
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
|
||||
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
|
||||
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
|
||||
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
|
||||
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
|
||||
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
|
||||
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
|
||||
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
|
||||
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
|
||||
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
|
||||
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
|
||||
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
|
||||
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,
|
||||
};
|
||||
|
||||
/*
|
||||
* 函数功能:计算 CRC-16 Modbus
|
||||
* 入口参数:data - 数据指针
|
||||
* len - 数据长度
|
||||
* 返 回 值:16 位 CRC 值
|
||||
* 限定条件:data 为非空指针
|
||||
* 函数说明:多项式 0x8005,初始值 0xFFFF,结果异或 0x0000
|
||||
*/
|
||||
uint16_t crc16_modbus(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
crc = (crc >> 8) ^ s_crc16_table[(crc ^ data[i]) & 0xFF];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:计算 CRC-8
|
||||
* 入口参数:data - 数据指针
|
||||
* len - 数据长度
|
||||
* 返 回 值:8 位 CRC 值
|
||||
* 限定条件:data 为非空指针
|
||||
* 函数说明:多项式 0x07,初始值 0x00
|
||||
*/
|
||||
uint8_t crc8(const uint8_t *data, size_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
crc = s_crc8_table[crc ^ data[i]];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
28
App/util/crc.h
Normal file
28
App/util/crc.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 模块名称:CRC Utility
|
||||
* 模块功能:CRC-16 Modbus & CRC-8 查表计算
|
||||
* 适用平台:通用嵌入式平台
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __CRC_H
|
||||
#define __CRC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
uint16_t crc16_modbus(const uint8_t *data, size_t len);
|
||||
uint8_t crc8(const uint8_t *data, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CRC_H */
|
||||
164
App/util/ringbuf.c
Normal file
164
App/util/ringbuf.c
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 模块名称:Ring Buffer
|
||||
* 模块功能:字节环形缓冲区实现,head == tail 为空,head + 1 == tail 为满
|
||||
* 适用平台:通用嵌入式平台
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include "ringbuf.h"
|
||||
|
||||
/*
|
||||
* 函数功能:初始化环形缓冲区
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* pool - 存储池指针
|
||||
* size - 存储池大小(字节)
|
||||
* 返 回 值:无
|
||||
* 限定条件:rb 和 pool 均为非空指针
|
||||
* 函数说明:size 保留一个空位用于区分空/满状态
|
||||
*/
|
||||
void ringbuf_init(ringbuf_t *rb, uint8_t *pool, size_t size)
|
||||
{
|
||||
rb->buf = pool;
|
||||
rb->size = size;
|
||||
rb->head = 0;
|
||||
rb->tail = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:写入数据到环形缓冲区
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* data - 源数据指针
|
||||
* len - 待写入长度
|
||||
* 返 回 值:实际写入字节数
|
||||
* 限定条件:rb 和 data 均为非空指针
|
||||
* 函数说明:空间不足时写入能容纳的最大字节数
|
||||
*/
|
||||
size_t ringbuf_put(ringbuf_t *rb, const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t cap = 0;
|
||||
size_t n = 0;
|
||||
size_t i = 0;
|
||||
|
||||
cap = rb->size - 1 - ringbuf_avail(rb);
|
||||
n = (len < cap) ? len : cap;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
rb->buf[rb->head] = data[i];
|
||||
rb->head = (rb->head + 1) % rb->size;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:从环形缓冲区读取数据
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* data - 目标数据指针
|
||||
* len - 期望读取长度
|
||||
* 返 回 值:实际读取字节数
|
||||
* 限定条件:rb 和 data 均为非空指针
|
||||
* 函数说明:读取后数据从缓冲区移除
|
||||
*/
|
||||
size_t ringbuf_get(ringbuf_t *rb, uint8_t *data, size_t len)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t i = 0;
|
||||
|
||||
n = ringbuf_avail(rb);
|
||||
n = (n < len) ? n : len;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
data[i] = rb->buf[rb->tail];
|
||||
rb->tail = (rb->tail + 1) % rb->size;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:预读取数据(不移除)
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* data - 目标数据指针
|
||||
* len - 期望读取长度
|
||||
* 返 回 值:实际可读取字节数
|
||||
* 限定条件:rb 和 data 均为非空指针
|
||||
* 函数说明:与 ringbuf_get 的区别是不移动 tail 指针
|
||||
*/
|
||||
size_t ringbuf_peek(const ringbuf_t *rb, uint8_t *data, size_t len)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t i = 0;
|
||||
size_t idx = 0;
|
||||
|
||||
n = ringbuf_avail(rb);
|
||||
n = (n < len) ? n : len;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
idx = (rb->tail + i) % rb->size;
|
||||
data[i] = rb->buf[idx];
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取缓冲区中有效数据长度
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* 返 回 值:有效数据字节数
|
||||
* 限定条件:rb 为非空指针
|
||||
*/
|
||||
size_t ringbuf_avail(const ringbuf_t *rb)
|
||||
{
|
||||
if (rb->head >= rb->tail) {
|
||||
return rb->head - rb->tail;
|
||||
}
|
||||
return rb->size - rb->tail + rb->head;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取缓冲区剩余空间
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* 返 回 值:剩余空间字节数(保留一个空位)
|
||||
* 限定条件:rb 为非空指针
|
||||
*/
|
||||
size_t ringbuf_space(const ringbuf_t *rb)
|
||||
{
|
||||
return rb->size - 1 - ringbuf_avail(rb);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:判断缓冲区是否为空
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* 返 回 值:1 - 空,0 - 非空
|
||||
* 限定条件:rb 为非空指针
|
||||
*/
|
||||
int ringbuf_is_empty(const ringbuf_t *rb)
|
||||
{
|
||||
return (rb->head == rb->tail) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:判断缓冲区是否为满
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* 返 回 值:1 - 满,0 - 非满
|
||||
* 限定条件:rb 为非空指针
|
||||
*/
|
||||
int ringbuf_is_full(const ringbuf_t *rb)
|
||||
{
|
||||
return (ringbuf_avail(rb) == rb->size - 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:重置缓冲区
|
||||
* 入口参数:rb - 缓冲区控制块指针
|
||||
* 返 回 值:无
|
||||
* 限定条件:rb 为非空指针
|
||||
*/
|
||||
void ringbuf_reset(ringbuf_t *rb)
|
||||
{
|
||||
rb->head = 0;
|
||||
rb->tail = 0;
|
||||
}
|
||||
42
App/util/ringbuf.h
Normal file
42
App/util/ringbuf.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 模块名称:Ring Buffer
|
||||
* 模块功能:线程安全的字节环形缓冲区(单生产者-单消费者模型)
|
||||
* 适用平台:通用嵌入式平台
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __RINGBUF_H
|
||||
#define __RINGBUF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buf;
|
||||
size_t size;
|
||||
size_t head;
|
||||
size_t tail;
|
||||
} ringbuf_t;
|
||||
|
||||
void ringbuf_init(ringbuf_t *rb, uint8_t *pool, size_t size);
|
||||
size_t ringbuf_put(ringbuf_t *rb, const uint8_t *data, size_t len);
|
||||
size_t ringbuf_get(ringbuf_t *rb, uint8_t *data, size_t len);
|
||||
size_t ringbuf_peek(const ringbuf_t *rb, uint8_t *data, size_t len);
|
||||
size_t ringbuf_avail(const ringbuf_t *rb);
|
||||
size_t ringbuf_space(const ringbuf_t *rb);
|
||||
int ringbuf_is_empty(const ringbuf_t *rb);
|
||||
int ringbuf_is_full(const ringbuf_t *rb);
|
||||
void ringbuf_reset(ringbuf_t *rb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RINGBUF_H */
|
||||
1333
Drivers/BSP/CH395F/ch395f.c
Normal file
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
722
Drivers/BSP/CH395F/ch395f.h
Normal 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 MSS(CH395F_CMD_SET_TCP_MSS 参数)
|
||||
* 由 net_init 经 ch395f_set_tcp_mss() 下发;此处作为单一真值源,
|
||||
* DMA 缓冲大小亦由此推导(见下)。当前选取依据:8 Socket 共享 24KB 缓冲时,
|
||||
* 各 Socket 接收缓冲 = 2×MSS,故 MSS 取 1024 为最大值(详见 net_socket.c 分配注释)。
|
||||
*/
|
||||
#define CH395F_TCP_MSS 1024U
|
||||
|
||||
/* 内部缓冲 RAM 每块字节数(手册 §5.45:48 块 × 512B = 24KB) */
|
||||
#define CH395F_RAM_BLOCK_SIZE 512U
|
||||
/* 单 Socket 缓冲块数由 MSS 推导:接收 = 2×MSS(满足手册 §9.2.8 接收 ≥ 2×MSS),发送 = 1×MSS;向上取整 */
|
||||
#define CH395F_RECV_BLOCKS ((2U * CH395F_TCP_MSS + CH395F_RAM_BLOCK_SIZE - 1U) / CH395F_RAM_BLOCK_SIZE)
|
||||
#define CH395F_SEND_BLOCKS ((CH395F_TCP_MSS + CH395F_RAM_BLOCK_SIZE - 1U) / CH395F_RAM_BLOCK_SIZE)
|
||||
/* 单 Socket 硬件收发缓冲字节数(= 块数 × 块大小)。写入发送 FIFO 的单次长度严禁超过此值,
|
||||
* 否则会写穿有限的发送缓冲(见 Trap 20)。 */
|
||||
#define CH395F_SEND_BUF_SIZE_BYTES (CH395F_SEND_BLOCKS * CH395F_RAM_BLOCK_SIZE)
|
||||
#define CH395F_RECV_BUF_SIZE_BYTES (CH395F_RECV_BLOCKS * CH395F_RAM_BLOCK_SIZE)
|
||||
|
||||
/*
|
||||
* SPI DMA 事务限制
|
||||
* CH395F_SPI_DMA_MAX_PAYLOAD: 单次 DMA 事务最大有效载荷(总缓冲 - 4 字节命令头)
|
||||
* 接收方向需一次性读空整个 Socket 接收缓冲(= 2×MSS,避免 Trap 13 部分读导致数据不可见),
|
||||
* 故总缓冲 = 2×MSS + 4(4 为命令头开销),有效载荷 = 2×MSS。
|
||||
*/
|
||||
#define CH395F_SPI_DMA_BUF_SIZE (CH395F_TCP_MSS * 2U + 4U)
|
||||
#define CH395F_SPI_DMA_MAX_PAYLOAD (CH395F_SPI_DMA_BUF_SIZE - 4U)
|
||||
|
||||
/* 其他命令 */
|
||||
#define CH395F_CMD_SET_TCP_MSS 0x50U
|
||||
#define CH395F_CMD_SET_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 已正确初始化
|
||||
* 函数说明:复位耗时约 15ms(TE0),函数内部已包含延时
|
||||
*/
|
||||
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() 复位
|
||||
* 函数说明:阻塞等待初始化完成或超时(典型 5ms,TE1)
|
||||
*/
|
||||
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*
|
||||
* 限定条件:指针非空,芯片已初始化
|
||||
* 函数说明:执行耗时约 30us(TE5)
|
||||
*/
|
||||
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
|
||||
* 限定条件:指针非空,长度大于 0,TCP 已连接或 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_t(0=使用默认值5)
|
||||
* 限定条件:必须在 ch395f_init() 之前调用
|
||||
*/
|
||||
void ch395f_set_retrans_count(uint8_t cnt);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 TCP 重传周期
|
||||
* 入口参数:period - 重传周期(毫秒) uint16_t
|
||||
* 限定条件:必须在 ch395f_init() 之前调用
|
||||
*/
|
||||
void ch395f_set_retrans_period(uint16_t period);
|
||||
|
||||
/*
|
||||
* 函数声明区 - ARP / TTL / 不可达信息
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:设置 ARP 重传参数
|
||||
* 入口参数:period_100ms - ARP 重传周期(单位 100ms,0=默认值=10即1秒) uint8_t
|
||||
* cnt - ARP 重传次数(0=默认值=3,255=无限重传) uint8_t
|
||||
* 限定条件:应在 ch395f_init() 之后调用
|
||||
*/
|
||||
void ch395f_set_arp(uint8_t period_100ms, uint8_t cnt);
|
||||
|
||||
/*
|
||||
* 函数功能:设置 Socket TTL
|
||||
* 入口参数:sock - Socket 索引 uint8_t 0 - 7
|
||||
* ttl - TTL 值 uint8_t 最大 128
|
||||
* 限定条件:Socket 已打开,TCP Server 模式下不生效
|
||||
*/
|
||||
void ch395f_set_ttl(uint8_t sock, uint8_t ttl);
|
||||
|
||||
/*
|
||||
* 函数功能:获取不可达信息(用于调试连接失败原因)
|
||||
* 出口参数:p_buf - 8 字节输出缓冲区(类型+协议码+端口+IP)
|
||||
* 限定条件:收到 GINT_STAT_UNREACH 中断后调用
|
||||
*/
|
||||
void ch395f_get_unreach_info(uint8_t *p_buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CH395F_H */
|
||||
962
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c
Normal file
962
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.c
Normal 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. 扫描出厂坏块构建 BBT(ECC 使能前,读每块首页 spare[0])
|
||||
* 4. 使能内部 ECC (B0h bit4)
|
||||
* 5. 解除所有块保护 (A0h = 0x00)
|
||||
* 6. 定位保留块池并加载持久化 BBT(ECC 使能后)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读取芯片 ID(MID + 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:加载持久化 BBT(OR 进 RAM BBT)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:gd5f_private_bbt_locate_pool() 已成功执行
|
||||
* 函数说明:工厂扫描已填好块,持久化副本补充运行期坏块
|
||||
*/
|
||||
static int gd5f_bbt_load(void) {
|
||||
gd5f_private_bbt_find_best();
|
||||
if (s_bbt_pool_count == 0) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
for (uint32_t i = 0; i < GD5F_BBT_SIZE; i++) {
|
||||
s_bbt[i] |= s_bbt_best[i];
|
||||
}
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:将当前 RAM BBT 持久化到保留池(轮转写入,掉电安全)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,GD5F_ERROR - 写入失败
|
||||
* 限定条件:gd5f_private_bbt_locate_pool() 已成功执行
|
||||
* 函数说明:版本号自增;先擦除目标块再编程;损坏块标记后跳到下一槽
|
||||
*/
|
||||
static int gd5f_bbt_save(void) {
|
||||
if (s_bbt_pool_count == 0) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
uint32_t slot = s_bbt_write_slot;
|
||||
|
||||
while (s_bbt_slot_dead & (1U << slot)) {
|
||||
slot = (slot + 1) % s_bbt_pool_count;
|
||||
if (slot == s_bbt_write_slot) {
|
||||
break; /* 所有池块均损坏 */
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t block = s_bbt_pool_blocks[slot];
|
||||
uint32_t page = block * GD5F_PAGES_PER_BLOCK;
|
||||
uint32_t ver = s_bbt_version + 1;
|
||||
uint32_t len = GD5F_BBT_SIZE;
|
||||
uint32_t crc = gd5f_private_crc32(s_bbt, GD5F_BBT_SIZE);
|
||||
|
||||
if (gd5f_block_erase(block) != GD5F_OK) {
|
||||
s_bbt_slot_dead |= (1U << slot);
|
||||
s_bbt_write_slot = (slot + 1) % s_bbt_pool_count;
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
memcpy(s_bbt_page_buf, s_bbt_magic, 4);
|
||||
memcpy(s_bbt_page_buf + 4, &ver, 4);
|
||||
memcpy(s_bbt_page_buf + 8, &len, 4);
|
||||
memcpy(s_bbt_page_buf + 12, &crc, 4);
|
||||
memcpy(s_bbt_page_buf + GD5F_BBT_HDR_SIZE, s_bbt, GD5F_BBT_SIZE);
|
||||
|
||||
gd5f_write_enable();
|
||||
gd5f_program_load(0, s_bbt_page_buf, GD5F_BBT_HDR_SIZE + GD5F_BBT_SIZE);
|
||||
gd5f_program_exec(page);
|
||||
gd5f_wait_busy(100);
|
||||
|
||||
s_bbt_version = ver;
|
||||
s_bbt_write_slot = (slot + 1) % s_bbt_pool_count;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:获取 dhara 可用块数(= 最低保留池块号)
|
||||
* 入口参数:无
|
||||
* 返回值:dhara 可见的块数量(= 保留块池起始块号)
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:保留块池用于存储持久化 BBT,不交给 dhara 管理
|
||||
*/
|
||||
uint32_t gd5f_get_usable_blocks(void) {
|
||||
return s_usable_blocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:从闪存读取当前生效的 BBT 位图(测试/调试用)
|
||||
* 入口参数:无
|
||||
* 出口参数:p_bbt - 位图输出缓冲 uint8_t* 不为 NULL,长度 >= GD5F_BBT_SIZE
|
||||
* p_version - 版本号输出 uint32_t* 不为 NULL
|
||||
* 返回值:0 - 成功,GD5F_ERROR - 参数非法
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:返回闪存中版本号最高的有效 BBT 拷贝(供测试验证持久化)
|
||||
*/
|
||||
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version) {
|
||||
if (p_bbt == NULL || p_version == NULL) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
gd5f_private_bbt_find_best();
|
||||
memcpy(p_bbt, s_bbt_best, GD5F_BBT_SIZE);
|
||||
*p_version = s_bbt_version;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:擦除保留块池(撤销持久化 BBT,回到仅工厂扫描态)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:仅供测试/出厂重置;调用方需自行还原 RAM BBT
|
||||
*/
|
||||
int32_t gd5f_bbt_wipe_pool(void) {
|
||||
if (s_bbt_pool_count == 0) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
for (uint32_t i = 0; i < s_bbt_pool_count; i++) {
|
||||
gd5f_block_erase(s_bbt_pool_blocks[i]);
|
||||
}
|
||||
s_bbt_write_slot = 0;
|
||||
s_bbt_version = 0;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:从保留块池重新加载持久化 BBT 到 RAM(测试/调试用)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:将闪存中版本号最高的有效副本 OR 进 RAM BBT
|
||||
*/
|
||||
int32_t gd5f_bbt_reload(void) {
|
||||
return gd5f_bbt_load();
|
||||
}
|
||||
|
||||
238
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h
Normal file
238
Drivers/BSP/GD5F2GQ5UE/gd5f2gq5ue.h
Normal 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);
|
||||
|
||||
/*
|
||||
* 函数功能:读取芯片 ID(MID + 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() 已成功调用
|
||||
* 函数说明:通过 BBT(Bad Block Table)查询,BBT 在 init 阶段 ECC 使能前扫描构建
|
||||
*/
|
||||
int32_t gd5f2gq5ue_is_block_bad(uint32_t block);
|
||||
|
||||
/*
|
||||
* 函数功能:标记块为坏块
|
||||
* 入口参数:block - 块编号 uint32_t 0 ~ GD5F_TOTAL_BLOCKS-1
|
||||
* 返回值:无
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:更新 RAM 中的 BBT,并持久化到保留块池(带版本号 + CRC,
|
||||
* 轮转写入,掉电安全);仅在管理区内的块才纳入持久化 BBT
|
||||
*/
|
||||
void gd5f2gq5ue_mark_block_bad(uint32_t block);
|
||||
void gd5f2gq5ue_bbt_clear(void);
|
||||
void gd5f2gq5ue_print_bbt(void);
|
||||
|
||||
/*
|
||||
* 函数功能:重建 BBT(重新扫描出厂坏块并持久化,覆盖运行时注入的坏块)
|
||||
* 入口参数:无
|
||||
* 返回值:GD5F_OK - 成功,其他 - 错误码
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:用于测试(如 TC-STO-03 注入坏块后)或出厂重置后恢复。
|
||||
* 临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记,覆盖 RAM BBT,
|
||||
* 再写回持久化 BBT 池(版本 +1,掉电安全),从而清除运行时 mark 的坏块。
|
||||
* 调用后建议再执行 nand_ftl_format() 清空 dhara map。
|
||||
*/
|
||||
int gd5f2gq5ue_bbt_rebuild(void);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 dhara 可用块数(总块数减去保留块池)
|
||||
* 入口参数:无
|
||||
* 返回值:dhara 可见的块数量(= 保留块池起始块号)
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:保留块池用于存储持久化 BBT,不交给 dhara 管理
|
||||
*/
|
||||
uint32_t gd5f_get_usable_blocks(void);
|
||||
|
||||
/*
|
||||
* 函数功能:从闪存读取当前生效的 BBT 位图(测试/调试用)
|
||||
* 入口参数:p_bbt - 位图输出缓冲 uint8_t* 不为 NULL,长度 >= GD5F_BBT_SIZE
|
||||
* p_version - 版本号输出 uint32_t* 不为 NULL
|
||||
* 返回值:0 - 成功,其他 - 错误码
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:返回闪存中版本号最高的有效 BBT 拷贝(供测试验证持久化)
|
||||
*/
|
||||
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version);
|
||||
|
||||
/*
|
||||
* 函数功能:擦除保留块池(撤销持久化 BBT,回到仅工厂扫描态)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:仅供测试/出厂重置使用;擦除后下次启动 load 找不到副本,
|
||||
* 仅保留 RAM 中工厂扫描结果(调用方需自行还原 RAM BBT)。
|
||||
*/
|
||||
int32_t gd5f_bbt_wipe_pool(void);
|
||||
|
||||
/*
|
||||
* 函数功能:从保留块池重新加载持久化 BBT 到 RAM(测试/调试用)
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功
|
||||
* 限定条件:gd5f2gq5ue_init() 已成功调用
|
||||
* 函数说明:将闪存中版本号最高的有效副本 OR 进 RAM BBT
|
||||
*/
|
||||
int32_t gd5f_bbt_reload(void);
|
||||
|
||||
/* ==================== SPI 原语(FTL 共享) ==================== */
|
||||
|
||||
int32_t gd5f_wait_busy(uint32_t timeout_ms);
|
||||
int32_t gd5f_write_enable(void);
|
||||
int32_t gd5f_read_status(uint8_t *p_status);
|
||||
int32_t gd5f_page_read(uint32_t page_addr);
|
||||
int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf, uint32_t size);
|
||||
int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, uint32_t size);
|
||||
int32_t gd5f_program_exec(uint32_t page_addr);
|
||||
int32_t gd5f_block_erase(uint32_t block_addr);
|
||||
int32_t gd5f_check_ecc(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __GD5F2GQ5UE_H */
|
||||
403
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c
Normal file
403
Drivers/BSP/GD5F2GQ5UE/nand_ftl.c
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 模块名称:NAND FTL 适配层(dhara + FatFS diskio 胶水)
|
||||
* 模块功能:实现 dhara NAND HAL 和 FatFS 磁盘 I/O,连接 GD5F2GQ5UE 底层驱动
|
||||
* 适用平台:STM32F407ZGT6
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-20
|
||||
* 修改记录:
|
||||
* 2026-07-20 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
#include <string.h>
|
||||
#include "gd5f2gq5ue.h"
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
#include "nand.h"
|
||||
#include "map.h"
|
||||
|
||||
/* FTL 分区起始地址(块 0,偏移 0,占用全部 256MB) */
|
||||
#define FTL_FATFS_OFFSET 0U
|
||||
|
||||
/* 调试输出配置 */
|
||||
#define DBG_TAG "[NAND_FTL]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
#define FTL_PAGE_SIZE GD5F_PAGE_SIZE
|
||||
#define FTL_SECTORS_PER_PAGE (FTL_PAGE_SIZE / 512)
|
||||
#define FTL_START_BLOCK (FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE)
|
||||
#define FTL_NUM_BLOCKS (GD5F_TOTAL_BLOCKS - FTL_START_BLOCK)
|
||||
#define FTL_START_PAGE (FTL_START_BLOCK * GD5F_PAGES_PER_BLOCK)
|
||||
|
||||
/* dhara 逻辑地址到物理地址转换 */
|
||||
static inline uint32_t ftl_phy_block(dhara_block_t b) { return b + FTL_START_BLOCK; }
|
||||
static inline uint32_t ftl_phy_page(dhara_page_t p) { return p + FTL_START_PAGE; }
|
||||
|
||||
/* ======================== 私有变量 ======================== */
|
||||
|
||||
/* dhara 核心数据结构 */
|
||||
static struct dhara_nand s_nand;
|
||||
static struct dhara_map s_map;
|
||||
static uint8_t s_page_buf[FTL_PAGE_SIZE]; /* dhara 内部页缓冲区 */
|
||||
|
||||
/* 页面缓存(FatFS 读写缓存) */
|
||||
static uint8_t s_cache_buf[FTL_PAGE_SIZE];
|
||||
static dhara_sector_t s_cached_lpn;
|
||||
static uint8_t s_cache_dirty;
|
||||
|
||||
/* 拷贝临时缓冲区 */
|
||||
static uint8_t s_copy_buf[FTL_PAGE_SIZE];
|
||||
|
||||
/* 初始化状态 */
|
||||
static uint8_t s_initialized;
|
||||
|
||||
/* ======================== 私有函数声明 ======================== */
|
||||
|
||||
static int ftl_flush_cache(void);
|
||||
static int ftl_read_page(dhara_sector_t lpn);
|
||||
|
||||
/* SPI 原语从 gd5f2gq5ue.h 获取 */
|
||||
|
||||
/* ======================== 页面缓存管理 ======================== */
|
||||
|
||||
static int ftl_flush_cache(void) {
|
||||
if (!s_cache_dirty) {
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
dhara_error_t err;
|
||||
if (dhara_map_write(&s_map, s_cached_lpn, s_cache_buf, &err) < 0) {
|
||||
DBG_ERROR("Cache flush failed: LPN=%lu err=%d", s_cached_lpn, err);
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
s_cache_dirty = 0;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
static int ftl_read_page(dhara_sector_t lpn) {
|
||||
dhara_error_t err;
|
||||
|
||||
if (dhara_map_read(&s_map, lpn, s_cache_buf, &err) < 0) {
|
||||
DBG_ERROR("Page read failed: LPN=%lu err=%d", lpn, err);
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
s_cached_lpn = lpn;
|
||||
s_cache_dirty = 0;
|
||||
return GD5F_OK;
|
||||
}
|
||||
|
||||
/* ======================== dhara NAND HAL ======================== */
|
||||
|
||||
int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) {
|
||||
(void)n;
|
||||
return gd5f2gq5ue_is_block_bad(ftl_phy_block(b));
|
||||
}
|
||||
|
||||
void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) {
|
||||
(void)n;
|
||||
gd5f2gq5ue_mark_block_bad(ftl_phy_block(b));
|
||||
}
|
||||
|
||||
int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b, dhara_error_t *err) {
|
||||
(void)n;
|
||||
int ret = gd5f_block_erase(ftl_phy_block(b));
|
||||
if (ret == GD5F_ERASE_FAIL || ret != GD5F_OK) {
|
||||
dhara_nand_mark_bad(n, b);
|
||||
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *data, dhara_error_t *err) {
|
||||
int ret;
|
||||
uint32_t phy_pg = ftl_phy_page(p);
|
||||
|
||||
gd5f_write_enable();
|
||||
ret = gd5f_program_load(0, data, FTL_PAGE_SIZE);
|
||||
if (ret != GD5F_OK) {
|
||||
dhara_nand_mark_bad(n, p >> n->log2_ppb);
|
||||
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gd5f_program_exec(phy_pg);
|
||||
if (ret == GD5F_PROGRAM_FAIL || ret != GD5F_OK) {
|
||||
dhara_nand_mark_bad(n, p >> n->log2_ppb);
|
||||
dhara_set_error(err, DHARA_E_BAD_BLOCK);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) {
|
||||
(void)n;
|
||||
uint32_t i;
|
||||
uint8_t buf[64];
|
||||
uint32_t phy_pg = ftl_phy_page(p);
|
||||
|
||||
if (gd5f_page_read(phy_pg) != GD5F_OK) {
|
||||
return 0;
|
||||
}
|
||||
gd5f_read_from_cache(0, buf, sizeof(buf));
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
if (buf[i] != 0xFF) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) {
|
||||
(void)n;
|
||||
uint32_t phy_pg = ftl_phy_page(p);
|
||||
|
||||
if (gd5f_page_read(phy_pg) != GD5F_OK) {
|
||||
dhara_set_error(err, DHARA_E_ECC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gd5f_check_ecc() != GD5F_OK) {
|
||||
dhara_set_error(err, DHARA_E_ECC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gd5f_read_from_cache((uint16_t)offset, data, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t dst, dhara_error_t *err) {
|
||||
(void)n;
|
||||
|
||||
if (dhara_nand_read(n, src, 0, FTL_PAGE_SIZE, s_copy_buf, err) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dhara_nand_prog(n, dst, s_copy_buf, err) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ======================== FatFS 磁盘 I/O ======================== */
|
||||
|
||||
DSTATUS disk_initialize(BYTE pdrv) {
|
||||
if (pdrv != 0) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
if (s_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBG_INFO("Initializing NAND FTL...");
|
||||
|
||||
s_nand.log2_page_size = 11;
|
||||
s_nand.log2_ppb = 6;
|
||||
/* dhara 可见块数 = 总块数 - 保留块池(BBT 存储区不交给 dhara,避免被擦写) */
|
||||
s_nand.num_blocks = gd5f_get_usable_blocks();
|
||||
|
||||
dhara_map_init(&s_map, &s_nand, s_page_buf, 4);
|
||||
|
||||
memset(s_cache_buf, 0, FTL_PAGE_SIZE);
|
||||
s_cached_lpn = (dhara_sector_t)-1; /* -1 表示缓存未加载;LPN 0 合法,不能用 0 作哨兵 */
|
||||
s_cache_dirty = 0;
|
||||
|
||||
{
|
||||
dhara_error_t err;
|
||||
if (dhara_map_resume(&s_map, &err) < 0) {
|
||||
DBG_INFO("No valid map found, creating fresh (err=%d)", err);
|
||||
dhara_map_clear(&s_map);
|
||||
} else {
|
||||
DBG_INFO("Map resumed: %lu/%lu sectors used",
|
||||
dhara_map_size(&s_map), dhara_map_capacity(&s_map));
|
||||
}
|
||||
}
|
||||
|
||||
s_initialized = 1;
|
||||
DBG_INFO("NAND FTL ready (capacity: %lu pages = %lu MB)",
|
||||
dhara_map_capacity(&s_map),
|
||||
(dhara_map_capacity(&s_map) * FTL_PAGE_SIZE) / (1024 * 1024));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nand_ftl_init(void)
|
||||
{
|
||||
DSTATUS sta = disk_initialize(0);
|
||||
return (sta == 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
void nand_ftl_deinit(void)
|
||||
{
|
||||
/* 复位 FTL 状态,使下次 nand_ftl_init() 真正重新走 dhara_map_resume,
|
||||
用于测试"掉电恢复"与"坏块注入后重建"场景(模拟设备重启) */
|
||||
s_initialized = 0;
|
||||
s_cached_lpn = (dhara_sector_t)-1;
|
||||
s_cache_dirty = 0;
|
||||
}
|
||||
|
||||
DSTATUS disk_status(BYTE pdrv) {
|
||||
if (pdrv != 0) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
if (!s_initialized) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
|
||||
if (pdrv != 0 || !buff) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (!s_initialized && disk_initialize(pdrv) != 0) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
while (count > 0) {
|
||||
dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
|
||||
uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
|
||||
uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);
|
||||
|
||||
if (batch > count) {
|
||||
batch = count;
|
||||
}
|
||||
|
||||
if (lpn != s_cached_lpn) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(buff, s_cache_buf + offset, batch * 512);
|
||||
buff += batch * 512;
|
||||
sector += batch;
|
||||
count -= (UINT)batch;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
|
||||
if (pdrv != 0 || !buff) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (!s_initialized && disk_initialize(pdrv) != 0) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
while (count > 0) {
|
||||
dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
|
||||
uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
|
||||
uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);
|
||||
|
||||
if (batch > count) {
|
||||
batch = count;
|
||||
}
|
||||
|
||||
if (lpn != s_cached_lpn) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
if (ftl_read_page(lpn) != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(s_cache_buf + offset, buff, batch * 512);
|
||||
s_cache_dirty = 1;
|
||||
|
||||
if (offset == 0 && batch == FTL_SECTORS_PER_PAGE) {
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
buff += batch * 512;
|
||||
sector += batch;
|
||||
count -= (UINT)batch;
|
||||
}
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
|
||||
if (pdrv != 0) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if (!s_initialized) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
{
|
||||
dhara_error_t err;
|
||||
if (dhara_map_sync(&s_map, &err) < 0) {
|
||||
DBG_ERROR("Map sync failed: %d", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
}
|
||||
return RES_OK;
|
||||
|
||||
case GET_SECTOR_COUNT: {
|
||||
LBA_t *p_sectors = (LBA_t *)buff;
|
||||
dhara_sector_t pages = dhara_map_capacity(&s_map);
|
||||
*p_sectors = pages * FTL_SECTORS_PER_PAGE;
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
case GET_SECTOR_SIZE:
|
||||
*(WORD *)buff = 512;
|
||||
return RES_OK;
|
||||
|
||||
case GET_BLOCK_SIZE:
|
||||
*(DWORD *)buff = 1;
|
||||
return RES_OK;
|
||||
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================== 公共函数定义 ======================== */
|
||||
|
||||
int nand_ftl_format(void) {
|
||||
dhara_error_t err;
|
||||
|
||||
if (!s_initialized) {
|
||||
if (disk_initialize(0) != 0) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ftl_flush_cache() != GD5F_OK) {
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
dhara_map_clear(&s_map);
|
||||
|
||||
if (dhara_map_sync(&s_map, &err) < 0) {
|
||||
DBG_ERROR("Map sync after clear failed: %d", err);
|
||||
return GD5F_ERROR;
|
||||
}
|
||||
|
||||
s_cache_dirty = 0;
|
||||
s_cached_lpn = (dhara_sector_t)-1;
|
||||
|
||||
DBG_INFO("NAND FTL formatted, capacity: %lu pages", dhara_map_capacity(&s_map));
|
||||
return GD5F_OK;
|
||||
}
|
||||
18
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h
Normal file
18
Drivers/BSP/GD5F2GQ5UE/nand_ftl.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef __NAND_FTL_H
|
||||
#define __NAND_FTL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int nand_ftl_init(void);
|
||||
int nand_ftl_format(void);
|
||||
void nand_ftl_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
21
Drivers/BSP/NET/lftpd/LICENSE.txt
Normal file
21
Drivers/BSP/NET/lftpd/LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Jason von Nieda <jason@vonnieda.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
675
Drivers/BSP/NET/lftpd/lftpd.c
Normal file
675
Drivers/BSP/NET/lftpd/lftpd.c
Normal file
@@ -0,0 +1,675 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "lftpd.h"
|
||||
#include "private/lftpd_status.h"
|
||||
#include "private/lftpd_inet.h"
|
||||
#include "private/lftpd_log.h"
|
||||
#include "private/lftpd_string.h"
|
||||
#include "private/lftpd_io.h"
|
||||
#include "net_socket.h"
|
||||
#include "net_config.h"
|
||||
#include "ff.h"
|
||||
|
||||
typedef struct {
|
||||
const char *command;
|
||||
int (*handler)(lftpd_client_t *client, const char *arg);
|
||||
} command_t;
|
||||
|
||||
static int cmd_cwd(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_dele(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_epsv(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_feat(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_list(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_nlst(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_noop(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_pass(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_pasv(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_pwd(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_quit(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_retr(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_size(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_stor(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_syst(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_type(lftpd_client_t *client, const char *arg);
|
||||
static int cmd_user(lftpd_client_t *client, const char *arg);
|
||||
|
||||
static command_t commands[] = {
|
||||
{ "CWD", cmd_cwd },
|
||||
{ "DELE", cmd_dele },
|
||||
{ "EPSV", cmd_epsv },
|
||||
{ "FEAT", cmd_feat },
|
||||
{ "LIST", cmd_list },
|
||||
{ "NLST", cmd_nlst },
|
||||
{ "NOOP", cmd_noop },
|
||||
{ "PASS", cmd_pass },
|
||||
{ "PASV", cmd_pasv },
|
||||
{ "PWD", cmd_pwd },
|
||||
{ "QUIT", cmd_quit },
|
||||
{ "RETR", cmd_retr },
|
||||
{ "SIZE", cmd_size },
|
||||
{ "STOR", cmd_stor },
|
||||
{ "SYST", cmd_syst },
|
||||
{ "TYPE", cmd_type },
|
||||
{ "USER", cmd_user },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static int send_response(int socket, int code, bool include_code,
|
||||
bool multiline_start, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char message[256];
|
||||
char response[512];
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(message, sizeof(message), format, args);
|
||||
va_end(args);
|
||||
|
||||
if (include_code) {
|
||||
if (multiline_start) {
|
||||
snprintf(response, sizeof(response), "%d-%s%s", code, message, CRLF);
|
||||
} else {
|
||||
snprintf(response, sizeof(response), "%d %s%s", code, message, CRLF);
|
||||
}
|
||||
} else {
|
||||
snprintf(response, sizeof(response), "%s%s", message, CRLF);
|
||||
}
|
||||
|
||||
return lftpd_inet_write_string(socket, response);
|
||||
}
|
||||
|
||||
#define send_simple_response(socket, code, format, ...) \
|
||||
send_response(socket, code, true, false, format, ##__VA_ARGS__)
|
||||
|
||||
#define send_multiline_response_begin(socket, code, format, ...) \
|
||||
send_response(socket, code, true, true, format, ##__VA_ARGS__)
|
||||
|
||||
#define send_multiline_response_line(socket, format, ...) \
|
||||
send_response(socket, 0, false, false, format, ##__VA_ARGS__)
|
||||
|
||||
#define send_multiline_response_end(socket, code, format, ...) \
|
||||
send_response(socket, code, true, false, format, ##__VA_ARGS__)
|
||||
|
||||
static int send_list(int socket, const char *path)
|
||||
{
|
||||
static const char *directory_format = "drw-rw-rw- 1 owner group %13lu Jan 01 1970 %s";
|
||||
static const char *file_format = "-rw-rw-rw- 1 owner group %13lu Jan 01 1970 %s";
|
||||
|
||||
void *dp = lftpd_io_opendir(path);
|
||||
if (dp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char name[256];
|
||||
uint32_t size;
|
||||
int is_dir;
|
||||
|
||||
while (lftpd_io_readdir(dp, name, sizeof(name), &size, &is_dir) == 0) {
|
||||
if (is_dir) {
|
||||
send_multiline_response_line(socket, directory_format,
|
||||
(unsigned long)size, name);
|
||||
} else {
|
||||
send_multiline_response_line(socket, file_format,
|
||||
(unsigned long)size, name);
|
||||
}
|
||||
}
|
||||
|
||||
lftpd_io_closedir(dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_nlst(int socket, const char *path)
|
||||
{
|
||||
void *dp = lftpd_io_opendir(path);
|
||||
if (dp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char name[256];
|
||||
uint32_t size;
|
||||
int is_dir;
|
||||
|
||||
while (lftpd_io_readdir(dp, name, sizeof(name), &size, &is_dir) == 0) {
|
||||
if (!is_dir) {
|
||||
send_multiline_response_line(socket, name);
|
||||
}
|
||||
}
|
||||
|
||||
lftpd_io_closedir(dp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_file(int socket, const char *path)
|
||||
{
|
||||
int fh = lftpd_io_open_read(path);
|
||||
if (fh < 0) {
|
||||
lftpd_log_error("failed to open file for read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char buffer[1024];
|
||||
int read_len;
|
||||
|
||||
while ((read_len = lftpd_io_read(fh, buffer, sizeof(buffer))) > 0) {
|
||||
unsigned char *p = buffer;
|
||||
while (read_len > 0) {
|
||||
int write_len = lftpd_inet_write(socket, p, read_len);
|
||||
if (write_len < 0) {
|
||||
lftpd_log_error("write error");
|
||||
lftpd_io_close(fh);
|
||||
return -1;
|
||||
}
|
||||
p += write_len;
|
||||
read_len -= write_len;
|
||||
}
|
||||
}
|
||||
|
||||
lftpd_io_close(fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int receive_file(int socket, const char *path)
|
||||
{
|
||||
int fh = lftpd_io_open_write(path);
|
||||
if (fh < 0) {
|
||||
lftpd_log_error("failed to open file for write");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char buffer[1024];
|
||||
int err;
|
||||
int total = 0;
|
||||
|
||||
while ((err = lftpd_inet_read(socket, buffer, sizeof(buffer))) > 0) {
|
||||
total += err;
|
||||
if (lftpd_io_write(fh, buffer, err) != err) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lftpd_io_close(fh);
|
||||
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
if (total == 0) {
|
||||
lftpd_log_error("received 0 bytes");
|
||||
lftpd_io_unlink(path);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_cwd(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (arg == NULL || strlen(arg) == 0) {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||
|
||||
/* 根目录始终有效,跳过 stat 检查(FatFS FF_FS_RPATH=0 下 stat 根目录可能失败) */
|
||||
if (strcmp(path, "/") != 0) {
|
||||
uint32_t size;
|
||||
int is_dir;
|
||||
if (lftpd_io_stat(path, &size, &is_dir) != 0 || !is_dir) {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(client->directory, path, sizeof(client->directory) - 1);
|
||||
client->directory[sizeof(client->directory) - 1] = '\0';
|
||||
free(path);
|
||||
|
||||
send_simple_response(client->socket, 250, STATUS_250);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_dele(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (arg == NULL || strlen(arg) == 0) {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||
|
||||
uint32_t size;
|
||||
int is_dir;
|
||||
if (lftpd_io_stat(path, &size, &is_dir) != 0) {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_dir) {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
free(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lftpd_io_unlink(path);
|
||||
free(path);
|
||||
send_simple_response(client->socket, 250, STATUS_250);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_epsv(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (client->data_socket >= 0) {
|
||||
lftpd_inet_close(client->data_socket);
|
||||
client->data_socket = -1;
|
||||
}
|
||||
|
||||
int listener_socket = lftpd_inet_listen(0);
|
||||
if (listener_socket < 0) {
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int port = lftpd_inet_get_socket_port(listener_socket);
|
||||
|
||||
send_simple_response(client->socket, 229, STATUS_229, port);
|
||||
|
||||
lftpd_log_debug("EPSV listener on port %d", port);
|
||||
|
||||
client->data_socket = listener_socket;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_feat(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_multiline_response_begin(client->socket, 211, STATUS_211);
|
||||
send_multiline_response_line(client->socket, "EPSV");
|
||||
send_multiline_response_line(client->socket, "PASV");
|
||||
send_multiline_response_line(client->socket, "SIZE");
|
||||
send_multiline_response_line(client->socket, "NLST");
|
||||
send_multiline_response_end(client->socket, 211, STATUS_211);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_list(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (client->data_socket == -1) {
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
return -1;
|
||||
}
|
||||
|
||||
send_simple_response(client->socket, 150, STATUS_150);
|
||||
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||
if (data_sock < 0) {
|
||||
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
lftpd_inet_close(client->data_socket);
|
||||
client->data_socket = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = send_list(data_sock, client->directory);
|
||||
lftpd_inet_close(data_sock);
|
||||
client->data_socket = -1;
|
||||
|
||||
if (err == 0) {
|
||||
send_simple_response(client->socket, 226, STATUS_226);
|
||||
} else {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_nlst(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (client->data_socket == -1) {
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
return -1;
|
||||
}
|
||||
|
||||
send_simple_response(client->socket, 150, STATUS_150);
|
||||
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||
if (data_sock < 0) {
|
||||
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
lftpd_inet_close(client->data_socket);
|
||||
client->data_socket = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = send_nlst(data_sock, client->directory);
|
||||
lftpd_inet_close(data_sock);
|
||||
client->data_socket = -1;
|
||||
|
||||
if (err == 0) {
|
||||
send_simple_response(client->socket, 226, STATUS_226);
|
||||
} else {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_noop(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 200, STATUS_200);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_pass(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 230, STATUS_230);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_pasv(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
int listener_socket = -1;
|
||||
int retry;
|
||||
|
||||
if (client->data_socket >= 0) {
|
||||
lftpd_inet_close(client->data_socket);
|
||||
client->data_socket = -1;
|
||||
}
|
||||
|
||||
for (retry = 0; retry < 3; retry++) {
|
||||
listener_socket = lftpd_inet_listen(0);
|
||||
if (listener_socket >= 0) break;
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
if (listener_socket < 0) {
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int port = lftpd_inet_get_socket_port(listener_socket);
|
||||
|
||||
{
|
||||
struct net_sockaddr_in local_addr;
|
||||
int addrlen = sizeof(local_addr);
|
||||
int err = net_getsockname(client->socket,
|
||||
(struct net_sockaddr *)&local_addr, &addrlen);
|
||||
if (err != 0) {
|
||||
lftpd_log_error("error getting client IP info");
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
lftpd_inet_close(listener_socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t ip = net_htonl(local_addr.sin_addr.s_addr);
|
||||
send_simple_response(client->socket, 227, STATUS_227,
|
||||
(ip >> 24) & 0xff,
|
||||
(ip >> 16) & 0xff,
|
||||
(ip >> 8) & 0xff,
|
||||
(ip >> 0) & 0xff,
|
||||
(port >> 8) & 0xff, (port >> 0) & 0xff);
|
||||
}
|
||||
|
||||
lftpd_log_debug("PASV listener on port %d", port);
|
||||
|
||||
client->data_socket = listener_socket;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_pwd(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 257, "\"%s\"", client->directory);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_quit(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 221, STATUS_221);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cmd_retr(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (client->data_socket == -1) {
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
return -1;
|
||||
}
|
||||
|
||||
send_simple_response(client->socket, 150, STATUS_150);
|
||||
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||
if (data_sock < 0) {
|
||||
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
lftpd_inet_close(client->data_socket);
|
||||
client->data_socket = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||
lftpd_log_debug("send '%s'", path);
|
||||
int err = send_file(data_sock, path);
|
||||
lftpd_inet_close(data_sock);
|
||||
client->data_socket = -1;
|
||||
|
||||
if (err == 0) {
|
||||
lftpd_log_info("DOWNLOAD '%s' OK", path);
|
||||
send_simple_response(client->socket, 226, STATUS_226);
|
||||
} else {
|
||||
lftpd_log_error("DOWNLOAD '%s' FAILED", path);
|
||||
send_simple_response(client->socket, 450, STATUS_450);
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_size(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (arg == NULL) {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||
lftpd_log_debug("size %s", path);
|
||||
|
||||
uint32_t size;
|
||||
int is_dir;
|
||||
if (lftpd_io_stat(path, &size, &is_dir) == 0 && !is_dir) {
|
||||
send_simple_response(client->socket, 213, "%lu", (unsigned long)size);
|
||||
} else {
|
||||
send_simple_response(client->socket, 550, STATUS_550);
|
||||
}
|
||||
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_stor(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
if (client->data_socket == -1) {
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
return -1;
|
||||
}
|
||||
|
||||
send_simple_response(client->socket, 150, STATUS_150);
|
||||
int data_sock = lftpd_inet_accept_timeout(client->data_socket, 5000);
|
||||
if (data_sock < 0) {
|
||||
lftpd_log_error("%s accept failed, sk=%d", __func__, client->data_socket);
|
||||
send_simple_response(client->socket, 425, STATUS_425);
|
||||
lftpd_inet_close(client->data_socket);
|
||||
client->data_socket = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *path = lftpd_io_canonicalize_path(client->directory, arg);
|
||||
lftpd_log_debug("receive '%s'", path);
|
||||
int err = receive_file(data_sock, path);
|
||||
lftpd_inet_close(data_sock);
|
||||
client->data_socket = -1;
|
||||
|
||||
if (err == 0) {
|
||||
uint32_t size = 0;
|
||||
int is_dir = 0;
|
||||
lftpd_io_stat(path, &size, &is_dir);
|
||||
lftpd_log_info("UPLOAD '%s' %lu bytes OK", path, (unsigned long)size);
|
||||
send_simple_response(client->socket, 226, STATUS_226);
|
||||
} else {
|
||||
lftpd_log_error("UPLOAD '%s' FAILED", path);
|
||||
send_simple_response(client->socket, 450, STATUS_450);
|
||||
}
|
||||
free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_syst(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 215, "UNIX Type: L8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_type(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 200, STATUS_200);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_user(lftpd_client_t *client, const char *arg)
|
||||
{
|
||||
send_simple_response(client->socket, 230, STATUS_230);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_control_channel(lftpd_client_t *client)
|
||||
{
|
||||
int err = send_simple_response(client->socket, 220, STATUS_220);
|
||||
if (err != 0) {
|
||||
lftpd_log_error("error sending welcome message");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char read_buffer[512];
|
||||
|
||||
while (err == 0) {
|
||||
int line_len = lftpd_inet_read_line(client->socket, read_buffer,
|
||||
sizeof(read_buffer));
|
||||
if (line_len != 0) {
|
||||
lftpd_log_error("error reading next command");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
int index;
|
||||
char *p = strchr(read_buffer, ' ');
|
||||
if (p != NULL) {
|
||||
index = (int)(p - read_buffer);
|
||||
} else {
|
||||
index = (int)strlen(read_buffer);
|
||||
}
|
||||
|
||||
if (index >= 5) {
|
||||
err = send_simple_response(client->socket, 500, STATUS_500);
|
||||
continue;
|
||||
}
|
||||
|
||||
char command_tmp[4 + 1];
|
||||
memset(command_tmp, 0, sizeof(command_tmp));
|
||||
memcpy(command_tmp, read_buffer, (size_t)index);
|
||||
|
||||
for (int i = 0; command_tmp[i]; i++) {
|
||||
command_tmp[i] = (char)toupper((int)command_tmp[i]);
|
||||
}
|
||||
|
||||
bool matched = false;
|
||||
for (int i = 0; commands[i].command; i++) {
|
||||
if (strcmp(commands[i].command, command_tmp) == 0) {
|
||||
char arg_buf[512];
|
||||
const char *arg = NULL;
|
||||
|
||||
if (index < (int)strlen(read_buffer)) {
|
||||
strncpy(arg_buf, read_buffer + index + 1, sizeof(arg_buf) - 1);
|
||||
arg_buf[sizeof(arg_buf) - 1] = '\0';
|
||||
arg = lftpd_string_trim(arg_buf);
|
||||
}
|
||||
|
||||
lftpd_log_info("CMD %s %s", command_tmp, arg ? arg : "");
|
||||
err = commands[i].handler(client, arg);
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
send_simple_response(client->socket, 502, STATUS_502);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
lftpd_inet_close(client->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lftpd_start(const char *directory, int port, lftpd_t *lftpd)
|
||||
{
|
||||
memset(lftpd, 0, sizeof(lftpd_t));
|
||||
|
||||
lftpd->directory = directory;
|
||||
lftpd->port = port;
|
||||
|
||||
while (true) {
|
||||
lftpd->server_socket = lftpd_inet_listen(port);
|
||||
if (lftpd->server_socket < 0) {
|
||||
lftpd_log_error("error creating listener");
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
continue;
|
||||
}
|
||||
|
||||
lftpd_log_info("waiting for connection...");
|
||||
|
||||
int client_socket = lftpd_inet_accept(lftpd->server_socket);
|
||||
if (client_socket < 0) {
|
||||
lftpd_log_error("error accepting client socket");
|
||||
lftpd_inet_close(lftpd->server_socket);
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
char ip_str[16];
|
||||
struct net_sockaddr_in addr;
|
||||
int addrlen = sizeof(addr);
|
||||
if (net_getsockname(client_socket,
|
||||
(struct net_sockaddr *)&addr, &addrlen) == 0) {
|
||||
net_inet_ntoa(addr.sin_addr.s_addr, ip_str);
|
||||
int remote_port = lftpd_inet_get_socket_port(client_socket);
|
||||
lftpd_log_info("connection received from %s:%d...",
|
||||
ip_str, remote_port);
|
||||
} else {
|
||||
lftpd_log_info("connection received...");
|
||||
}
|
||||
}
|
||||
|
||||
lftpd_client_t client;
|
||||
memset(&client, 0, sizeof(client));
|
||||
strncpy(client.directory, directory, sizeof(client.directory) - 1);
|
||||
client.directory[sizeof(client.directory) - 1] = '\0';
|
||||
client.socket = client_socket;
|
||||
client.data_socket = -1;
|
||||
|
||||
lftpd->client = &client;
|
||||
handle_control_channel(&client);
|
||||
lftpd->client = NULL;
|
||||
|
||||
/* standalone mode: socket was closed in handle_control_channel,
|
||||
* loop back to create a new listener */
|
||||
}
|
||||
}
|
||||
|
||||
int lftpd_stop(lftpd_t *lftpd)
|
||||
{
|
||||
if (lftpd->server_socket >= 0) {
|
||||
lftpd_inet_close(lftpd->server_socket);
|
||||
}
|
||||
if (lftpd->client) {
|
||||
lftpd_inet_close(lftpd->client->socket);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
19
Drivers/BSP/NET/lftpd/lftpd.h
Normal file
19
Drivers/BSP/NET/lftpd/lftpd.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
char directory[256];
|
||||
int socket;
|
||||
int data_socket;
|
||||
} lftpd_client_t;
|
||||
|
||||
typedef struct {
|
||||
const char *directory;
|
||||
int port;
|
||||
int server_socket;
|
||||
lftpd_client_t *client;
|
||||
} lftpd_t;
|
||||
|
||||
int lftpd_start(const char *directory, int port, lftpd_t *lftpd);
|
||||
int lftpd_stop(lftpd_t *lftpd);
|
||||
166
Drivers/BSP/NET/lftpd/lftpd_inet.c
Normal file
166
Drivers/BSP/NET/lftpd/lftpd_inet.c
Normal file
@@ -0,0 +1,166 @@
|
||||
#define DBG_TAG "[LFT_INET]"
|
||||
|
||||
#include "private/lftpd_inet.h"
|
||||
#include "dbg_log.h"
|
||||
#include "net_socket.h"
|
||||
#include <string.h>
|
||||
|
||||
int lftpd_inet_listen(int port)
|
||||
{
|
||||
int retry;
|
||||
int s;
|
||||
|
||||
for (retry = 0; retry < 5; retry++) {
|
||||
s = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||
if (s < 0) {
|
||||
if (retry == 0) DBG_ERROR("create socket");
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
continue;
|
||||
}
|
||||
|
||||
struct net_sockaddr_in addr;
|
||||
addr.sin_family = NET_AF_INET;
|
||||
addr.sin_port = net_htons((uint16_t)port);
|
||||
addr.sin_addr.s_addr = 0;
|
||||
|
||||
if (net_bind(s, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
DBG_ERROR("bind port %d", port);
|
||||
net_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (net_listen(s) >= 0) {
|
||||
return s;
|
||||
}
|
||||
|
||||
net_close(s);
|
||||
if (retry < 4) {
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
||||
}
|
||||
|
||||
DBG_ERROR("listen failed after 5 retries (total 10s wait)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lftpd_inet_get_socket_port(int socket)
|
||||
{
|
||||
struct net_sockaddr_in addr;
|
||||
int addrlen = sizeof(addr);
|
||||
|
||||
if (net_getsockname(socket, (struct net_sockaddr *)&addr, &addrlen) < 0) {
|
||||
DBG_ERROR("error getting socket port number");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)net_ntohs(addr.sin_port);
|
||||
}
|
||||
|
||||
int lftpd_inet_accept(int socket)
|
||||
{
|
||||
while (1) {
|
||||
int s = net_accept(socket, NULL, NULL);
|
||||
if (s >= 0) {
|
||||
return s;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
int lftpd_inet_accept_timeout(int socket, int timeout_ms)
|
||||
{
|
||||
int elapsed = 0;
|
||||
|
||||
while (elapsed < timeout_ms) {
|
||||
int s = net_accept(socket, NULL, NULL);
|
||||
if (s >= 0) {
|
||||
return s;
|
||||
}
|
||||
/* 数据连接已被对端关闭时提前退出,不等待超时 */
|
||||
net_sock_t *p_sock = net_get_sock(socket);
|
||||
if (p_sock == NULL || p_sock->state != NET_SOCK_STATE_LISTENING) {
|
||||
DBG_ERROR("data socket closed, accept aborted");
|
||||
return -1;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
elapsed += 100;
|
||||
}
|
||||
|
||||
DBG_ERROR("accept timeout (%dms)", timeout_ms);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len)
|
||||
{
|
||||
memset(buffer, 0, buffer_len);
|
||||
int total_read_len = 0;
|
||||
|
||||
while (total_read_len < (int)buffer_len) {
|
||||
int read_len = net_recv(socket,
|
||||
buffer + total_read_len,
|
||||
buffer_len - total_read_len - 1,
|
||||
0);
|
||||
if (read_len == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (read_len < 0) {
|
||||
return read_len;
|
||||
}
|
||||
|
||||
total_read_len += read_len;
|
||||
|
||||
char *p = strstr(buffer, "\r\n");
|
||||
if (p != NULL) {
|
||||
*p = '\0';
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lftpd_inet_write_string(int socket, const char *message)
|
||||
{
|
||||
const char *p = message;
|
||||
int length = (int)strlen(message);
|
||||
|
||||
while (length > 0) {
|
||||
int write_len = net_send(socket, p, length, 0);
|
||||
if (write_len < 0) {
|
||||
DBG_ERROR("write error");
|
||||
return write_len;
|
||||
}
|
||||
p += write_len;
|
||||
length -= write_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lftpd_inet_write(int socket, const void *buf, int len)
|
||||
{
|
||||
const char *p = (const char *)buf;
|
||||
int remaining = len;
|
||||
|
||||
while (remaining > 0) {
|
||||
int n = net_send(socket, p, remaining, 0);
|
||||
if (n < 0) {
|
||||
return n;
|
||||
}
|
||||
p += n;
|
||||
remaining -= n;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int lftpd_inet_read(int socket, void *buf, int len)
|
||||
{
|
||||
int n = net_recv(socket, buf, len, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
int lftpd_inet_close(int socket)
|
||||
{
|
||||
return net_close(socket);
|
||||
}
|
||||
236
Drivers/BSP/NET/lftpd/lftpd_io.c
Normal file
236
Drivers/BSP/NET/lftpd/lftpd_io.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ff.h"
|
||||
#include "private/lftpd_io.h"
|
||||
|
||||
#define DBG_TAG "[LFT_IO]"
|
||||
#include "dbg_log.h"
|
||||
|
||||
/*
|
||||
* FatFS 在 FF_USE_LFN=0 时不接受 "/" 前缀路径。
|
||||
* 剥离前导 "/" 后用 FatFS 兼容路径,根目录用 "0:"(驱动号格式)替代。
|
||||
*/
|
||||
static const char *to_fatfs_path(const char *path)
|
||||
{
|
||||
if (path == NULL || *path == '\0') {
|
||||
return "0:";
|
||||
}
|
||||
if (*path == '/') {
|
||||
path++;
|
||||
if (*path == '\0') {
|
||||
return "0:";
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
static int s_file_open = 0;
|
||||
static FIL s_ftp_file;
|
||||
|
||||
char *lftpd_io_canonicalize_path(const char *base, const char *name)
|
||||
{
|
||||
if (base == NULL) {
|
||||
base = "";
|
||||
}
|
||||
if (name == NULL) {
|
||||
name = "";
|
||||
}
|
||||
|
||||
char *path;
|
||||
if (name[0] == '/') {
|
||||
path = (char *)malloc(strlen(name) + 1);
|
||||
if (path == NULL) return NULL;
|
||||
strcpy(path, name);
|
||||
} else {
|
||||
size_t len = strlen(base) + 1 + strlen(name) + 1;
|
||||
path = (char *)malloc(len);
|
||||
if (path == NULL) return NULL;
|
||||
snprintf(path, len, "%s/%s", base, name);
|
||||
}
|
||||
|
||||
char *abs_path = (char *)malloc(strlen(path) + 2);
|
||||
if (abs_path == NULL) {
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
abs_path[0] = '\0';
|
||||
|
||||
char *p = path;
|
||||
while (*p != '\0') {
|
||||
while (*p == '/') p++;
|
||||
if (*p == '\0') break;
|
||||
|
||||
char *seg_start = p;
|
||||
while (*p != '\0' && *p != '/') p++;
|
||||
|
||||
char saved = *p;
|
||||
*p = '\0';
|
||||
|
||||
if (strcmp(seg_start, ".") == 0) {
|
||||
/* ignore */
|
||||
} else if (strcmp(seg_start, "..") == 0) {
|
||||
char *slash = strrchr(abs_path, '/');
|
||||
if (slash != NULL) {
|
||||
*slash = '\0';
|
||||
}
|
||||
} else {
|
||||
strcat(abs_path, "/");
|
||||
strcat(abs_path, seg_start);
|
||||
}
|
||||
|
||||
*p = saved;
|
||||
}
|
||||
free(path);
|
||||
|
||||
if (strlen(abs_path) == 0) {
|
||||
strcpy(abs_path, "/");
|
||||
}
|
||||
|
||||
return abs_path;
|
||||
}
|
||||
|
||||
int lftpd_io_open_read(const char *path)
|
||||
{
|
||||
if (s_file_open) {
|
||||
DBG_ERROR("open_read '%s': already open", path);
|
||||
return -1;
|
||||
}
|
||||
const char *fpath = to_fatfs_path(path);
|
||||
FRESULT fr = f_open(&s_ftp_file, fpath, FA_READ);
|
||||
if (fr != FR_OK) {
|
||||
DBG_ERROR("open_read '%s': f_open err=%d", fpath, (int)fr);
|
||||
return -1;
|
||||
}
|
||||
s_file_open = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lftpd_io_open_write(const char *path)
|
||||
{
|
||||
if (s_file_open) {
|
||||
DBG_ERROR("open_write '%s': already open", path);
|
||||
return -1;
|
||||
}
|
||||
const char *fpath = to_fatfs_path(path);
|
||||
FRESULT fr = f_open(&s_ftp_file, fpath, FA_WRITE | FA_CREATE_ALWAYS);
|
||||
if (fr != FR_OK) {
|
||||
DBG_ERROR("open_write '%s': f_open err=%d", fpath, (int)fr);
|
||||
return -1;
|
||||
}
|
||||
s_file_open = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lftpd_io_read(int handle, void *buf, int len)
|
||||
{
|
||||
(void)handle;
|
||||
UINT br;
|
||||
if (f_read(&s_ftp_file, buf, (UINT)len, &br) != FR_OK) {
|
||||
return -1;
|
||||
}
|
||||
return (int)br;
|
||||
}
|
||||
|
||||
int lftpd_io_write(int handle, const void *buf, int len)
|
||||
{
|
||||
(void)handle;
|
||||
UINT bw;
|
||||
if (f_write(&s_ftp_file, buf, (UINT)len, &bw) != FR_OK) {
|
||||
return -1;
|
||||
}
|
||||
if ((int)bw != len) {
|
||||
return -1;
|
||||
}
|
||||
return (int)bw;
|
||||
}
|
||||
|
||||
void lftpd_io_close(int handle)
|
||||
{
|
||||
(void)handle;
|
||||
if (s_file_open) {
|
||||
f_close(&s_ftp_file);
|
||||
s_file_open = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int lftpd_io_stat(const char *path, uint32_t *p_size, int *p_is_dir)
|
||||
{
|
||||
FILINFO fno;
|
||||
const char *fpath = to_fatfs_path(path);
|
||||
|
||||
if (f_stat(fpath, &fno) != FR_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p_size != NULL) {
|
||||
*p_size = (uint32_t)fno.fsize;
|
||||
}
|
||||
if (p_is_dir != NULL) {
|
||||
*p_is_dir = (fno.fattrib & AM_DIR) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lftpd_io_unlink(const char *path)
|
||||
{
|
||||
const char *fpath = to_fatfs_path(path);
|
||||
if (f_unlink(fpath) == FR_OK) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *lftpd_io_opendir(const char *path)
|
||||
{
|
||||
DIR *dp = (DIR *)malloc(sizeof(DIR));
|
||||
if (dp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
const char *fpath = to_fatfs_path(path);
|
||||
if (f_opendir(dp, fpath) != FR_OK) {
|
||||
free(dp);
|
||||
return NULL;
|
||||
}
|
||||
return (void *)dp;
|
||||
}
|
||||
|
||||
int lftpd_io_readdir(void *dp, char *name, int name_max, uint32_t *p_size, int *p_is_dir)
|
||||
{
|
||||
FILINFO fno;
|
||||
FRESULT fr;
|
||||
|
||||
while (1) {
|
||||
fr = f_readdir((DIR *)dp, &fno);
|
||||
if (fr != FR_OK || fno.fname[0] == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (fno.fname[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
strncpy(name, fno.fname, (size_t)(name_max - 1));
|
||||
name[name_max - 1] = '\0';
|
||||
|
||||
if (p_size != NULL) {
|
||||
*p_size = (uint32_t)fno.fsize;
|
||||
}
|
||||
if (p_is_dir != NULL) {
|
||||
*p_is_dir = (fno.fattrib & AM_DIR) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lftpd_io_closedir(void *dp)
|
||||
{
|
||||
if (dp != NULL) {
|
||||
f_closedir((DIR *)dp);
|
||||
free(dp);
|
||||
}
|
||||
}
|
||||
18
Drivers/BSP/NET/lftpd/lftpd_log.c
Normal file
18
Drivers/BSP/NET/lftpd/lftpd_log.c
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "private/lftpd_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
void lftpd_log_internal(const char* level, const char* format, ...) {
|
||||
char buffer[256];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int err = vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
va_end(args);
|
||||
if (err >= sizeof(buffer)) {
|
||||
return;
|
||||
}
|
||||
printf("%s %s\n", level, buffer);
|
||||
}
|
||||
16
Drivers/BSP/NET/lftpd/lftpd_string.c
Normal file
16
Drivers/BSP/NET/lftpd/lftpd_string.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "private/lftpd_string.h"
|
||||
|
||||
char *lftpd_string_trim(char *s)
|
||||
{
|
||||
char *p = s;
|
||||
for (int i = 0, len = (int)strlen(s); i < len && isspace((int)s[i]); i++) {
|
||||
p++;
|
||||
}
|
||||
for (int i = (int)strlen(p); i >= 0 && isspace((int)p[i]); i--) {
|
||||
p[i] = '\0';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
14
Drivers/BSP/NET/lftpd/private/lftpd_inet.h
Normal file
14
Drivers/BSP/NET/lftpd/private/lftpd_inet.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int lftpd_inet_listen(int port);
|
||||
int lftpd_inet_get_socket_port(int socket);
|
||||
int lftpd_inet_accept(int socket);
|
||||
int lftpd_inet_accept_timeout(int socket, int timeout_ms);
|
||||
int lftpd_inet_read_line(int socket, char *buffer, size_t buffer_len);
|
||||
int lftpd_inet_write_string(int socket, const char *message);
|
||||
int lftpd_inet_write(int socket, const void *buf, int len);
|
||||
int lftpd_inet_read(int socket, void *buf, int len);
|
||||
int lftpd_inet_close(int socket);
|
||||
18
Drivers/BSP/NET/lftpd/private/lftpd_io.h
Normal file
18
Drivers/BSP/NET/lftpd/private/lftpd_io.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
char *lftpd_io_canonicalize_path(const char *base, const char *name);
|
||||
|
||||
int lftpd_io_open_read(const char *path);
|
||||
int lftpd_io_open_write(const char *path);
|
||||
int lftpd_io_read(int handle, void *buf, int len);
|
||||
int lftpd_io_write(int handle, const void *buf, int len);
|
||||
void lftpd_io_close(int handle);
|
||||
|
||||
int lftpd_io_stat(const char *path, uint32_t *p_size, int *p_is_dir);
|
||||
int lftpd_io_unlink(const char *path);
|
||||
|
||||
void *lftpd_io_opendir(const char *path);
|
||||
int lftpd_io_readdir(void *dp, char *name, int name_max, uint32_t *p_size, int *p_is_dir);
|
||||
void lftpd_io_closedir(void *dp);
|
||||
7
Drivers/BSP/NET/lftpd/private/lftpd_log.h
Normal file
7
Drivers/BSP/NET/lftpd/private/lftpd_log.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "dbg_log.h"
|
||||
|
||||
#define lftpd_log_error(format, ...) DBG_ERROR("[FTP] " format, ##__VA_ARGS__)
|
||||
#define lftpd_log_info(format, ...) DBG_INFO("[FTP] " format, ##__VA_ARGS__)
|
||||
#define lftpd_log_debug(format, ...)
|
||||
42
Drivers/BSP/NET/lftpd/private/lftpd_status.h
Normal file
42
Drivers/BSP/NET/lftpd/private/lftpd_status.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#define STATUS_110 "Restart marker reply."
|
||||
#define STATUS_120 "Service ready in %d minutes."
|
||||
#define STATUS_125 "Data connection already open; transfer starting."
|
||||
#define STATUS_150 "File status okay; about to open data connection."
|
||||
#define STATUS_200 "Command okay."
|
||||
#define STATUS_202 "Command not implemented, superfluous at this site."
|
||||
#define STATUS_211 "System status, or system help reply."
|
||||
#define STATUS_212 "Directory status."
|
||||
#define STATUS_213 "File status."
|
||||
#define STATUS_214 "Help message."
|
||||
#define STATUS_215 "%s system type."
|
||||
#define STATUS_220 "Service ready for new user."
|
||||
#define STATUS_221 "Service closing control connection."
|
||||
#define STATUS_225 "Data connection open; no transfer in progress."
|
||||
#define STATUS_226 "Closing data connection."
|
||||
#define STATUS_227 "Entering Passive Mode (%d,%d,%d,%d,%d,%d)."
|
||||
#define STATUS_229 "Entering Extended Passive Mode (|||%d|)."
|
||||
#define STATUS_230 "User logged in, proceed."
|
||||
#define STATUS_250 "Requested file action okay, completed."
|
||||
#define STATUS_257 "\"%s\" created."
|
||||
#define STATUS_331 "User name okay, need password."
|
||||
#define STATUS_332 "Need account for login."
|
||||
#define STATUS_350 "Requested file action pending further information."
|
||||
#define STATUS_421 "Service not available, closing control connection."
|
||||
#define STATUS_425 "Can't open data connection."
|
||||
#define STATUS_426 "Connection closed; transfer aborted."
|
||||
#define STATUS_450 "Requested file action not taken. File unavailable (e.g., file busy)."
|
||||
#define STATUS_451 "Requested action aborted: local error in processing."
|
||||
#define STATUS_452 "Requested action not taken. Insufficient storage space in system."
|
||||
#define STATUS_500 "Syntax error, command unrecognized. This may include errors such as command line too long."
|
||||
#define STATUS_501 "Syntax error in parameters or arguments."
|
||||
#define STATUS_502 "Command not implemented."
|
||||
#define STATUS_503 "Bad sequence of commands."
|
||||
#define STATUS_504 "Command not implemented for that parameter."
|
||||
#define STATUS_530 "Not logged in."
|
||||
#define STATUS_532 "Need account for storing files."
|
||||
#define STATUS_550 "Requested action not taken. File unavailable (e.g., file not found, no access)."
|
||||
#define STATUS_551 "Requested action aborted: page type unknown."
|
||||
#define STATUS_552 "Requested file action aborted. Exceeded storage allocation (for current directory or dataset)."
|
||||
#define STATUS_553 "Requested action not taken. File name not allowed."
|
||||
5
Drivers/BSP/NET/lftpd/private/lftpd_string.h
Normal file
5
Drivers/BSP/NET/lftpd/private/lftpd_string.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define CRLF "\r\n"
|
||||
|
||||
char* lftpd_string_trim(char* s);
|
||||
104
Drivers/BSP/NET/net_config.h
Normal file
104
Drivers/BSP/NET/net_config.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 模块名称:Network Configuration
|
||||
* 模块功能:网络子系统配置宏定义,包括最大Socket数量、缓冲区大小、
|
||||
* 超时时间等参数配置
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
*/
|
||||
|
||||
#ifndef __NET_CONFIG_H
|
||||
#define __NET_CONFIG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 最大支持的 Socket 数量(CH395F 支持 0~7)
|
||||
*/
|
||||
#ifndef NET_MAX_SOCKETS
|
||||
#define NET_MAX_SOCKETS 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TCP Server 多连接模式配置
|
||||
* 监听 Socket 索引和数据 Socket 起始索引
|
||||
*/
|
||||
#ifndef NET_TCP_SERVER_LISTEN_SOCK
|
||||
#define NET_TCP_SERVER_LISTEN_SOCK 0 /* 监听 Socket 索引 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 缓冲区大小配置
|
||||
*/
|
||||
#ifndef NET_SEND_BUF_SIZE
|
||||
#define NET_SEND_BUF_SIZE 4096 /* 发送缓冲区大小(字节) */
|
||||
#endif
|
||||
|
||||
#ifndef NET_RECV_BUF_SIZE
|
||||
#define NET_RECV_BUF_SIZE 4096 /* 接收缓冲区大小(字节) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 超时时间配置(毫秒)
|
||||
* 0 = 无限等待(阻塞模式)
|
||||
*/
|
||||
#ifndef NET_CONNECT_TIMEOUT_MS
|
||||
#define NET_CONNECT_TIMEOUT_MS 5000 /* 连接超时 */
|
||||
#endif
|
||||
|
||||
#ifndef NET_ACCEPT_TIMEOUT_MS
|
||||
#define NET_ACCEPT_TIMEOUT_MS 0 /* 接受连接超时(0=无限等待) */
|
||||
#endif
|
||||
|
||||
#ifndef NET_SEND_TIMEOUT_MS
|
||||
#define NET_SEND_TIMEOUT_MS 1000 /* 发送超时 */
|
||||
#endif
|
||||
|
||||
#ifndef NET_RECV_TIMEOUT_MS
|
||||
#define NET_RECV_TIMEOUT_MS 30000 /* 接收超时(30秒,0=无限等待) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TCP KeepAlive 配置
|
||||
*/
|
||||
#ifndef NET_KEEPALIVE_IDLE_MS
|
||||
#define NET_KEEPALIVE_IDLE_MS 7200000 /* KeepAlive 空闲时间(默认2小时) */
|
||||
#endif
|
||||
|
||||
#ifndef NET_KEEPALIVE_INTVL_MS
|
||||
#define NET_KEEPALIVE_INTVL_MS 75000 /* KeepAlive 探测间隔(默认75秒) */
|
||||
#endif
|
||||
|
||||
#ifndef NET_KEEPALIVE_CNT
|
||||
#define NET_KEEPALIVE_CNT 9 /* KeepAlive 探测次数(默认9次) */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* select/poll 最大 fd 数量
|
||||
*/
|
||||
#ifndef NET_SELECT_MAX_FDS
|
||||
#define NET_SELECT_MAX_FDS NET_MAX_SOCKETS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 轮询延时配置(毫秒)
|
||||
*/
|
||||
#ifndef NET_POLL_DELAY_MS
|
||||
#define NET_POLL_DELAY_MS 1 /* 非阻塞轮询延时 */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 消息队列配置
|
||||
*/
|
||||
#ifndef NET_MSG_QUEUE_LENGTH
|
||||
#define NET_MSG_QUEUE_LENGTH 8 /* 消息队列深度 */
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NET_CONFIG_H */
|
||||
320
Drivers/BSP/NET/net_select.c
Normal file
320
Drivers/BSP/NET/net_select.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 模块名称:Network Select Implementation
|
||||
* 模块功能:I/O 多路复用实现,包括 select 和 poll 两种模式,用于同时监控多个 Socket 的读写事件
|
||||
* 适用平台:STM32F407ZGTx + CH395F 以太网芯片
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
* 2026-07-18 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "cmsis_os.h" /* CMSIS-RTOS2 API: osDelay() */
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include "net_select.h"
|
||||
#include "net_socket.h"
|
||||
#include "ch395f.h"
|
||||
#include "main.h"
|
||||
|
||||
/*
|
||||
* 私有函数声明区
|
||||
*/
|
||||
|
||||
static int check_socket_readable(int sockfd);
|
||||
static int check_socket_writable(int sockfd);
|
||||
static int check_socket_error(int sockfd);
|
||||
|
||||
/*
|
||||
* 公共函数实现区
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:I/O 多路复用(select 模式),等待多个 Socket 的读写事件就绪
|
||||
* 入口参数:nfds - 最大文件描述符数 +1 int > 0
|
||||
* readfds - 读事件集合指针 net_fd_set *(可为 NULL)
|
||||
* writefds - 写事件集合指针 net_fd_set *(可为 NULL)
|
||||
* exceptfds- 异常事件集合指针 net_fd_set *(可为 NULL)
|
||||
* timeout - 超时时间结构体指针 net_timeval *(NULL=无限等待)
|
||||
* 出口参数:readfds/writefds/exceptfds - 仅保留就绪的 Socket 位 net_fd_set *
|
||||
* 返回值:就绪的 Socket 数量(>= 0);-1 表示参数无效 int
|
||||
* 限定条件:net_init() + MX_FREERTOS_Init() 已执行;可在任意任务上下文调用
|
||||
* 函数说明:1. 轮询 Socket 控制块(RAM)中的读/写/异常状态,判断事件是否就绪
|
||||
* 2. 不在此直接访问 CH395F SPI——所有 ch395f 事务统一由 netTask 的 net_poll()
|
||||
* 每 10ms 串行执行并刷新控制块状态(send_ready / readable),本函数仅做
|
||||
* RAM 读取,避免与应用任务发起的 SPI 访问竞争(见 Trap 16)
|
||||
* 3. 阻塞等待直到有事件就绪或超时
|
||||
* 4. 通过 osDelay(1) 让出 CPU,平衡实时性与 CPU 利用率(状态刷新延迟 <=10ms)
|
||||
*/
|
||||
int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
|
||||
net_fd_set *exceptfds, net_timeval *timeout) {
|
||||
uint32_t start_tick;
|
||||
uint32_t timeout_ms = 0;
|
||||
net_fd_set result_read = {0};
|
||||
net_fd_set result_write = {0};
|
||||
net_fd_set result_except = {0};
|
||||
int ready_count = 0;
|
||||
int i;
|
||||
|
||||
/* 检查输入参数合法性 */
|
||||
if ((0 >= nfds) || (NET_MAX_SOCKETS < nfds)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 计算超时时间(毫秒) */
|
||||
if (NULL != timeout) {
|
||||
timeout_ms = (uint32_t)(timeout->tv_sec * 1000) +
|
||||
(uint32_t)(timeout->tv_usec / 1000);
|
||||
}
|
||||
|
||||
start_tick = HAL_GetTick();
|
||||
|
||||
while (1) {
|
||||
/* 仅读取 netTask 的 net_poll() 已刷新的控制块状态(无 CH395F SPI 访问) */
|
||||
ready_count = 0;
|
||||
|
||||
/* 检查哪个 Socket 有事件 */
|
||||
for (i = 0; i < nfds && i < NET_MAX_SOCKETS; i++) {
|
||||
net_sock_t *p_sock = net_get_sock(i);
|
||||
|
||||
if (NULL == p_sock) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 检查可读事件 */
|
||||
if ((NULL != readfds) && NET_FD_ISSET(i, readfds)) {
|
||||
if (check_socket_readable(i)) {
|
||||
NET_FD_SET(i, &result_read);
|
||||
ready_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 检查可写事件 */
|
||||
if ((NULL != writefds) && NET_FD_ISSET(i, writefds)) {
|
||||
if (check_socket_writable(i)) {
|
||||
NET_FD_SET(i, &result_write);
|
||||
ready_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 检查异常事件 */
|
||||
if ((NULL != exceptfds) && NET_FD_ISSET(i, exceptfds)) {
|
||||
if (check_socket_error(i)) {
|
||||
NET_FD_SET(i, &result_except);
|
||||
ready_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 有事件就绪,返回 */
|
||||
if (0 < ready_count) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* 超时检测 */
|
||||
if (NULL != timeout) {
|
||||
if ((0 < timeout_ms) &&
|
||||
((HAL_GetTick() - start_tick) >= timeout_ms)) {
|
||||
break; /* 超时返回 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/* 让出 CPU:FreeRTOS osDelay() 会正确进入休眠,允许其他任务运行 */
|
||||
osDelay(1); /* 1ms 周期轮询,平衡实时性和 CPU 利用率 */
|
||||
}
|
||||
|
||||
/* 复制结果集合 */
|
||||
if (NULL != readfds) {
|
||||
readfds->fd_bits = result_read.fd_bits;
|
||||
}
|
||||
if (NULL != writefds) {
|
||||
writefds->fd_bits = result_write.fd_bits;
|
||||
}
|
||||
if (NULL != exceptfds) {
|
||||
exceptfds->fd_bits = result_except.fd_bits;
|
||||
}
|
||||
|
||||
return ready_count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:I/O 多路复用(poll 模式),等待多个 Socket 的读写事件就绪
|
||||
* 入口参数:fds - pollfd 数组指针 net_pollfd *
|
||||
* nfds - 数组元素数量 int > 0
|
||||
* timeout - 超时时间(毫秒) int >= 0(-1=无限等待,但本实现按 >=0 处理)
|
||||
* 出口参数:fds[i].revents - 填充实际发生的事件掩码 short
|
||||
* 返回值:就绪的 fd 数量(>= 0);-1 表示参数无效 int
|
||||
* 限定条件:net_init() + MX_FREERTOS_Init() 已执行;可在任意任务上下文调用
|
||||
* 函数说明:1. 轮询 Socket 控制块(RAM)中的读/写/错误/挂起状态,判断事件是否就绪
|
||||
* 2. 不在此直接访问 CH395F SPI——状态由 netTask 的 net_poll() 每 10ms 刷新
|
||||
* 3. 阻塞等待直到有事件就绪或超时
|
||||
* 4. 通过 osDelay(1) 让出 CPU,平衡实时性与 CPU 利用率(状态刷新延迟 <=10ms)
|
||||
*/
|
||||
int net_poll_events(net_pollfd *fds, int nfds, int timeout) {
|
||||
uint32_t start_tick;
|
||||
int ready = 0;
|
||||
int i;
|
||||
|
||||
if ((NULL == fds) || (0 >= nfds)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
start_tick = HAL_GetTick();
|
||||
|
||||
while (1) {
|
||||
/* 仅读取 netTask 的 net_poll() 已刷新的控制块状态(无 CH395F SPI 访问) */
|
||||
ready = 0;
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
fds[i].revents = 0;
|
||||
|
||||
/* 检查 fd 有效性 */
|
||||
if ((0 > fds[i].fd) || (NET_MAX_SOCKETS <= fds[i].fd)) {
|
||||
fds[i].revents |= NET_POLLNVAL;
|
||||
ready++;
|
||||
continue;
|
||||
}
|
||||
|
||||
net_sock_t *p_sock = net_get_sock(fds[i].fd);
|
||||
|
||||
if (NULL == p_sock) {
|
||||
fds[i].revents |= NET_POLLNVAL;
|
||||
ready++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 检查可读 */
|
||||
if (fds[i].events & NET_POLLIN) {
|
||||
if (check_socket_readable(fds[i].fd)) {
|
||||
fds[i].revents |= NET_POLLIN;
|
||||
ready++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 检查可写 */
|
||||
if (fds[i].events & NET_POLLOUT) {
|
||||
if (check_socket_writable(fds[i].fd)) {
|
||||
fds[i].revents |= NET_POLLOUT;
|
||||
ready++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 检查错误 */
|
||||
if (check_socket_error(fds[i].fd)) {
|
||||
fds[i].revents |= NET_POLLERR;
|
||||
ready++;
|
||||
}
|
||||
|
||||
/* 检查挂起(连接断开) */
|
||||
if (NET_SOCK_STATE_CLOSED == p_sock->state) {
|
||||
fds[i].revents |= NET_POLLHUP;
|
||||
ready++;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < ready) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* 超时检测 */
|
||||
if (0 <= timeout) {
|
||||
if ((HAL_GetTick() - start_tick) >= (uint32_t)timeout) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 让出 CPU:FreeRTOS osDelay() 会正确进入休眠,允许其他任务运行 */
|
||||
osDelay(1); /* 1ms 周期轮询,平衡实时性和 CPU 利用率 */
|
||||
}
|
||||
|
||||
return ready;
|
||||
}
|
||||
|
||||
/*
|
||||
* 私有函数实现区
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:检查指定 Socket 当前是否可读
|
||||
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 可读(监听态有新连接,或已连接态接收缓冲非空);0 - 不可读或描述符无效 int
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:1. 监听态 Socket 视为可读(存在可 accept 的新连接)
|
||||
* 2. 已建立连接且接收缓冲区长度大于 0 时视为可读
|
||||
*/
|
||||
static int check_socket_readable(int sockfd) {
|
||||
net_sock_t *p_sock;
|
||||
|
||||
p_sock = net_get_sock(sockfd);
|
||||
if (NULL == p_sock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 监听 Socket 有新连接时可读 */
|
||||
if (NET_SOCK_STATE_LISTENING == p_sock->state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 已建立连接或 UDP 等有数据可读:readable 由 net_poll 在 RECV_OK 时置位、
|
||||
* net_recv_locked 读空后清零。避免应用任务直接访问 CH395F SPI(见 Trap 16) */
|
||||
if (p_sock->readable) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:检查指定 Socket 当前是否可写
|
||||
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 可写(已连接且发送缓冲空闲);0 - 不可写或描述符无效 int
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:1. 仅已建立连接且 send_ready 标志置位的 Socket 视为可写
|
||||
*/
|
||||
static int check_socket_writable(int sockfd) {
|
||||
net_sock_t *p_sock;
|
||||
|
||||
p_sock = net_get_sock(sockfd);
|
||||
if (NULL == p_sock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 已建立连接的 Socket 发送缓冲区空闲时可写 */
|
||||
if (NET_SOCK_STATE_ESTABLISHED == p_sock->state) {
|
||||
if (p_sock->send_ready) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:检查指定 Socket 是否存在错误状态
|
||||
* 入口参数:sockfd - 待检查的 Socket 描述符 int 0 - NET_MAX_SOCKETS-1
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 有错误(含描述符越界或 Socket 未分配);0 - 无错误 int
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 描述符越界视为错误
|
||||
* 2. 未分配(net_get_sock 返回 NULL)的 Socket 视为错误
|
||||
*/
|
||||
static int check_socket_error(int sockfd) {
|
||||
net_sock_t *p_sock;
|
||||
|
||||
/* 无效 fd 视为错误 */
|
||||
if ((0 > sockfd) || (NET_MAX_SOCKETS <= sockfd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
p_sock = net_get_sock(sockfd);
|
||||
if (NULL == p_sock) {
|
||||
return 1; /* 未使用的 Socket 视为错误 */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
114
Drivers/BSP/NET/net_select.h
Normal file
114
Drivers/BSP/NET/net_select.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 模块名称:Network Select API
|
||||
* 模块功能:I/O 多路复用接口,支持 select 和 poll 模式,
|
||||
* 用于同时监控多个 Socket 的读写事件
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
*/
|
||||
|
||||
#ifndef __NET_SELECT_H
|
||||
#define __NET_SELECT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "net_types.h"
|
||||
#include "net_config.h"
|
||||
|
||||
/*
|
||||
* 文件描述符集合
|
||||
* 每个位对应一个 socket 描述符 (0~7)
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t fd_bits; /* 位掩码 */
|
||||
} net_fd_set;
|
||||
|
||||
/*
|
||||
* 时间结构体
|
||||
*/
|
||||
typedef struct {
|
||||
long tv_sec; /* 秒 */
|
||||
long tv_usec; /* 微秒 */
|
||||
} net_timeval;
|
||||
|
||||
/*
|
||||
* poll 事件结构体
|
||||
*/
|
||||
typedef struct {
|
||||
int fd; /* socket 描述符 */
|
||||
short events; /* 请求的事件 */
|
||||
short revents; /* 实际发生的事件 */
|
||||
} net_pollfd;
|
||||
|
||||
/*
|
||||
* poll 事件掩码
|
||||
*/
|
||||
#define NET_POLLIN 0x0001 /* 可读 */
|
||||
#define NET_POLLOUT 0x0004 /* 可写 */
|
||||
#define NET_POLLERR 0x0008 /* 错误 */
|
||||
#define NET_POLLHUP 0x0010 /* 挂起 */
|
||||
#define NET_POLLNVAL 0x0020 /* 无效请求 */
|
||||
|
||||
/*
|
||||
* fd_set 操作宏
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:清空 fd_set
|
||||
*/
|
||||
#define NET_FD_ZERO(fdset) ((fdset)->fd_bits = 0)
|
||||
|
||||
/*
|
||||
* 函数功能:将 fd 加入 fd_set
|
||||
*/
|
||||
#define NET_FD_SET(fd, fdset) ((fdset)->fd_bits |= (1U << (fd)))
|
||||
|
||||
/*
|
||||
* 函数功能:将 fd 从 fd_set 移除
|
||||
*/
|
||||
#define NET_FD_CLR(fd, fdset) ((fdset)->fd_bits &= ~(1U << (fd)))
|
||||
|
||||
/*
|
||||
* 函数功能:检查 fd 是否在 fd_set 中
|
||||
*/
|
||||
#define NET_FD_ISSET(fd, fdset) (((fdset)->fd_bits & (1U << (fd))) != 0)
|
||||
|
||||
/*
|
||||
* I/O 多路复用 API
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:I/O 多路复用(select 模式)
|
||||
* 入口参数:nfds - 最大 fd + 1(通常为 NET_MAX_SOCKETS)
|
||||
* readfds - 可读事件集合,NULL 表示不关心
|
||||
* writefds - 可写事件集合,NULL 表示不关心
|
||||
* exceptfds - 异常事件集合,NULL 表示不关心
|
||||
* timeout - 超时时间,NULL 表示无限等待
|
||||
* 返回值:就绪的 fd 数量,0=超时,-1=错误
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:此函数会调用 net_poll() 轮询所有 Socket,
|
||||
* 并检查哪些 Socket 有指定的事件发生。
|
||||
*/
|
||||
int net_select(int nfds, net_fd_set *readfds, net_fd_set *writefds,
|
||||
net_fd_set *exceptfds, net_timeval *timeout);
|
||||
|
||||
/*
|
||||
* 函数功能:I/O 多路复用(poll 模式)
|
||||
* 入口参数:fds - net_pollfd 数组
|
||||
* nfds - 数组元素数量
|
||||
* timeout - 超时时间(毫秒),-1 表示无限等待
|
||||
* 返回值:就绪的 fd 数量,0=超时,-1=错误
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:与 select 类似,但使用 pollfd 结构体数组,
|
||||
* 更灵活,支持更多事件类型。
|
||||
*/
|
||||
int net_poll_events(net_pollfd *fds, int nfds, int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NET_SELECT_H */
|
||||
1876
Drivers/BSP/NET/net_socket.c
Normal file
1876
Drivers/BSP/NET/net_socket.c
Normal file
File diff suppressed because it is too large
Load Diff
343
Drivers/BSP/NET/net_socket.h
Normal file
343
Drivers/BSP/NET/net_socket.h
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 模块名称:Network Socket API
|
||||
* 模块功能:BSD Socket API 兼容接口,提供 TCP/UDP 网络通信功能,
|
||||
* 底层基于 CH395F 以太网协议栈芯片
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
*/
|
||||
|
||||
#ifndef __NET_SOCKET_H
|
||||
#define __NET_SOCKET_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "net_types.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
/*
|
||||
* 消息队列类型定义
|
||||
*/
|
||||
typedef enum {
|
||||
NET_MSG_SEND,
|
||||
NET_MSG_RECV,
|
||||
NET_MSG_CLOSE,
|
||||
NET_MSG_CONNECT,
|
||||
NET_MSG_LISTEN,
|
||||
NET_MSG_ACCEPT,
|
||||
} net_msg_type_t;
|
||||
|
||||
typedef struct {
|
||||
net_msg_type_t type; /* 操作类型 */
|
||||
int sockfd; /* Socket 描述符 */
|
||||
void *buf; /* 数据缓冲区(send/recv) */
|
||||
int len; /* 数据长度/缓冲区大小 */
|
||||
int flags; /* 接收标志 */
|
||||
TaskHandle_t caller; /* 调用方任务句柄 */
|
||||
int result; /* 操作结果 */
|
||||
/* 连接参数 */
|
||||
struct net_sockaddr_in addr; /* 连接地址 */
|
||||
int addrlen; /* 地址长度 */
|
||||
} net_msg_t;
|
||||
|
||||
/*
|
||||
* 网络初始化接口
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:网络子系统初始化(配置 CH395F + 启用多连接模式)
|
||||
* 入口参数:ip - 本地 IP 地址字符串 "192.168.1.100",NULL 使用 DHCP
|
||||
* mask - 子网掩码字符串 "255.255.255.0",NULL 使用默认
|
||||
* gateway - 网关地址字符串 "192.168.1.1",NULL 使用默认
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:SPI2 已正确初始化
|
||||
* 函数说明:必须在所有网络操作之前调用
|
||||
*/
|
||||
int net_init(const char *ip, const char *mask, const char *gateway);
|
||||
|
||||
/*
|
||||
* 函数功能:轮询所有 Socket 状态(必须在主循环中调用)
|
||||
* 返回值:有事件发生的 socket 数量
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:此函数处理所有 Socket 的中断事件,更新状态,
|
||||
* 并触发事件回调。在非阻塞模式下必须定期调用。
|
||||
*/
|
||||
int net_poll(void);
|
||||
|
||||
/*
|
||||
* 核心 Socket API
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:创建 socket
|
||||
* 入口参数:domain - 地址族,仅支持 AF_INET
|
||||
* type - SOCK_STREAM(TCP) 或 SOCK_DGRAM(UDP)
|
||||
* 返回值:socket 描述符 (0~7),失败返回 -1
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:自动分配空闲 Socket 控制块
|
||||
*/
|
||||
int net_socket(int domain, int type);
|
||||
|
||||
/*
|
||||
* 函数功能:绑定本地地址和端口
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* addr - 本地地址结构体指针
|
||||
* addrlen - 地址结构体长度
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:socket 已创建且未绑定
|
||||
* 函数说明:设置 CH395F 源端口
|
||||
*/
|
||||
int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen);
|
||||
|
||||
/*
|
||||
* 函数功能:TCP Server 监听
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:socket 已绑定
|
||||
* 函数说明:内部调用 ch395f_tcp_listen,监听 Socket 自身即为数据通道
|
||||
*/
|
||||
int net_listen(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:TCP Server 接受连接
|
||||
* 入口参数:sockfd - 监听 socket 描述符
|
||||
* addr - 输出:客户端地址
|
||||
* addrlen - 输入输出:地址长度
|
||||
* 返回值:新 socket 描述符,失败返回 -1
|
||||
* 限定条件:socket 正在监听
|
||||
* 函数说明:在非阻塞模式下,如果没有新连接,返回 -1 并设置
|
||||
* net_errno 为 NET_ERR_WOULDBLOCK
|
||||
*/
|
||||
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||
|
||||
/*
|
||||
* 函数功能:TCP Client 发起连接
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* addr - 服务器地址
|
||||
* addrlen - 地址长度
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:socket 已创建
|
||||
* 函数说明:连接过程是异步的,需要调用 net_poll() 等待连接完成,
|
||||
* 或使用 select() 等待可写事件
|
||||
*/
|
||||
int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen);
|
||||
|
||||
/*
|
||||
* 函数功能:发送数据(TCP)
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* buf - 数据缓冲区
|
||||
* len - 数据长度
|
||||
* flags - 通常为 0
|
||||
* 返回值:实际发送字节数,失败返回 -1
|
||||
* 限定条件:TCP 连接已建立,指针非空
|
||||
* 函数说明:阻塞等待发送缓冲区空闲,拷贝数据后触发发送
|
||||
*/
|
||||
int net_send(int sockfd, const void *buf, int len, int flags);
|
||||
|
||||
/*
|
||||
* 函数功能:接收数据(TCP)
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* buf - 接收缓冲区
|
||||
* len - 缓冲区大小
|
||||
* flags - 通常为 0,可设置 NET_MSG_DONTWAIT 非阻塞
|
||||
* 返回值:实际接收字节数,0=对端关闭,-1=错误
|
||||
* 限定条件:TCP 连接已建立
|
||||
* 函数说明:在阻塞模式下,如果没有数据,函数会等待;
|
||||
* 在非阻塞模式下,如果没有数据,返回 -1 并设置
|
||||
* net_errno 为 NET_ERR_WOULDBLOCK
|
||||
*/
|
||||
int net_recv(int sockfd, void *buf, int len, int flags);
|
||||
|
||||
/*
|
||||
* 函数功能:发送 UDP 数据
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* buf - 数据缓冲区
|
||||
* len - 数据长度
|
||||
* flags - 通常为 0
|
||||
* dest_addr - 目标地址
|
||||
* addrlen - 地址长度
|
||||
* 返回值:实际发送字节数,失败返回 -1
|
||||
* 限定条件:socket 已创建,指针非空
|
||||
* 函数说明:设置目标地址后调用 CH395F 发送
|
||||
*/
|
||||
int net_sendto(int sockfd, const void *buf, int len, int flags,
|
||||
const struct net_sockaddr *dest_addr, int addrlen);
|
||||
|
||||
/*
|
||||
* 函数功能:接收 UDP 数据
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* buf - 接收缓冲区
|
||||
* len - 缓冲区大小
|
||||
* flags - 通常为 0
|
||||
* src_addr - 输出:发送方地址
|
||||
* addrlen - 输入输出:地址长度
|
||||
* 返回值:实际接收字节数,失败返回 -1
|
||||
* 限定条件:socket 已创建,指针非空
|
||||
* 函数说明:读取 CH395F 接收缓冲区,获取对端地址
|
||||
*/
|
||||
int net_recvfrom(int sockfd, void *buf, int len, int flags,
|
||||
struct net_sockaddr *src_addr, int *addrlen);
|
||||
|
||||
/*
|
||||
* 函数功能:关闭 socket
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:socket 已打开
|
||||
* 函数说明:释放 Socket 控制块,TCP 先断开连接
|
||||
*/
|
||||
int net_close(int sockfd);
|
||||
|
||||
/*
|
||||
* 事件回调 API
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:注册事件回调
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* cb - 回调函数
|
||||
* arg - 用户参数
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:socket 已创建
|
||||
* 函数说明:回调在 net_poll 上下文中触发
|
||||
*/
|
||||
int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg);
|
||||
|
||||
/*
|
||||
* 辅助函数
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:获取 socket 最后错误码
|
||||
* 返回值:错误码(NET_ERR_xxx)
|
||||
*/
|
||||
int net_get_errno(void);
|
||||
|
||||
/*
|
||||
* 函数功能:将 IP 地址字符串转为网络字节序
|
||||
* 入口参数:cp - 点分十进制字符串 "192.168.1.100"
|
||||
* 返回值:网络字节序 IP,失败返回 0
|
||||
* 限定条件:cp 指针非空
|
||||
* 函数说明:不支持域名解析
|
||||
*/
|
||||
uint32_t net_inet_addr(const char *cp);
|
||||
|
||||
/*
|
||||
* 函数功能:将网络字节序 IP 转为字符串
|
||||
* 入口参数:addr - 网络字节序 IP
|
||||
* buf - 输出缓冲区(至少 16 字节)
|
||||
* 返回值:buf 指针
|
||||
* 限定条件:buf 指针非空
|
||||
*/
|
||||
char *net_inet_ntoa(uint32_t addr, char *buf);
|
||||
|
||||
/*
|
||||
* 函数功能:端口字节序转换(主机序 -> 网络序)
|
||||
* 入口参数:hostshort - 主机序端口
|
||||
* 返回值:网络序端口
|
||||
* 限定条件:无
|
||||
* 函数说明:小端 → 大端
|
||||
*/
|
||||
uint16_t net_htons(uint16_t hostshort);
|
||||
|
||||
/*
|
||||
* 函数功能:端口字节序转换(网络序 -> 主机序)
|
||||
* 入口参数:netshort - 网络序端口
|
||||
* 返回值:主机序端口
|
||||
* 限定条件:无
|
||||
* 函数说明:大端 → 小端
|
||||
*/
|
||||
uint16_t net_ntohs(uint16_t netshort);
|
||||
|
||||
/*
|
||||
* 函数功能:IP 地址字节序转换(主机序 -> 网络序)
|
||||
* 入口参数:hostlong - 主机序 IP
|
||||
* 返回值:网络序 IP
|
||||
* 限定条件:无
|
||||
* 函数说明:小端 → 大端
|
||||
*/
|
||||
uint32_t net_htonl(uint32_t hostlong);
|
||||
|
||||
/*
|
||||
* 函数功能:IP 地址字节序转换(网络序 -> 主机序)
|
||||
* 入口参数:netlong - 网络序 IP
|
||||
* 返回值:主机序 IP
|
||||
* 限定条件:无
|
||||
* 函数说明:大端 → 小端
|
||||
*/
|
||||
uint32_t net_ntohl(uint32_t netlong);
|
||||
|
||||
/*
|
||||
* 函数功能:处理消息队列中的请求(必须在 netTask 主循环中调用)
|
||||
* 限定条件:net_init() 已调用
|
||||
* 函数说明:处理其他任务通过 net_send/net_recv 等接口发送的请求,
|
||||
* 在 netTask 上下文中串行化执行所有 CH395F 操作
|
||||
*/
|
||||
void net_process_messages(void);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 Socket 控制块指针(内部使用)
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* 返回值:Socket 控制块指针,失败返回 NULL
|
||||
*/
|
||||
net_sock_t *net_get_sock(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:获取 Socket 本地地址(IP + Port)
|
||||
* 入口参数:sockfd - socket 描述符
|
||||
* addr - 输出:本地地址
|
||||
* addrlen - 输入输出:地址结构体长度
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:socket 已创建或已绑定
|
||||
*/
|
||||
int net_getsockname(int sockfd, struct net_sockaddr *addr, int *addrlen);
|
||||
|
||||
/*
|
||||
* 内部 API(需在 netTask 上下文中调用,绕过消息队列)
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:发送数据(内部版本,直接操作,不经过消息队列)
|
||||
* 入口参数:p_sock - Socket 控制块指针
|
||||
* buf - 数据缓冲区
|
||||
* len - 数据长度
|
||||
* 返回值:实际发送字节数,失败返回 -1
|
||||
* 限定条件:必须在 netTask 上下文中调用
|
||||
*/
|
||||
int net_send_sock(net_sock_t *p_sock, const void *buf, int len);
|
||||
|
||||
/*
|
||||
* 函数功能:接收数据(内部版本,直接操作,不经过消息队列)
|
||||
* 入口参数:p_sock - Socket 控制块指针
|
||||
* buf - 接收缓冲区
|
||||
* len - 缓冲区大小
|
||||
* flags - 通常为 0,可设置 NET_MSG_DONTWAIT
|
||||
* 返回值:实际接收字节数,0=对端关闭,-1=错误
|
||||
* 限定条件:必须在 netTask 上下文中调用
|
||||
*/
|
||||
int net_recv_sock(net_sock_t *p_sock, void *buf, int len, int flags);
|
||||
|
||||
/*
|
||||
* 函数功能:关闭 socket(内部版本,直接操作,不经过消息队列)
|
||||
* 入口参数:p_sock - Socket 控制块指针
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:必须在 netTask 上下文中调用
|
||||
*/
|
||||
int net_close_sock(net_sock_t *p_sock);
|
||||
|
||||
/*
|
||||
* 函数功能:启动 TCP Server 监听(内部版本,直接操作,不经过消息队列)
|
||||
* 入口参数:sockfd - Socket 描述符
|
||||
* 返回值:0 成功,-1 失败
|
||||
* 限定条件:必须在 netTask 上下文中调用
|
||||
*/
|
||||
int net_listen_locked(int sockfd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NET_SOCKET_H */
|
||||
178
Drivers/BSP/NET/net_types.h
Normal file
178
Drivers/BSP/NET/net_types.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 模块名称:Network Types
|
||||
* 模块功能:网络子系统类型定义,包括地址结构体、Socket状态、
|
||||
* 事件类型、Socket控制块等
|
||||
* 适用平台:STM32F4 系列(CH395F 以太网芯片)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
*/
|
||||
|
||||
#ifndef __NET_TYPES_H
|
||||
#define __NET_TYPES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include "net_config.h"
|
||||
|
||||
/*
|
||||
* 地址族定义
|
||||
*/
|
||||
#define NET_AF_INET 2 /* IPv4 */
|
||||
#define NET_AF_INET6 10 /* IPv6(未支持) */
|
||||
#define NET_AF_UNSPEC 0 /* 未指定 */
|
||||
|
||||
/*
|
||||
* Socket 类型定义
|
||||
*/
|
||||
#define NET_SOCK_STREAM 1 /* TCP */
|
||||
#define NET_SOCK_DGRAM 2 /* UDP */
|
||||
|
||||
/*
|
||||
* 协议号定义
|
||||
*/
|
||||
#define NET_IPPROTO_TCP 6
|
||||
#define NET_IPPROTO_UDP 17
|
||||
#define NET_IPPROTO_ICMP 1
|
||||
|
||||
/*
|
||||
* 特殊地址定义
|
||||
*/
|
||||
#define NET_INADDR_ANY 0 /* 任意地址 */
|
||||
#define NET_INADDR_NONE 0xFFFFFFFF /* 无效地址 */
|
||||
|
||||
/*
|
||||
* 接收标志定义
|
||||
*/
|
||||
#define NET_MSG_DONTWAIT 0x01 /* 非阻塞模式 */
|
||||
|
||||
/*
|
||||
* 网络字节序转换宏
|
||||
*/
|
||||
#ifdef __BIG_ENDIAN
|
||||
#define NET_htonl(x) (x)
|
||||
#define NET_htons(x) (x)
|
||||
#define NET_ntohl(x) (x)
|
||||
#define NET_ntohs(x) (x)
|
||||
#else
|
||||
#define NET_htonl(x) (((uint32_t)(x) << 24) | \
|
||||
(((uint32_t)(x) & 0xFF00) << 8) | \
|
||||
(((uint32_t)(x) & 0xFF0000) >> 8) | \
|
||||
((uint32_t)(x) >> 24))
|
||||
#define NET_htons(x) (((uint16_t)(x) << 8) | ((uint16_t)(x) >> 8))
|
||||
#define NET_ntohl(x) NET_htonl(x)
|
||||
#define NET_ntohs(x) NET_htons(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 套接字地址结构体
|
||||
*/
|
||||
struct net_sockaddr {
|
||||
uint16_t sa_family; /* 地址族 */
|
||||
char sa_data[14]; /* 地址数据 */
|
||||
};
|
||||
|
||||
/*
|
||||
* IPv4 套接字地址结构体
|
||||
*/
|
||||
struct net_sockaddr_in {
|
||||
uint16_t sin_family; /* 地址族,必须为 AF_INET */
|
||||
uint16_t sin_port; /* 端口号(网络字节序) */
|
||||
struct {
|
||||
uint32_t s_addr; /* IP 地址(网络字节序) */
|
||||
} sin_addr;
|
||||
char sin_zero[8]; /* 填充字节 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Socket 错误码定义
|
||||
*/
|
||||
#define NET_OK 0 /* 成功 */
|
||||
#define NET_ERR -1 /* 通用错误 */
|
||||
#define NET_ERR_BADF -2 /* 无效 socket 描述符 */
|
||||
#define NET_ERR_INVAL -3 /* 无效参数 */
|
||||
#define NET_ERR_NOMEM -4 /* 内存不足 */
|
||||
#define NET_ERR_NOTCONN -5 /* 未连接 */
|
||||
#define NET_ERR_ISCONN -6 /* 已连接 */
|
||||
#define NET_ERR_WOULDBLOCK -7 /* 非阻塞模式下操作未完成 */
|
||||
#define NET_ERR_CONNRESET -8 /* 连接被重置 */
|
||||
#define NET_ERR_TIMEDOUT -9 /* 操作超时 */
|
||||
#define NET_ERR_NOSPACE -10 /* 发送缓冲区无空间 */
|
||||
#define NET_ERR_BUSY -11 /* 操作未完成(CH395F BUSY) */
|
||||
|
||||
/*
|
||||
* Socket 状态枚举
|
||||
*/
|
||||
typedef enum {
|
||||
NET_SOCK_STATE_CLOSED = 0, /* 未使用 */
|
||||
NET_SOCK_STATE_CREATED, /* 已创建,未绑定 */
|
||||
NET_SOCK_STATE_BOUND, /* 已绑定 */
|
||||
NET_SOCK_STATE_LISTENING, /* TCP Server 监听中(仅监听 Socket) */
|
||||
NET_SOCK_STATE_CONNECTING, /* TCP Client 连接中 */
|
||||
NET_SOCK_STATE_ESTABLISHED, /* TCP 已连接 */
|
||||
NET_SOCK_STATE_UDP_OPEN, /* UDP 已打开 */
|
||||
NET_SOCK_STATE_TCP_CLIENT_RECONNECTING, /* TCP Client 重连中 */
|
||||
} net_sock_state_t;
|
||||
|
||||
/*
|
||||
* 网络事件类型枚举
|
||||
*/
|
||||
typedef enum {
|
||||
NET_EVENT_CONNECTED = 0, /* 连接建立 */
|
||||
NET_EVENT_DISCONNECTED, /* 连接断开 */
|
||||
NET_EVENT_DATA_RECEIVED, /* 收到数据 */
|
||||
NET_EVENT_SEND_COMPLETE, /* 发送完成 */
|
||||
NET_EVENT_TIMEOUT, /* 操作超时 */
|
||||
NET_EVENT_ERROR, /* 错误发生 */
|
||||
} net_event_t;
|
||||
|
||||
/*
|
||||
* 事件回调函数类型定义
|
||||
* 参数:sockfd - socket 描述符
|
||||
* event - 事件类型
|
||||
* arg - 用户参数
|
||||
*/
|
||||
typedef void (*net_event_cb_t)(int sockfd, net_event_t event, void *arg);
|
||||
|
||||
/*
|
||||
* Socket 控制块结构体
|
||||
* 每个 socket 对应一个控制块,管理其状态和参数
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t ch395_sock; /* CH395F 物理 Socket 索引 (0~7) */
|
||||
net_sock_state_t state; /* Socket 状态 */
|
||||
uint8_t type; /* SOCK_STREAM / SOCK_DGRAM */
|
||||
uint8_t in_use; /* 是否在用标志 */
|
||||
uint16_t local_port; /* 本地端口(主机序) */
|
||||
uint16_t remote_port; /* 远端端口(主机序) */
|
||||
uint32_t remote_ip; /* 远端 IP(网络字节序) */
|
||||
uint8_t remote_ip_arr[4]; /* 远端 IP 数组(大端/网络字节序) */
|
||||
|
||||
/* 事件回调(可选) */
|
||||
net_event_cb_t event_cb;
|
||||
void *event_arg;
|
||||
|
||||
/* 发送就绪标志 */
|
||||
uint8_t send_ready;
|
||||
|
||||
/* 接收可读标志:net_poll 检测到 RECV_OK 时置 1,net_recv_locked 读空后清零。
|
||||
* net_select 据此判断可读性,从而无需在应用/测试任务上下文直接访问 CH395F SPI
|
||||
* (所有 ch395f 事务统一由 netTask 串行执行,避免与 net_poll 竞争,见 Trap 16)。 */
|
||||
uint8_t readable;
|
||||
|
||||
/* 单连接模式自动重监听:连接断开后由 net 层自动回到 LISTEN(BSD 风格监听常驻) */
|
||||
uint8_t auto_relisten;
|
||||
|
||||
/* 统计信息 */
|
||||
uint32_t recv_bytes; /* 累计接收字节数 */
|
||||
uint32_t send_bytes; /* 累计发送字节数 */
|
||||
} net_sock_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __NET_TYPES_H */
|
||||
152
Drivers/BSP/RS485/rs485.c
Normal file
152
Drivers/BSP/RS485/rs485.c
Normal 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
110
Drivers/BSP/RS485/rs485.h
Normal 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
497
Drivers/BSP/SD2506/sd2506.c
Normal 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() 已调用成功
|
||||
* 函数说明:读取寄存器 0x16,bit7 为符号位
|
||||
* return: 0=OK, -2=I2C error
|
||||
* note: reads register 0x16
|
||||
*/
|
||||
int sd2506_get_temperature(int8_t *p_temp) {
|
||||
int ret = 0;
|
||||
uint8_t val = 0;
|
||||
|
||||
if (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
296
Drivers/BSP/SD2506/sd2506.h
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* 模块名称:SD2506API-G RTC 实时时钟驱动
|
||||
* 模块功能:SD2506API-G 高精度温补实时时钟模块 I2C 驱动,提供时间读写、
|
||||
* 温度读取、电池电压检测、ID 读取等功能
|
||||
* 适用平台:STM32F407ZGT6,I2C1 接口 (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 */
|
||||
314
Drivers/BSP/TPAFE5160/tpafe5160.c
Normal file
314
Drivers/BSP/TPAFE5160/tpafe5160.c
Normal 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;
|
||||
}
|
||||
233
Drivers/BSP/TPAFE5160/tpafe5160.h
Normal file
233
Drivers/BSP/TPAFE5160/tpafe5160.h
Normal 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
46
Drivers/BSP/dbg_cfg.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 模块名称:Debug Output Configuration
|
||||
* 模块功能:全局调试输出配置,定义各调试级别的编译开关
|
||||
* 参考 FlashDB fdb_cfg.h 设计模式
|
||||
* 使用方法:
|
||||
* - DBG_ENABLE 总开关,定义后启用 DBG_ERROR / DBG_INFO
|
||||
* - DBG_DEBUG_ENABLE 子开关,同时定义 DBG_ENABLE 后启用 DBG_DEBUG(含 __func__:__LINE__)
|
||||
* - 两者都不定义:所有 DBG_* 宏为空操作(release 版本)
|
||||
*
|
||||
* 用户可在本文件中直接控制,也可在编译器预定义中覆盖
|
||||
* 适用平台:所有 BSP 驱动
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
* 2026-07-18 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __DBG_CFG_H
|
||||
#define __DBG_CFG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 总开关:取消注释启用所有调试输出
|
||||
*/
|
||||
#define DBG_ENABLE
|
||||
|
||||
/*
|
||||
* 调试级别开关:取消注释额外输出 DBG_DEBUG 级别
|
||||
* 仅在 DBG_ENABLE 定义时生效
|
||||
*/
|
||||
/* #define DBG_DEBUG_ENABLE */
|
||||
|
||||
/*
|
||||
* 系统时间戳开关:取消注释在所有日志行首追加 [YYYY-MM-DD HH:MM:SS] 时间戳
|
||||
* 依赖 sys_clock_init() 已调用(SD2506 RTC 初始化完成)
|
||||
*/
|
||||
#define APP_TIMESTAMP_ENABLE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DBG_CFG_H */
|
||||
138
Drivers/BSP/dbg_log.h
Normal file
138
Drivers/BSP/dbg_log.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 模块名称:Unified Debug Logging
|
||||
* 模块功能:提供统一的调试输出宏,支持 DEBUG / INFO / ERROR 三级日志控制
|
||||
* 参考 FlashDB FDB_DEBUG/FDB_INFO 设计模式
|
||||
* 使用方法:
|
||||
* 1. 在 dbg_cfg.h 中定义 DBG_ENABLE(总开关)和 DBG_DEBUG_ENABLE(DEBUG 级别)
|
||||
* 2. 每个 .c 文件在包含本文件前定义 DBG_TAG,如:
|
||||
* #define DBG_TAG "[CH395]"
|
||||
* #include "dbg_log.h"
|
||||
* 适用平台:所有 BSP 驱动
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-07-18
|
||||
* 修改记录:
|
||||
* 2026-07-18 王建锋 创建初始版本
|
||||
*/
|
||||
|
||||
#ifndef __DBG_LOG_H
|
||||
#define __DBG_LOG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include "dbg_cfg.h"
|
||||
|
||||
#ifdef APP_TIMESTAMP_ENABLE
|
||||
#include "sys_clock.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 总开关:DBG_ENABLE
|
||||
* 定义 → 输出 DBG_ERROR / DBG_INFO;若 DBG_DEBUG_ENABLE 也定义则输出 DBG_DEBUG
|
||||
* 不定义 → 所有输出宏为空操作(release 版本)
|
||||
* 在 net_config.h 或编译器预定义中控制
|
||||
*/
|
||||
#ifdef DBG_ENABLE
|
||||
|
||||
/*
|
||||
* 可重定向的打印函数,用户可在编译选项或 net_config.h 中预定义
|
||||
* #define DBG_PRINT(...) my_printf(__VA_ARGS__)
|
||||
*/
|
||||
#ifndef DBG_PRINT
|
||||
#define DBG_PRINT(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 模块标签默认值,每个 .c 文件应在包含本文件前定义 DBG_TAG
|
||||
* #define DBG_TAG "[CH395]"
|
||||
*/
|
||||
#ifndef DBG_TAG
|
||||
#define DBG_TAG ""
|
||||
#endif
|
||||
|
||||
#ifdef APP_TIMESTAMP_ENABLE
|
||||
/*
|
||||
* 时间戳输出宏 — 在日志行首追加 [YYYY-MM-DD HH:MM:SS] 格式的系统墙钟时间
|
||||
* sys_clock_get_str() 线程安全(内部使用 DWT US 计数,无锁)
|
||||
*/
|
||||
#define DBG_TS() sys_clock_get_str((char[32]){0}, 32)
|
||||
|
||||
/*
|
||||
* DBG_ERROR - 错误输出,受 DBG_ENABLE + APP_TIMESTAMP_ENABLE 控制
|
||||
*/
|
||||
#define DBG_ERROR(fmt, ...) do { \
|
||||
char __ts_buf[32]; \
|
||||
(void)__ts_buf; \
|
||||
DBG_PRINT("[%s]" "[ERR]" DBG_TAG " " fmt "\r\n", \
|
||||
sys_clock_get_str(__ts_buf, sizeof(__ts_buf)), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* DBG_INFO - 信息输出,受 DBG_ENABLE + APP_TIMESTAMP_ENABLE 控制
|
||||
*/
|
||||
#define DBG_INFO(fmt, ...) do { \
|
||||
char __ts_buf[32]; \
|
||||
(void)__ts_buf; \
|
||||
DBG_PRINT("[%s]" DBG_TAG " " fmt "\r\n", \
|
||||
sys_clock_get_str(__ts_buf, sizeof(__ts_buf)), ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* DBG_DEBUG - 调试输出,需 DBG_ENABLE + DBG_DEBUG_ENABLE + APP_TIMESTAMP_ENABLE 同时定义
|
||||
*/
|
||||
#ifdef DBG_DEBUG_ENABLE
|
||||
#define DBG_DEBUG(fmt, ...) do { \
|
||||
char __ts_buf[32]; \
|
||||
(void)__ts_buf; \
|
||||
DBG_PRINT("[%s]" DBG_TAG "(%s:%d) " fmt "\r\n", \
|
||||
sys_clock_get_str(__ts_buf, sizeof(__ts_buf)), __func__, __LINE__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DBG_DEBUG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#else /* APP_TIMESTAMP_ENABLE not defined */
|
||||
|
||||
/*
|
||||
* DBG_ERROR - 错误输出,受 DBG_ENABLE 控制(无时间戳)
|
||||
*/
|
||||
#define DBG_ERROR(fmt, ...) do { \
|
||||
DBG_PRINT("[ERR]" DBG_TAG " " fmt "\r\n", ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* DBG_INFO - 信息输出,受 DBG_ENABLE 控制(无时间戳)
|
||||
*/
|
||||
#define DBG_INFO(fmt, ...) do { \
|
||||
DBG_PRINT(DBG_TAG " " fmt "\r\n", ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* DBG_DEBUG - 调试输出,需 DBG_ENABLE + DBG_DEBUG_ENABLE 同时定义(无时间戳)
|
||||
*/
|
||||
#ifdef DBG_DEBUG_ENABLE
|
||||
#define DBG_DEBUG(fmt, ...) do { \
|
||||
DBG_PRINT(DBG_TAG "(%s:%d) " fmt "\r\n", __func__, __LINE__, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DBG_DEBUG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#endif /* APP_TIMESTAMP_ENABLE */
|
||||
|
||||
#else /* DBG_ENABLE not defined — all debug output disabled */
|
||||
|
||||
#define DBG_ERROR(fmt, ...)
|
||||
#define DBG_INFO(fmt, ...)
|
||||
#define DBG_DEBUG(fmt, ...)
|
||||
|
||||
#endif /* DBG_ENABLE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DBG_LOG_H */
|
||||
741
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h
Normal file
741
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c.h
Normal 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 */
|
||||
|
||||
115
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h
Normal file
115
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_i2c_ex.h
Normal 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 */
|
||||
|
||||
|
||||
733
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h
Normal file
733
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_spi.h
Normal 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 */
|
||||
|
||||
2157
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h
Normal file
2157
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim.h
Normal file
File diff suppressed because it is too large
Load Diff
357
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h
Normal file
357
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_tim_ex.h
Normal file
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32f4xx_hal_tim_ex.h
|
||||
* @author MCD Application Team
|
||||
* @brief Header file of TIM HAL Extended module.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2016 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
#ifndef STM32F4xx_HAL_TIM_EX_H
|
||||
#define STM32F4xx_HAL_TIM_EX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32f4xx_hal_def.h"
|
||||
|
||||
/** @addtogroup STM32F4xx_HAL_Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Exported types ------------------------------------------------------------*/
|
||||
/** @defgroup TIMEx_Exported_Types TIM Extended Exported Types
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief TIM Hall sensor Configuration Structure definition
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t IC1Polarity; /*!< Specifies the active edge of the input signal.
|
||||
This parameter can be a value of @ref TIM_Input_Capture_Polarity */
|
||||
|
||||
uint32_t IC1Prescaler; /*!< Specifies the Input Capture Prescaler.
|
||||
This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
|
||||
|
||||
uint32_t IC1Filter; /*!< Specifies the input capture filter.
|
||||
This parameter can be a number between Min_Data = 0x0 and Max_Data = 0xF */
|
||||
|
||||
uint32_t Commutation_Delay; /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
|
||||
This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
|
||||
} TIM_HallSensor_InitTypeDef;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* End of exported types -----------------------------------------------------*/
|
||||
|
||||
/* Exported constants --------------------------------------------------------*/
|
||||
/** @defgroup TIMEx_Exported_Constants TIM Extended Exported Constants
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup TIMEx_Remap TIM Extended Remapping
|
||||
* @{
|
||||
*/
|
||||
#if defined (TIM2)
|
||||
#if defined(TIM8)
|
||||
#define TIM_TIM2_TIM8_TRGO 0x00000000U /*!< TIM2 ITR1 is connected to TIM8 TRGO */
|
||||
#endif /* TIM8 */
|
||||
#define TIM_TIM2_ETH_PTP TIM_OR_ITR1_RMP_0 /*!< TIM2 ITR1 is connected to PTP trigger output */
|
||||
#define TIM_TIM2_USBFS_SOF TIM_OR_ITR1_RMP_1 /*!< TIM2 ITR1 is connected to OTG FS SOF */
|
||||
#define TIM_TIM2_USBHS_SOF (TIM_OR_ITR1_RMP_1 | TIM_OR_ITR1_RMP_0) /*!< TIM2 ITR1 is connected to OTG HS SOF */
|
||||
#endif /* TIM2 */
|
||||
|
||||
#define TIM_TIM5_GPIO 0x00000000U /*!< TIM5 TI4 is connected to GPIO */
|
||||
#define TIM_TIM5_LSI TIM_OR_TI4_RMP_0 /*!< TIM5 TI4 is connected to LSI */
|
||||
#define TIM_TIM5_LSE TIM_OR_TI4_RMP_1 /*!< TIM5 TI4 is connected to LSE */
|
||||
#define TIM_TIM5_RTC (TIM_OR_TI4_RMP_1 | TIM_OR_TI4_RMP_0) /*!< TIM5 TI4 is connected to the RTC wakeup interrupt */
|
||||
|
||||
#define TIM_TIM11_GPIO 0x00000000U /*!< TIM11 TI1 is connected to GPIO */
|
||||
#define TIM_TIM11_HSE TIM_OR_TI1_RMP_1 /*!< TIM11 TI1 is connected to HSE_RTC clock */
|
||||
#if defined(SPDIFRX)
|
||||
#define TIM_TIM11_SPDIFRX TIM_OR_TI1_RMP_0 /*!< TIM11 TI1 is connected to SPDIFRX_FRAME_SYNC */
|
||||
#endif /* SPDIFRX*/
|
||||
|
||||
#if defined(LPTIM_OR_TIM1_ITR2_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP)
|
||||
#define LPTIM_REMAP_MASK 0x10000000U
|
||||
|
||||
#define TIM_TIM9_TIM3_TRGO LPTIM_REMAP_MASK /*!< TIM9 ITR1 is connected to TIM3 TRGO */
|
||||
#define TIM_TIM9_LPTIM (LPTIM_REMAP_MASK | LPTIM_OR_TIM9_ITR1_RMP) /*!< TIM9 ITR1 is connected to LPTIM1 output */
|
||||
|
||||
#define TIM_TIM5_TIM3_TRGO LPTIM_REMAP_MASK /*!< TIM5 ITR1 is connected to TIM3 TRGO */
|
||||
#define TIM_TIM5_LPTIM (LPTIM_REMAP_MASK | LPTIM_OR_TIM5_ITR1_RMP) /*!< TIM5 ITR1 is connected to LPTIM1 output */
|
||||
|
||||
#define TIM_TIM1_TIM3_TRGO LPTIM_REMAP_MASK /*!< TIM1 ITR2 is connected to TIM3 TRGO */
|
||||
#define TIM_TIM1_LPTIM (LPTIM_REMAP_MASK | LPTIM_OR_TIM1_ITR2_RMP) /*!< TIM1 ITR2 is connected to LPTIM1 output */
|
||||
#endif /* LPTIM_OR_TIM1_ITR2_RMP && LPTIM_OR_TIM5_ITR1_RMP && LPTIM_OR_TIM5_ITR1_RMP */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* End of exported constants -------------------------------------------------*/
|
||||
|
||||
/* Exported macro ------------------------------------------------------------*/
|
||||
/** @defgroup TIMEx_Exported_Macros TIM Extended Exported Macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* End of exported macro -----------------------------------------------------*/
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/** @defgroup TIMEx_Private_Macros TIM Extended Private Macros
|
||||
* @{
|
||||
*/
|
||||
#if defined(SPDIFRX)
|
||||
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_TIM8_TRGO) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM11_SPDIFRX) || \
|
||||
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||
#elif defined(TIM2)
|
||||
#if defined(LPTIM_OR_TIM1_ITR2_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP) && defined(LPTIM_OR_TIM5_ITR1_RMP)
|
||||
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_TIM8_TRGO) || \
|
||||
((TIM_REMAP) == TIM_TIM2_ETH_PTP) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM11_HSE))) || \
|
||||
(((INSTANCE) == TIM1) && (((TIM_REMAP) == TIM_TIM1_TIM3_TRGO) || \
|
||||
((TIM_REMAP) == TIM_TIM1_LPTIM))) || \
|
||||
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_TIM3_TRGO) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LPTIM))) || \
|
||||
(((INSTANCE) == TIM9) && (((TIM_REMAP) == TIM_TIM9_TIM3_TRGO) || \
|
||||
((TIM_REMAP) == TIM_TIM9_LPTIM))))
|
||||
#elif defined(TIM8)
|
||||
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_TIM8_TRGO) || \
|
||||
((TIM_REMAP) == TIM_TIM2_ETH_PTP) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||
#else
|
||||
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||
((((INSTANCE) == TIM2) && (((TIM_REMAP) == TIM_TIM2_ETH_PTP) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBFS_SOF) || \
|
||||
((TIM_REMAP) == TIM_TIM2_USBHS_SOF))) || \
|
||||
(((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||
#endif /* LPTIM_OR_TIM1_ITR2_RMP && LPTIM_OR_TIM5_ITR1_RMP && LPTIM_OR_TIM5_ITR1_RMP */
|
||||
#else
|
||||
#define IS_TIM_REMAP(INSTANCE, TIM_REMAP) \
|
||||
((((INSTANCE) == TIM5) && (((TIM_REMAP) == TIM_TIM5_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSI) || \
|
||||
((TIM_REMAP) == TIM_TIM5_LSE) || \
|
||||
((TIM_REMAP) == TIM_TIM5_RTC))) || \
|
||||
(((INSTANCE) == TIM11) && (((TIM_REMAP) == TIM_TIM11_GPIO) || \
|
||||
((TIM_REMAP) == TIM_TIM11_HSE))))
|
||||
#endif /* SPDIFRX */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* End of private macro ------------------------------------------------------*/
|
||||
|
||||
/* Exported functions --------------------------------------------------------*/
|
||||
/** @addtogroup TIMEx_Exported_Functions TIM Extended Exported Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group1 Extended Timer Hall Sensor functions
|
||||
* @brief Timer Hall Sensor functions
|
||||
* @{
|
||||
*/
|
||||
/* Timer Hall Sensor functions **********************************************/
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Init(TIM_HandleTypeDef *htim, const TIM_HallSensor_InitTypeDef *sConfig);
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_DeInit(TIM_HandleTypeDef *htim);
|
||||
|
||||
void HAL_TIMEx_HallSensor_MspInit(TIM_HandleTypeDef *htim);
|
||||
void HAL_TIMEx_HallSensor_MspDeInit(TIM_HandleTypeDef *htim);
|
||||
|
||||
/* Blocking mode: Polling */
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start(TIM_HandleTypeDef *htim);
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop(TIM_HandleTypeDef *htim);
|
||||
/* Non-Blocking mode: Interrupt */
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_IT(TIM_HandleTypeDef *htim);
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_IT(TIM_HandleTypeDef *htim);
|
||||
/* Non-Blocking mode: DMA */
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length);
|
||||
HAL_StatusTypeDef HAL_TIMEx_HallSensor_Stop_DMA(TIM_HandleTypeDef *htim);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group2 Extended Timer Complementary Output Compare functions
|
||||
* @brief Timer Complementary Output Compare functions
|
||||
* @{
|
||||
*/
|
||||
/* Timer Complementary Output Compare functions *****************************/
|
||||
/* Blocking mode: Polling */
|
||||
HAL_StatusTypeDef HAL_TIMEx_OCN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
HAL_StatusTypeDef HAL_TIMEx_OCN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
|
||||
/* Non-Blocking mode: Interrupt */
|
||||
HAL_StatusTypeDef HAL_TIMEx_OCN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
|
||||
/* Non-Blocking mode: DMA */
|
||||
HAL_StatusTypeDef HAL_TIMEx_OCN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
|
||||
uint16_t Length);
|
||||
HAL_StatusTypeDef HAL_TIMEx_OCN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group3 Extended Timer Complementary PWM functions
|
||||
* @brief Timer Complementary PWM functions
|
||||
* @{
|
||||
*/
|
||||
/* Timer Complementary PWM functions ****************************************/
|
||||
/* Blocking mode: Polling */
|
||||
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
|
||||
/* Non-Blocking mode: Interrupt */
|
||||
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
/* Non-Blocking mode: DMA */
|
||||
HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, const uint32_t *pData,
|
||||
uint16_t Length);
|
||||
HAL_StatusTypeDef HAL_TIMEx_PWMN_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group4 Extended Timer Complementary One Pulse functions
|
||||
* @brief Timer Complementary One Pulse functions
|
||||
* @{
|
||||
*/
|
||||
/* Timer Complementary One Pulse functions **********************************/
|
||||
/* Blocking mode: Polling */
|
||||
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||
|
||||
/* Non-Blocking mode: Interrupt */
|
||||
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Start_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||
HAL_StatusTypeDef HAL_TIMEx_OnePulseN_Stop_IT(TIM_HandleTypeDef *htim, uint32_t OutputChannel);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group5 Extended Peripheral Control functions
|
||||
* @brief Peripheral Control functions
|
||||
* @{
|
||||
*/
|
||||
/* Extended Control functions ************************************************/
|
||||
HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent(TIM_HandleTypeDef *htim, uint32_t InputTrigger,
|
||||
uint32_t CommutationSource);
|
||||
HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_IT(TIM_HandleTypeDef *htim, uint32_t InputTrigger,
|
||||
uint32_t CommutationSource);
|
||||
HAL_StatusTypeDef HAL_TIMEx_ConfigCommutEvent_DMA(TIM_HandleTypeDef *htim, uint32_t InputTrigger,
|
||||
uint32_t CommutationSource);
|
||||
HAL_StatusTypeDef HAL_TIMEx_MasterConfigSynchronization(TIM_HandleTypeDef *htim,
|
||||
const TIM_MasterConfigTypeDef *sMasterConfig);
|
||||
HAL_StatusTypeDef HAL_TIMEx_ConfigBreakDeadTime(TIM_HandleTypeDef *htim,
|
||||
const TIM_BreakDeadTimeConfigTypeDef *sBreakDeadTimeConfig);
|
||||
HAL_StatusTypeDef HAL_TIMEx_RemapConfig(TIM_HandleTypeDef *htim, uint32_t Remap);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group6 Extended Callbacks functions
|
||||
* @brief Extended Callbacks functions
|
||||
* @{
|
||||
*/
|
||||
/* Extended Callback **********************************************************/
|
||||
void HAL_TIMEx_CommutCallback(TIM_HandleTypeDef *htim);
|
||||
void HAL_TIMEx_CommutHalfCpltCallback(TIM_HandleTypeDef *htim);
|
||||
void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup TIMEx_Exported_Functions_Group7 Extended Peripheral State functions
|
||||
* @brief Extended Peripheral State functions
|
||||
* @{
|
||||
*/
|
||||
/* Extended Peripheral State functions ***************************************/
|
||||
HAL_TIM_StateTypeDef HAL_TIMEx_HallSensor_GetState(const TIM_HandleTypeDef *htim);
|
||||
HAL_TIM_ChannelStateTypeDef HAL_TIMEx_GetChannelNState(const TIM_HandleTypeDef *htim, uint32_t ChannelN);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* End of exported functions -------------------------------------------------*/
|
||||
|
||||
/* Private functions----------------------------------------------------------*/
|
||||
/** @addtogroup TIMEx_Private_Functions TIM Extended Private Functions
|
||||
* @{
|
||||
*/
|
||||
void TIMEx_DMACommutationCplt(DMA_HandleTypeDef *hdma);
|
||||
void TIMEx_DMACommutationHalfCplt(DMA_HandleTypeDef *hdma);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/* End of private functions --------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* STM32F4xx_HAL_TIM_EX_H */
|
||||
909
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h
Normal file
909
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_uart.h
Normal 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 */
|
||||
|
||||
1890
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_i2c.h
Normal file
1890
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_i2c.h
Normal file
File diff suppressed because it is too large
Load Diff
2042
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_spi.h
Normal file
2042
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_spi.h
Normal file
File diff suppressed because it is too large
Load Diff
2521
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usart.h
Normal file
2521
Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_ll_usart.h
Normal file
File diff suppressed because it is too large
Load Diff
7567
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c
Normal file
7567
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c.c
Normal file
File diff suppressed because it is too large
Load Diff
182
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c
Normal file
182
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_i2c_ex.c
Normal 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 */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
3945
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c
Normal file
3945
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_spi.c
Normal file
File diff suppressed because it is too large
Load Diff
7629
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
Normal file
7629
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c
Normal file
File diff suppressed because it is too large
Load Diff
2410
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
Normal file
2410
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c
Normal file
File diff suppressed because it is too large
Load Diff
3807
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c
Normal file
3807
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c
Normal file
File diff suppressed because it is too large
Load Diff
170
Inc/FreeRTOSConfig.h
Normal file
170
Inc/FreeRTOSConfig.h
Normal file
@@ -0,0 +1,170 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/*
|
||||
* FreeRTOS Kernel V10.3.1
|
||||
* Portion Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
* Portion Copyright (C) 2019 StMicroelectronics, Inc. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* http://www.FreeRTOS.org
|
||||
* http://aws.amazon.com/freertos
|
||||
*
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
#ifndef FREERTOS_CONFIG_H
|
||||
#define FREERTOS_CONFIG_H
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Application specific definitions.
|
||||
*
|
||||
* These definitions should be adjusted for your particular hardware and
|
||||
* application requirements.
|
||||
*
|
||||
* These parameters and more are described within the 'configuration' section of the
|
||||
* FreeRTOS API documentation available on the FreeRTOS.org web site.
|
||||
*
|
||||
* See http://www.freertos.org/a00110.html
|
||||
*----------------------------------------------------------*/
|
||||
|
||||
/* USER CODE BEGIN Includes */
|
||||
/* Section where include file can be added */
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Ensure definitions are only used by the compiler, and not by the assembler. */
|
||||
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
|
||||
#include <stdint.h>
|
||||
extern uint32_t SystemCoreClock;
|
||||
#endif
|
||||
#ifndef CMSIS_device_header
|
||||
#define CMSIS_device_header "stm32f4xx.h"
|
||||
#endif /* CMSIS_device_header */
|
||||
|
||||
#define configENABLE_FPU 1
|
||||
#define configENABLE_MPU 0
|
||||
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configSUPPORT_STATIC_ALLOCATION 1
|
||||
#define configSUPPORT_DYNAMIC_ALLOCATION 1
|
||||
#define configUSE_IDLE_HOOK 0
|
||||
#define configUSE_TICK_HOOK 0
|
||||
#define configCPU_CLOCK_HZ ( SystemCoreClock )
|
||||
#define configTICK_RATE_HZ ((TickType_t)1000)
|
||||
#define configMAX_PRIORITIES ( 56 )
|
||||
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
|
||||
#define configTOTAL_HEAP_SIZE ((size_t)30720)
|
||||
#define configMAX_TASK_NAME_LEN ( 16 )
|
||||
#define configUSE_TRACE_FACILITY 1
|
||||
#define configUSE_16_BIT_TICKS 0
|
||||
#define configUSE_MUTEXES 1
|
||||
#define configQUEUE_REGISTRY_SIZE 8
|
||||
#define configUSE_RECURSIVE_MUTEXES 1
|
||||
#define configUSE_COUNTING_SEMAPHORES 1
|
||||
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
|
||||
/* USER CODE BEGIN MESSAGE_BUFFER_LENGTH_TYPE */
|
||||
/* Defaults to size_t for backward compatibility, but can be changed
|
||||
if lengths will always be less than the number of bytes in a size_t. */
|
||||
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t
|
||||
/* USER CODE END MESSAGE_BUFFER_LENGTH_TYPE */
|
||||
|
||||
/* Co-routine definitions. */
|
||||
#define configUSE_CO_ROUTINES 0
|
||||
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
|
||||
|
||||
/* Software timer definitions. */
|
||||
#define configUSE_TIMERS 1
|
||||
#define configTIMER_TASK_PRIORITY ( 2 )
|
||||
#define configTIMER_QUEUE_LENGTH 10
|
||||
#define configTIMER_TASK_STACK_DEPTH 256
|
||||
|
||||
/* CMSIS-RTOS V2 flags */
|
||||
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
|
||||
#define configUSE_OS2_THREAD_ENUMERATE 1
|
||||
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
|
||||
#define configUSE_OS2_THREAD_FLAGS 1
|
||||
#define configUSE_OS2_TIMER 1
|
||||
#define configUSE_OS2_MUTEX 1
|
||||
|
||||
/* Set the following definitions to 1 to include the API function, or zero
|
||||
to exclude the API function. */
|
||||
#define INCLUDE_vTaskPrioritySet 1
|
||||
#define INCLUDE_uxTaskPriorityGet 1
|
||||
#define INCLUDE_vTaskDelete 1
|
||||
#define INCLUDE_vTaskCleanUpResources 0
|
||||
#define INCLUDE_vTaskSuspend 1
|
||||
#define INCLUDE_vTaskDelayUntil 1
|
||||
#define INCLUDE_vTaskDelay 1
|
||||
#define INCLUDE_xTaskGetSchedulerState 1
|
||||
#define INCLUDE_xTimerPendFunctionCall 1
|
||||
#define INCLUDE_xQueueGetMutexHolder 1
|
||||
#define INCLUDE_uxTaskGetStackHighWaterMark 1
|
||||
#define INCLUDE_xTaskGetCurrentTaskHandle 1
|
||||
#define INCLUDE_eTaskGetState 1
|
||||
|
||||
/*
|
||||
* The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
|
||||
* by the application thus the correct define need to be enabled below
|
||||
*/
|
||||
#define USE_FreeRTOS_HEAP_4
|
||||
|
||||
/* Cortex-M specific definitions. */
|
||||
#ifdef __NVIC_PRIO_BITS
|
||||
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
|
||||
#define configPRIO_BITS __NVIC_PRIO_BITS
|
||||
#else
|
||||
#define configPRIO_BITS 4
|
||||
#endif
|
||||
|
||||
/* The lowest interrupt priority that can be used in a call to a "set priority"
|
||||
function. */
|
||||
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
|
||||
|
||||
/* The highest interrupt priority that can be used by any interrupt service
|
||||
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
|
||||
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
|
||||
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
|
||||
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
|
||||
|
||||
/* Interrupt priorities used by the kernel port layer itself. These are generic
|
||||
to all Cortex-M ports, and do not rely on any particular library functions. */
|
||||
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
|
||||
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
|
||||
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
|
||||
|
||||
/* Normal assert() semantics without relying on the provision of an assert.h
|
||||
header file. */
|
||||
/* USER CODE BEGIN 1 */
|
||||
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
|
||||
/* USER CODE END 1 */
|
||||
|
||||
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
|
||||
standard names. */
|
||||
#define vPortSVCHandler SVC_Handler
|
||||
#define xPortPendSVHandler PendSV_Handler
|
||||
|
||||
/* IMPORTANT: After 10.3.1 update, Systick_Handler comes from NVIC (if SYS timebase = systick), otherwise from cmsis_os2.c */
|
||||
|
||||
#define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0
|
||||
|
||||
/* USER CODE BEGIN Defines */
|
||||
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
|
||||
/* USER CODE END Defines */
|
||||
|
||||
#endif /* FREERTOS_CONFIG_H */
|
||||
52
Inc/dma.h
Normal file
52
Inc/dma.h
Normal 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
52
Inc/i2c.h
Normal 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__ */
|
||||
|
||||
99
Inc/main.h
99
Inc/main.h
@@ -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
55
Inc/spi.h
Normal 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__ */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
64
Inc/usart.h
Normal 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
43
Lib/FatFs/diskio.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
typedef enum {
|
||||
RES_OK = 0,
|
||||
RES_ERROR,
|
||||
RES_WRPRT,
|
||||
RES_NOTRDY,
|
||||
RES_PARERR
|
||||
} DRESULT;
|
||||
|
||||
DSTATUS disk_initialize(BYTE pdrv);
|
||||
DSTATUS disk_status(BYTE pdrv);
|
||||
DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
#define STA_NOINIT 0x01
|
||||
#define STA_NODISK 0x02
|
||||
#define STA_PROTECT 0x04
|
||||
|
||||
#define CTRL_SYNC 0
|
||||
#define GET_SECTOR_COUNT 1
|
||||
#define GET_SECTOR_SIZE 2
|
||||
#define GET_BLOCK_SIZE 3
|
||||
#define CTRL_TRIM 4
|
||||
|
||||
#define CTRL_POWER 5
|
||||
#define CTRL_LOCK 6
|
||||
#define CTRL_EJECT 7
|
||||
#define CTRL_FORMAT 8
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
7084
Lib/FatFs/ff.c
Normal file
7084
Lib/FatFs/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
429
Lib/FatFs/ff.h
Normal file
429
Lib/FatFs/ff.h
Normal file
@@ -0,0 +1,429 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.15 /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2022, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
/
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 80286 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
/* Integer types used for FatFs API */
|
||||
|
||||
#if defined(_WIN32) /* Windows VC++ (for development only) */
|
||||
#define FF_INTDEF 2
|
||||
#include <windows.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
#include <float.h>
|
||||
#define isnan(v) _isnan(v)
|
||||
#define isinf(v) (!_finite(v))
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
#endif
|
||||
|
||||
|
||||
/* Type of file size and LBA variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#if FF_LBA64
|
||||
typedef QWORD LBA_t;
|
||||
#else
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
#else
|
||||
#if FF_LBA64
|
||||
#error exFAT needs to be enabled when enable 64-bit LBA
|
||||
#endif
|
||||
typedef DWORD FSIZE_t;
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API (TCHAR) */
|
||||
|
||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) u8 ## x
|
||||
#define _TEXT(x) u8 ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||
typedef DWORD TCHAR;
|
||||
#define _T(x) U ## x
|
||||
#define _TEXT(x) U ## x
|
||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||
#error Wrong FF_LFN_UNICODE setting
|
||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Volume hosting physical drive */
|
||||
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] status (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Number of sectors per FAT */
|
||||
LBA_t volbase; /* Volume base sector */
|
||||
LBA_t fatbase; /* FAT base sector */
|
||||
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
|
||||
LBA_t database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
LBA_t bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
LBA_t winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume's mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
|
||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[12 + 1]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* Format parameter structure (MKFS_PARM) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||
BYTE n_fat; /* Number of FATs */
|
||||
UINT align; /* Data area alignment (sector) */
|
||||
UINT n_root; /* Number of root directory entries */
|
||||
DWORD au_size; /* Cluster size (byte) */
|
||||
} MKFS_PARM;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs Module Application Interface */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
/* Some API fucntions are implemented as macro */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional Functions */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
/* RTC function (provided by user) */
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void); /* Get current time */
|
||||
#endif
|
||||
|
||||
|
||||
/* LFN support functions (defined in ffunicode.c) */
|
||||
|
||||
#if FF_USE_LFN >= 1
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
|
||||
|
||||
/* O/S dependent functions (samples available in ffsystem.c) */
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT /* Sync functions */
|
||||
int ff_mutex_create (int vol); /* Create a sync object */
|
||||
void ff_mutex_delete (int vol); /* Delete a sync object */
|
||||
int ff_mutex_take (int vol); /* Lock sync object */
|
||||
void ff_mutex_give (int vol); /* Unlock sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and Offset Address */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
#define FM_ANY 0x07
|
||||
#define FM_SFD 0x08
|
||||
|
||||
/* Filesystem type (FATFS.fs_type) */
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FF_DEFINED */
|
||||
41
Lib/FatFs/ffconf.h
Normal file
41
Lib/FatFs/ffconf.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#define FFCONF_DEF 80286
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
#define FF_FS_MINIMIZE 0
|
||||
#define FF_USE_FIND 0
|
||||
#define FF_USE_MKFS 1
|
||||
#define FF_USE_FASTSEEK 0
|
||||
#define FF_USE_EXPAND 0
|
||||
#define FF_USE_CHMOD 0
|
||||
#define FF_USE_LABEL 0
|
||||
#define FF_USE_FORWARD 0
|
||||
#define FF_USE_STRFUNC 0
|
||||
#define FF_PRINT_LLI 1
|
||||
#define FF_PRINT_FLOAT 1
|
||||
#define FF_STRF_ENCODE 3
|
||||
#define FF_CODE_PAGE 437
|
||||
#define FF_USE_LFN 0
|
||||
#define FF_MAX_LFN 255
|
||||
#define FF_LFN_UNICODE 0
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
#define FF_FS_RPATH 0
|
||||
#define FF_VOLUMES 1
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||
#define FF_MULTI_PARTITION 0
|
||||
#define FF_MIN_SS 512
|
||||
#define FF_MAX_SS 512
|
||||
#define FF_LBA64 0
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
#define FF_USE_TRIM 0
|
||||
#define FF_FS_TINY 0
|
||||
#define FF_FS_EXFAT 0
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2026
|
||||
#define FF_FS_NOFSINFO 0
|
||||
#define FF_FS_LOCK 0
|
||||
#define FF_FS_REENTRANT 0
|
||||
#define FF_FS_TIMEOUT 1000
|
||||
50
Lib/dhara/bytes.h
Normal file
50
Lib/dhara/bytes.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DHARA_BYTES_H_
|
||||
#define DHARA_BYTES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint16_t dhara_r16(const uint8_t *data)
|
||||
{
|
||||
return ((uint16_t)data[0]) |
|
||||
(((uint16_t)data[1]) << 8);
|
||||
}
|
||||
|
||||
static inline void dhara_w16(uint8_t *data, uint16_t v)
|
||||
{
|
||||
data[0] = v;
|
||||
data[1] = v >> 8;
|
||||
}
|
||||
|
||||
static inline uint32_t dhara_r32(const uint8_t *data)
|
||||
{
|
||||
return ((uint32_t)data[0]) |
|
||||
(((uint32_t)data[1]) << 8) |
|
||||
(((uint32_t)data[2]) << 16) |
|
||||
(((uint32_t)data[3]) << 24);
|
||||
}
|
||||
|
||||
static inline void dhara_w32(uint8_t *data, uint32_t v)
|
||||
{
|
||||
data[0] = v;
|
||||
data[1] = v >> 8;
|
||||
data[2] = v >> 16;
|
||||
data[3] = v >> 24;
|
||||
}
|
||||
|
||||
#endif
|
||||
45
Lib/dhara/error.h
Normal file
45
Lib/dhara/error.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DHARA_ERROR_H_
|
||||
#define DHARA_ERROR_H_
|
||||
|
||||
typedef enum {
|
||||
DHARA_E_NONE = 0,
|
||||
DHARA_E_BAD_BLOCK,
|
||||
DHARA_E_ECC,
|
||||
DHARA_E_TOO_BAD,
|
||||
DHARA_E_RECOVER,
|
||||
DHARA_E_JOURNAL_FULL,
|
||||
DHARA_E_NOT_FOUND,
|
||||
DHARA_E_MAP_FULL,
|
||||
DHARA_E_CORRUPT_MAP,
|
||||
DHARA_E_MAX
|
||||
} dhara_error_t;
|
||||
|
||||
/* Produce a human-readable error message. This function is kept in a
|
||||
* separate compilation unit and can be omitted to reduce binary size.
|
||||
*/
|
||||
const char *dhara_strerror(dhara_error_t err);
|
||||
|
||||
/* Save an error */
|
||||
static inline void dhara_set_error(dhara_error_t *err, dhara_error_t v)
|
||||
{
|
||||
if (err)
|
||||
*err = v;
|
||||
}
|
||||
|
||||
#endif
|
||||
884
Lib/dhara/journal.c
Normal file
884
Lib/dhara/journal.c
Normal file
@@ -0,0 +1,884 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "journal.h"
|
||||
#include "bytes.h"
|
||||
|
||||
/************************************************************************
|
||||
* Metapage binary format
|
||||
*/
|
||||
|
||||
/* Does the page buffer contain a valid checkpoint page? */
|
||||
static inline int hdr_has_magic(const uint8_t *buf)
|
||||
{
|
||||
return (buf[0] == 'D') &&
|
||||
(buf[1] == 'h') &&
|
||||
(buf[2] == 'a');
|
||||
}
|
||||
|
||||
static inline void hdr_put_magic(uint8_t *buf)
|
||||
{
|
||||
buf[0] = 'D';
|
||||
buf[1] = 'h';
|
||||
buf[2] = 'a';
|
||||
}
|
||||
|
||||
/* What epoch is this page? */
|
||||
static inline uint8_t hdr_get_epoch(const uint8_t *buf)
|
||||
{
|
||||
return buf[3];
|
||||
}
|
||||
|
||||
static inline void hdr_set_epoch(uint8_t *buf, uint8_t e)
|
||||
{
|
||||
buf[3] = e;
|
||||
}
|
||||
|
||||
static inline dhara_page_t hdr_get_tail(const uint8_t *buf)
|
||||
{
|
||||
return dhara_r32(buf + 4);
|
||||
}
|
||||
|
||||
static inline void hdr_set_tail(uint8_t *buf, dhara_page_t tail)
|
||||
{
|
||||
dhara_w32(buf + 4, tail);
|
||||
}
|
||||
|
||||
static inline dhara_page_t hdr_get_bb_current(const uint8_t *buf)
|
||||
{
|
||||
return dhara_r32(buf + 8);
|
||||
}
|
||||
|
||||
static inline void hdr_set_bb_current(uint8_t *buf, dhara_page_t count)
|
||||
{
|
||||
dhara_w32(buf + 8, count);
|
||||
}
|
||||
|
||||
static inline dhara_page_t hdr_get_bb_last(const uint8_t *buf)
|
||||
{
|
||||
return dhara_r32(buf + 12);
|
||||
}
|
||||
|
||||
static inline void hdr_set_bb_last(uint8_t *buf, dhara_page_t count)
|
||||
{
|
||||
dhara_w32(buf + 12, count);
|
||||
}
|
||||
|
||||
/* Clear user metadata */
|
||||
static inline void hdr_clear_user(uint8_t *buf, uint8_t log2_page_size)
|
||||
{
|
||||
memset(buf + DHARA_HEADER_SIZE + DHARA_COOKIE_SIZE, 0xff,
|
||||
(1 << log2_page_size) - DHARA_HEADER_SIZE - DHARA_COOKIE_SIZE);
|
||||
}
|
||||
|
||||
/* Obtain pointers to user data */
|
||||
static inline size_t hdr_user_offset(uint8_t which)
|
||||
{
|
||||
return DHARA_HEADER_SIZE + DHARA_COOKIE_SIZE +
|
||||
which * DHARA_META_SIZE;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Page geometry helpers
|
||||
*/
|
||||
|
||||
/* Is this page index aligned to N bits? */
|
||||
static inline int is_aligned(dhara_page_t p, int n)
|
||||
{
|
||||
return !(p & ((1 << n) - 1));
|
||||
}
|
||||
|
||||
/* Are these two pages from the same alignment group? */
|
||||
static inline int align_eq(dhara_page_t a, dhara_page_t b,
|
||||
int n)
|
||||
{
|
||||
return !((a ^ b) >> n);
|
||||
}
|
||||
|
||||
/* What is the successor of this block? */
|
||||
static dhara_block_t next_block(const struct dhara_nand *n, dhara_block_t blk)
|
||||
{
|
||||
blk++;
|
||||
if (blk >= n->num_blocks)
|
||||
blk = 0;
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
static dhara_page_t next_upage(const struct dhara_journal *j,
|
||||
dhara_page_t p)
|
||||
{
|
||||
p++;
|
||||
if (is_aligned(p + 1, j->log2_ppc))
|
||||
p++;
|
||||
|
||||
if (p >= (j->nand->num_blocks << j->nand->log2_ppb))
|
||||
p = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Calculate a checkpoint period: the largest value of ppc such that
|
||||
* (2**ppc - 1) metadata blocks can fit on a page with one journal
|
||||
* header.
|
||||
*/
|
||||
static int choose_ppc(int log2_page_size, int max)
|
||||
{
|
||||
const int max_meta = (1 << log2_page_size) -
|
||||
DHARA_HEADER_SIZE - DHARA_COOKIE_SIZE;
|
||||
int total_meta = DHARA_META_SIZE;
|
||||
int ppc = 1;
|
||||
|
||||
while (ppc < max) {
|
||||
total_meta <<= 1;
|
||||
total_meta += DHARA_META_SIZE;
|
||||
|
||||
if (total_meta > max_meta)
|
||||
break;
|
||||
|
||||
ppc++;
|
||||
}
|
||||
|
||||
return ppc;
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Journal setup/resume
|
||||
*/
|
||||
|
||||
/* Clear recovery status */
|
||||
static void clear_recovery(struct dhara_journal *j)
|
||||
{
|
||||
j->recover_next = DHARA_PAGE_NONE;
|
||||
j->recover_root = DHARA_PAGE_NONE;
|
||||
j->recover_meta = DHARA_PAGE_NONE;
|
||||
j->flags &= ~(DHARA_JOURNAL_F_BAD_META |
|
||||
DHARA_JOURNAL_F_RECOVERY |
|
||||
DHARA_JOURNAL_F_ENUM_DONE);
|
||||
}
|
||||
|
||||
/* Set up an empty journal */
|
||||
static void reset_journal(struct dhara_journal *j)
|
||||
{
|
||||
/* We don't yet have a bad block estimate, so make a
|
||||
* conservative guess.
|
||||
*/
|
||||
j->epoch = 0;
|
||||
j->bb_last = j->nand->num_blocks >> 6;
|
||||
j->bb_current = 0;
|
||||
|
||||
j->flags = 0;
|
||||
|
||||
/* Empty journal */
|
||||
j->head = 0;
|
||||
j->tail = 0;
|
||||
j->tail_sync = 0;
|
||||
j->root = DHARA_PAGE_NONE;
|
||||
|
||||
/* No recovery required */
|
||||
clear_recovery(j);
|
||||
|
||||
/* Empty metadata buffer */
|
||||
memset(j->page_buf, 0xff, 1 << j->nand->log2_page_size);
|
||||
}
|
||||
|
||||
static void roll_stats(struct dhara_journal *j)
|
||||
{
|
||||
j->bb_last = j->bb_current;
|
||||
j->bb_current = 0;
|
||||
j->epoch++;
|
||||
}
|
||||
|
||||
void dhara_journal_init(struct dhara_journal *j,
|
||||
const struct dhara_nand *n,
|
||||
uint8_t *page_buf)
|
||||
{
|
||||
/* Set fixed parameters */
|
||||
j->nand = n;
|
||||
j->page_buf = page_buf;
|
||||
j->log2_ppc = choose_ppc(n->log2_page_size, n->log2_ppb);
|
||||
|
||||
reset_journal(j);
|
||||
}
|
||||
|
||||
/* Find the first checkpoint-containing block. If a block contains any
|
||||
* checkpoints at all, then it must contain one in the first checkpoint
|
||||
* location -- otherwise, we would have considered the block eraseable.
|
||||
*/
|
||||
static int find_checkblock(struct dhara_journal *j,
|
||||
dhara_block_t blk, dhara_block_t *where,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; (blk < j->nand->num_blocks) &&
|
||||
(i < DHARA_MAX_RETRIES); i++) {
|
||||
const dhara_page_t p =
|
||||
(blk << j->nand->log2_ppb) |
|
||||
((1 << j->log2_ppc) - 1);
|
||||
|
||||
if (!(dhara_nand_is_bad(j->nand, blk) ||
|
||||
dhara_nand_read(j->nand, p,
|
||||
0, 1 << j->nand->log2_page_size,
|
||||
j->page_buf, err)) &&
|
||||
hdr_has_magic(j->page_buf)) {
|
||||
*where = blk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
blk++;
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static dhara_block_t find_last_checkblock(struct dhara_journal *j,
|
||||
dhara_block_t first)
|
||||
{
|
||||
dhara_block_t low = first;
|
||||
dhara_block_t high = j->nand->num_blocks - 1;
|
||||
|
||||
while (low <= high) {
|
||||
const dhara_block_t mid = (low + high) >> 1;
|
||||
dhara_block_t found;
|
||||
|
||||
if ((find_checkblock(j, mid, &found, NULL) < 0) ||
|
||||
(hdr_get_epoch(j->page_buf) != j->epoch)) {
|
||||
if (!mid)
|
||||
return first;
|
||||
|
||||
high = mid - 1;
|
||||
} else {
|
||||
dhara_block_t nf;
|
||||
|
||||
if (((found + 1) >= j->nand->num_blocks) ||
|
||||
(find_checkblock(j, found + 1,
|
||||
&nf, NULL) < 0) ||
|
||||
(hdr_get_epoch(j->page_buf) != j->epoch))
|
||||
return found;
|
||||
|
||||
low = nf;
|
||||
}
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
/* Test whether a checkpoint group is in a state fit for reprogramming,
|
||||
* but allow for the fact that is_free() might not have any way of
|
||||
* distinguishing between an unprogrammed page, and a page programmed
|
||||
* with all-0xff bytes (but if so, it must be ok to reprogram such a
|
||||
* page).
|
||||
*
|
||||
* We used to test for an unprogrammed checkpoint group by checking to
|
||||
* see if the first user-page had been programmed since last erase (by
|
||||
* testing only the first page with is_free). This works if is_free is
|
||||
* precise, because the pages are written in order.
|
||||
*
|
||||
* If is_free is imprecise, we need to check all pages in the group.
|
||||
* That also works, because the final page in a checkpoint group is
|
||||
* guaranteed to contain non-0xff bytes. Therefore, we return 1 only if
|
||||
* the group is truly unprogrammed, or if it was partially programmed
|
||||
* with some all-0xff user pages (which changes nothing for us).
|
||||
*/
|
||||
static int cp_free(struct dhara_journal *j, dhara_page_t first_user)
|
||||
{
|
||||
const int count = 1 << j->log2_ppc;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (!dhara_nand_is_free(j->nand, first_user + i))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static dhara_page_t find_last_group(struct dhara_journal *j,
|
||||
dhara_block_t blk)
|
||||
{
|
||||
const int num_groups = 1 << (j->nand->log2_ppb - j->log2_ppc);
|
||||
int low = 0;
|
||||
int high = num_groups - 1;
|
||||
|
||||
/* If a checkpoint group is completely unprogrammed, everything
|
||||
* following it will be completely unprogrammed also.
|
||||
*
|
||||
* Therefore, binary search checkpoint groups until we find the
|
||||
* last programmed one.
|
||||
*/
|
||||
while (low <= high) {
|
||||
int mid = (low + high) >> 1;
|
||||
const dhara_page_t p = (mid << j->log2_ppc) |
|
||||
(blk << j->nand->log2_ppb);
|
||||
|
||||
if (cp_free(j, p)) {
|
||||
high = mid - 1;
|
||||
} else if (((mid + 1) >= num_groups) ||
|
||||
cp_free(j, p + (1 << j->log2_ppc))) {
|
||||
return p;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return blk << j->nand->log2_ppb;
|
||||
}
|
||||
|
||||
static int find_root(struct dhara_journal *j, dhara_page_t start,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
const dhara_block_t blk = start >> j->nand->log2_ppb;
|
||||
int i = (start & ((1 << j->nand->log2_ppb) - 1)) >> j->log2_ppc;
|
||||
|
||||
while (i >= 0) {
|
||||
const dhara_page_t p = (blk << j->nand->log2_ppb) +
|
||||
((i + 1) << j->log2_ppc) - 1;
|
||||
|
||||
if (!dhara_nand_read(j->nand, p,
|
||||
0, 1 << j->nand->log2_page_size,
|
||||
j->page_buf, err) &&
|
||||
(hdr_has_magic(j->page_buf)) &&
|
||||
(hdr_get_epoch(j->page_buf) == j->epoch)) {
|
||||
j->root = p - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_head(struct dhara_journal *j, dhara_page_t start,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
j->head = next_upage(j, start);
|
||||
if (!j->head)
|
||||
roll_stats(j);
|
||||
|
||||
/* Starting from the last good checkpoint, find either:
|
||||
*
|
||||
* (a) the next free user-page in the same block
|
||||
* (b) or, the first page of the next block
|
||||
*
|
||||
* The block we end up on might be bad, but that's ok -- we'll
|
||||
* skip it when we go to prepare the next write.
|
||||
*/
|
||||
for (;;) {
|
||||
/* How many free pages trail this checkpoint group? */
|
||||
const unsigned int ppc = 1 << j->log2_ppc;
|
||||
unsigned int n = 0;
|
||||
dhara_page_t first = j->head & ~(dhara_page_t)(ppc - 1);
|
||||
|
||||
while (n < ppc &&
|
||||
dhara_nand_is_free(j->nand, first + ppc - n - 1))
|
||||
n++;
|
||||
|
||||
/* If we have some, then we've found our next free
|
||||
* userpage.
|
||||
*/
|
||||
if (n > 1) {
|
||||
j->head = first + ppc - n;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip to the next checkpoint group */
|
||||
j->head = first + ppc;
|
||||
if (j->head >= (j->nand->num_blocks << j->nand->log2_ppb)) {
|
||||
j->head = 0;
|
||||
roll_stats(j);
|
||||
}
|
||||
|
||||
/* If we hit the end of the block, we're done */
|
||||
if (is_aligned(j->head, j->nand->log2_ppb)) {
|
||||
/* Make sure we don't chase over the tail */
|
||||
if (align_eq(j->head, j->tail, j->nand->log2_ppb))
|
||||
j->tail = next_block(j->nand,
|
||||
j->tail >> j->nand->log2_ppb) <<
|
||||
j->nand->log2_ppb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_journal_resume(struct dhara_journal *j, dhara_error_t *err)
|
||||
{
|
||||
dhara_block_t first, last;
|
||||
dhara_page_t last_group;
|
||||
|
||||
/* Find the first checkpoint-containing block */
|
||||
if (find_checkblock(j, 0, &first, err) < 0) {
|
||||
reset_journal(j);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find the last checkpoint-containing block in this epoch */
|
||||
j->epoch = hdr_get_epoch(j->page_buf);
|
||||
last = find_last_checkblock(j, first);
|
||||
|
||||
/* Find the last programmed checkpoint group in the block */
|
||||
last_group = find_last_group(j, last);
|
||||
|
||||
/* Perform a linear scan to find the last good checkpoint (and
|
||||
* therefore the root).
|
||||
*/
|
||||
if (find_root(j, last_group, err) < 0) {
|
||||
reset_journal(j);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Restore settings from checkpoint */
|
||||
j->tail = hdr_get_tail(j->page_buf);
|
||||
j->bb_current = hdr_get_bb_current(j->page_buf);
|
||||
j->bb_last = hdr_get_bb_last(j->page_buf);
|
||||
hdr_clear_user(j->page_buf, j->nand->log2_page_size);
|
||||
|
||||
/* Perform another linear scan to find the next free user page */
|
||||
if (find_head(j, last_group, err) < 0) {
|
||||
reset_journal(j);
|
||||
return -1;
|
||||
}
|
||||
|
||||
j->flags = 0;
|
||||
j->tail_sync = j->tail;
|
||||
|
||||
clear_recovery(j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Public interface
|
||||
*/
|
||||
|
||||
dhara_page_t dhara_journal_capacity(const struct dhara_journal *j)
|
||||
{
|
||||
const dhara_block_t max_bad = j->bb_last > j->bb_current ?
|
||||
j->bb_last : j->bb_current;
|
||||
const dhara_block_t good_blocks = j->nand->num_blocks - max_bad - 1;
|
||||
const int log2_cpb = j->nand->log2_ppb - j->log2_ppc;
|
||||
const dhara_page_t good_cps = good_blocks << log2_cpb;
|
||||
|
||||
/* Good checkpoints * (checkpoint period - 1) */
|
||||
return (good_cps << j->log2_ppc) - good_cps;
|
||||
}
|
||||
|
||||
dhara_page_t dhara_journal_size(const struct dhara_journal *j)
|
||||
{
|
||||
/* Find the number of raw pages, and the number of checkpoints
|
||||
* between the head and the tail. The difference between the two
|
||||
* is the number of user pages (upper limit).
|
||||
*/
|
||||
dhara_page_t num_pages = j->head;
|
||||
dhara_page_t num_cps = j->head >> j->log2_ppc;
|
||||
|
||||
if (j->head < j->tail_sync) {
|
||||
const dhara_page_t total_pages =
|
||||
j->nand->num_blocks << j->nand->log2_ppb;
|
||||
|
||||
num_pages += total_pages;
|
||||
num_cps += total_pages >> j->log2_ppc;
|
||||
}
|
||||
|
||||
num_pages -= j->tail_sync;
|
||||
num_cps -= j->tail_sync >> j->log2_ppc;
|
||||
|
||||
return num_pages - num_cps;
|
||||
}
|
||||
|
||||
int dhara_journal_read_meta(struct dhara_journal *j, dhara_page_t p,
|
||||
uint8_t *buf, dhara_error_t *err)
|
||||
{
|
||||
/* Offset of metadata within the metadata page */
|
||||
const dhara_page_t ppc_mask = (1 << j->log2_ppc) - 1;
|
||||
const size_t offset = hdr_user_offset(p & ppc_mask);
|
||||
|
||||
/* Special case: buffered metadata */
|
||||
if (align_eq(p, j->head, j->log2_ppc)) {
|
||||
memcpy(buf, j->page_buf + offset, DHARA_META_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Special case: incomplete metadata dumped at start of
|
||||
* recovery.
|
||||
*/
|
||||
if ((j->recover_meta != DHARA_PAGE_NONE) &&
|
||||
align_eq(p, j->recover_root, j->log2_ppc))
|
||||
return dhara_nand_read(j->nand, j->recover_meta,
|
||||
offset, DHARA_META_SIZE,
|
||||
buf, err);
|
||||
|
||||
/* General case: fetch from metadata page for checkpoint group */
|
||||
return dhara_nand_read(j->nand, p | ppc_mask,
|
||||
offset, DHARA_META_SIZE,
|
||||
buf, err);
|
||||
}
|
||||
|
||||
dhara_page_t dhara_journal_peek(struct dhara_journal *j)
|
||||
{
|
||||
if (j->head == j->tail)
|
||||
return DHARA_PAGE_NONE;
|
||||
|
||||
if (is_aligned(j->tail, j->nand->log2_ppb)) {
|
||||
dhara_block_t blk = j->tail >> j->nand->log2_ppb;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||
if ((blk == (j->head >> j->nand->log2_ppb)) ||
|
||||
!dhara_nand_is_bad(j->nand, blk)) {
|
||||
j->tail = blk << j->nand->log2_ppb;
|
||||
|
||||
if (j->tail == j->head)
|
||||
j->root = DHARA_PAGE_NONE;
|
||||
|
||||
return j->tail;
|
||||
}
|
||||
|
||||
blk = next_block(j->nand, blk);
|
||||
}
|
||||
}
|
||||
|
||||
return j->tail;
|
||||
}
|
||||
|
||||
static dhara_page_t wrap(dhara_page_t a, dhara_page_t b)
|
||||
{
|
||||
return a >= b ? (a - b) : a;
|
||||
}
|
||||
|
||||
void dhara_journal_dequeue(struct dhara_journal *j)
|
||||
{
|
||||
if (j->head == j->tail)
|
||||
return;
|
||||
|
||||
j->tail = next_upage(j, j->tail);
|
||||
|
||||
/* If the journal is clean at the time of dequeue, then this
|
||||
* data was always obsolete, and can be reused immediately.
|
||||
*/
|
||||
if (!(j->flags & (DHARA_JOURNAL_F_DIRTY | DHARA_JOURNAL_F_RECOVERY)))
|
||||
j->tail_sync = j->tail;
|
||||
|
||||
const dhara_page_t chip_size = j->nand->num_blocks << j->nand->log2_ppb;
|
||||
const dhara_page_t raw_size = wrap(j->head + chip_size - j->tail,
|
||||
chip_size);
|
||||
const dhara_page_t root_offset = wrap(j->head + chip_size - j->root,
|
||||
chip_size);
|
||||
|
||||
if (root_offset > raw_size)
|
||||
j->root = DHARA_PAGE_NONE;
|
||||
}
|
||||
|
||||
void dhara_journal_clear(struct dhara_journal *j)
|
||||
{
|
||||
j->tail = j->head;
|
||||
j->root = DHARA_PAGE_NONE;
|
||||
j->flags |= DHARA_JOURNAL_F_DIRTY;
|
||||
|
||||
hdr_clear_user(j->page_buf, j->nand->log2_page_size);
|
||||
}
|
||||
|
||||
static int skip_block(struct dhara_journal *j, dhara_error_t *err)
|
||||
{
|
||||
const dhara_block_t next = next_block(j->nand,
|
||||
j->head >> j->nand->log2_ppb);
|
||||
|
||||
/* We can't roll onto the same block as the tail */
|
||||
if ((j->tail_sync >> j->nand->log2_ppb) == next) {
|
||||
dhara_set_error(err, DHARA_E_JOURNAL_FULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
j->head = next << j->nand->log2_ppb;
|
||||
if (!j->head)
|
||||
roll_stats(j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure the head pointer is on a ready-to-program page. */
|
||||
static int prepare_head(struct dhara_journal *j, dhara_error_t *err)
|
||||
{
|
||||
const dhara_page_t next = next_upage(j, j->head);
|
||||
int i;
|
||||
|
||||
/* We can't write if doing so would cause the head pointer to
|
||||
* roll onto the same block as the last-synced tail.
|
||||
*/
|
||||
if (align_eq(next, j->tail_sync, j->nand->log2_ppb) &&
|
||||
!align_eq(next, j->head, j->nand->log2_ppb)) {
|
||||
dhara_set_error(err, DHARA_E_JOURNAL_FULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
j->flags |= DHARA_JOURNAL_F_DIRTY;
|
||||
if (!is_aligned(j->head, j->nand->log2_ppb))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||
const dhara_block_t blk = j->head >> j->nand->log2_ppb;
|
||||
|
||||
if (!dhara_nand_is_bad(j->nand, blk))
|
||||
return dhara_nand_erase(j->nand, blk, err);
|
||||
|
||||
j->bb_current++;
|
||||
if (skip_block(j, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void restart_recovery(struct dhara_journal *j, dhara_page_t old_head)
|
||||
{
|
||||
/* Mark the current head bad immediately, unless we're also
|
||||
* using it to hold our dumped metadata (it will then be marked
|
||||
* bad at the end of recovery).
|
||||
*/
|
||||
if ((j->recover_meta == DHARA_PAGE_NONE) ||
|
||||
!align_eq(j->recover_meta, old_head, j->nand->log2_ppb))
|
||||
dhara_nand_mark_bad(j->nand, old_head >> j->nand->log2_ppb);
|
||||
else
|
||||
j->flags |= DHARA_JOURNAL_F_BAD_META;
|
||||
|
||||
/* Start recovery again. Reset the source enumeration to
|
||||
* the start of the original bad block, and reset the
|
||||
* destination enumeration to the newly found good
|
||||
* block.
|
||||
*/
|
||||
j->flags &= ~DHARA_JOURNAL_F_ENUM_DONE;
|
||||
j->recover_next =
|
||||
j->recover_root & ~((1 << j->nand->log2_ppb) - 1);
|
||||
|
||||
j->root = j->recover_root;
|
||||
}
|
||||
|
||||
static int dump_meta(struct dhara_journal *j, dhara_error_t *err)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* We've just begun recovery on a new erasable block, but we
|
||||
* have buffered metadata from the failed block.
|
||||
*/
|
||||
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||
dhara_error_t my_err;
|
||||
|
||||
/* Try to dump metadata on this page */
|
||||
if (!(prepare_head(j, &my_err) ||
|
||||
dhara_nand_prog(j->nand, j->head,
|
||||
j->page_buf, &my_err))) {
|
||||
j->recover_meta = j->head;
|
||||
j->head = next_upage(j, j->head);
|
||||
if (!j->head)
|
||||
roll_stats(j);
|
||||
hdr_clear_user(j->page_buf, j->nand->log2_page_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Report fatal errors */
|
||||
if (my_err != DHARA_E_BAD_BLOCK) {
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
j->bb_current++;
|
||||
dhara_nand_mark_bad(j->nand, j->head >> j->nand->log2_ppb);
|
||||
|
||||
if (skip_block(j, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int recover_from(struct dhara_journal *j,
|
||||
dhara_error_t write_err,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
const dhara_page_t old_head = j->head;
|
||||
|
||||
if (write_err != DHARA_E_BAD_BLOCK) {
|
||||
dhara_set_error(err, write_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Advance to the next free page */
|
||||
j->bb_current++;
|
||||
if (skip_block(j, err) < 0)
|
||||
return -1;
|
||||
|
||||
/* Are we already in the middle of a recovery? */
|
||||
if (dhara_journal_in_recovery(j)) {
|
||||
restart_recovery(j, old_head);
|
||||
dhara_set_error(err, DHARA_E_RECOVER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Were we block aligned? No recovery required! */
|
||||
if (is_aligned(old_head, j->nand->log2_ppb)) {
|
||||
dhara_nand_mark_bad(j->nand, old_head >> j->nand->log2_ppb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
j->recover_root = j->root;
|
||||
j->recover_next =
|
||||
j->recover_root & ~((1 << j->nand->log2_ppb) - 1);
|
||||
|
||||
/* Are we holding buffered metadata? Dump it first. */
|
||||
if (!is_aligned(old_head, j->log2_ppc) &&
|
||||
dump_meta(j, err) < 0)
|
||||
return -1;
|
||||
|
||||
j->flags |= DHARA_JOURNAL_F_RECOVERY;
|
||||
dhara_set_error(err, DHARA_E_RECOVER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void finish_recovery(struct dhara_journal *j)
|
||||
{
|
||||
/* We just recovered the last page. Mark the recovered
|
||||
* block as bad.
|
||||
*/
|
||||
dhara_nand_mark_bad(j->nand,
|
||||
j->recover_root >> j->nand->log2_ppb);
|
||||
|
||||
/* If we had to dump metadata, and the page on which we
|
||||
* did this also went bad, mark it bad too.
|
||||
*/
|
||||
if (j->flags & DHARA_JOURNAL_F_BAD_META)
|
||||
dhara_nand_mark_bad(j->nand,
|
||||
j->recover_meta >> j->nand->log2_ppb);
|
||||
|
||||
/* Was the tail on this page? Skip it forward */
|
||||
clear_recovery(j);
|
||||
}
|
||||
|
||||
static int push_meta(struct dhara_journal *j, const uint8_t *meta,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
const dhara_page_t old_head = j->head;
|
||||
dhara_error_t my_err;
|
||||
const size_t offset =
|
||||
hdr_user_offset(j->head & ((1 << j->log2_ppc) - 1));
|
||||
|
||||
/* We've just written a user page. Add the metadata to the
|
||||
* buffer.
|
||||
*/
|
||||
if (meta)
|
||||
memcpy(j->page_buf + offset, meta, DHARA_META_SIZE);
|
||||
else
|
||||
memset(j->page_buf + offset, 0xff, DHARA_META_SIZE);
|
||||
|
||||
/* Unless we've filled the buffer, don't do any IO */
|
||||
if (!is_aligned(j->head + 2, j->log2_ppc)) {
|
||||
j->root = j->head;
|
||||
j->head++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't need to check for immediate recover, because that'll
|
||||
* never happen -- we're not block-aligned.
|
||||
*/
|
||||
hdr_put_magic(j->page_buf);
|
||||
hdr_set_epoch(j->page_buf, j->epoch);
|
||||
hdr_set_tail(j->page_buf, j->tail);
|
||||
hdr_set_bb_current(j->page_buf, j->bb_current);
|
||||
hdr_set_bb_last(j->page_buf, j->bb_last);
|
||||
|
||||
if (dhara_nand_prog(j->nand, j->head + 1, j->page_buf, &my_err) < 0)
|
||||
return recover_from(j, my_err, err);
|
||||
|
||||
j->flags &= ~DHARA_JOURNAL_F_DIRTY;
|
||||
|
||||
j->root = old_head;
|
||||
j->head = next_upage(j, j->head);
|
||||
|
||||
if (!j->head)
|
||||
roll_stats(j);
|
||||
|
||||
if (j->flags & DHARA_JOURNAL_F_ENUM_DONE)
|
||||
finish_recovery(j);
|
||||
|
||||
if (!(j->flags & DHARA_JOURNAL_F_RECOVERY))
|
||||
j->tail_sync = j->tail;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_journal_enqueue(struct dhara_journal *j,
|
||||
const uint8_t *data, const uint8_t *meta,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
dhara_error_t my_err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||
if (!(prepare_head(j, &my_err) ||
|
||||
(data && dhara_nand_prog(j->nand, j->head, data,
|
||||
&my_err))))
|
||||
return push_meta(j, meta, err);
|
||||
|
||||
if (recover_from(j, my_err, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dhara_journal_copy(struct dhara_journal *j,
|
||||
dhara_page_t p, const uint8_t *meta,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
dhara_error_t my_err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DHARA_MAX_RETRIES; i++) {
|
||||
if (!(prepare_head(j, &my_err) ||
|
||||
dhara_nand_copy(j->nand, p, j->head, &my_err)))
|
||||
return push_meta(j, meta, err);
|
||||
|
||||
if (recover_from(j, my_err, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dhara_page_t dhara_journal_next_recoverable(struct dhara_journal *j)
|
||||
{
|
||||
const dhara_page_t n = j->recover_next;
|
||||
|
||||
if (!dhara_journal_in_recovery(j))
|
||||
return DHARA_PAGE_NONE;
|
||||
|
||||
if (j->flags & DHARA_JOURNAL_F_ENUM_DONE)
|
||||
return DHARA_PAGE_NONE;
|
||||
|
||||
if (j->recover_next == j->recover_root)
|
||||
j->flags |= DHARA_JOURNAL_F_ENUM_DONE;
|
||||
else
|
||||
j->recover_next = next_upage(j, j->recover_next);
|
||||
|
||||
return n;
|
||||
}
|
||||
256
Lib/dhara/journal.h
Normal file
256
Lib/dhara/journal.h
Normal file
@@ -0,0 +1,256 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DHARA_JOURNAL_H_
|
||||
#define DHARA_JOURNAL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "nand.h"
|
||||
|
||||
/* Number of bytes used by the journal checkpoint header. */
|
||||
#define DHARA_HEADER_SIZE 16
|
||||
|
||||
/* Global metadata available for a higher layer. This metadata is
|
||||
* persistent once the journal reaches a checkpoint, and is restored on
|
||||
* startup.
|
||||
*/
|
||||
#define DHARA_COOKIE_SIZE 4
|
||||
|
||||
/* This is the size of the metadata slice which accompanies each written
|
||||
* page. This is independent of the underlying page/OOB size.
|
||||
*/
|
||||
#define DHARA_META_SIZE 132
|
||||
|
||||
/* When a block fails, or garbage is encountered, we try again on the
|
||||
* next block/checkpoint. We can do this up to the given number of
|
||||
* times.
|
||||
*/
|
||||
#define DHARA_MAX_RETRIES 8
|
||||
|
||||
/* This is a page number which can be used to represent "no such page".
|
||||
* It's guaranteed to never be a valid user page.
|
||||
*/
|
||||
#define DHARA_PAGE_NONE ((dhara_page_t)0xffffffff)
|
||||
|
||||
/* State flags */
|
||||
#define DHARA_JOURNAL_F_DIRTY 0x01
|
||||
#define DHARA_JOURNAL_F_BAD_META 0x02
|
||||
#define DHARA_JOURNAL_F_RECOVERY 0x04
|
||||
#define DHARA_JOURNAL_F_ENUM_DONE 0x08
|
||||
|
||||
/* The journal layer presents the NAND pages as a double-ended queue.
|
||||
* Pages, with associated metadata may be pushed onto the end of the
|
||||
* queue, and pages may be popped from the end.
|
||||
*
|
||||
* Block erase, metadata storage are handled automatically. Bad blocks
|
||||
* are handled by relocating data to the next available non-bad page in
|
||||
* the sequence.
|
||||
*
|
||||
* It's up to the user to ensure that the queue doesn't grow beyond the
|
||||
* capacity of the NAND chip, but helper functions are provided to
|
||||
* assist with this. If the head meets the tail, the journal will refuse
|
||||
* to enqueue more pages.
|
||||
*/
|
||||
struct dhara_journal {
|
||||
const struct dhara_nand *nand;
|
||||
uint8_t *page_buf;
|
||||
|
||||
/* In the journal, user data is grouped into checkpoints of
|
||||
* 2**log2_ppc contiguous aligned pages.
|
||||
*
|
||||
* The last page of each checkpoint contains the journal header
|
||||
* and the metadata for the other pages in the period (the user
|
||||
* pages).
|
||||
*/
|
||||
uint8_t log2_ppc;
|
||||
|
||||
/* Epoch counter. This is incremented whenever the journal head
|
||||
* passes the end of the chip and wraps around.
|
||||
*/
|
||||
uint8_t epoch;
|
||||
|
||||
/* General purpose flags field */
|
||||
uint8_t flags;
|
||||
|
||||
/* Bad-block counters. bb_last is our best estimate of the
|
||||
* number of bad blocks in the chip as a whole. bb_current is
|
||||
* the number of bad blocks in all blocks before the current
|
||||
* head.
|
||||
*/
|
||||
dhara_block_t bb_current;
|
||||
dhara_block_t bb_last;
|
||||
|
||||
/* Log head and tail. The tail pointer points to the last user
|
||||
* page in the log, and the head pointer points to the next free
|
||||
* raw page. The root points to the last written user page.
|
||||
*/
|
||||
dhara_page_t tail_sync;
|
||||
dhara_page_t tail;
|
||||
dhara_page_t head;
|
||||
|
||||
/* This points to the last written user page in the journal */
|
||||
dhara_page_t root;
|
||||
|
||||
/* Recovery mode: recover_root points to the last valid user
|
||||
* page in the block requiring recovery. recover_next points to
|
||||
* the next user page needing recovery.
|
||||
*
|
||||
* If we had buffered metadata before recovery started, it will
|
||||
* have been dumped to a free page, indicated by recover_meta.
|
||||
* If this block later goes bad, we will have to defer bad-block
|
||||
* marking until recovery is complete (F_BAD_META).
|
||||
*/
|
||||
dhara_page_t recover_next;
|
||||
dhara_page_t recover_root;
|
||||
dhara_page_t recover_meta;
|
||||
};
|
||||
|
||||
/* Initialize a journal. You must supply a pointer to a NAND chip
|
||||
* driver, and a single page buffer. This page buffer will be used
|
||||
* exclusively by the journal, but you are responsible for allocating
|
||||
* it, and freeing it (if necessary) at the end.
|
||||
*
|
||||
* No NAND operations are performed at this point.
|
||||
*/
|
||||
void dhara_journal_init(struct dhara_journal *j,
|
||||
const struct dhara_nand *n,
|
||||
uint8_t *page_buf);
|
||||
|
||||
/* Start up the journal -- search the NAND for the journal head, or
|
||||
* initialize a blank journal if one isn't found. Returns 0 on success
|
||||
* or -1 if a (fatal) error occurs.
|
||||
*
|
||||
* This operation is O(log N), where N is the number of pages in the
|
||||
* NAND chip. All other operations are O(1).
|
||||
*
|
||||
* If this operation fails, the journal will be reset to an empty state.
|
||||
*/
|
||||
int dhara_journal_resume(struct dhara_journal *j, dhara_error_t *err);
|
||||
|
||||
/* Obtain an upper bound on the number of user pages storable in the
|
||||
* journal.
|
||||
*/
|
||||
dhara_page_t dhara_journal_capacity(const struct dhara_journal *j);
|
||||
|
||||
/* Obtain an upper bound on the number of user pages consumed by the
|
||||
* journal.
|
||||
*/
|
||||
dhara_page_t dhara_journal_size(const struct dhara_journal *j);
|
||||
|
||||
/* Obtain a pointer to the cookie data */
|
||||
static inline uint8_t *dhara_journal_cookie(const struct dhara_journal *j)
|
||||
{
|
||||
return j->page_buf + DHARA_HEADER_SIZE;
|
||||
}
|
||||
|
||||
/* Obtain the locations of the first and last pages in the journal.
|
||||
*/
|
||||
static inline dhara_page_t dhara_journal_root(const struct dhara_journal *j)
|
||||
{
|
||||
return j->root;
|
||||
}
|
||||
|
||||
/* Read metadata associated with a page. This assumes that the page
|
||||
* provided is a valid data page. The actual page data is read via the
|
||||
* normal NAND interface.
|
||||
*/
|
||||
int dhara_journal_read_meta(struct dhara_journal *j, dhara_page_t p,
|
||||
uint8_t *buf, dhara_error_t *err);
|
||||
|
||||
/* Advance the tail to the next non-bad block and return the page that's
|
||||
* ready to read. If no page is ready, return DHARA_PAGE_NONE.
|
||||
*/
|
||||
dhara_page_t dhara_journal_peek(struct dhara_journal *j);
|
||||
|
||||
/* Remove the last page from the journal. This doesn't take permanent
|
||||
* effect until the next checkpoint.
|
||||
*/
|
||||
void dhara_journal_dequeue(struct dhara_journal *j);
|
||||
|
||||
/* Remove all pages form the journal. This doesn't take permanent effect
|
||||
* until the next checkpoint.
|
||||
*/
|
||||
void dhara_journal_clear(struct dhara_journal *j);
|
||||
|
||||
/* Append a page to the journal. Both raw page data and metadata must be
|
||||
* specified. The push operation is not persistent until a checkpoint is
|
||||
* reached.
|
||||
*
|
||||
* This operation may fail with the error code E_RECOVER. If this
|
||||
* occurs, the upper layer must complete the assisted recovery procedure
|
||||
* and then try again.
|
||||
*
|
||||
* This operation may be used as part of a recovery. If further errors
|
||||
* occur during recovery, E_RECOVER is returned, and the procedure must
|
||||
* be restarted.
|
||||
*/
|
||||
int dhara_journal_enqueue(struct dhara_journal *j,
|
||||
const uint8_t *data, const uint8_t *meta,
|
||||
dhara_error_t *err);
|
||||
|
||||
/* Copy an existing page to the front of the journal. New metadata must
|
||||
* be specified. This operation is not persistent until a checkpoint is
|
||||
* reached.
|
||||
*
|
||||
* This operation may fail with the error code E_RECOVER. If this
|
||||
* occurs, the upper layer must complete the assisted recovery procedure
|
||||
* and then try again.
|
||||
*
|
||||
* This operation may be used as part of a recovery. If further errors
|
||||
* occur during recovery, E_RECOVER is returned, and the procedure must
|
||||
* be restarted.
|
||||
*/
|
||||
int dhara_journal_copy(struct dhara_journal *j,
|
||||
dhara_page_t p, const uint8_t *meta,
|
||||
dhara_error_t *err);
|
||||
|
||||
/* Mark the journal dirty. */
|
||||
static inline void dhara_journal_mark_dirty(struct dhara_journal *j)
|
||||
{
|
||||
j->flags |= DHARA_JOURNAL_F_DIRTY;
|
||||
}
|
||||
|
||||
/* Is the journal checkpointed? If true, then all pages enqueued are now
|
||||
* persistent.
|
||||
*/
|
||||
static inline int dhara_journal_is_clean(const struct dhara_journal *j)
|
||||
{
|
||||
return !(j->flags & DHARA_JOURNAL_F_DIRTY);
|
||||
}
|
||||
|
||||
/* If an operation returns E_RECOVER, you must begin the recovery
|
||||
* procedure. You must then:
|
||||
*
|
||||
* - call dhara_journal_next_recoverable() to obtain the next block
|
||||
* to be recovered (if any). If there are no blocks remaining to be
|
||||
* recovered, DHARA_JOURNAL_PAGE_NONE is returned.
|
||||
*
|
||||
* - proceed to the next checkpoint. Once the journal is clean,
|
||||
* recovery will finish automatically.
|
||||
*
|
||||
* If any operation during recovery fails due to a bad block, E_RECOVER
|
||||
* is returned again, and recovery restarts. Do not add new data to the
|
||||
* journal (rewrites of recovered data are fine) until recovery is
|
||||
* complete.
|
||||
*/
|
||||
static inline int dhara_journal_in_recovery(const struct dhara_journal *j)
|
||||
{
|
||||
return j->flags & DHARA_JOURNAL_F_RECOVERY;
|
||||
}
|
||||
|
||||
dhara_page_t dhara_journal_next_recoverable(struct dhara_journal *j);
|
||||
|
||||
#endif
|
||||
530
Lib/dhara/map.c
Normal file
530
Lib/dhara/map.c
Normal file
@@ -0,0 +1,530 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "bytes.h"
|
||||
#include "map.h"
|
||||
|
||||
#define DHARA_RADIX_DEPTH (sizeof(dhara_sector_t) << 3)
|
||||
|
||||
static inline dhara_sector_t d_bit(int depth)
|
||||
{
|
||||
return ((dhara_sector_t)1) << (DHARA_RADIX_DEPTH - depth - 1);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Metadata/cookie layout
|
||||
*/
|
||||
|
||||
static inline void ck_set_count(uint8_t *cookie, dhara_sector_t count)
|
||||
{
|
||||
dhara_w32(cookie, count);
|
||||
}
|
||||
|
||||
static inline dhara_sector_t ck_get_count(const uint8_t *cookie)
|
||||
{
|
||||
return dhara_r32(cookie);
|
||||
}
|
||||
|
||||
static inline void meta_clear(uint8_t *meta)
|
||||
{
|
||||
memset(meta, 0xff, DHARA_META_SIZE);
|
||||
}
|
||||
|
||||
static inline dhara_sector_t meta_get_id(const uint8_t *meta)
|
||||
{
|
||||
return dhara_r32(meta);
|
||||
}
|
||||
|
||||
static inline void meta_set_id(uint8_t *meta, dhara_sector_t id)
|
||||
{
|
||||
dhara_w32(meta, id);
|
||||
}
|
||||
|
||||
static inline dhara_page_t meta_get_alt(const uint8_t *meta, int level)
|
||||
{
|
||||
return dhara_r32(meta + 4 + (level << 2));
|
||||
}
|
||||
|
||||
static inline void meta_set_alt(uint8_t *meta, int level, dhara_page_t alt)
|
||||
{
|
||||
dhara_w32(meta + 4 + (level << 2), alt);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Public interface
|
||||
*/
|
||||
|
||||
void dhara_map_init(struct dhara_map *m, const struct dhara_nand *n,
|
||||
uint8_t *page_buf, uint8_t gc_ratio)
|
||||
{
|
||||
if (!gc_ratio)
|
||||
gc_ratio = 1;
|
||||
|
||||
dhara_journal_init(&m->journal, n, page_buf);
|
||||
m->gc_ratio = gc_ratio;
|
||||
}
|
||||
|
||||
int dhara_map_resume(struct dhara_map *m, dhara_error_t *err)
|
||||
{
|
||||
if (dhara_journal_resume(&m->journal, err) < 0) {
|
||||
m->count = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
m->count = ck_get_count(dhara_journal_cookie(&m->journal));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dhara_map_clear(struct dhara_map *m)
|
||||
{
|
||||
if (m->count) {
|
||||
m->count = 0;
|
||||
dhara_journal_clear(&m->journal);
|
||||
}
|
||||
}
|
||||
|
||||
dhara_sector_t dhara_map_capacity(const struct dhara_map *m)
|
||||
{
|
||||
const dhara_sector_t cap = dhara_journal_capacity(&m->journal);
|
||||
const dhara_sector_t reserve = cap / (m->gc_ratio + 1);
|
||||
const dhara_sector_t safety_margin =
|
||||
DHARA_MAX_RETRIES << m->journal.nand->log2_ppb;
|
||||
|
||||
if (reserve + safety_margin >= cap)
|
||||
return 0;
|
||||
|
||||
return cap - reserve - safety_margin;
|
||||
}
|
||||
|
||||
/* Trace the path from the root to the given sector, emitting
|
||||
* alt-pointers and alt-full bits in the given metadata buffer. This
|
||||
* also returns the physical page containing the given sector, if it
|
||||
* exists.
|
||||
*
|
||||
* If the page can't be found, a suitable path will be constructed
|
||||
* (containing PAGE_NONE alt-pointers), and DHARA_E_NOT_FOUND will be
|
||||
* returned.
|
||||
*/
|
||||
static int trace_path(struct dhara_map *m, dhara_sector_t target,
|
||||
dhara_page_t *loc, uint8_t *new_meta,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
uint8_t meta[DHARA_META_SIZE];
|
||||
int depth = 0;
|
||||
dhara_page_t p = dhara_journal_root(&m->journal);
|
||||
|
||||
if (new_meta)
|
||||
meta_set_id(new_meta, target);
|
||||
|
||||
if (p == DHARA_PAGE_NONE)
|
||||
goto not_found;
|
||||
|
||||
if (dhara_journal_read_meta(&m->journal, p, meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
while (depth < DHARA_RADIX_DEPTH) {
|
||||
const dhara_sector_t id = meta_get_id(meta);
|
||||
|
||||
if (id == DHARA_SECTOR_NONE)
|
||||
goto not_found;
|
||||
|
||||
if ((target ^ id) & d_bit(depth)) {
|
||||
if (new_meta)
|
||||
meta_set_alt(new_meta, depth, p);
|
||||
|
||||
p = meta_get_alt(meta, depth);
|
||||
if (p == DHARA_PAGE_NONE) {
|
||||
depth++;
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if (dhara_journal_read_meta(&m->journal, p,
|
||||
meta, err) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
if (new_meta)
|
||||
meta_set_alt(new_meta, depth,
|
||||
meta_get_alt(meta, depth));
|
||||
}
|
||||
|
||||
depth++;
|
||||
}
|
||||
|
||||
if (loc)
|
||||
*loc = p;
|
||||
|
||||
return 0;
|
||||
|
||||
not_found:
|
||||
if (new_meta) {
|
||||
while (depth < DHARA_RADIX_DEPTH)
|
||||
meta_set_alt(new_meta, depth++, DHARA_SECTOR_NONE);
|
||||
}
|
||||
|
||||
dhara_set_error(err, DHARA_E_NOT_FOUND);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dhara_map_find(struct dhara_map *m, dhara_sector_t target,
|
||||
dhara_page_t *loc, dhara_error_t *err)
|
||||
{
|
||||
return trace_path(m, target, loc, NULL, err);
|
||||
}
|
||||
|
||||
int dhara_map_read(struct dhara_map *m, dhara_sector_t s,
|
||||
uint8_t *data, dhara_error_t *err)
|
||||
{
|
||||
const struct dhara_nand *n = m->journal.nand;
|
||||
dhara_error_t my_err;
|
||||
dhara_page_t p;
|
||||
|
||||
if (dhara_map_find(m, s, &p, &my_err) < 0) {
|
||||
if (my_err == DHARA_E_NOT_FOUND) {
|
||||
memset(data, 0xff, 1 << n->log2_page_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dhara_nand_read(n, p, 0, 1 << n->log2_page_size, data, err);
|
||||
}
|
||||
|
||||
/* Check the given page. If it's garbage, do nothing. Otherwise, rewrite
|
||||
* it at the front of the map. Return raw errors from the journal (do
|
||||
* not perform recovery).
|
||||
*/
|
||||
static int raw_gc(struct dhara_map *m, dhara_page_t src,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
dhara_sector_t target;
|
||||
dhara_page_t current;
|
||||
dhara_error_t my_err;
|
||||
uint8_t meta[DHARA_META_SIZE];
|
||||
|
||||
if (dhara_journal_read_meta(&m->journal, src, meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
/* Is the page just filler/garbage? */
|
||||
target = meta_get_id(meta);
|
||||
if (target == DHARA_SECTOR_NONE)
|
||||
return 0;
|
||||
|
||||
/* Find out where the sector once represented by this page
|
||||
* currently resides (if anywhere).
|
||||
*/
|
||||
if (trace_path(m, target, ¤t, meta, &my_err) < 0) {
|
||||
if (my_err == DHARA_E_NOT_FOUND)
|
||||
return 0;
|
||||
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Is this page still the most current representative? If not,
|
||||
* do nothing.
|
||||
*/
|
||||
if (current != src)
|
||||
return 0;
|
||||
|
||||
/* Rewrite it at the front of the journal with updated metadata */
|
||||
ck_set_count(dhara_journal_cookie(&m->journal), m->count);
|
||||
if (dhara_journal_copy(&m->journal, src, meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pad_queue(struct dhara_map *m, dhara_error_t *err)
|
||||
{
|
||||
dhara_page_t p = dhara_journal_root(&m->journal);
|
||||
uint8_t root_meta[DHARA_META_SIZE];
|
||||
|
||||
ck_set_count(dhara_journal_cookie(&m->journal), m->count);
|
||||
|
||||
if (p == DHARA_PAGE_NONE)
|
||||
return dhara_journal_enqueue(&m->journal, NULL, NULL, err);
|
||||
|
||||
if (dhara_journal_read_meta(&m->journal, p, root_meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
return dhara_journal_copy(&m->journal, p, root_meta, err);
|
||||
}
|
||||
|
||||
/* Attempt to recover the journal */
|
||||
static int try_recover(struct dhara_map *m, dhara_error_t cause,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
int restart_count = 0;
|
||||
|
||||
if (cause != DHARA_E_RECOVER) {
|
||||
dhara_set_error(err, cause);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (dhara_journal_in_recovery(&m->journal)) {
|
||||
dhara_page_t p = dhara_journal_next_recoverable(&m->journal);
|
||||
dhara_error_t my_err;
|
||||
int ret;
|
||||
|
||||
if (p == DHARA_PAGE_NONE)
|
||||
ret = pad_queue(m, &my_err);
|
||||
else
|
||||
ret = raw_gc(m, p, &my_err);
|
||||
|
||||
if (ret < 0) {
|
||||
if (my_err != DHARA_E_RECOVER) {
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (restart_count >= DHARA_MAX_RETRIES) {
|
||||
dhara_set_error(err, DHARA_E_TOO_BAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
restart_count++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auto_gc(struct dhara_map *m, dhara_error_t *err)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dhara_journal_size(&m->journal) < dhara_map_capacity(m))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i <= m->gc_ratio; i++)
|
||||
if (dhara_map_gc(m, err) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_write(struct dhara_map *m, dhara_sector_t dst,
|
||||
uint8_t *meta, dhara_error_t *err)
|
||||
{
|
||||
dhara_error_t my_err;
|
||||
|
||||
if (auto_gc(m, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (trace_path(m, dst, NULL, meta, &my_err) < 0) {
|
||||
if (my_err != DHARA_E_NOT_FOUND) {
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (m->count >= dhara_map_capacity(m)) {
|
||||
dhara_set_error(err, DHARA_E_MAP_FULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m->count++;
|
||||
}
|
||||
|
||||
ck_set_count(dhara_journal_cookie(&m->journal), m->count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_map_write(struct dhara_map *m, dhara_sector_t dst,
|
||||
const uint8_t *data, dhara_error_t *err)
|
||||
{
|
||||
for (;;) {
|
||||
uint8_t meta[DHARA_META_SIZE];
|
||||
dhara_error_t my_err;
|
||||
const dhara_sector_t old_count = m->count;
|
||||
|
||||
if (prepare_write(m, dst, meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (!dhara_journal_enqueue(&m->journal, data, meta, &my_err))
|
||||
break;
|
||||
|
||||
m->count = old_count;
|
||||
|
||||
if (try_recover(m, my_err, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_map_copy_page(struct dhara_map *m, dhara_page_t src,
|
||||
dhara_sector_t dst, dhara_error_t *err)
|
||||
{
|
||||
for (;;) {
|
||||
uint8_t meta[DHARA_META_SIZE];
|
||||
dhara_error_t my_err;
|
||||
const dhara_sector_t old_count = m->count;
|
||||
|
||||
if (prepare_write(m, dst, meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (!dhara_journal_copy(&m->journal, src, meta, &my_err))
|
||||
break;
|
||||
|
||||
m->count = old_count;
|
||||
|
||||
if (try_recover(m, my_err, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_map_copy_sector(struct dhara_map *m, dhara_sector_t src,
|
||||
dhara_sector_t dst, dhara_error_t *err)
|
||||
{
|
||||
dhara_error_t my_err;
|
||||
dhara_page_t p;
|
||||
|
||||
if (dhara_map_find(m, src, &p, &my_err) < 0) {
|
||||
if (my_err == DHARA_E_NOT_FOUND)
|
||||
return dhara_map_trim(m, dst, err);
|
||||
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dhara_map_copy_page(m, p, dst, err);
|
||||
}
|
||||
|
||||
static int try_delete(struct dhara_map *m, dhara_sector_t s,
|
||||
dhara_error_t *err)
|
||||
{
|
||||
dhara_error_t my_err;
|
||||
uint8_t meta[DHARA_META_SIZE];
|
||||
dhara_page_t alt_page;
|
||||
uint8_t alt_meta[DHARA_META_SIZE];
|
||||
int level = DHARA_RADIX_DEPTH - 1;
|
||||
int i;
|
||||
|
||||
if (trace_path(m, s, NULL, meta, &my_err) < 0) {
|
||||
if (my_err == DHARA_E_NOT_FOUND)
|
||||
return 0;
|
||||
|
||||
dhara_set_error(err, my_err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Select any of the closest cousins of this node which are
|
||||
* subtrees of at least the requested order.
|
||||
*/
|
||||
while (level >= 0) {
|
||||
alt_page = meta_get_alt(meta, level);
|
||||
if (alt_page != DHARA_PAGE_NONE)
|
||||
break;
|
||||
level--;
|
||||
}
|
||||
|
||||
/* Special case: deletion of last sector */
|
||||
if (level < 0) {
|
||||
m->count = 0;
|
||||
dhara_journal_clear(&m->journal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Rewrite the cousin with an up-to-date path which doesn't
|
||||
* point to the original node.
|
||||
*/
|
||||
if (dhara_journal_read_meta(&m->journal, alt_page, alt_meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
meta_set_id(meta, meta_get_id(alt_meta));
|
||||
|
||||
meta_set_alt(meta, level, DHARA_PAGE_NONE);
|
||||
for (i = level + 1; i < DHARA_RADIX_DEPTH; i++)
|
||||
meta_set_alt(meta, i, meta_get_alt(alt_meta, i));
|
||||
|
||||
meta_set_alt(meta, level, DHARA_PAGE_NONE);
|
||||
|
||||
ck_set_count(dhara_journal_cookie(&m->journal), m->count - 1);
|
||||
if (dhara_journal_copy(&m->journal, alt_page, meta, err) < 0)
|
||||
return -1;
|
||||
|
||||
m->count--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_map_trim(struct dhara_map *m, dhara_sector_t s, dhara_error_t *err)
|
||||
{
|
||||
for (;;) {
|
||||
dhara_error_t my_err;
|
||||
|
||||
if (auto_gc(m, err) < 0)
|
||||
return -1;
|
||||
|
||||
if (!try_delete(m, s, &my_err))
|
||||
break;
|
||||
|
||||
if (try_recover(m, my_err, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_map_sync(struct dhara_map *m, dhara_error_t *err)
|
||||
{
|
||||
while (!dhara_journal_is_clean(&m->journal)) {
|
||||
dhara_page_t p = dhara_journal_peek(&m->journal);
|
||||
dhara_error_t my_err;
|
||||
int ret;
|
||||
|
||||
if (p == DHARA_PAGE_NONE) {
|
||||
ret = pad_queue(m, &my_err);
|
||||
} else {
|
||||
ret = raw_gc(m, p, &my_err);
|
||||
if (!ret)
|
||||
dhara_journal_dequeue(&m->journal);
|
||||
}
|
||||
|
||||
if ((ret < 0) && (try_recover(m, my_err, err) < 0))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhara_map_gc(struct dhara_map *m, dhara_error_t *err)
|
||||
{
|
||||
if (!m->count)
|
||||
return 0;
|
||||
|
||||
for (;;) {
|
||||
dhara_page_t tail = dhara_journal_peek(&m->journal);
|
||||
dhara_error_t my_err;
|
||||
|
||||
if (tail == DHARA_PAGE_NONE)
|
||||
break;
|
||||
|
||||
if (!raw_gc(m, tail, &my_err)) {
|
||||
dhara_journal_dequeue(&m->journal);
|
||||
break;
|
||||
}
|
||||
|
||||
if (try_recover(m, my_err, err) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
114
Lib/dhara/map.h
Normal file
114
Lib/dhara/map.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DHARA_MAP_H_
|
||||
#define DHARA_MAP_H_
|
||||
|
||||
#include "journal.h"
|
||||
|
||||
/* The map is a journal indexing format. It maps virtual sectors to
|
||||
* pages of data in flash memory.
|
||||
*/
|
||||
typedef uint32_t dhara_sector_t;
|
||||
|
||||
/* This sector value is reserved */
|
||||
#define DHARA_SECTOR_NONE 0xffffffff
|
||||
|
||||
struct dhara_map {
|
||||
struct dhara_journal journal;
|
||||
|
||||
uint8_t gc_ratio;
|
||||
dhara_sector_t count;
|
||||
};
|
||||
|
||||
/* Initialize a map. You need to supply a buffer for page metadata, and
|
||||
* a garbage collection ratio. This is the ratio of garbage collection
|
||||
* operations to real writes when automatic collection is active.
|
||||
*
|
||||
* Smaller values lead to faster and more predictable IO, at the
|
||||
* expense of capacity. You should always initialize the same chip with
|
||||
* the same garbage collection ratio.
|
||||
*/
|
||||
void dhara_map_init(struct dhara_map *m, const struct dhara_nand *n,
|
||||
uint8_t *page_buf, uint8_t gc_ratio);
|
||||
|
||||
/* Recover stored state, if possible. If there is no valid stored state
|
||||
* on the chip, -1 is returned, and an empty map is initialized.
|
||||
*/
|
||||
int dhara_map_resume(struct dhara_map *m, dhara_error_t *err);
|
||||
|
||||
/* Clear the map (delete all sectors). */
|
||||
void dhara_map_clear(struct dhara_map *m);
|
||||
|
||||
/* Obtain the maximum capacity of the map. */
|
||||
dhara_sector_t dhara_map_capacity(const struct dhara_map *m);
|
||||
|
||||
/* Obtain the current number of allocated sectors. */
|
||||
static inline dhara_sector_t dhara_map_size(const struct dhara_map *m)
|
||||
{
|
||||
return m->count;
|
||||
}
|
||||
|
||||
/* Find the physical page which holds the current data for this sector.
|
||||
* Returns 0 on success or -1 if an error occurs. If the sector doesn't
|
||||
* exist, the error is E_NOT_FOUND.
|
||||
*/
|
||||
int dhara_map_find(struct dhara_map *m, dhara_sector_t s,
|
||||
dhara_page_t *loc, dhara_error_t *err);
|
||||
|
||||
/* Read from the given logical sector. If the sector is unmapped, a
|
||||
* blank page (0xff) will be returned.
|
||||
*/
|
||||
int dhara_map_read(struct dhara_map *m, dhara_sector_t s,
|
||||
uint8_t *data, dhara_error_t *err);
|
||||
|
||||
/* Write data to a logical sector. */
|
||||
int dhara_map_write(struct dhara_map *m, dhara_sector_t s,
|
||||
const uint8_t *data, dhara_error_t *err);
|
||||
|
||||
/* Copy any flash page to a logical sector. */
|
||||
int dhara_map_copy_page(struct dhara_map *m, dhara_page_t src,
|
||||
dhara_sector_t dst, dhara_error_t *err);
|
||||
|
||||
/* Copy one sector to another. If the source sector is unmapped, the
|
||||
* destination sector will be trimmed.
|
||||
*/
|
||||
int dhara_map_copy_sector(struct dhara_map *m, dhara_sector_t src,
|
||||
dhara_sector_t dst, dhara_error_t *err);
|
||||
|
||||
/* Delete a logical sector. You don't necessarily need to do this, but
|
||||
* it's a useful hint if you no longer require the sector's data to be
|
||||
* kept.
|
||||
*
|
||||
* If order is non-zero, it specifies that all sectors in the
|
||||
* (2**order)-aligned group of s are to be deleted.
|
||||
*/
|
||||
int dhara_map_trim(struct dhara_map *m, dhara_sector_t s,
|
||||
dhara_error_t *err);
|
||||
|
||||
/* Synchronize the map. Once this returns successfully, all changes to
|
||||
* date are persistent and durable. Conversely, there is no guarantee
|
||||
* that unsynchronized changes will be persistent.
|
||||
*/
|
||||
int dhara_map_sync(struct dhara_map *m, dhara_error_t *err);
|
||||
|
||||
/* Perform one garbage collection step. You can do this whenever you
|
||||
* like, but it's not necessary -- garbage collection happens
|
||||
* automatically and is interleaved with other operations.
|
||||
*/
|
||||
int dhara_map_gc(struct dhara_map *m, dhara_error_t *err);
|
||||
|
||||
#endif
|
||||
105
Lib/dhara/nand.h
Normal file
105
Lib/dhara/nand.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Dhara - NAND flash management layer
|
||||
* Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DHARA_NAND_H_
|
||||
#define DHARA_NAND_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "error.h"
|
||||
|
||||
/* Each page in a NAND device is indexed, starting at 0. It's required
|
||||
* that there be a power-of-two number of pages in a eraseblock, so you can
|
||||
* view a page number is being a concatenation (in binary) of a block
|
||||
* number and the number of a page within a block.
|
||||
*/
|
||||
typedef uint32_t dhara_page_t;
|
||||
|
||||
/* Blocks are also indexed, starting at 0. */
|
||||
typedef uint32_t dhara_block_t;
|
||||
|
||||
/* Each NAND chip must be represented by one of these structures. It's
|
||||
* intended that this structure be embedded in a larger structure for
|
||||
* context.
|
||||
*
|
||||
* The functions declared below are not implemented -- they must be
|
||||
* provided and satisfy the documented conditions.
|
||||
*/
|
||||
struct dhara_nand {
|
||||
/* Base-2 logarithm of the page size. If your device supports
|
||||
* partial programming, you may want to subdivide the actual
|
||||
* pages into separate ECC-correctable regions and present those
|
||||
* as pages.
|
||||
*/
|
||||
uint8_t log2_page_size;
|
||||
|
||||
/* Base-2 logarithm of the number of pages within an eraseblock */
|
||||
uint8_t log2_ppb;
|
||||
|
||||
/* Total number of eraseblocks */
|
||||
unsigned int num_blocks;
|
||||
};
|
||||
|
||||
/* Is the given block bad? */
|
||||
int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b);
|
||||
|
||||
/* Mark bad the given block (or attempt to). No return value is
|
||||
* required, because there's nothing that can be done in response.
|
||||
*/
|
||||
void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b);
|
||||
|
||||
/* Erase the given block. This function should return 0 on success or -1
|
||||
* on failure.
|
||||
*
|
||||
* The status reported by the chip should be checked. If an erase
|
||||
* operation fails, return -1 and set err to E_BAD_BLOCK.
|
||||
*/
|
||||
int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b,
|
||||
dhara_error_t *err);
|
||||
|
||||
/* Program the given page. The data pointer is a pointer to an entire
|
||||
* page ((1 << log2_page_size) bytes). The operation status should be
|
||||
* checked. If the operation fails, return -1 and set err to
|
||||
* E_BAD_BLOCK.
|
||||
*
|
||||
* Pages will be programmed sequentially within a block, and will not be
|
||||
* reprogrammed.
|
||||
*/
|
||||
int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p,
|
||||
const uint8_t *data,
|
||||
dhara_error_t *err);
|
||||
|
||||
/* Check that the given page is erased */
|
||||
int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p);
|
||||
|
||||
/* Read a portion of a page. ECC must be handled by the NAND
|
||||
* implementation. Returns 0 on sucess or -1 if an error occurs. If an
|
||||
* uncorrectable ECC error occurs, return -1 and set err to E_ECC.
|
||||
*/
|
||||
int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p,
|
||||
size_t offset, size_t length,
|
||||
uint8_t *data,
|
||||
dhara_error_t *err);
|
||||
|
||||
/* Read a page from one location and reprogram it in another location.
|
||||
* This might be done using the chip's internal buffers, but it must use
|
||||
* ECC.
|
||||
*/
|
||||
int dhara_nand_copy(const struct dhara_nand *n,
|
||||
dhara_page_t src, dhara_page_t dst,
|
||||
dhara_error_t *err);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
1
MDK-ARM/_build.cmd
Normal file
1
MDK-ARM/_build.cmd
Normal file
@@ -0,0 +1 @@
|
||||
@echo off && cd /d "%~dp0" && C:\Keil_v5\UV4\UV4.exe -r "STM32F407-Demo.uvprojx" -t STM32F407-Demo -j0
|
||||
19
MDK-ARM/build.bat
Normal file
19
MDK-ARM/build.bat
Normal file
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set UV4=C:\Keil_v5\UV4\UV4.exe
|
||||
set PROJ=%~dp0STM32F407-Demo.uvprojx
|
||||
set LOG=%~dp0build_log.txt
|
||||
set TARGET=STM32F407-Demo
|
||||
|
||||
"%UV4%" -r "%PROJ%" -t%TARGET% -j0 -o "%LOG%"
|
||||
set RET=%ERRORLEVEL%
|
||||
|
||||
if %RET% equ 0 (
|
||||
echo BUILD_OK: 0 Error(s), 0 Warning(s)
|
||||
) else (
|
||||
echo BUILD_FAIL: exit code %RET%
|
||||
if exist "%LOG%" type "%LOG%"
|
||||
)
|
||||
|
||||
exit /b %RET%
|
||||
76
MDK-ARM/build_log.txt
Normal file
76
MDK-ARM/build_log.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
*** Using Compiler 'V5.06 update 7 (build 960)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
|
||||
Rebuild target 'STM32F407-Demo'
|
||||
assembling startup_stm32f407xx.s...
|
||||
compiling crc.c...
|
||||
compiling ringbuf.c...
|
||||
compiling stm32f4xx_it.c...
|
||||
compiling app_main.c...
|
||||
compiling dma.c...
|
||||
compiling stm32f4xx_hal_flash_ramfunc.c...
|
||||
compiling i2c.c...
|
||||
compiling sys_clock.c...
|
||||
compiling stm32f4xx_hal_timebase_tim.c...
|
||||
compiling stm32f4xx_hal_gpio.c...
|
||||
compiling stm32f4xx_hal_dma_ex.c...
|
||||
compiling stm32f4xx_hal_dma.c...
|
||||
compiling stm32f4xx_hal_flash_ex.c...
|
||||
compiling stm32f4xx_hal_pwr.c...
|
||||
compiling spi.c...
|
||||
compiling stm32f4xx_hal_msp.c...
|
||||
compiling stm32f4xx_hal_rcc.c...
|
||||
compiling stm32f4xx_hal_flash.c...
|
||||
compiling gpio.c...
|
||||
compiling usart.c...
|
||||
compiling stm32f4xx_hal_tim_ex.c...
|
||||
compiling net_task.c...
|
||||
compiling freertos.c...
|
||||
compiling stm32f4xx_hal_rcc_ex.c...
|
||||
compiling rs485_task.c...
|
||||
compiling main.c...
|
||||
compiling adc_task.c...
|
||||
compiling time_sync.c...
|
||||
compiling lftpd_string.c...
|
||||
compiling croutine.c...
|
||||
compiling stm32f4xx_hal_tim.c...
|
||||
compiling list.c...
|
||||
compiling event_groups.c...
|
||||
compiling queue.c...
|
||||
compiling stream_buffer.c...
|
||||
compiling timers.c...
|
||||
compiling tasks.c...
|
||||
compiling heap_4.c...
|
||||
compiling port.c...
|
||||
compiling stm32f4xx_hal_pwr_ex.c...
|
||||
compiling journal.c...
|
||||
compiling map.c...
|
||||
compiling ff.c...
|
||||
compiling stm32f4xx_hal.c...
|
||||
compiling system_stm32f4xx.c...
|
||||
compiling lftpd_io.c...
|
||||
compiling stm32f4xx_hal_cortex.c...
|
||||
compiling stm32f4xx_hal_i2c_ex.c...
|
||||
compiling stm32f4xx_hal_exti.c...
|
||||
compiling rs485.c...
|
||||
compiling tpafe5160.c...
|
||||
compiling sd2506.c...
|
||||
compiling ch395f.c...
|
||||
compiling nand_ftl.c...
|
||||
compiling stm32f4xx_hal_spi.c...
|
||||
compiling lftpd_inet.c...
|
||||
compiling gd5f2gq5ue.c...
|
||||
compiling net_select.c...
|
||||
compiling stm32f4xx_hal_uart.c...
|
||||
compiling lftpd.c...
|
||||
compiling net_socket.c...
|
||||
compiling stm32f4xx_hal_i2c.c...
|
||||
compiling cmsis_os2.c...
|
||||
compiling ch395f_test_task.c...
|
||||
compiling net_test_task.c...
|
||||
compiling gd5f_test_task.c...
|
||||
compiling storage_test_task.c...
|
||||
compiling sd2506_test_task.c...
|
||||
linking...
|
||||
Program Size: Code=64436 RO-data=3936 RW-data=420 ZI-data=51868
|
||||
FromELF: creating hex file...
|
||||
".\STM32F407-Demo\STM32F407-Demo.axf" - 0 Error(s), 0 Warning(s).
|
||||
Build Time Elapsed: 00:00:14
|
||||
0
MDK-ARM/err.txt
Normal file
0
MDK-ARM/err.txt
Normal file
0
MDK-ARM/out.txt
Normal file
0
MDK-ARM/out.txt
Normal file
846
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h
vendored
Normal file
846
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os.h
vendored
Normal file
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2019 ARM Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* $Date: 10. January 2017
|
||||
* $Revision: V2.1.0
|
||||
*
|
||||
* Project: CMSIS-RTOS API
|
||||
* Title: cmsis_os.h FreeRTOS header file
|
||||
*
|
||||
* Version 0.02
|
||||
* Initial Proposal Phase
|
||||
* Version 0.03
|
||||
* osKernelStart added, optional feature: main started as thread
|
||||
* osSemaphores have standard behavior
|
||||
* osTimerCreate does not start the timer, added osTimerStart
|
||||
* osThreadPass is renamed to osThreadYield
|
||||
* Version 1.01
|
||||
* Support for C++ interface
|
||||
* - const attribute removed from the osXxxxDef_t typedefs
|
||||
* - const attribute added to the osXxxxDef macros
|
||||
* Added: osTimerDelete, osMutexDelete, osSemaphoreDelete
|
||||
* Added: osKernelInitialize
|
||||
* Version 1.02
|
||||
* Control functions for short timeouts in microsecond resolution:
|
||||
* Added: osKernelSysTick, osKernelSysTickFrequency, osKernelSysTickMicroSec
|
||||
* Removed: osSignalGet
|
||||
* Version 2.0.0
|
||||
* OS objects creation without macros (dynamic creation and resource allocation):
|
||||
* - added: osXxxxNew functions which replace osXxxxCreate
|
||||
* - added: osXxxxAttr_t structures
|
||||
* - deprecated: osXxxxCreate functions, osXxxxDef_t structures
|
||||
* - deprecated: osXxxxDef and osXxxx macros
|
||||
* osStatus codes simplified and renamed to osStatus_t
|
||||
* osEvent return structure deprecated
|
||||
* Kernel:
|
||||
* - added: osKernelInfo_t and osKernelGetInfo
|
||||
* - added: osKernelState_t and osKernelGetState (replaces osKernelRunning)
|
||||
* - added: osKernelLock, osKernelUnlock
|
||||
* - added: osKernelSuspend, osKernelResume
|
||||
* - added: osKernelGetTickCount, osKernelGetTickFreq
|
||||
* - renamed osKernelSysTick to osKernelGetSysTimerCount
|
||||
* - replaced osKernelSysTickFrequency with osKernelGetSysTimerFreq
|
||||
* - deprecated osKernelSysTickMicroSec
|
||||
* Thread:
|
||||
* - extended number of thread priorities
|
||||
* - renamed osPrioriry to osPrioriry_t
|
||||
* - replaced osThreadCreate with osThreadNew
|
||||
* - added: osThreadGetName
|
||||
* - added: osThreadState_t and osThreadGetState
|
||||
* - added: osThreadGetStackSize, osThreadGetStackSpace
|
||||
* - added: osThreadSuspend, osThreadResume
|
||||
* - added: osThreadJoin, osThreadDetach, osThreadExit
|
||||
* - added: osThreadGetCount, osThreadEnumerate
|
||||
* - added: Thread Flags (moved from Signals)
|
||||
* Signals:
|
||||
* - renamed osSignals to osThreadFlags (moved to Thread Flags)
|
||||
* - changed return value of Set/Clear/Wait functions
|
||||
* - Clear function limited to current running thread
|
||||
* - extended Wait function (options)
|
||||
* - added: osThreadFlagsGet
|
||||
* Event Flags:
|
||||
* - added new independent object for handling Event Flags
|
||||
* Delay and Wait functions:
|
||||
* - added: osDelayUntil
|
||||
* - deprecated: osWait
|
||||
* Timer:
|
||||
* - replaced osTimerCreate with osTimerNew
|
||||
* - added: osTimerGetName, osTimerIsRunning
|
||||
* Mutex:
|
||||
* - extended: attributes (Recursive, Priority Inherit, Robust)
|
||||
* - replaced osMutexCreate with osMutexNew
|
||||
* - renamed osMutexWait to osMutexAcquire
|
||||
* - added: osMutexGetName, osMutexGetOwner
|
||||
* Semaphore:
|
||||
* - extended: maximum and initial token count
|
||||
* - replaced osSemaphoreCreate with osSemaphoreNew
|
||||
* - renamed osSemaphoreWait to osSemaphoreAcquire (changed return value)
|
||||
* - added: osSemaphoreGetName, osSemaphoreGetCount
|
||||
* Memory Pool:
|
||||
* - using osMemoryPool prefix instead of osPool
|
||||
* - replaced osPoolCreate with osMemoryPoolNew
|
||||
* - extended osMemoryPoolAlloc (timeout)
|
||||
* - added: osMemoryPoolGetName
|
||||
* - added: osMemoryPoolGetCapacity, osMemoryPoolGetBlockSize
|
||||
* - added: osMemoryPoolGetCount, osMemoryPoolGetSpace
|
||||
* - added: osMemoryPoolDelete
|
||||
* - deprecated: osPoolCAlloc
|
||||
* Message Queue:
|
||||
* - extended: fixed size message instead of a single 32-bit value
|
||||
* - using osMessageQueue prefix instead of osMessage
|
||||
* - replaced osMessageCreate with osMessageQueueNew
|
||||
* - updated: osMessageQueuePut, osMessageQueueGet
|
||||
* - added: osMessageQueueGetName
|
||||
* - added: osMessageQueueGetCapacity, osMessageQueueGetMsgSize
|
||||
* - added: osMessageQueueGetCount, osMessageQueueGetSpace
|
||||
* - added: osMessageQueueReset, osMessageQueueDelete
|
||||
* Mail Queue:
|
||||
* - deprecated (superseded by extended Message Queue functionality)
|
||||
* Version 2.1.0
|
||||
* Support for critical and uncritical sections (nesting safe):
|
||||
* - updated: osKernelLock, osKernelUnlock
|
||||
* - added: osKernelRestoreLock
|
||||
* Updated Thread and Event Flags:
|
||||
* - changed flags parameter and return type from int32_t to uint32_t
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CMSIS_OS_H_
|
||||
#define CMSIS_OS_H_
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#define RTOS_ID_n ((tskKERNEL_VERSION_MAJOR << 16) | (tskKERNEL_VERSION_MINOR))
|
||||
#define RTOS_ID_s ("FreeRTOS " tskKERNEL_VERSION_NUMBER)
|
||||
|
||||
#define osCMSIS 0x20001U ///< API version (main[31:16].sub[15:0])
|
||||
|
||||
#define osCMSIS_FreeRTOS RTOS_ID_n ///< RTOS identification and version (main[31:16].sub[15:0])
|
||||
|
||||
#define osKernelSystemId RTOS_ID_s ///< RTOS identification string
|
||||
|
||||
#define osFeature_MainThread 0 ///< main thread 1=main can be thread, 0=not available
|
||||
#define osFeature_Signals 24U ///< maximum number of Signal Flags available per thread
|
||||
#define osFeature_Semaphore 65535U ///< maximum count for \ref osSemaphoreCreate function
|
||||
#define osFeature_Wait 0 ///< osWait function: 1=available, 0=not available
|
||||
#define osFeature_SysTick 1 ///< osKernelSysTick functions: 1=available, 0=not available
|
||||
#define osFeature_Pool 0 ///< Memory Pools: 1=available, 0=not available
|
||||
#define osFeature_MessageQ 1 ///< Message Queues: 1=available, 0=not available
|
||||
#define osFeature_MailQ 0 ///< Mail Queues: 1=available, 0=not available
|
||||
|
||||
#if defined(__CC_ARM)
|
||||
#define os_InRegs __value_in_regs
|
||||
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
|
||||
#define os_InRegs __attribute__((value_in_regs))
|
||||
#else
|
||||
#define os_InRegs
|
||||
#endif
|
||||
|
||||
#include "cmsis_os2.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
// ==== Enumerations, structures, defines ====
|
||||
|
||||
/// Priority values.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef enum {
|
||||
osPriorityIdle = -3, ///< Priority: idle (lowest)
|
||||
osPriorityLow = -2, ///< Priority: low
|
||||
osPriorityBelowNormal = -1, ///< Priority: below normal
|
||||
osPriorityNormal = 0, ///< Priority: normal (default)
|
||||
osPriorityAboveNormal = +1, ///< Priority: above normal
|
||||
osPriorityHigh = +2, ///< Priority: high
|
||||
osPriorityRealtime = +3, ///< Priority: realtime (highest)
|
||||
osPriorityError = 0x84, ///< System cannot determine priority or illegal priority.
|
||||
osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||
} osPriority;
|
||||
#else
|
||||
#define osPriority osPriority_t
|
||||
#endif
|
||||
|
||||
/// Entry point of a thread.
|
||||
typedef void (*os_pthread) (void const *argument);
|
||||
|
||||
/// Entry point of a timer call back function.
|
||||
typedef void (*os_ptimer) (void const *argument);
|
||||
|
||||
/// Timer type.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef enum {
|
||||
osTimerOnce = 0, ///< One-shot timer.
|
||||
osTimerPeriodic = 1 ///< Repeating timer.
|
||||
} os_timer_type;
|
||||
#else
|
||||
#define os_timer_type osTimerType_t
|
||||
#endif
|
||||
|
||||
/// Timeout value.
|
||||
#define osWaitForever 0xFFFFFFFFU ///< Wait forever timeout value.
|
||||
|
||||
/// Status code values returned by CMSIS-RTOS functions.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef enum {
|
||||
osOK = 0, ///< Function completed; no error or event occurred.
|
||||
osEventSignal = 0x08, ///< Function completed; signal event occurred.
|
||||
osEventMessage = 0x10, ///< Function completed; message event occurred.
|
||||
osEventMail = 0x20, ///< Function completed; mail event occurred.
|
||||
osEventTimeout = 0x40, ///< Function completed; timeout occurred.
|
||||
osErrorParameter = 0x80, ///< Parameter error: a mandatory parameter was missing or specified an incorrect object.
|
||||
osErrorResource = 0x81, ///< Resource not available: a specified resource was not available.
|
||||
osErrorTimeoutResource = 0xC1, ///< Resource not available within given time: a specified resource was not available within the timeout period.
|
||||
osErrorISR = 0x82, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
|
||||
osErrorISRRecursive = 0x83, ///< Function called multiple times from ISR with same object.
|
||||
osErrorPriority = 0x84, ///< System cannot determine priority or thread has illegal priority.
|
||||
osErrorNoMemory = 0x85, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
|
||||
osErrorValue = 0x86, ///< Value of a parameter is out of range.
|
||||
osErrorOS = 0xFF, ///< Unspecified RTOS error: run-time error but no other error message fits.
|
||||
osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||
} osStatus;
|
||||
#else
|
||||
typedef int32_t osStatus;
|
||||
#define osEventSignal (0x08)
|
||||
#define osEventMessage (0x10)
|
||||
#define osEventMail (0x20)
|
||||
#define osEventTimeout (0x40)
|
||||
#define osErrorOS osError
|
||||
#define osErrorTimeoutResource osErrorTimeout
|
||||
#define osErrorISRRecursive (-126)
|
||||
#define osErrorValue (-127)
|
||||
#define osErrorPriority (-128)
|
||||
#endif
|
||||
|
||||
|
||||
// >>> the following data type definitions may be adapted towards a specific RTOS
|
||||
|
||||
/// Thread ID identifies the thread.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef void *osThreadId;
|
||||
#else
|
||||
#define osThreadId osThreadId_t
|
||||
#endif
|
||||
|
||||
/// Timer ID identifies the timer.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef void *osTimerId;
|
||||
#else
|
||||
#define osTimerId osTimerId_t
|
||||
#endif
|
||||
|
||||
/// Mutex ID identifies the mutex.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef void *osMutexId;
|
||||
#else
|
||||
#define osMutexId osMutexId_t
|
||||
#endif
|
||||
|
||||
/// Semaphore ID identifies the semaphore.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef void *osSemaphoreId;
|
||||
#else
|
||||
#define osSemaphoreId osSemaphoreId_t
|
||||
#endif
|
||||
|
||||
/// Pool ID identifies the memory pool.
|
||||
typedef void *osPoolId;
|
||||
|
||||
/// Message ID identifies the message queue.
|
||||
typedef void *osMessageQId;
|
||||
|
||||
/// Mail ID identifies the mail queue.
|
||||
typedef void *osMailQId;
|
||||
|
||||
|
||||
/// Thread Definition structure contains startup information of a thread.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_thread_def {
|
||||
os_pthread pthread; ///< start address of thread function
|
||||
osPriority tpriority; ///< initial thread priority
|
||||
uint32_t instances; ///< maximum number of instances of that thread function
|
||||
uint32_t stacksize; ///< stack size requirements in bytes; 0 is default stack size
|
||||
} osThreadDef_t;
|
||||
#else
|
||||
typedef struct os_thread_def {
|
||||
os_pthread pthread; ///< start address of thread function
|
||||
osThreadAttr_t attr; ///< thread attributes
|
||||
} osThreadDef_t;
|
||||
#endif
|
||||
|
||||
/// Timer Definition structure contains timer parameters.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_timer_def {
|
||||
os_ptimer ptimer; ///< start address of a timer function
|
||||
} osTimerDef_t;
|
||||
#else
|
||||
typedef struct os_timer_def {
|
||||
os_ptimer ptimer; ///< start address of a timer function
|
||||
osTimerAttr_t attr; ///< timer attributes
|
||||
} osTimerDef_t;
|
||||
#endif
|
||||
|
||||
/// Mutex Definition structure contains setup information for a mutex.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_mutex_def {
|
||||
uint32_t dummy; ///< dummy value
|
||||
} osMutexDef_t;
|
||||
#else
|
||||
#define osMutexDef_t osMutexAttr_t
|
||||
#endif
|
||||
|
||||
/// Semaphore Definition structure contains setup information for a semaphore.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_semaphore_def {
|
||||
uint32_t dummy; ///< dummy value
|
||||
} osSemaphoreDef_t;
|
||||
#else
|
||||
#define osSemaphoreDef_t osSemaphoreAttr_t
|
||||
#endif
|
||||
|
||||
/// Definition structure for memory block allocation.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_pool_def {
|
||||
uint32_t pool_sz; ///< number of items (elements) in the pool
|
||||
uint32_t item_sz; ///< size of an item
|
||||
void *pool; ///< pointer to memory for pool
|
||||
} osPoolDef_t;
|
||||
#else
|
||||
typedef struct os_pool_def {
|
||||
uint32_t pool_sz; ///< number of items (elements) in the pool
|
||||
uint32_t item_sz; ///< size of an item
|
||||
osMemoryPoolAttr_t attr; ///< memory pool attributes
|
||||
} osPoolDef_t;
|
||||
#endif
|
||||
|
||||
/// Definition structure for message queue.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_messageQ_def {
|
||||
uint32_t queue_sz; ///< number of elements in the queue
|
||||
void *pool; ///< memory array for messages
|
||||
} osMessageQDef_t;
|
||||
#else
|
||||
typedef struct os_messageQ_def {
|
||||
uint32_t queue_sz; ///< number of elements in the queue
|
||||
osMessageQueueAttr_t attr; ///< message queue attributes
|
||||
} osMessageQDef_t;
|
||||
#endif
|
||||
|
||||
/// Definition structure for mail queue.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
typedef struct os_mailQ_def {
|
||||
uint32_t queue_sz; ///< number of elements in the queue
|
||||
uint32_t item_sz; ///< size of an item
|
||||
void *pool; ///< memory array for mail
|
||||
} osMailQDef_t;
|
||||
#else
|
||||
typedef struct os_mailQ_def {
|
||||
uint32_t queue_sz; ///< number of elements in the queue
|
||||
uint32_t item_sz; ///< size of an item
|
||||
void *mail; ///< pointer to mail
|
||||
osMemoryPoolAttr_t mp_attr; ///< memory pool attributes
|
||||
osMessageQueueAttr_t mq_attr; ///< message queue attributes
|
||||
} osMailQDef_t;
|
||||
#endif
|
||||
|
||||
|
||||
/// Event structure contains detailed information about an event.
|
||||
typedef struct {
|
||||
osStatus status; ///< status code: event or error information
|
||||
union {
|
||||
uint32_t v; ///< message as 32-bit value
|
||||
void *p; ///< message or mail as void pointer
|
||||
int32_t signals; ///< signal flags
|
||||
} value; ///< event value
|
||||
union {
|
||||
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
|
||||
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
|
||||
} def; ///< event definition
|
||||
} osEvent;
|
||||
|
||||
|
||||
// ==== Kernel Management Functions ====
|
||||
|
||||
/// Initialize the RTOS Kernel for creating objects.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osKernelInitialize (void);
|
||||
#endif
|
||||
|
||||
/// Start the RTOS Kernel scheduler.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osKernelStart (void);
|
||||
#endif
|
||||
|
||||
/// Check if the RTOS kernel is already started.
|
||||
/// \return 0 RTOS is not started, 1 RTOS is started.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
int32_t osKernelRunning(void);
|
||||
#endif
|
||||
|
||||
#if (defined(osFeature_SysTick) && (osFeature_SysTick != 0)) // System Timer available
|
||||
|
||||
/// Get the RTOS kernel system timer counter.
|
||||
/// \return RTOS kernel system timer as 32-bit value
|
||||
#if (osCMSIS < 0x20000U)
|
||||
uint32_t osKernelSysTick (void);
|
||||
#else
|
||||
#define osKernelSysTick osKernelGetSysTimerCount
|
||||
#endif
|
||||
|
||||
/// The RTOS kernel system timer frequency in Hz.
|
||||
/// \note Reflects the system timer setting and is typically defined in a configuration file.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
#define osKernelSysTickFrequency 100000000
|
||||
#endif
|
||||
|
||||
/// Convert a microseconds value to a RTOS kernel system timer value.
|
||||
/// \param microsec time value in microseconds.
|
||||
/// \return time value normalized to the \ref osKernelSysTickFrequency
|
||||
#if (osCMSIS < 0x20000U)
|
||||
#define osKernelSysTickMicroSec(microsec) (((uint64_t)microsec * (osKernelSysTickFrequency)) / 1000000)
|
||||
#else
|
||||
#define osKernelSysTickMicroSec(microsec) (((uint64_t)microsec * osKernelGetSysTimerFreq()) / 1000000)
|
||||
#endif
|
||||
|
||||
#endif // System Timer available
|
||||
|
||||
|
||||
// ==== Thread Management Functions ====
|
||||
|
||||
/// Create a Thread Definition with function, priority, and stack requirements.
|
||||
/// \param name name of the thread function.
|
||||
/// \param priority initial priority of the thread function.
|
||||
/// \param instances number of possible thread instances.
|
||||
/// \param stacksz stack size (in bytes) requirements for the thread function.
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osThreadDef(name, priority, instances, stacksz) \
|
||||
extern const osThreadDef_t os_thread_def_##name
|
||||
#else // define the object
|
||||
#define osThreadDef(name, priority, instances, stacksz) \
|
||||
static uint64_t os_thread_stack##name[(stacksz)?(((stacksz+7)/8)):1]; \
|
||||
static StaticTask_t os_thread_cb_##name; \
|
||||
const osThreadDef_t os_thread_def_##name = \
|
||||
{ (name), \
|
||||
{ NULL, osThreadDetached, \
|
||||
(instances == 1) ? (&os_thread_cb_##name) : NULL,\
|
||||
(instances == 1) ? sizeof(StaticTask_t) : 0U, \
|
||||
((stacksz) && (instances == 1)) ? (&os_thread_stack##name) : NULL, \
|
||||
8*((stacksz+7)/8), \
|
||||
(priority), 0U, 0U } }
|
||||
#endif
|
||||
|
||||
/// Access a Thread definition.
|
||||
/// \param name name of the thread definition object.
|
||||
#define osThread(name) \
|
||||
&os_thread_def_##name
|
||||
|
||||
/// Create a thread and add it to Active Threads and set it to state READY.
|
||||
/// \param[in] thread_def thread definition referenced with \ref osThread.
|
||||
/// \param[in] argument pointer that is passed to the thread function as start argument.
|
||||
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument);
|
||||
|
||||
/// Return the thread ID of the current running thread.
|
||||
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osThreadId osThreadGetId (void);
|
||||
#endif
|
||||
|
||||
/// Change priority of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||
/// \param[in] priority new priority value for the thread function.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority);
|
||||
#endif
|
||||
|
||||
/// Get current priority of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||
/// \return current priority value of the specified thread.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osPriority osThreadGetPriority (osThreadId thread_id);
|
||||
#endif
|
||||
|
||||
/// Pass control to next thread that is in state \b READY.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osThreadYield (void);
|
||||
#endif
|
||||
|
||||
/// Terminate execution of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osThreadTerminate (osThreadId thread_id);
|
||||
#endif
|
||||
|
||||
|
||||
// ==== Signal Management ====
|
||||
|
||||
/// Set the specified Signal Flags of an active thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||
/// \param[in] signals specifies the signal flags of the thread that should be set.
|
||||
/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters.
|
||||
int32_t osSignalSet (osThreadId thread_id, int32_t signals);
|
||||
|
||||
/// Clear the specified Signal Flags of an active thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId.
|
||||
/// \param[in] signals specifies the signal flags of the thread that shall be cleared.
|
||||
/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters or call from ISR.
|
||||
int32_t osSignalClear (osThreadId thread_id, int32_t signals);
|
||||
|
||||
/// Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread.
|
||||
/// \param[in] signals wait until all specified signal flags set or 0 for any single signal flag.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return event flag information or error code.
|
||||
os_InRegs osEvent osSignalWait (int32_t signals, uint32_t millisec);
|
||||
|
||||
|
||||
// ==== Generic Wait Functions ====
|
||||
|
||||
/// Wait for Timeout (Time Delay).
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue "time delay" value
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osDelay (uint32_t millisec);
|
||||
#endif
|
||||
|
||||
#if (defined (osFeature_Wait) && (osFeature_Wait != 0)) // Generic Wait available
|
||||
|
||||
/// Wait for Signal, Message, Mail, or Timeout.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out
|
||||
/// \return event that contains signal, message, or mail information or error code.
|
||||
os_InRegs osEvent osWait (uint32_t millisec);
|
||||
|
||||
#endif // Generic Wait available
|
||||
|
||||
|
||||
// ==== Timer Management Functions ====
|
||||
|
||||
/// Define a Timer object.
|
||||
/// \param name name of the timer object.
|
||||
/// \param function name of the timer call back function.
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osTimerDef(name, function) \
|
||||
extern const osTimerDef_t os_timer_def_##name
|
||||
#else // define the object
|
||||
#define osTimerDef(name, function) \
|
||||
static StaticTimer_t os_timer_cb_##name; \
|
||||
const osTimerDef_t os_timer_def_##name = \
|
||||
{ (function), { NULL, 0U, (&os_timer_cb_##name), sizeof(StaticTimer_t) } }
|
||||
#endif
|
||||
|
||||
/// Access a Timer definition.
|
||||
/// \param name name of the timer object.
|
||||
#define osTimer(name) \
|
||||
&os_timer_def_##name
|
||||
|
||||
/// Create and Initialize a timer.
|
||||
/// \param[in] timer_def timer object referenced with \ref osTimer.
|
||||
/// \param[in] type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior.
|
||||
/// \param[in] argument argument to the timer call back function.
|
||||
/// \return timer ID for reference by other functions or NULL in case of error.
|
||||
osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument);
|
||||
|
||||
/// Start or restart a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue "time delay" value of the timer.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osTimerStart (osTimerId timer_id, uint32_t millisec);
|
||||
#endif
|
||||
|
||||
/// Stop a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerCreate.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osTimerStop (osTimerId timer_id);
|
||||
#endif
|
||||
|
||||
/// Delete a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerCreate.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osTimerDelete (osTimerId timer_id);
|
||||
#endif
|
||||
|
||||
|
||||
// ==== Mutex Management Functions ====
|
||||
|
||||
/// Define a Mutex.
|
||||
/// \param name name of the mutex object.
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osMutexDef(name) \
|
||||
extern const osMutexDef_t os_mutex_def_##name
|
||||
#else // define the object
|
||||
#define osMutexDef(name) \
|
||||
static StaticSemaphore_t os_mutex_cb_##name; \
|
||||
const osMutexDef_t os_mutex_def_##name = \
|
||||
{ NULL, osMutexRecursive | osMutexPrioInherit, (&os_mutex_cb_##name), sizeof(StaticSemaphore_t) }
|
||||
#endif
|
||||
|
||||
/// Access a Mutex definition.
|
||||
/// \param name name of the mutex object.
|
||||
#define osMutex(name) \
|
||||
&os_mutex_def_##name
|
||||
|
||||
/// Create and Initialize a Mutex object.
|
||||
/// \param[in] mutex_def mutex definition referenced with \ref osMutex.
|
||||
/// \return mutex ID for reference by other functions or NULL in case of error.
|
||||
osMutexId osMutexCreate (const osMutexDef_t *mutex_def);
|
||||
|
||||
/// Wait until a Mutex becomes available.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec);
|
||||
#else
|
||||
#define osMutexWait osMutexAcquire
|
||||
#endif
|
||||
|
||||
/// Release a Mutex that was obtained by \ref osMutexWait.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osMutexRelease (osMutexId mutex_id);
|
||||
#endif
|
||||
|
||||
/// Delete a Mutex object.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osMutexDelete (osMutexId mutex_id);
|
||||
#endif
|
||||
|
||||
|
||||
// ==== Semaphore Management Functions ====
|
||||
|
||||
#if (defined (osFeature_Semaphore) && (osFeature_Semaphore != 0U)) // Semaphore available
|
||||
|
||||
/// Define a Semaphore object.
|
||||
/// \param name name of the semaphore object.
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osSemaphoreDef(name) \
|
||||
extern const osSemaphoreDef_t os_semaphore_def_##name
|
||||
#else // define the object
|
||||
#define osSemaphoreDef(name) \
|
||||
static StaticSemaphore_t os_semaphore_cb_##name; \
|
||||
const osSemaphoreDef_t os_semaphore_def_##name = \
|
||||
{ NULL, 0U, (&os_semaphore_cb_##name), sizeof(StaticSemaphore_t) }
|
||||
#endif
|
||||
|
||||
/// Access a Semaphore definition.
|
||||
/// \param name name of the semaphore object.
|
||||
#define osSemaphore(name) \
|
||||
&os_semaphore_def_##name
|
||||
|
||||
/// Create and Initialize a Semaphore object.
|
||||
/// \param[in] semaphore_def semaphore definition referenced with \ref osSemaphore.
|
||||
/// \param[in] count maximum and initial number of available tokens.
|
||||
/// \return semaphore ID for reference by other functions or NULL in case of error.
|
||||
osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count);
|
||||
|
||||
/// Wait until a Semaphore token becomes available.
|
||||
/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return number of available tokens, or -1 in case of incorrect parameters.
|
||||
int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec);
|
||||
|
||||
/// Release a Semaphore token.
|
||||
/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osSemaphoreRelease (osSemaphoreId semaphore_id);
|
||||
#endif
|
||||
|
||||
/// Delete a Semaphore object.
|
||||
/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
#if (osCMSIS < 0x20000U)
|
||||
osStatus osSemaphoreDelete (osSemaphoreId semaphore_id);
|
||||
#endif
|
||||
|
||||
#endif // Semaphore available
|
||||
|
||||
|
||||
// ==== Memory Pool Management Functions ====
|
||||
|
||||
#if (defined(osFeature_Pool) && (osFeature_Pool != 0)) // Memory Pool available
|
||||
|
||||
/// \brief Define a Memory Pool.
|
||||
/// \param name name of the memory pool.
|
||||
/// \param no maximum number of blocks (objects) in the memory pool.
|
||||
/// \param type data type of a single block (object).
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osPoolDef(name, no, type) \
|
||||
extern const osPoolDef_t os_pool_def_##name
|
||||
#else // define the object
|
||||
#define osPoolDef(name, no, type) \
|
||||
const osPoolDef_t os_pool_def_##name = \
|
||||
{ (no), sizeof(type), {NULL} }
|
||||
#endif
|
||||
|
||||
/// \brief Access a Memory Pool definition.
|
||||
/// \param name name of the memory pool
|
||||
#define osPool(name) \
|
||||
&os_pool_def_##name
|
||||
|
||||
/// Create and Initialize a Memory Pool object.
|
||||
/// \param[in] pool_def memory pool definition referenced with \ref osPool.
|
||||
/// \return memory pool ID for reference by other functions or NULL in case of error.
|
||||
osPoolId osPoolCreate (const osPoolDef_t *pool_def);
|
||||
|
||||
/// Allocate a memory block from a Memory Pool.
|
||||
/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate.
|
||||
/// \return address of the allocated memory block or NULL in case of no memory available.
|
||||
void *osPoolAlloc (osPoolId pool_id);
|
||||
|
||||
/// Allocate a memory block from a Memory Pool and set memory block to zero.
|
||||
/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate.
|
||||
/// \return address of the allocated memory block or NULL in case of no memory available.
|
||||
void *osPoolCAlloc (osPoolId pool_id);
|
||||
|
||||
/// Return an allocated memory block back to a Memory Pool.
|
||||
/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate.
|
||||
/// \param[in] block address of the allocated memory block to be returned to the memory pool.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus osPoolFree (osPoolId pool_id, void *block);
|
||||
|
||||
#endif // Memory Pool available
|
||||
|
||||
|
||||
// ==== Message Queue Management Functions ====
|
||||
|
||||
#if (defined(osFeature_MessageQ) && (osFeature_MessageQ != 0)) // Message Queue available
|
||||
|
||||
/// \brief Create a Message Queue Definition.
|
||||
/// \param name name of the queue.
|
||||
/// \param queue_sz maximum number of messages in the queue.
|
||||
/// \param type data type of a single message element (for debugger).
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osMessageQDef(name, queue_sz, type) \
|
||||
extern const osMessageQDef_t os_messageQ_def_##name
|
||||
#else // define the object
|
||||
#define osMessageQDef(name, queue_sz, type) \
|
||||
static StaticQueue_t os_mq_cb_##name; \
|
||||
static uint32_t os_mq_data_##name[(queue_sz) * sizeof(type)]; \
|
||||
const osMessageQDef_t os_messageQ_def_##name = \
|
||||
{ (queue_sz), \
|
||||
{ NULL, 0U, (&os_mq_cb_##name), sizeof(StaticQueue_t), \
|
||||
(&os_mq_data_##name), sizeof(os_mq_data_##name) } }
|
||||
#endif
|
||||
|
||||
/// \brief Access a Message Queue Definition.
|
||||
/// \param name name of the queue
|
||||
#define osMessageQ(name) \
|
||||
&os_messageQ_def_##name
|
||||
|
||||
/// Create and Initialize a Message Queue object.
|
||||
/// \param[in] queue_def message queue definition referenced with \ref osMessageQ.
|
||||
/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL.
|
||||
/// \return message queue ID for reference by other functions or NULL in case of error.
|
||||
osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id);
|
||||
|
||||
/// Put a Message to a Queue.
|
||||
/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate.
|
||||
/// \param[in] info message information.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec);
|
||||
|
||||
/// Get a Message from a Queue or timeout if Queue is empty.
|
||||
/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return event information that includes status code.
|
||||
os_InRegs osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec);
|
||||
|
||||
#endif // Message Queue available
|
||||
|
||||
|
||||
// ==== Mail Queue Management Functions ====
|
||||
|
||||
#if (defined(osFeature_MailQ) && (osFeature_MailQ != 0)) // Mail Queue available
|
||||
|
||||
/// \brief Create a Mail Queue Definition.
|
||||
/// \param name name of the queue.
|
||||
/// \param queue_sz maximum number of mails in the queue.
|
||||
/// \param type data type of a single mail element.
|
||||
#if defined (osObjectsExternal) // object is external
|
||||
#define osMailQDef(name, queue_sz, type) \
|
||||
extern const osMailQDef_t os_mailQ_def_##name
|
||||
#else // define the object
|
||||
#define osMailQDef(name, queue_sz, type) \
|
||||
const osMailQDef_t os_mailQ_def_##name = \
|
||||
{ (queue_sz), sizeof(type), NULL }
|
||||
#endif
|
||||
|
||||
/// \brief Access a Mail Queue Definition.
|
||||
/// \param name name of the queue
|
||||
#define osMailQ(name) \
|
||||
&os_mailQ_def_##name
|
||||
|
||||
/// Create and Initialize a Mail Queue object.
|
||||
/// \param[in] queue_def mail queue definition referenced with \ref osMailQ.
|
||||
/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL.
|
||||
/// \return mail queue ID for reference by other functions or NULL in case of error.
|
||||
osMailQId osMailCreate (const osMailQDef_t *queue_def, osThreadId thread_id);
|
||||
|
||||
/// Allocate a memory block for mail from a mail memory pool.
|
||||
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out
|
||||
/// \return pointer to memory block that can be filled with mail or NULL in case of error.
|
||||
void *osMailAlloc (osMailQId queue_id, uint32_t millisec);
|
||||
|
||||
/// Allocate a memory block for mail from a mail memory pool and set memory block to zero.
|
||||
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out
|
||||
/// \return pointer to memory block that can be filled with mail or NULL in case of error.
|
||||
void *osMailCAlloc (osMailQId queue_id, uint32_t millisec);
|
||||
|
||||
/// Put a Mail into a Queue.
|
||||
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||
/// \param[in] mail pointer to memory with mail to put into a queue.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus osMailPut (osMailQId queue_id, const void *mail);
|
||||
|
||||
/// Get a Mail from a Queue or timeout if Queue is empty.
|
||||
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||
/// \param[in] millisec \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return event information that includes status code.
|
||||
os_InRegs osEvent osMailGet (osMailQId queue_id, uint32_t millisec);
|
||||
|
||||
/// Free a memory block by returning it to a mail memory pool.
|
||||
/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate.
|
||||
/// \param[in] mail pointer to memory block that was obtained with \ref osMailGet.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus osMailFree (osMailQId queue_id, void *mail);
|
||||
|
||||
#endif // Mail Queue available
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CMSIS_OS_H_
|
||||
2482
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
vendored
Normal file
2482
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
734
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h
vendored
Normal file
734
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/cmsis_os2.h
vendored
Normal file
@@ -0,0 +1,734 @@
|
||||
/* --------------------------------------------------------------------------
|
||||
* Portions Copyright © 2017 STMicroelectronics International N.V. All rights reserved.
|
||||
* Portions Copyright (c) 2013-2017 ARM Limited. All rights reserved.
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Name: cmsis_os2.h
|
||||
* Purpose: CMSIS RTOS2 wrapper for FreeRTOS
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef CMSIS_OS2_H_
|
||||
#define CMSIS_OS2_H_
|
||||
|
||||
#ifndef __NO_RETURN
|
||||
#if defined(__CC_ARM)
|
||||
#define __NO_RETURN __declspec(noreturn)
|
||||
#elif defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
|
||||
#define __NO_RETURN __attribute__((__noreturn__))
|
||||
#elif defined(__GNUC__)
|
||||
#define __NO_RETURN __attribute__((__noreturn__))
|
||||
#elif defined(__ICCARM__)
|
||||
#define __NO_RETURN __noreturn
|
||||
#else
|
||||
#define __NO_RETURN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
|
||||
// ==== Enumerations, structures, defines ====
|
||||
|
||||
/// Version information.
|
||||
typedef struct {
|
||||
uint32_t api; ///< API version (major.minor.rev: mmnnnrrrr dec).
|
||||
uint32_t kernel; ///< Kernel version (major.minor.rev: mmnnnrrrr dec).
|
||||
} osVersion_t;
|
||||
|
||||
/// Kernel state.
|
||||
typedef enum {
|
||||
osKernelInactive = 0, ///< Inactive.
|
||||
osKernelReady = 1, ///< Ready.
|
||||
osKernelRunning = 2, ///< Running.
|
||||
osKernelLocked = 3, ///< Locked.
|
||||
osKernelSuspended = 4, ///< Suspended.
|
||||
osKernelError = -1, ///< Error.
|
||||
osKernelReserved = 0x7FFFFFFFU ///< Prevents enum down-size compiler optimization.
|
||||
} osKernelState_t;
|
||||
|
||||
/// Thread state.
|
||||
typedef enum {
|
||||
osThreadInactive = 0, ///< Inactive.
|
||||
osThreadReady = 1, ///< Ready.
|
||||
osThreadRunning = 2, ///< Running.
|
||||
osThreadBlocked = 3, ///< Blocked.
|
||||
osThreadTerminated = 4, ///< Terminated.
|
||||
osThreadError = -1, ///< Error.
|
||||
osThreadReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||
} osThreadState_t;
|
||||
|
||||
/// Priority values.
|
||||
typedef enum {
|
||||
osPriorityNone = 0, ///< No priority (not initialized).
|
||||
osPriorityIdle = 1, ///< Reserved for Idle thread.
|
||||
osPriorityLow = 8, ///< Priority: low
|
||||
osPriorityLow1 = 8+1, ///< Priority: low + 1
|
||||
osPriorityLow2 = 8+2, ///< Priority: low + 2
|
||||
osPriorityLow3 = 8+3, ///< Priority: low + 3
|
||||
osPriorityLow4 = 8+4, ///< Priority: low + 4
|
||||
osPriorityLow5 = 8+5, ///< Priority: low + 5
|
||||
osPriorityLow6 = 8+6, ///< Priority: low + 6
|
||||
osPriorityLow7 = 8+7, ///< Priority: low + 7
|
||||
osPriorityBelowNormal = 16, ///< Priority: below normal
|
||||
osPriorityBelowNormal1 = 16+1, ///< Priority: below normal + 1
|
||||
osPriorityBelowNormal2 = 16+2, ///< Priority: below normal + 2
|
||||
osPriorityBelowNormal3 = 16+3, ///< Priority: below normal + 3
|
||||
osPriorityBelowNormal4 = 16+4, ///< Priority: below normal + 4
|
||||
osPriorityBelowNormal5 = 16+5, ///< Priority: below normal + 5
|
||||
osPriorityBelowNormal6 = 16+6, ///< Priority: below normal + 6
|
||||
osPriorityBelowNormal7 = 16+7, ///< Priority: below normal + 7
|
||||
osPriorityNormal = 24, ///< Priority: normal
|
||||
osPriorityNormal1 = 24+1, ///< Priority: normal + 1
|
||||
osPriorityNormal2 = 24+2, ///< Priority: normal + 2
|
||||
osPriorityNormal3 = 24+3, ///< Priority: normal + 3
|
||||
osPriorityNormal4 = 24+4, ///< Priority: normal + 4
|
||||
osPriorityNormal5 = 24+5, ///< Priority: normal + 5
|
||||
osPriorityNormal6 = 24+6, ///< Priority: normal + 6
|
||||
osPriorityNormal7 = 24+7, ///< Priority: normal + 7
|
||||
osPriorityAboveNormal = 32, ///< Priority: above normal
|
||||
osPriorityAboveNormal1 = 32+1, ///< Priority: above normal + 1
|
||||
osPriorityAboveNormal2 = 32+2, ///< Priority: above normal + 2
|
||||
osPriorityAboveNormal3 = 32+3, ///< Priority: above normal + 3
|
||||
osPriorityAboveNormal4 = 32+4, ///< Priority: above normal + 4
|
||||
osPriorityAboveNormal5 = 32+5, ///< Priority: above normal + 5
|
||||
osPriorityAboveNormal6 = 32+6, ///< Priority: above normal + 6
|
||||
osPriorityAboveNormal7 = 32+7, ///< Priority: above normal + 7
|
||||
osPriorityHigh = 40, ///< Priority: high
|
||||
osPriorityHigh1 = 40+1, ///< Priority: high + 1
|
||||
osPriorityHigh2 = 40+2, ///< Priority: high + 2
|
||||
osPriorityHigh3 = 40+3, ///< Priority: high + 3
|
||||
osPriorityHigh4 = 40+4, ///< Priority: high + 4
|
||||
osPriorityHigh5 = 40+5, ///< Priority: high + 5
|
||||
osPriorityHigh6 = 40+6, ///< Priority: high + 6
|
||||
osPriorityHigh7 = 40+7, ///< Priority: high + 7
|
||||
osPriorityRealtime = 48, ///< Priority: realtime
|
||||
osPriorityRealtime1 = 48+1, ///< Priority: realtime + 1
|
||||
osPriorityRealtime2 = 48+2, ///< Priority: realtime + 2
|
||||
osPriorityRealtime3 = 48+3, ///< Priority: realtime + 3
|
||||
osPriorityRealtime4 = 48+4, ///< Priority: realtime + 4
|
||||
osPriorityRealtime5 = 48+5, ///< Priority: realtime + 5
|
||||
osPriorityRealtime6 = 48+6, ///< Priority: realtime + 6
|
||||
osPriorityRealtime7 = 48+7, ///< Priority: realtime + 7
|
||||
osPriorityISR = 56, ///< Reserved for ISR deferred thread.
|
||||
osPriorityError = -1, ///< System cannot determine priority or illegal priority.
|
||||
osPriorityReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||
} osPriority_t;
|
||||
|
||||
/// Entry point of a thread.
|
||||
typedef void (*osThreadFunc_t) (void *argument);
|
||||
|
||||
/// Timer callback function.
|
||||
typedef void (*osTimerFunc_t) (void *argument);
|
||||
|
||||
/// Timer type.
|
||||
typedef enum {
|
||||
osTimerOnce = 0, ///< One-shot timer.
|
||||
osTimerPeriodic = 1 ///< Repeating timer.
|
||||
} osTimerType_t;
|
||||
|
||||
// Timeout value.
|
||||
#define osWaitForever 0xFFFFFFFFU ///< Wait forever timeout value.
|
||||
|
||||
// Flags options (\ref osThreadFlagsWait and \ref osEventFlagsWait).
|
||||
#define osFlagsWaitAny 0x00000000U ///< Wait for any flag (default).
|
||||
#define osFlagsWaitAll 0x00000001U ///< Wait for all flags.
|
||||
#define osFlagsNoClear 0x00000002U ///< Do not clear flags which have been specified to wait for.
|
||||
|
||||
// Flags errors (returned by osThreadFlagsXxxx and osEventFlagsXxxx).
|
||||
#define osFlagsError 0x80000000U ///< Error indicator.
|
||||
#define osFlagsErrorUnknown 0xFFFFFFFFU ///< osError (-1).
|
||||
#define osFlagsErrorTimeout 0xFFFFFFFEU ///< osErrorTimeout (-2).
|
||||
#define osFlagsErrorResource 0xFFFFFFFDU ///< osErrorResource (-3).
|
||||
#define osFlagsErrorParameter 0xFFFFFFFCU ///< osErrorParameter (-4).
|
||||
#define osFlagsErrorISR 0xFFFFFFFAU ///< osErrorISR (-6).
|
||||
|
||||
// Thread attributes (attr_bits in \ref osThreadAttr_t).
|
||||
#define osThreadDetached 0x00000000U ///< Thread created in detached mode (default)
|
||||
#define osThreadJoinable 0x00000001U ///< Thread created in joinable mode
|
||||
|
||||
// Mutex attributes (attr_bits in \ref osMutexAttr_t).
|
||||
#define osMutexRecursive 0x00000001U ///< Recursive mutex.
|
||||
#define osMutexPrioInherit 0x00000002U ///< Priority inherit protocol.
|
||||
#define osMutexRobust 0x00000008U ///< Robust mutex.
|
||||
|
||||
/// Status code values returned by CMSIS-RTOS functions.
|
||||
typedef enum {
|
||||
osOK = 0, ///< Operation completed successfully.
|
||||
osError = -1, ///< Unspecified RTOS error: run-time error but no other error message fits.
|
||||
osErrorTimeout = -2, ///< Operation not completed within the timeout period.
|
||||
osErrorResource = -3, ///< Resource not available.
|
||||
osErrorParameter = -4, ///< Parameter error.
|
||||
osErrorNoMemory = -5, ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
|
||||
osErrorISR = -6, ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
|
||||
osStatusReserved = 0x7FFFFFFF ///< Prevents enum down-size compiler optimization.
|
||||
} osStatus_t;
|
||||
|
||||
|
||||
/// \details Thread ID identifies the thread.
|
||||
typedef void *osThreadId_t;
|
||||
|
||||
/// \details Timer ID identifies the timer.
|
||||
typedef void *osTimerId_t;
|
||||
|
||||
/// \details Event Flags ID identifies the event flags.
|
||||
typedef void *osEventFlagsId_t;
|
||||
|
||||
/// \details Mutex ID identifies the mutex.
|
||||
typedef void *osMutexId_t;
|
||||
|
||||
/// \details Semaphore ID identifies the semaphore.
|
||||
typedef void *osSemaphoreId_t;
|
||||
|
||||
/// \details Memory Pool ID identifies the memory pool.
|
||||
typedef void *osMemoryPoolId_t;
|
||||
|
||||
/// \details Message Queue ID identifies the message queue.
|
||||
typedef void *osMessageQueueId_t;
|
||||
|
||||
|
||||
#ifndef TZ_MODULEID_T
|
||||
#define TZ_MODULEID_T
|
||||
/// \details Data type that identifies secure software modules called by a process.
|
||||
typedef uint32_t TZ_ModuleId_t;
|
||||
#endif
|
||||
|
||||
|
||||
/// Attributes structure for thread.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the thread
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
void *stack_mem; ///< memory for stack
|
||||
uint32_t stack_size; ///< size of stack
|
||||
osPriority_t priority; ///< initial thread priority (default: osPriorityNormal)
|
||||
TZ_ModuleId_t tz_module; ///< TrustZone module identifier
|
||||
uint32_t reserved; ///< reserved (must be 0)
|
||||
} osThreadAttr_t;
|
||||
|
||||
/// Attributes structure for timer.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the timer
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
} osTimerAttr_t;
|
||||
|
||||
/// Attributes structure for event flags.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the event flags
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
} osEventFlagsAttr_t;
|
||||
|
||||
/// Attributes structure for mutex.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the mutex
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
} osMutexAttr_t;
|
||||
|
||||
/// Attributes structure for semaphore.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the semaphore
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
} osSemaphoreAttr_t;
|
||||
|
||||
/// Attributes structure for memory pool.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the memory pool
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
void *mp_mem; ///< memory for data storage
|
||||
uint32_t mp_size; ///< size of provided memory for data storage
|
||||
} osMemoryPoolAttr_t;
|
||||
|
||||
/// Attributes structure for message queue.
|
||||
typedef struct {
|
||||
const char *name; ///< name of the message queue
|
||||
uint32_t attr_bits; ///< attribute bits
|
||||
void *cb_mem; ///< memory for control block
|
||||
uint32_t cb_size; ///< size of provided memory for control block
|
||||
void *mq_mem; ///< memory for data storage
|
||||
uint32_t mq_size; ///< size of provided memory for data storage
|
||||
} osMessageQueueAttr_t;
|
||||
|
||||
|
||||
// ==== Kernel Management Functions ====
|
||||
|
||||
/// Initialize the RTOS Kernel.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osKernelInitialize (void);
|
||||
|
||||
/// Get RTOS Kernel Information.
|
||||
/// \param[out] version pointer to buffer for retrieving version information.
|
||||
/// \param[out] id_buf pointer to buffer for retrieving kernel identification string.
|
||||
/// \param[in] id_size size of buffer for kernel identification string.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osKernelGetInfo (osVersion_t *version, char *id_buf, uint32_t id_size);
|
||||
|
||||
/// Get the current RTOS Kernel state.
|
||||
/// \return current RTOS Kernel state.
|
||||
osKernelState_t osKernelGetState (void);
|
||||
|
||||
/// Start the RTOS Kernel scheduler.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osKernelStart (void);
|
||||
|
||||
/// Lock the RTOS Kernel scheduler.
|
||||
/// \return previous lock state (1 - locked, 0 - not locked, error code if negative).
|
||||
int32_t osKernelLock (void);
|
||||
|
||||
/// Unlock the RTOS Kernel scheduler.
|
||||
/// \return previous lock state (1 - locked, 0 - not locked, error code if negative).
|
||||
int32_t osKernelUnlock (void);
|
||||
|
||||
/// Restore the RTOS Kernel scheduler lock state.
|
||||
/// \param[in] lock lock state obtained by \ref osKernelLock or \ref osKernelUnlock.
|
||||
/// \return new lock state (1 - locked, 0 - not locked, error code if negative).
|
||||
int32_t osKernelRestoreLock (int32_t lock);
|
||||
|
||||
/// Suspend the RTOS Kernel scheduler.
|
||||
/// \return time in ticks, for how long the system can sleep or power-down.
|
||||
uint32_t osKernelSuspend (void);
|
||||
|
||||
/// Resume the RTOS Kernel scheduler.
|
||||
/// \param[in] sleep_ticks time in ticks for how long the system was in sleep or power-down mode.
|
||||
void osKernelResume (uint32_t sleep_ticks);
|
||||
|
||||
/// Get the RTOS kernel tick count.
|
||||
/// \return RTOS kernel current tick count.
|
||||
uint32_t osKernelGetTickCount (void);
|
||||
|
||||
/// Get the RTOS kernel tick frequency.
|
||||
/// \return frequency of the kernel tick in hertz, i.e. kernel ticks per second.
|
||||
uint32_t osKernelGetTickFreq (void);
|
||||
|
||||
/// Get the RTOS kernel system timer count.
|
||||
/// \return RTOS kernel current system timer count as 32-bit value.
|
||||
uint32_t osKernelGetSysTimerCount (void);
|
||||
|
||||
/// Get the RTOS kernel system timer frequency.
|
||||
/// \return frequency of the system timer in hertz, i.e. timer ticks per second.
|
||||
uint32_t osKernelGetSysTimerFreq (void);
|
||||
|
||||
|
||||
// ==== Thread Management Functions ====
|
||||
|
||||
/// Create a thread and add it to Active Threads.
|
||||
/// \param[in] func thread function.
|
||||
/// \param[in] argument pointer that is passed to the thread function as start argument.
|
||||
/// \param[in] attr thread attributes; NULL: default values.
|
||||
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||
osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
|
||||
|
||||
/// Get name of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osThreadGetName (osThreadId_t thread_id);
|
||||
|
||||
/// Return the thread ID of the current running thread.
|
||||
/// \return thread ID for reference by other functions or NULL in case of error.
|
||||
osThreadId_t osThreadGetId (void);
|
||||
|
||||
/// Get current thread state of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return current thread state of the specified thread.
|
||||
osThreadState_t osThreadGetState (osThreadId_t thread_id);
|
||||
|
||||
/// Get stack size of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return stack size in bytes.
|
||||
uint32_t osThreadGetStackSize (osThreadId_t thread_id);
|
||||
|
||||
/// Get available stack space of a thread based on stack watermark recording during execution.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return remaining stack space in bytes.
|
||||
uint32_t osThreadGetStackSpace (osThreadId_t thread_id);
|
||||
|
||||
/// Change priority of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \param[in] priority new priority value for the thread function.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadSetPriority (osThreadId_t thread_id, osPriority_t priority);
|
||||
|
||||
/// Get current priority of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return current priority value of the specified thread.
|
||||
osPriority_t osThreadGetPriority (osThreadId_t thread_id);
|
||||
|
||||
/// Pass control to next thread that is in state \b READY.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadYield (void);
|
||||
|
||||
/// Suspend execution of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadSuspend (osThreadId_t thread_id);
|
||||
|
||||
/// Resume execution of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadResume (osThreadId_t thread_id);
|
||||
|
||||
/// Detach a thread (thread storage can be reclaimed when thread terminates).
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadDetach (osThreadId_t thread_id);
|
||||
|
||||
/// Wait for specified thread to terminate.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadJoin (osThreadId_t thread_id);
|
||||
|
||||
/// Terminate execution of current running thread.
|
||||
__NO_RETURN void osThreadExit (void);
|
||||
|
||||
/// Terminate execution of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osThreadTerminate (osThreadId_t thread_id);
|
||||
|
||||
/// Get number of active threads.
|
||||
/// \return number of active threads.
|
||||
uint32_t osThreadGetCount (void);
|
||||
|
||||
/// Enumerate active threads.
|
||||
/// \param[out] thread_array pointer to array for retrieving thread IDs.
|
||||
/// \param[in] array_items maximum number of items in array for retrieving thread IDs.
|
||||
/// \return number of enumerated threads.
|
||||
uint32_t osThreadEnumerate (osThreadId_t *thread_array, uint32_t array_items);
|
||||
|
||||
|
||||
// ==== Thread Flags Functions ====
|
||||
|
||||
/// Set the specified Thread Flags of a thread.
|
||||
/// \param[in] thread_id thread ID obtained by \ref osThreadNew or \ref osThreadGetId.
|
||||
/// \param[in] flags specifies the flags of the thread that shall be set.
|
||||
/// \return thread flags after setting or error code if highest bit set.
|
||||
uint32_t osThreadFlagsSet (osThreadId_t thread_id, uint32_t flags);
|
||||
|
||||
/// Clear the specified Thread Flags of current running thread.
|
||||
/// \param[in] flags specifies the flags of the thread that shall be cleared.
|
||||
/// \return thread flags before clearing or error code if highest bit set.
|
||||
uint32_t osThreadFlagsClear (uint32_t flags);
|
||||
|
||||
/// Get the current Thread Flags of current running thread.
|
||||
/// \return current thread flags.
|
||||
uint32_t osThreadFlagsGet (void);
|
||||
|
||||
/// Wait for one or more Thread Flags of the current running thread to become signaled.
|
||||
/// \param[in] flags specifies the flags to wait for.
|
||||
/// \param[in] options specifies flags options (osFlagsXxxx).
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return thread flags before clearing or error code if highest bit set.
|
||||
uint32_t osThreadFlagsWait (uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
|
||||
// ==== Generic Wait Functions ====
|
||||
|
||||
/// Wait for Timeout (Time Delay).
|
||||
/// \param[in] ticks \ref CMSIS_RTOS_TimeOutValue "time ticks" value
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osDelay (uint32_t ticks);
|
||||
|
||||
/// Wait until specified time.
|
||||
/// \param[in] ticks absolute time in ticks
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osDelayUntil (uint32_t ticks);
|
||||
|
||||
|
||||
// ==== Timer Management Functions ====
|
||||
|
||||
/// Create and Initialize a timer.
|
||||
/// \param[in] func function pointer to callback function.
|
||||
/// \param[in] type \ref osTimerOnce for one-shot or \ref osTimerPeriodic for periodic behavior.
|
||||
/// \param[in] argument argument to the timer callback function.
|
||||
/// \param[in] attr timer attributes; NULL: default values.
|
||||
/// \return timer ID for reference by other functions or NULL in case of error.
|
||||
osTimerId_t osTimerNew (osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr);
|
||||
|
||||
/// Get name of a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osTimerGetName (osTimerId_t timer_id);
|
||||
|
||||
/// Start or restart a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||
/// \param[in] ticks \ref CMSIS_RTOS_TimeOutValue "time ticks" value of the timer.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osTimerStart (osTimerId_t timer_id, uint32_t ticks);
|
||||
|
||||
/// Stop a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osTimerStop (osTimerId_t timer_id);
|
||||
|
||||
/// Check if a timer is running.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||
/// \return 0 not running, 1 running.
|
||||
uint32_t osTimerIsRunning (osTimerId_t timer_id);
|
||||
|
||||
/// Delete a timer.
|
||||
/// \param[in] timer_id timer ID obtained by \ref osTimerNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osTimerDelete (osTimerId_t timer_id);
|
||||
|
||||
|
||||
// ==== Event Flags Management Functions ====
|
||||
|
||||
/// Create and Initialize an Event Flags object.
|
||||
/// \param[in] attr event flags attributes; NULL: default values.
|
||||
/// \return event flags ID for reference by other functions or NULL in case of error.
|
||||
osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr);
|
||||
|
||||
/// Get name of an Event Flags object.
|
||||
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osEventFlagsGetName (osEventFlagsId_t ef_id);
|
||||
|
||||
/// Set the specified Event Flags.
|
||||
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||
/// \param[in] flags specifies the flags that shall be set.
|
||||
/// \return event flags after setting or error code if highest bit set.
|
||||
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);
|
||||
|
||||
/// Clear the specified Event Flags.
|
||||
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||
/// \param[in] flags specifies the flags that shall be cleared.
|
||||
/// \return event flags before clearing or error code if highest bit set.
|
||||
uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags);
|
||||
|
||||
/// Get the current Event Flags.
|
||||
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||
/// \return current event flags.
|
||||
uint32_t osEventFlagsGet (osEventFlagsId_t ef_id);
|
||||
|
||||
/// Wait for one or more Event Flags to become signaled.
|
||||
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||
/// \param[in] flags specifies the flags to wait for.
|
||||
/// \param[in] options specifies flags options (osFlagsXxxx).
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return event flags before clearing or error code if highest bit set.
|
||||
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout);
|
||||
|
||||
/// Delete an Event Flags object.
|
||||
/// \param[in] ef_id event flags ID obtained by \ref osEventFlagsNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osEventFlagsDelete (osEventFlagsId_t ef_id);
|
||||
|
||||
|
||||
// ==== Mutex Management Functions ====
|
||||
|
||||
/// Create and Initialize a Mutex object.
|
||||
/// \param[in] attr mutex attributes; NULL: default values.
|
||||
/// \return mutex ID for reference by other functions or NULL in case of error.
|
||||
osMutexId_t osMutexNew (const osMutexAttr_t *attr);
|
||||
|
||||
/// Get name of a Mutex object.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osMutexGetName (osMutexId_t mutex_id);
|
||||
|
||||
/// Acquire a Mutex or timeout if it is locked.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
|
||||
|
||||
/// Release a Mutex that was acquired by \ref osMutexAcquire.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMutexRelease (osMutexId_t mutex_id);
|
||||
|
||||
/// Get Thread which owns a Mutex object.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||
/// \return thread ID of owner thread or NULL when mutex was not acquired.
|
||||
osThreadId_t osMutexGetOwner (osMutexId_t mutex_id);
|
||||
|
||||
/// Delete a Mutex object.
|
||||
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMutexDelete (osMutexId_t mutex_id);
|
||||
|
||||
|
||||
// ==== Semaphore Management Functions ====
|
||||
|
||||
/// Create and Initialize a Semaphore object.
|
||||
/// \param[in] max_count maximum number of available tokens.
|
||||
/// \param[in] initial_count initial number of available tokens.
|
||||
/// \param[in] attr semaphore attributes; NULL: default values.
|
||||
/// \return semaphore ID for reference by other functions or NULL in case of error.
|
||||
osSemaphoreId_t osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr);
|
||||
|
||||
/// Get name of a Semaphore object.
|
||||
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osSemaphoreGetName (osSemaphoreId_t semaphore_id);
|
||||
|
||||
/// Acquire a Semaphore token or timeout if no tokens are available.
|
||||
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout);
|
||||
|
||||
/// Release a Semaphore token up to the initial maximum count.
|
||||
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id);
|
||||
|
||||
/// Get current Semaphore token count.
|
||||
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||
/// \return number of tokens available.
|
||||
uint32_t osSemaphoreGetCount (osSemaphoreId_t semaphore_id);
|
||||
|
||||
/// Delete a Semaphore object.
|
||||
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osSemaphoreDelete (osSemaphoreId_t semaphore_id);
|
||||
|
||||
|
||||
// ==== Memory Pool Management Functions ====
|
||||
|
||||
/// Create and Initialize a Memory Pool object.
|
||||
/// \param[in] block_count maximum number of memory blocks in memory pool.
|
||||
/// \param[in] block_size memory block size in bytes.
|
||||
/// \param[in] attr memory pool attributes; NULL: default values.
|
||||
/// \return memory pool ID for reference by other functions or NULL in case of error.
|
||||
osMemoryPoolId_t osMemoryPoolNew (uint32_t block_count, uint32_t block_size, const osMemoryPoolAttr_t *attr);
|
||||
|
||||
/// Get name of a Memory Pool object.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osMemoryPoolGetName (osMemoryPoolId_t mp_id);
|
||||
|
||||
/// Allocate a memory block from a Memory Pool.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return address of the allocated memory block or NULL in case of no memory is available.
|
||||
void *osMemoryPoolAlloc (osMemoryPoolId_t mp_id, uint32_t timeout);
|
||||
|
||||
/// Return an allocated memory block back to a Memory Pool.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \param[in] block address of the allocated memory block to be returned to the memory pool.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMemoryPoolFree (osMemoryPoolId_t mp_id, void *block);
|
||||
|
||||
/// Get maximum number of memory blocks in a Memory Pool.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \return maximum number of memory blocks.
|
||||
uint32_t osMemoryPoolGetCapacity (osMemoryPoolId_t mp_id);
|
||||
|
||||
/// Get memory block size in a Memory Pool.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \return memory block size in bytes.
|
||||
uint32_t osMemoryPoolGetBlockSize (osMemoryPoolId_t mp_id);
|
||||
|
||||
/// Get number of memory blocks used in a Memory Pool.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \return number of memory blocks used.
|
||||
uint32_t osMemoryPoolGetCount (osMemoryPoolId_t mp_id);
|
||||
|
||||
/// Get number of memory blocks available in a Memory Pool.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \return number of memory blocks available.
|
||||
uint32_t osMemoryPoolGetSpace (osMemoryPoolId_t mp_id);
|
||||
|
||||
/// Delete a Memory Pool object.
|
||||
/// \param[in] mp_id memory pool ID obtained by \ref osMemoryPoolNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMemoryPoolDelete (osMemoryPoolId_t mp_id);
|
||||
|
||||
|
||||
// ==== Message Queue Management Functions ====
|
||||
|
||||
/// Create and Initialize a Message Queue object.
|
||||
/// \param[in] msg_count maximum number of messages in queue.
|
||||
/// \param[in] msg_size maximum message size in bytes.
|
||||
/// \param[in] attr message queue attributes; NULL: default values.
|
||||
/// \return message queue ID for reference by other functions or NULL in case of error.
|
||||
osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr);
|
||||
|
||||
/// Get name of a Message Queue object.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return name as NULL terminated string.
|
||||
const char *osMessageQueueGetName (osMessageQueueId_t mq_id);
|
||||
|
||||
/// Put a Message into a Queue or timeout if Queue is full.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \param[in] msg_ptr pointer to buffer with message to put into a queue.
|
||||
/// \param[in] msg_prio message priority.
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
|
||||
|
||||
/// Get a Message from a Queue or timeout if Queue is empty.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \param[out] msg_ptr pointer to buffer for message to get from a queue.
|
||||
/// \param[out] msg_prio pointer to buffer for message priority or NULL.
|
||||
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);
|
||||
|
||||
/// Get maximum number of messages in a Message Queue.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return maximum number of messages.
|
||||
uint32_t osMessageQueueGetCapacity (osMessageQueueId_t mq_id);
|
||||
|
||||
/// Get maximum message size in a Memory Pool.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return maximum message size in bytes.
|
||||
uint32_t osMessageQueueGetMsgSize (osMessageQueueId_t mq_id);
|
||||
|
||||
/// Get number of queued messages in a Message Queue.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return number of queued messages.
|
||||
uint32_t osMessageQueueGetCount (osMessageQueueId_t mq_id);
|
||||
|
||||
/// Get number of available slots for messages in a Message Queue.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return number of available slots for messages.
|
||||
uint32_t osMessageQueueGetSpace (osMessageQueueId_t mq_id);
|
||||
|
||||
/// Reset a Message Queue to initial empty state.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMessageQueueReset (osMessageQueueId_t mq_id);
|
||||
|
||||
/// Delete a Message Queue object.
|
||||
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
|
||||
/// \return status code that indicates the execution status of the function.
|
||||
osStatus_t osMessageQueueDelete (osMessageQueueId_t mq_id);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CMSIS_OS2_H_
|
||||
63
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_mpool.h
vendored
Normal file
63
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_mpool.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/* --------------------------------------------------------------------------
|
||||
* Copyright (c) 2013-2020 Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Name: freertos_mpool.h
|
||||
* Purpose: CMSIS RTOS2 wrapper for FreeRTOS
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef FREERTOS_MPOOL_H_
|
||||
#define FREERTOS_MPOOL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
|
||||
/* Memory Pool implementation definitions */
|
||||
#define MPOOL_STATUS 0x5EED0000U
|
||||
|
||||
/* Memory Block header */
|
||||
typedef struct {
|
||||
void *next; /* Pointer to next block */
|
||||
} MemPoolBlock_t;
|
||||
|
||||
/* Memory Pool control block */
|
||||
typedef struct MemPoolDef_t {
|
||||
MemPoolBlock_t *head; /* Pointer to head block */
|
||||
SemaphoreHandle_t sem; /* Pool semaphore handle */
|
||||
uint8_t *mem_arr; /* Pool memory array */
|
||||
uint32_t mem_sz; /* Pool memory array size */
|
||||
const char *name; /* Pointer to name string */
|
||||
uint32_t bl_sz; /* Size of a single block */
|
||||
uint32_t bl_cnt; /* Number of blocks */
|
||||
uint32_t n; /* Block allocation index */
|
||||
volatile uint32_t status; /* Object status flags */
|
||||
#if (configSUPPORT_STATIC_ALLOCATION == 1)
|
||||
StaticSemaphore_t mem_sem; /* Semaphore object memory */
|
||||
#endif
|
||||
} MemPool_t;
|
||||
|
||||
/* No need to hide static object type, just align to coding style */
|
||||
#define StaticMemPool_t MemPool_t
|
||||
|
||||
/* Define memory pool control block size */
|
||||
#define MEMPOOL_CB_SIZE (sizeof(StaticMemPool_t))
|
||||
|
||||
/* Define size of the byte array required to create count of blocks of given size */
|
||||
#define MEMPOOL_ARR_SIZE(bl_count, bl_size) (((((bl_size) + (4 - 1)) / 4) * 4)*(bl_count))
|
||||
|
||||
#endif /* FREERTOS_MPOOL_H_ */
|
||||
310
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_os2.h
vendored
Normal file
310
Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2/freertos_os2.h
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
/* --------------------------------------------------------------------------
|
||||
* Copyright (c) 2013-2020 Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Name: freertos_os2.h
|
||||
* Purpose: CMSIS RTOS2 wrapper for FreeRTOS
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef FREERTOS_OS2_H_
|
||||
#define FREERTOS_OS2_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core
|
||||
|
||||
#include CMSIS_device_header
|
||||
|
||||
/*
|
||||
CMSIS-RTOS2 FreeRTOS image size optimization definitions.
|
||||
|
||||
Note: Definitions configUSE_OS2 can be used to optimize FreeRTOS image size when
|
||||
certain functionality is not required when using CMSIS-RTOS2 API.
|
||||
In general optimization decisions are left to the tool chain but in cases
|
||||
when coding style prevents it to optimize the code following optional
|
||||
definitions can be used.
|
||||
*/
|
||||
|
||||
/*
|
||||
Option to exclude CMSIS-RTOS2 functions osThreadSuspend and osThreadResume from
|
||||
the application image.
|
||||
*/
|
||||
#ifndef configUSE_OS2_THREAD_SUSPEND_RESUME
|
||||
#define configUSE_OS2_THREAD_SUSPEND_RESUME 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
Option to exclude CMSIS-RTOS2 function osThreadEnumerate from the application image.
|
||||
*/
|
||||
#ifndef configUSE_OS2_THREAD_ENUMERATE
|
||||
#define configUSE_OS2_THREAD_ENUMERATE 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
Option to disable CMSIS-RTOS2 function osEventFlagsSet and osEventFlagsClear
|
||||
operation from ISR.
|
||||
*/
|
||||
#ifndef configUSE_OS2_EVENTFLAGS_FROM_ISR
|
||||
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
Option to exclude CMSIS-RTOS2 Thread Flags API functions from the application image.
|
||||
*/
|
||||
#ifndef configUSE_OS2_THREAD_FLAGS
|
||||
#define configUSE_OS2_THREAD_FLAGS configUSE_TASK_NOTIFICATIONS
|
||||
#endif
|
||||
|
||||
/*
|
||||
Option to exclude CMSIS-RTOS2 Timer API functions from the application image.
|
||||
*/
|
||||
#ifndef configUSE_OS2_TIMER
|
||||
#define configUSE_OS2_TIMER configUSE_TIMERS
|
||||
#endif
|
||||
|
||||
/*
|
||||
Option to exclude CMSIS-RTOS2 Mutex API functions from the application image.
|
||||
*/
|
||||
#ifndef configUSE_OS2_MUTEX
|
||||
#define configUSE_OS2_MUTEX configUSE_MUTEXES
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
CMSIS-RTOS2 FreeRTOS configuration check (FreeRTOSConfig.h).
|
||||
|
||||
Note: CMSIS-RTOS API requires functions included by using following definitions.
|
||||
In case if certain API function is not used compiler will optimize it away.
|
||||
*/
|
||||
#if (INCLUDE_xSemaphoreGetMutexHolder == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osMutexGetOwner uses FreeRTOS function xSemaphoreGetMutexHolder. In case if
|
||||
osMutexGetOwner is not used in the application image, compiler will optimize it away.
|
||||
Set #define INCLUDE_xSemaphoreGetMutexHolder 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_xSemaphoreGetMutexHolder must equal 1 to implement Mutex Management API."
|
||||
#endif
|
||||
#if (INCLUDE_vTaskDelay == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osDelay uses FreeRTOS function vTaskDelay. In case if
|
||||
osDelay is not used in the application image, compiler will optimize it away.
|
||||
Set #define INCLUDE_vTaskDelay 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_vTaskDelay must equal 1 to implement Generic Wait Functions API."
|
||||
#endif
|
||||
#if (INCLUDE_vTaskDelayUntil == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osDelayUntil uses FreeRTOS function vTaskDelayUntil. In case if
|
||||
osDelayUntil is not used in the application image, compiler will optimize it away.
|
||||
Set #define INCLUDE_vTaskDelayUntil 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_vTaskDelayUntil must equal 1 to implement Generic Wait Functions API."
|
||||
#endif
|
||||
#if (INCLUDE_vTaskDelete == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osThreadTerminate and osThreadExit uses FreeRTOS function
|
||||
vTaskDelete. In case if they are not used in the application image, compiler
|
||||
will optimize them away.
|
||||
Set #define INCLUDE_vTaskDelete 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_vTaskDelete must equal 1 to implement Thread Management API."
|
||||
#endif
|
||||
#if (INCLUDE_xTaskGetCurrentTaskHandle == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 API uses FreeRTOS function xTaskGetCurrentTaskHandle to implement
|
||||
functions osThreadGetId, osThreadFlagsClear and osThreadFlagsGet. In case if these
|
||||
functions are not used in the application image, compiler will optimize them away.
|
||||
Set #define INCLUDE_xTaskGetCurrentTaskHandle 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_xTaskGetCurrentTaskHandle must equal 1 to implement Thread Management API."
|
||||
#endif
|
||||
#if (INCLUDE_xTaskGetSchedulerState == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 API uses FreeRTOS function xTaskGetSchedulerState to implement Kernel
|
||||
tick handling and therefore it is vital that xTaskGetSchedulerState is included into
|
||||
the application image.
|
||||
Set #define INCLUDE_xTaskGetSchedulerState 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_xTaskGetSchedulerState must equal 1 to implement Kernel Information and Control API."
|
||||
#endif
|
||||
#if (INCLUDE_uxTaskGetStackHighWaterMark == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osThreadGetStackSpace uses FreeRTOS function uxTaskGetStackHighWaterMark.
|
||||
In case if osThreadGetStackSpace is not used in the application image, compiler will
|
||||
optimize it away.
|
||||
Set #define INCLUDE_uxTaskGetStackHighWaterMark 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_uxTaskGetStackHighWaterMark must equal 1 to implement Thread Management API."
|
||||
#endif
|
||||
#if (INCLUDE_uxTaskPriorityGet == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osThreadGetPriority uses FreeRTOS function uxTaskPriorityGet. In case if
|
||||
osThreadGetPriority is not used in the application image, compiler will optimize it away.
|
||||
Set #define INCLUDE_uxTaskPriorityGet 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_uxTaskPriorityGet must equal 1 to implement Thread Management API."
|
||||
#endif
|
||||
#if (INCLUDE_vTaskPrioritySet == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osThreadSetPriority uses FreeRTOS function vTaskPrioritySet. In case if
|
||||
osThreadSetPriority is not used in the application image, compiler will optimize it away.
|
||||
Set #define INCLUDE_vTaskPrioritySet 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_vTaskPrioritySet must equal 1 to implement Thread Management API."
|
||||
#endif
|
||||
#if (INCLUDE_eTaskGetState == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 API uses FreeRTOS function vTaskDelayUntil to implement functions osThreadGetState
|
||||
and osThreadTerminate. In case if these functions are not used in the application image,
|
||||
compiler will optimize them away.
|
||||
Set #define INCLUDE_eTaskGetState 1 to fix this error.
|
||||
*/
|
||||
#error "Definition INCLUDE_eTaskGetState must equal 1 to implement Thread Management API."
|
||||
#endif
|
||||
#if (INCLUDE_vTaskSuspend == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 API uses FreeRTOS functions vTaskSuspend and vTaskResume to implement
|
||||
functions osThreadSuspend and osThreadResume. In case if these functions are not
|
||||
used in the application image, compiler will optimize them away.
|
||||
Set #define INCLUDE_vTaskSuspend 1 to fix this error.
|
||||
|
||||
Alternatively, if the application does not use osThreadSuspend and
|
||||
osThreadResume they can be excluded from the image code by setting:
|
||||
#define configUSE_OS2_THREAD_SUSPEND_RESUME 0 (in FreeRTOSConfig.h)
|
||||
*/
|
||||
#if (configUSE_OS2_THREAD_SUSPEND_RESUME == 1)
|
||||
#error "Definition INCLUDE_vTaskSuspend must equal 1 to implement Kernel Information and Control API."
|
||||
#endif
|
||||
#endif
|
||||
#if (INCLUDE_xTimerPendFunctionCall == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osEventFlagsSet and osEventFlagsClear, when called from
|
||||
the ISR, call FreeRTOS functions xEventGroupSetBitsFromISR and
|
||||
xEventGroupClearBitsFromISR which are only enabled if timers are operational and
|
||||
xTimerPendFunctionCall in enabled.
|
||||
Set #define INCLUDE_xTimerPendFunctionCall 1 and #define configUSE_TIMERS 1
|
||||
to fix this error.
|
||||
|
||||
Alternatively, if the application does not use osEventFlagsSet and osEventFlagsClear
|
||||
from the ISR their operation from ISR can be restricted by setting:
|
||||
#define configUSE_OS2_EVENTFLAGS_FROM_ISR 0 (in FreeRTOSConfig.h)
|
||||
*/
|
||||
#if (configUSE_OS2_EVENTFLAGS_FROM_ISR == 1)
|
||||
#error "Definition INCLUDE_xTimerPendFunctionCall must equal 1 to implement Event Flags API."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (configUSE_TIMERS == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 Timer Management API functions use FreeRTOS timer functions to implement
|
||||
timer management. In case if these functions are not used in the application image,
|
||||
compiler will optimize them away.
|
||||
Set #define configUSE_TIMERS 1 to fix this error.
|
||||
|
||||
Alternatively, if the application does not use timer functions they can be
|
||||
excluded from the image code by setting:
|
||||
#define configUSE_OS2_TIMER 0 (in FreeRTOSConfig.h)
|
||||
*/
|
||||
#if (configUSE_OS2_TIMER == 1)
|
||||
#error "Definition configUSE_TIMERS must equal 1 to implement Timer Management API."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (configUSE_MUTEXES == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 Mutex Management API functions use FreeRTOS mutex functions to implement
|
||||
mutex management. In case if these functions are not used in the application image,
|
||||
compiler will optimize them away.
|
||||
Set #define configUSE_MUTEXES 1 to fix this error.
|
||||
|
||||
Alternatively, if the application does not use mutex functions they can be
|
||||
excluded from the image code by setting:
|
||||
#define configUSE_OS2_MUTEX 0 (in FreeRTOSConfig.h)
|
||||
*/
|
||||
#if (configUSE_OS2_MUTEX == 1)
|
||||
#error "Definition configUSE_MUTEXES must equal 1 to implement Mutex Management API."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (configUSE_COUNTING_SEMAPHORES == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 Memory Pool functions use FreeRTOS function xSemaphoreCreateCounting
|
||||
to implement memory pools. In case if these functions are not used in the application image,
|
||||
compiler will optimize them away.
|
||||
Set #define configUSE_COUNTING_SEMAPHORES 1 to fix this error.
|
||||
*/
|
||||
#error "Definition configUSE_COUNTING_SEMAPHORES must equal 1 to implement Memory Pool API."
|
||||
#endif
|
||||
#if (configUSE_TASK_NOTIFICATIONS == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 Thread Flags API functions use FreeRTOS Task Notification functions to implement
|
||||
thread flag management. In case if these functions are not used in the application image,
|
||||
compiler will optimize them away.
|
||||
Set #define configUSE_TASK_NOTIFICATIONS 1 to fix this error.
|
||||
|
||||
Alternatively, if the application does not use thread flags functions they can be
|
||||
excluded from the image code by setting:
|
||||
#define configUSE_OS2_THREAD_FLAGS 0 (in FreeRTOSConfig.h)
|
||||
*/
|
||||
#if (configUSE_OS2_THREAD_FLAGS == 1)
|
||||
#error "Definition configUSE_TASK_NOTIFICATIONS must equal 1 to implement Thread Flags API."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (configUSE_TRACE_FACILITY == 0)
|
||||
/*
|
||||
CMSIS-RTOS2 function osThreadEnumerate requires FreeRTOS function uxTaskGetSystemState
|
||||
which is only enabled if configUSE_TRACE_FACILITY == 1.
|
||||
Set #define configUSE_TRACE_FACILITY 1 to fix this error.
|
||||
|
||||
Alternatively, if the application does not use osThreadEnumerate it can be
|
||||
excluded from the image code by setting:
|
||||
#define configUSE_OS2_THREAD_ENUMERATE 0 (in FreeRTOSConfig.h)
|
||||
*/
|
||||
#if (configUSE_OS2_THREAD_ENUMERATE == 1)
|
||||
#error "Definition configUSE_TRACE_FACILITY must equal 1 to implement osThreadEnumerate."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (configUSE_16_BIT_TICKS == 1)
|
||||
/*
|
||||
CMSIS-RTOS2 wrapper for FreeRTOS relies on 32-bit tick timer which is also optimal on
|
||||
a 32-bit CPU architectures.
|
||||
Set #define configUSE_16_BIT_TICKS 0 to fix this error.
|
||||
*/
|
||||
#error "Definition configUSE_16_BIT_TICKS must be zero to implement CMSIS-RTOS2 API."
|
||||
#endif
|
||||
|
||||
#if (configMAX_PRIORITIES != 56)
|
||||
/*
|
||||
CMSIS-RTOS2 defines 56 different priorities (see osPriority_t) and portable CMSIS-RTOS2
|
||||
implementation should implement the same number of priorities.
|
||||
Set #define configMAX_PRIORITIES 56 to fix this error.
|
||||
*/
|
||||
#error "Definition configMAX_PRIORITIES must equal 56 to implement Thread Management API."
|
||||
#endif
|
||||
#if (configUSE_PORT_OPTIMISED_TASK_SELECTION != 0)
|
||||
/*
|
||||
CMSIS-RTOS2 requires handling of 56 different priorities (see osPriority_t) while FreeRTOS port
|
||||
optimised selection for Cortex core only handles 32 different priorities.
|
||||
Set #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 to fix this error.
|
||||
*/
|
||||
#error "Definition configUSE_PORT_OPTIMISED_TASK_SELECTION must be zero to implement Thread Management API."
|
||||
#endif
|
||||
|
||||
#endif /* FREERTOS_OS2_H_ */
|
||||
18
Middlewares/Third_Party/FreeRTOS/Source/LICENSE
vendored
Normal file
18
Middlewares/Third_Party/FreeRTOS/Source/LICENSE
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
353
Middlewares/Third_Party/FreeRTOS/Source/croutine.c
vendored
Normal file
353
Middlewares/Third_Party/FreeRTOS/Source/croutine.c
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* FreeRTOS Kernel V10.3.1
|
||||
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* http://www.FreeRTOS.org
|
||||
* http://aws.amazon.com/freertos
|
||||
*
|
||||
* 1 tab == 4 spaces!
|
||||
*/
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "croutine.h"
|
||||
|
||||
/* Remove the whole file is co-routines are not being used. */
|
||||
#if( configUSE_CO_ROUTINES != 0 )
|
||||
|
||||
/*
|
||||
* Some kernel aware debuggers require data to be viewed to be global, rather
|
||||
* than file scope.
|
||||
*/
|
||||
#ifdef portREMOVE_STATIC_QUALIFIER
|
||||
#define static
|
||||
#endif
|
||||
|
||||
|
||||
/* Lists for ready and blocked co-routines. --------------------*/
|
||||
static List_t pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */
|
||||
static List_t xDelayedCoRoutineList1; /*< Delayed co-routines. */
|
||||
static List_t xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */
|
||||
static List_t * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */
|
||||
static List_t * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */
|
||||
static List_t xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */
|
||||
|
||||
/* Other file private variables. --------------------------------*/
|
||||
CRCB_t * pxCurrentCoRoutine = NULL;
|
||||
static UBaseType_t uxTopCoRoutineReadyPriority = 0;
|
||||
static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0;
|
||||
|
||||
/* The initial state of the co-routine when it is created. */
|
||||
#define corINITIAL_STATE ( 0 )
|
||||
|
||||
/*
|
||||
* Place the co-routine represented by pxCRCB into the appropriate ready queue
|
||||
* for the priority. It is inserted at the end of the list.
|
||||
*
|
||||
* This macro accesses the co-routine ready lists and therefore must not be
|
||||
* used from within an ISR.
|
||||
*/
|
||||
#define prvAddCoRoutineToReadyQueue( pxCRCB ) \
|
||||
{ \
|
||||
if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \
|
||||
{ \
|
||||
uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \
|
||||
} \
|
||||
vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility to ready all the lists used by the scheduler. This is called
|
||||
* automatically upon the creation of the first co-routine.
|
||||
*/
|
||||
static void prvInitialiseCoRoutineLists( void );
|
||||
|
||||
/*
|
||||
* Co-routines that are readied by an interrupt cannot be placed directly into
|
||||
* the ready lists (there is no mutual exclusion). Instead they are placed in
|
||||
* in the pending ready list in order that they can later be moved to the ready
|
||||
* list by the co-routine scheduler.
|
||||
*/
|
||||
static void prvCheckPendingReadyList( void );
|
||||
|
||||
/*
|
||||
* Macro that looks at the list of co-routines that are currently delayed to
|
||||
* see if any require waking.
|
||||
*
|
||||
* Co-routines are stored in the queue in the order of their wake time -
|
||||
* meaning once one co-routine has been found whose timer has not expired
|
||||
* we need not look any further down the list.
|
||||
*/
|
||||
static void prvCheckDelayedList( void );
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex )
|
||||
{
|
||||
BaseType_t xReturn;
|
||||
CRCB_t *pxCoRoutine;
|
||||
|
||||
/* Allocate the memory that will store the co-routine control block. */
|
||||
pxCoRoutine = ( CRCB_t * ) pvPortMalloc( sizeof( CRCB_t ) );
|
||||
if( pxCoRoutine )
|
||||
{
|
||||
/* If pxCurrentCoRoutine is NULL then this is the first co-routine to
|
||||
be created and the co-routine data structures need initialising. */
|
||||
if( pxCurrentCoRoutine == NULL )
|
||||
{
|
||||
pxCurrentCoRoutine = pxCoRoutine;
|
||||
prvInitialiseCoRoutineLists();
|
||||
}
|
||||
|
||||
/* Check the priority is within limits. */
|
||||
if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES )
|
||||
{
|
||||
uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1;
|
||||
}
|
||||
|
||||
/* Fill out the co-routine control block from the function parameters. */
|
||||
pxCoRoutine->uxState = corINITIAL_STATE;
|
||||
pxCoRoutine->uxPriority = uxPriority;
|
||||
pxCoRoutine->uxIndex = uxIndex;
|
||||
pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode;
|
||||
|
||||
/* Initialise all the other co-routine control block parameters. */
|
||||
vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) );
|
||||
vListInitialiseItem( &( pxCoRoutine->xEventListItem ) );
|
||||
|
||||
/* Set the co-routine control block as a link back from the ListItem_t.
|
||||
This is so we can get back to the containing CRCB from a generic item
|
||||
in a list. */
|
||||
listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine );
|
||||
listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine );
|
||||
|
||||
/* Event lists are always in priority order. */
|
||||
listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) );
|
||||
|
||||
/* Now the co-routine has been initialised it can be added to the ready
|
||||
list at the correct priority. */
|
||||
prvAddCoRoutineToReadyQueue( pxCoRoutine );
|
||||
|
||||
xReturn = pdPASS;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList )
|
||||
{
|
||||
TickType_t xTimeToWake;
|
||||
|
||||
/* Calculate the time to wake - this may overflow but this is
|
||||
not a problem. */
|
||||
xTimeToWake = xCoRoutineTickCount + xTicksToDelay;
|
||||
|
||||
/* We must remove ourselves from the ready list before adding
|
||||
ourselves to the blocked list as the same list item is used for
|
||||
both lists. */
|
||||
( void ) uxListRemove( ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );
|
||||
|
||||
/* The list item will be inserted in wake time order. */
|
||||
listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake );
|
||||
|
||||
if( xTimeToWake < xCoRoutineTickCount )
|
||||
{
|
||||
/* Wake time has overflowed. Place this item in the
|
||||
overflow list. */
|
||||
vListInsert( ( List_t * ) pxOverflowDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The wake time has not overflowed, so we can use the
|
||||
current block list. */
|
||||
vListInsert( ( List_t * ) pxDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );
|
||||
}
|
||||
|
||||
if( pxEventList )
|
||||
{
|
||||
/* Also add the co-routine to an event list. If this is done then the
|
||||
function must be called with interrupts disabled. */
|
||||
vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) );
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvCheckPendingReadyList( void )
|
||||
{
|
||||
/* Are there any co-routines waiting to get moved to the ready list? These
|
||||
are co-routines that have been readied by an ISR. The ISR cannot access
|
||||
the ready lists itself. */
|
||||
while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE )
|
||||
{
|
||||
CRCB_t *pxUnblockedCRCB;
|
||||
|
||||
/* The pending ready list can be accessed by an ISR. */
|
||||
portDISABLE_INTERRUPTS();
|
||||
{
|
||||
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) );
|
||||
( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) );
|
||||
}
|
||||
portENABLE_INTERRUPTS();
|
||||
|
||||
( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) );
|
||||
prvAddCoRoutineToReadyQueue( pxUnblockedCRCB );
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvCheckDelayedList( void )
|
||||
{
|
||||
CRCB_t *pxCRCB;
|
||||
|
||||
xPassedTicks = xTaskGetTickCount() - xLastTickCount;
|
||||
while( xPassedTicks )
|
||||
{
|
||||
xCoRoutineTickCount++;
|
||||
xPassedTicks--;
|
||||
|
||||
/* If the tick count has overflowed we need to swap the ready lists. */
|
||||
if( xCoRoutineTickCount == 0 )
|
||||
{
|
||||
List_t * pxTemp;
|
||||
|
||||
/* Tick count has overflowed so we need to swap the delay lists. If there are
|
||||
any items in pxDelayedCoRoutineList here then there is an error! */
|
||||
pxTemp = pxDelayedCoRoutineList;
|
||||
pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList;
|
||||
pxOverflowDelayedCoRoutineList = pxTemp;
|
||||
}
|
||||
|
||||
/* See if this tick has made a timeout expire. */
|
||||
while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE )
|
||||
{
|
||||
pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList );
|
||||
|
||||
if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) )
|
||||
{
|
||||
/* Timeout not yet expired. */
|
||||
break;
|
||||
}
|
||||
|
||||
portDISABLE_INTERRUPTS();
|
||||
{
|
||||
/* The event could have occurred just before this critical
|
||||
section. If this is the case then the generic list item will
|
||||
have been moved to the pending ready list and the following
|
||||
line is still valid. Also the pvContainer parameter will have
|
||||
been set to NULL so the following lines are also valid. */
|
||||
( void ) uxListRemove( &( pxCRCB->xGenericListItem ) );
|
||||
|
||||
/* Is the co-routine waiting on an event also? */
|
||||
if( pxCRCB->xEventListItem.pxContainer )
|
||||
{
|
||||
( void ) uxListRemove( &( pxCRCB->xEventListItem ) );
|
||||
}
|
||||
}
|
||||
portENABLE_INTERRUPTS();
|
||||
|
||||
prvAddCoRoutineToReadyQueue( pxCRCB );
|
||||
}
|
||||
}
|
||||
|
||||
xLastTickCount = xCoRoutineTickCount;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void vCoRoutineSchedule( void )
|
||||
{
|
||||
/* See if any co-routines readied by events need moving to the ready lists. */
|
||||
prvCheckPendingReadyList();
|
||||
|
||||
/* See if any delayed co-routines have timed out. */
|
||||
prvCheckDelayedList();
|
||||
|
||||
/* Find the highest priority queue that contains ready co-routines. */
|
||||
while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) )
|
||||
{
|
||||
if( uxTopCoRoutineReadyPriority == 0 )
|
||||
{
|
||||
/* No more co-routines to check. */
|
||||
return;
|
||||
}
|
||||
--uxTopCoRoutineReadyPriority;
|
||||
}
|
||||
|
||||
/* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines
|
||||
of the same priority get an equal share of the processor time. */
|
||||
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) );
|
||||
|
||||
/* Call the co-routine. */
|
||||
( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex );
|
||||
|
||||
return;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
static void prvInitialiseCoRoutineLists( void )
|
||||
{
|
||||
UBaseType_t uxPriority;
|
||||
|
||||
for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ )
|
||||
{
|
||||
vListInitialise( ( List_t * ) &( pxReadyCoRoutineLists[ uxPriority ] ) );
|
||||
}
|
||||
|
||||
vListInitialise( ( List_t * ) &xDelayedCoRoutineList1 );
|
||||
vListInitialise( ( List_t * ) &xDelayedCoRoutineList2 );
|
||||
vListInitialise( ( List_t * ) &xPendingReadyCoRoutineList );
|
||||
|
||||
/* Start with pxDelayedCoRoutineList using list1 and the
|
||||
pxOverflowDelayedCoRoutineList using list2. */
|
||||
pxDelayedCoRoutineList = &xDelayedCoRoutineList1;
|
||||
pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2;
|
||||
}
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList )
|
||||
{
|
||||
CRCB_t *pxUnblockedCRCB;
|
||||
BaseType_t xReturn;
|
||||
|
||||
/* This function is called from within an interrupt. It can only access
|
||||
event lists and the pending ready list. This function assumes that a
|
||||
check has already been made to ensure pxEventList is not empty. */
|
||||
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );
|
||||
( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) );
|
||||
vListInsertEnd( ( List_t * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) );
|
||||
|
||||
if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority )
|
||||
{
|
||||
xReturn = pdTRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
xReturn = pdFALSE;
|
||||
}
|
||||
|
||||
return xReturn;
|
||||
}
|
||||
|
||||
#endif /* configUSE_CO_ROUTINES == 0 */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user