28 Commits
Author SHA1 Message Date
hulk 88431015d7 chore(version): dev 升至 V1.0.4(开发版,未发布)——与保底判定值拉开距离
golden 已整体替换为生产实装版原样复制(v26.1.0.3,即保底判定值),A 线版本必须 > V1.0.3。
相对生产实装版 v26.1.0.3 的主要改动:
- IIC 驱动升级(模板改进版:总线恢复重试/超时辅助/read_raw 无子地址读/16 位寄存器地址/scan AERR 处理)——最大一块
- 新增 M5(温度补偿涡流值,系数待标定暂等价 M3)与 M6(标定数据:涡流+温度)命令
- M4 温度换算修复负温度(有符号 int16 算法,生产版无符号算法负温会算错)
- 双固件 A 槽布局(V1.0.3 已引入,随包继承)
待定:LDC1612 驱动电流当前仍为 GEN(18),生产实装调参为 GEN(8)(2026-03-23),正式发布前需确认是否回合。

仅升版本号与文档同步,未发布 Release;AGENTS 同步 golden 线新状态(生产复制 V1.0.3 / 判定值 v26.1.0.3)。
2026-10-25 22:39:49 +08:00
hulk a99b44a6dc chore: 清理历史过程分支 dev_more_information / dev_feature_iic_more_byte(AGENTS 留档含提交哈希) 2026-10-24 21:43:10 +08:00
hulk 4e2ce4984c docs: 同步 golden 线 BL 专属分支说明(bootloader_CeramixNano_EDDY,撤销复用表述) 2026-10-24 21:33:31 +08:00
hulk 4634bd3d3d feat(dual-fw): 移植双固件布局——LD 收窄 36K 防溢出 B 槽,命名 CeramixNano-EDDY/板卡码 26/版本 V1.0.3 2026-10-24 21:14:18 +08:00
hulk b6da450f8a build: 恢复正式发布布局 0x08002000/54K + VECT_TAB_OFFSET=0x2000
调试裸机布局(0x08000000/VTOR=0)仅限本地临时修改,不进仓库;
2026-10-02 23:21:47 +08:00
hulk de6afa1717 docs: AGENTS.md 记录 V1.0.2 发布 2026-10-02 23:09:37 +08:00
hulk 57963d179a build: 调试阶段 APP 临时链接 0x08000000(VECT_TAB_OFFSET=0,脱离 bootloader 直接运行)
正式发布仍用 V1.0.2 tag 处的 0x08002000/54K Bootloader 布局打包(见 Release V1.0.2)
2026-10-02 23:09:19 +08:00
hulk 8d5c33236d release:(1.0.2) 版本号提升至 V1.0.2 2026-10-02 23:07:59 +08:00
hulk a40c2ec793 build(vscode): 新增 DAPLink 专用烧录配置与任务
- openocd_daplink_gd32e23x.cfg:去掉 connect_assert_srst(该选项导致 DAPLink 下 Cortex-M23 识别失败)
2026-10-02 23:07:55 +08:00
hulk e81c902341 docs: 修正协议文档命令编号并补全 M4~M9999 帧格式说明;新增项目 AGENTS.md
- 温度上报命令由 M3 笔误更正为 M4,补充 M4 回复示例
2026-10-02 23:07:51 +08:00
hulk 07b631b254 feat(i2c): 导入模板 IIC 驱动改进(总线恢复重试/超时辅助函数/read_raw/scan AERR 处理) 2026-10-02 23:07:41 +08:00
hulk 89e7191cd5 build: 对齐模板工具链与 .vscode,APP 迁移 Bootloader 布局(0x08002000/54K)
工程编译与开发环境(参照 gd32e23x_template_cmake_vscode 模板)
- cmake/arm-none-eabi-gcc.cmake:硬编码工具链路径改为 GD32_GCC_BIN
  环境变量 / -DTOOLCHAIN_DIRECTORY 参数,与模板逐字一致
- .vscode:launch/tasks/settings/extensions 对齐模板,烧录与调试全部
  读取 GD32_GCC_BIN / GD32_OPENOCD_BIN / GD32_OPENOCD_SCRIPTS 环境变量;
  新增 J-Link 调试配置与 flash/reset/erase.jlink 脚本
- CMakeLists.txt:POST_BUILD 增加 Application/Application.hex 生成
  (J-Link 烧录脚本 loadfile 依赖)
- 验证:Debug / Release 编译通过(本机 ARM GCC 15.2.1)

Bootloader 布局适配(与旧版独立镜像不兼容,升级需整片烧录一次)
- LD/gd32e23x_flash.ld:FLASH ORIGIN=0x08002000, LENGTH=54K
- Src/system_gd32e23x.c:VECT_TAB_OFFSET=0x2000——Bootloader 不写
  VTOR,APP 必须自设向量表,否则中断落回 Bootloader 向量表
- 验证:.vectors 链接于 0x08002000,固件 12164B(54KB 上限 22%)

驱动修复
- Src/ldc1612.c:ldc1612_config_single_channel 各步骤 I2C 写入检查
  返回值,失败立即返回错误(原审查项 L3)

文档
- README:重写本地编译/烧录/调试说明,新增"固件布局与 Bootloader"章节
2026-09-26 18:41:29 +08:00
hulk 59f6e917fd fix: 修正 LED 引脚配置为 PB1 并更新 .gitignore 2026-06-30 14:41:30 +08:00
hulk 1117bc469e fix(version):修改版本号错误 2026-04-21 11:46:42 +08:00
hulk 1d7afaa382 release:(1.0.1)添加涡流传感器温度标定方法和标定命令 2026-04-19 01:17:29 +08:00
hulk efda3469bf 更新部分不兼容问题 2026-02-02 10:49:34 +08:00
hulk e4d6a186f4 Merge branch 'dev' of https://gitea.hulk.wang/hulk/ldc1612_cmake_vscode into dev 2026-02-02 10:33:43 +08:00
hulk 60104f9363 更新版本号策略 2026-02-02 10:00:20 +08:00
hulk a98ec3474f Merge branch 'dev' of https://gitea.hulk.wang/hulk/ldc1612_cmake_vscode into dev 2026-02-02 00:35:32 +08:00
hulk 38490f6897 整理LDC1612驱动头文件相关定义 2026-02-02 00:34:21 +08:00
hulk fcf10601b9 debug version 2025-09-26 09:02:37 +08:00
hulk 233576fd9a test 2025-08-21 17:12:04 +08:00
hulk b78c1e416a detect MCU flash size to config diff usart 2025-08-21 00:39:30 +08:00
hulk b6485c5c39 add tmp112a code 2025-08-18 00:18:21 +08:00
hulk 443e9bf8f2 fix H-file 2025-08-17 18:01:01 +08:00
hulk b60cfbee97 Delete useless file 2025-08-17 03:10:50 +08:00
hulk 9b3cd46b09 clear repo 2025-08-17 03:06:46 +08:00
hulk 5cdd7ca58c release dev branch 2025-08-17 02:58:32 +08:00
47 changed files with 5905 additions and 375 deletions
+1
View File
@@ -4,3 +4,4 @@ Build/
# 忽略 Toolchain 目录下所有内容,但保留目录本身 # 忽略 Toolchain 目录下所有内容,但保留目录本身
Toolchain/* Toolchain/*
!Toolchain/.gitkeep !Toolchain/.gitkeep
.vscode/settings.json
+2
View File
@@ -0,0 +1,2 @@
erase
exit
+7 -25
View File
@@ -1,26 +1,8 @@
{ {
"recommendations": [ "recommendations": [
"ms-vscode.cmake-tools", "marus25.cortex-debug",
"ms-vscode.cpptools", "ms-vscode.cmake-tools",
"ms-vscode.cpptools-extension-pack", "twxs.cmake",
"ms-vscode.cpptools-themes", "ms-vscode.cpptools"
"ms-vscode.vscode-embedded-tools", ]
"ms-vscode.hexeditor", }
"ms-vscode.notepadplusplus-keybindings",
"twxs.cmake",
"xaver.clang-format",
"marus25.cortex-debug",
"cheshirekow.cmake-format",
"mcu-debug.debug-tracker-vscode",
"mcu-debug.memory-view",
"mcu-debug.peripheral-viewer",
"mcu-debug.rtos-views",
"trond-snekvik.gnu-mapfiles",
"zixuanwang.linkerscript",
"gurumukhi.selected-lines-count",
"gruntfuggly.todo-tree",
"vscode-icons-team.vscode-icons",
"jeff-hykin.better-cpp-syntax",
"dan-c-underwood.arm"
]
}
+4
View File
@@ -0,0 +1,4 @@
loadfile Application.hex
r
go
exit
+32 -10
View File
@@ -9,20 +9,19 @@
"type": "cortex-debug", "type": "cortex-debug",
"runToEntryPoint": "main", "runToEntryPoint": "main",
"showDevDebugOutput": "none", "showDevDebugOutput": "none",
"gdbPath": "${workspaceFolder}/Toolchain/xpack-arm-none-eabi-gcc-11.3.1-1.1/bin/arm-none-eabi-gdb.exe", "gdbPath": "${env:GD32_GCC_BIN}/arm-none-eabi-gdb.exe",
"servertype": "openocd", "servertype": "openocd",
"serverpath": "${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/bin/openocd.exe", "serverpath": "${env:GD32_OPENOCD_BIN}/openocd.exe",
"svdFile": "${workspaceFolder}/GD32E230.svd", "svdFile": "${workspaceFolder}/GD32E230.svd",
"liveWatch": {
"enabled": true,
"samplesPerSecond": 1
},
"configFiles": [ "configFiles": [
"${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/scripts/target/openocd_gdlink_gd32e23x.cfg" "${env:GD32_OPENOCD_SCRIPTS}/${config:gd32.openocd.targetConfig}"
], ],
"serverArgs": [ "serverArgs": [
"-s", "${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/scripts" "-s", "${env:GD32_OPENOCD_SCRIPTS}"
], ],
"env": {
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
},
"searchDir": [ "searchDir": [
"${workspaceFolder}" "${workspaceFolder}"
], ],
@@ -30,7 +29,30 @@
"preRestartCommands": [ "preRestartCommands": [
"load", "load",
"continue" "continue"
], ]
}, },
{
"cwd": "${workspaceFolder}",
"executable": "${workspaceFolder}/Build/Debug/Application/Application.elf",
"name": "Debug with J-Link",
"request": "launch",
"type": "cortex-debug",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"gdbPath": "${env:GD32_GCC_BIN}/arm-none-eabi-gdb.exe",
"servertype": "jlink",
"device": "${config:gd32.jlink.device}",
"interface": "${config:gd32.jlink.interface}",
"svdFile": "${workspaceFolder}/GD32E230.svd",
"liveWatch": {
"enabled": true,
"samplesPerSecond": 1
},
"preLaunchTask": "Build",
"preRestartCommands": [
"load",
"continue"
]
}
] ]
} }
+23
View File
@@ -0,0 +1,23 @@
# DAPLink-friendly GD32E23x flash/debug config
# Difference vs target/openocd_gdlink_gd32e23x.cfg:
# - no connect_assert_srst (breaks target examination with the current DAPLink)
adapter driver cmsis-dap
transport select swd
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
set _CHIPNAME gd32e23x
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id 0x0BF11477
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x1000 -work-area-backup 0
flash bank $_CHIPNAME.flash gd32e23x 0x08000000 0 0 0 $_TARGETNAME
adapter speed 1000
reset_config srst_only srst_nogate
cortex_m reset_config sysresetreq
+2
View File
@@ -0,0 +1,2 @@
r
exit
+10 -15
View File
@@ -1,20 +1,15 @@
{ {
"terminal.integrated.tabs.enabled": true,
"terminal.integrated.profiles.windows": {
"Git Bash": {
"path": "C:\\Program Files\\Git\\bin\\bash.exe",
"icon": "terminal-bash"
}
},
"terminal.integrated.defaultProfile.windows": "Git-Bash",
"clang-format.assumeFilename": ".clang-format",
"clang-format.executable": "clang-format",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cmake.configureOnOpen": true, "cmake.configureOnOpen": true,
"cmake.buildDirectory": "${workspaceFolder}/Build", "cmake.useCMakePresets": "always",
"vcpkg.storageLocation": "C:\\Dev\\Tools\\vcpkg", "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cortex-debug.variableUseNaturalFormat": true,
"gd32.openocd.targetConfig": "target/openocd_gdlink_gd32e23x.cfg",
"gd32.openocd.massEraseCommand": "gd32e23x mass_erase 0",
"gd32.jlink.device": "GD32E230F8",
"gd32.jlink.interface": "SWD",
"gd32.jlink.speed": "4000",
"files.associations": { "files.associations": {
"*.h": "c", "*.h": "c",
"*.c": "c" "*.c": "c"
}, }
} }
+225 -96
View File
@@ -1,100 +1,6 @@
{ {
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{
"label": "Build and Flash",
"group": {
"kind": "build",
"isDefault": true
},
"dependsOn": [
"Build",
"Flash MCU"
],
"dependsOrder": "sequence"
},
{
"label": "Flash MCU",
"type": "shell",
"command": "'${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/bin/openocd.exe' -s '${workspaceFolder}' -f '${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/scripts/target/openocd_gdlink_gd32e23x.cfg' -c 'init; reset halt; flash write_image erase ${command:cmake.launchTargetFilename}; reset; exit'",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"CLICOLOR_FORCE": "1",
"OPENOCD_SCRIPTS": ""
}
},
"presentation": {
"clear": true
}
},
{
"label": "Reset MCU",
"type": "shell",
"command": "'${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/bin/openocd.exe' -s '${workspaceFolder}' -f '${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/scripts/target/openocd_gdlink_gd32e23x.cfg' -c 'init; reset; exit'",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"CLICOLOR_FORCE": "1",
"OPENOCD_SCRIPTS": ""
}
},
"presentation": {
"clear": true
}
},
{
"label": "Mass Erase MCU",
"type": "shell",
"command": "'${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/bin/openocd.exe' -s '${workspaceFolder}' -f '${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/scripts/target/openocd_gdlink_gd32e23x.cfg' -c 'init; reset halt; gd32e23x mass_erase 0; exit'",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"CLICOLOR_FORCE": "1",
"OPENOCD_SCRIPTS": ""
}
},
"presentation": {
"clear": true
}
},
{
"label": "OpenOCD Server",
"type": "shell",
"command": [
"'${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/bin/openocd.exe' -s '${workspaceFolder}' -f '${workspaceFolder}/Toolchain/xpack-openocd-0.11.0-3/scripts/target/openocd_gdlink_gd32e23x.cfg'"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"CLICOLOR_FORCE": "1",
"OPENOCD_SCRIPTS": ""
}
},
"presentation": {
"clear": true
}
},
{ {
"label": "Build", "label": "Build",
"type": "cmake", "type": "cmake",
@@ -114,12 +20,235 @@
], ],
"options": { "options": {
"environment": { "environment": {
"CLICOLOR_FORCE": "1" "CLICOLOR_FORCE": "1",
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
}
},
"presentation": {
"clear": true
},
"icon": {
"id": "code",
"tooltip": "Build"
}
},
{
"label": "Build and Flash",
"dependsOn": [
"Build",
"Flash MCU"
],
"dependsOrder": "sequence",
"group": "build",
"icon": {
"id": "insert",
"tooltip": "Build and Flash"
}
},
{
"label": "Flash MCU",
"type": "process",
"command": "${env:GD32_OPENOCD_BIN}/openocd.exe",
"args": [
"-s", "${env:GD32_OPENOCD_SCRIPTS}",
"-f", "${env:GD32_OPENOCD_SCRIPTS}/${config:gd32.openocd.targetConfig}",
"-c", "init; reset halt; flash write_image erase ${command:cmake.launchTargetFilename}; reset; exit"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
}
},
"presentation": {
"clear": true
},
"icon": {
"id": "gather",
"tooltip": "Flash MCU"
}
},
{
"label": "Flash MCU (DAPLink)",
"type": "process",
"command": "${env:GD32_OPENOCD_BIN}/openocd.exe",
"args": [
"-s", "${env:GD32_OPENOCD_SCRIPTS}",
"-f", "${workspaceFolder}/.vscode/openocd_daplink_gd32e23x.cfg",
"-c", "program Application.elf verify reset exit"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
}
},
"presentation": {
"clear": true
},
"icon": {
"id": "zap",
"tooltip": "Flash MCU via DAPLink (no connect_assert_srst)"
}
},
{
"label": "Reset MCU",
"type": "process",
"command": "${env:GD32_OPENOCD_BIN}/openocd.exe",
"args": [
"-s", "${env:GD32_OPENOCD_SCRIPTS}",
"-f", "${env:GD32_OPENOCD_SCRIPTS}/${config:gd32.openocd.targetConfig}",
"-c", "init; reset; exit"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
}
},
"presentation": {
"clear": true
},
"icon": {
"id": "discard",
"tooltip": "Reset MCU"
}
},
{
"label": "Mass Erase MCU",
"type": "process",
"command": "${env:GD32_OPENOCD_BIN}/openocd.exe",
"args": [
"-s", "${env:GD32_OPENOCD_SCRIPTS}",
"-f", "${env:GD32_OPENOCD_SCRIPTS}/${config:gd32.openocd.targetConfig}",
"-c", "init; reset halt; ${config:gd32.openocd.massEraseCommand}; exit"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
}
},
"presentation": {
"clear": true
},
"icon": {
"id": "clear-all",
"tooltip": "Erase MCU"
}
},
{
"label": "OpenOCD Server",
"type": "process",
"command": "${env:GD32_OPENOCD_BIN}/openocd.exe",
"args": [
"-s", "${env:GD32_OPENOCD_SCRIPTS}",
"-f", "${env:GD32_OPENOCD_SCRIPTS}/${config:gd32.openocd.targetConfig}"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application",
"environment": {
"OPENOCD_SCRIPTS": "${env:GD32_OPENOCD_SCRIPTS}"
} }
}, },
"presentation": { "presentation": {
"clear": true "clear": true
} }
},
{
"label": "Build and Flash (Jlink)",
"dependsOn": [
"Build",
"Flash MCU (Jlink)"
],
"dependsOrder": "sequence",
"group": "build",
"icon": {
"id": "insert",
"tooltip": "Build and Flash (Jlink)"
}
},
{
"label": "Flash MCU (Jlink)",
"type": "process",
"command": "JLink.exe",
"args": [
"-device", "${config:gd32.jlink.device}",
"-if", "${config:gd32.jlink.interface}",
"-speed", "${config:gd32.jlink.speed}",
"-autoconnect", "1",
"-CommanderScript", "${workspaceFolder}/.vscode/flash.jlink"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${command:cmake.buildDirectory}/Application"
},
"presentation": {
"clear": true
},
"icon": {
"id": "gather",
"tooltip": "Flash MCU (Jlink)"
}
},
{
"label": "Reset MCU (Jlink)",
"type": "process",
"command": "JLink.exe",
"args": [
"-device", "${config:gd32.jlink.device}",
"-if", "${config:gd32.jlink.interface}",
"-speed", "${config:gd32.jlink.speed}",
"-autoconnect", "1",
"-CommanderScript", "${workspaceFolder}/.vscode/reset.jlink"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
},
"presentation": {
"clear": true
},
"icon": {
"id": "discard",
"tooltip": "Reset MCU (Jlink)"
}
},
{
"label": "Mass Erase MCU (Jlink)",
"type": "process",
"command": "JLink.exe",
"args": [
"-device", "${config:gd32.jlink.device}",
"-if", "${config:gd32.jlink.interface}",
"-speed", "${config:gd32.jlink.speed}",
"-autoconnect", "1",
"-CommanderScript", "${workspaceFolder}/.vscode/erase.jlink"
],
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
},
"presentation": {
"clear": true
},
"icon": {
"id": "clear-all",
"tooltip": "Erase MCU (Jlink)"
}
} }
] ]
} }
+153
View File
@@ -0,0 +1,153 @@
# AGENTS.md — CeramixNano EDDY(ldc1612_cmake_vscode)项目约定与进度
## 项目概述
CeramixNano EDDY 电涡流传感器单板固件(工程目录名沿用 ldc1612_cmake_vscode):
LDC1612(电涡流/电感数字转换器)+ TMP112(板载温度),RS485 串口命令协议
(`D5 03 LEN [cmd] CRC`,板卡类型 0x03),支持温度补偿与标定。
- 协议文档:`CommunicationProtocol.md`;寄存器速查:`LDC1612_REG_LIST.md`
- 命名/版本规则(2026-10-24 起):产物名 `CeramixNano-EDDY_*`,板卡码 **26**
(M999 应答 `v26.主.次.修订`,与 MB 的 25 同源规则);版本维护在
`cmake/project_config.cmake`(编译时生成 version.h,`Inc/version.h` 说法已过时)
## 工具链与构建
- 语言/环境:C99,GD32E23x 标准库(`SDK/`),MCU GD32E230F8(64K Flash/8K RAM)
- 编译器:xpack-arm-none-eabi-gcc 15.2.1(`GD32_GCC_BIN` 环境变量解析)
- 构建:CMake + Ninja,preset `Debug` / `Release`,输出 `Build/<preset>/Application/`
及 `Build/<preset>/CeramixNano-EDDY_<版本>_<variant>_<日期>.bin/.hex` 等
(variant:dev 线 `APP`,golden 线 `golden`)
- 烧录/调试(.vscode/tasks.json、launch.json):
- OpenOCD:`GD32_OPENOCD_BIN` 指向定制构建(含 gd32e23x flash 驱动,CMSIS-DAP 后端)
- `Flash MCU (DAPLink)` 任务 + `.vscode/openocd_daplink_gd32e23x.cfg`:
适配 DAPLink 探针(无 connect_assert_srst;该选项在此 DAPLink 上会导致
examination 失败,但 SRST 线是接通的,见下文排障)
- J-Link 任务需 SEGGER 探针 + PATH 中 JLink.exe(当前接的是 DAPLink,不适用)
- 调试器探针历史问题(2026-10-02 已解决):DAPLink 的 CMSIS-DAP v1 接口曾被 Zadig
装上 libwdi WinUSB 驱动(oem244.inf),导致 OpenOCD 枚举不到探针;删除该驱动包后
恢复 HID。若复发:管理员运行 `pnputil /delete-driver <oemNN.inf> /uninstall`。
- 串口验证:RS485 转 USB(FTDI,COM15),115200-8N1,**需同时置位 RTS+DTR**
(转换器方向控制),否则命令发不出去;DAPLink 虚拟串口为 COM8。
- 本地测试工具(git 不跟踪,在 `Toolchain/eddy_test/`):`eddy_cmd.py`(D5 帧命令行)、
`soc_ota.py`(PC 模拟 SOC 完整 OTA/Ymodem,支持 `--abort-after`/`--no-ca` 中止模式)、
`swd.py`(OpenOCD 封装:probe/image/boot/erasepage/read/dump)、`verify_pack.py`、
`verify_dump.py`(独立 CRC 复算与读回比对)。
## 硬件/板级约定(Inc/board_config.h)
- MCU:GD32E230(自动探测 Flash 容量,64KB → USART1 作 RS485)
- RS485:SP3487(无自动方向),USART1 PA2(TX)/PA3(RX),DE=PA1(USART 硬件 DE,AF1)
- I2C0:PF0(SDA)/PF1(SCL),硬件 IIC 100kHz(`SOFTWARE_IIC` 可切换软模拟,默认关闭)
- LED:PB1
- 传感器:LDC1612 @0x2B(40MHz 外部时钟),TMP112 @0x49(ADD0=GND)
- 调试宏(均默认关闭):DEBUG_VERBOSE / COM_DEBUG / LDC_DEBUG / EDDY_DRIVE_CURRENT_DETECTION
## 双固件布局(2026-10-24 移植完成,与 CeramixNano-MB 完全相同的 Flash 分配)
```
0x08000000 ┌─────────────────┐
│ Bootloader │ 8K 页 0~7 专属分支 bootloader_CeramixNano_EDDY(V0.1.0,4,040B)
0x08002000 ├─────────────────┤
│ FW_A 运行槽 │ 36K 页 8~43 OTA/Ymodem 接收区;锚点 A 记录@0x0800AFFC
0x0800B000 ├─────────────────┤
│ FW_B 出厂保底槽│ 19K 页 44~62 golden 线构建;B 记录@0x0800FBF8
0x0800FC00 ├─────────────────┤
│ 末页标志页 │ 1K 页 63 legacy 0xEEEE@0x0800FFFC
0x08010000 └─────────────────┘
```
- **BL = 并列板卡分支 `bootloader_CeramixNano_EDDY`**(BL 仓库,2026-10-24 自 MB 分支
创建;板卡宏 CERAMIXNANO_EDDY,**LED=PB1**,Ymodem 每成功烧录一包翻转一次,逻辑
同 MB 分支;版本独立编号 V0.1.0 = 4,040B,MD5 1dcc17bf…)。RS485 配置与 MB 逐宏
相同;**PA14 不被 BL 占用 ⇒ 升级会话期间 SWD 保持可用**(MB 分支 BL 的 LED=PA14
会占 SWCLK,勿混烧)。
- **A 线 = dev/main**:链接 0x08002000 / **LENGTH 36K**(LD 上限防溢出 B 槽),
VTOR 0x2000;版本从 V1.0.3 起(首个双固件 A 版本,码 26)。
- **B 线 = release/golden**:链接 0x0800B000 / LENGTH 0x4BF8(19K−8B,B 记录由打包
工具写入,链接区不含尾 8B——与 MB 的 .fw_tail 方案等价),VTOR 0xB000;
**2026-10-25 起整体替换为生产实装版原样复制,版本冻结 V1.0.3**(保底判定值 =
M999 返回 `v26.1.0.3` ⇔ 在跑 FW_B;`v26.1.0.2` 与 `v26.1.0.3` 均永不再用于
OTA 升级包版本,A 线版本必须 > V1.0.3)。此分支改动不得合回 dev/main;
详见 golden 分支提交 c7433a8 与 Gitea Release `golden-v26.1.0.3`。
- 打包:`hulk-mcu-firmware` skill / merge tool profile **`CeramixNano_EDDY_Dual_FW`**
(布局与 `CeramixNano_MB_Dual_FW` 逐字节相同),产出 `_UPDATE/_FULL/_FACTORY + MD5`。
- 升级链路:上位机(原 SOC 职责)—RS485—> BL(Ymodem);APP 用 `M9999` 软复位进
升级模式(SWRSTF=1 ⇒ BL 直留升级模式)。握手:BL 发 `ok` → `0x30` → BL `ok` →
`0x31` → 预擦 A+末页 → `'C'` 周期 → Ymodem(文件头 ACK 后 **ACK+'C' 连发**,
发送器须一次等齐)→ EOT → ACK×2 → 跳新固件。
- 完全体方案文档:`E:\Hulk-Coding\capsule_mb_camke_vscode\doc\双固件方案_完全体说明.md`。
## 开发进度
### 已完成
- LDC1612 驱动(初始化/单通道配置/原始值读取/状态检查)
- TMP112 驱动(初始化/温度读取/阈值/单次转换)
- RS485 命令体系:M1/M2(周期上报开关)、M3(涡流原始值)、M4(温度×10000)、
M5(温度补偿涡流值)、M6(标定数据:涡流+温度)、M999(版本)、M9999(复位)
- v1.0.1:涡流传感器温度标定方法与标定命令(M6)
- 2026-10-02:IIC 驱动改进导入(总线恢复重试/超时辅助/read_raw/scan AERR 处理);
硬件全命令实测通过;DAPLink 烧录问题解决;发布 V1.0.2(Gitea Release + tag)
- **2026-10-24:双固件(出厂救援)方案移植完成并全链路实测通过**(详见下节)
- BL 仓库新建并列板卡分支 `bootloader_CeramixNano_EDDY`(LED=PB1,V0.1.0 = 4,040B,
每收包翻转一次;此前短暂采用"复用 MB bin"方案,因 LED 引脚不同已废弃并重打包)
- dev:LD 54K→36K、命名 CeramixNano-EDDY、板卡码 26、V1.0.3;
golden:V1.0.2 冻结命名 + variant=golden
- 两线 CMake 补齐 `--gap-fill=0xFF` 与 `LD LINK_DEPENDS`
- fw_pack 新增 `CeramixNano_EDDY_Dual_FW` profile(55 项回归过,skill 缓存已同步)
- 产物(`Build/test/dualfw_2026-10-24/`):UPDATE(12 页,trailer 0x5C09000C)、
FULL(EDDY BL + A=V1.0.3/B=V1.0.2,19 项校验过)、FACTORY(12 项校验过)+ MD5 清单;
独立 python XMODEM 复算全部命中
- **硬件实测矩阵(SWD 探针 PC 采样 + 485 命令双重手段,全部 PASS;先用 MB bin 实测,
换 EDDY 专属 BL 后 FULL/FACTORY/OTA 全周期复测通过)**:
FACTORY→跳B(v26.1.0.2) / FULL→记录路径跳A(v26.1.0.3) / PC 模拟 SOC 完整 OTA
44s 成功+读回整片逐字节与包一致(B 区在真实 OTA 中毫发无损)/
擦锚点页→legacy 跳A / 锚点在位毁A体→CRC 拦截直落B(防降级)/
再毁B体→拒跳坏固件停留 BL(PC=0x080001B6) / OTA 第4包 CA 中止→落B /
非整页裸 bin 包→头部预检 CA×2 拒收→落B / M3/M4/M6 功能全通
- **2026-10-25:dev 版本升至 V1.0.4(开发版,未发布 Release)**——与保底判定值
拉开距离(golden 已整体替换为生产实装版原样复制 v26.1.0.3,见 golden 分支
c7433a8 与 Release `golden-v26.1.0.3`;A 线版本必须 > V1.0.3,否则 M999 无法
区分保底)。相对生产实装版 v26.1.0.3 的主要改动:**IIC 驱动升级**(模板改进版:
总线恢复重试/超时辅助/read_raw 无子地址读/16 位寄存器地址/scan AERR 处理——
最大一块)、新增 **M5 温度补偿**(系数待标定,暂等价 M3)与 **M6 标定数据**
命令、**M4 负温度修复**(有符号 int16 算法);双固件 A 槽布局随 V1.0.3 继承。
待定:LDC1612 驱动电流当前仍为 GEN(18),生产实装调参为 GEN(8)(2026-03-23),
正式发布前需确认是否回合。
### 计划 / TODO
- dev → main 合并待发布节奏(V1.0.3 为首个双固件 A 版本,main 仍是 54K 单槽布局)
- M5 温度补偿系数目前为 0(TEMP_COMP_COEFF_*),待标定后填入实际温漂系数
- 上位机/SOC 侧需预存保底判定值 `v26.1.0.3`(跑 B ⇒ 升级未生效重推;2026-10-25 起 golden 为生产实装版原样复制,判定值从 v26.1.0.2 改为 v26.1.0.3)
- LDC1612 驱动电流:dev 线 GEN(18) vs 生产实装 GEN(8),A 线正式发布前需确认回合
- (可选)模板侧 SEGGER RTT / CFG_ENABLE 宏体系同步
### 已删除的历史分支(2026-10-24 清理,均为开发过程产物)
- `dev_more_information`(远端):零独有提交,内容早已全部并入 main/dev。
- `dev_feature_iic_more_byte`(远端):仅 2 个 2025-08-25 的孤立实验提交,从未接入构建——
`75ea93c`(iic_new.c 975 行并行驱动实验,实际采用的是后来模板导入 i2c.c 的改进)、
`dbb6569`("dlpc3421 driver from kimi",DLPC3421 为 MB 光机芯片,与本板无关)。
如需找回:本地对象库在 gc 前仍可 `git show <hash>`。
### 教训(避免复发)
- **SWRSTF 残留**:M9999/调试器软复位置位的 SWRSTF **不随 NRST/SRST 清除**(仅 POR
或 RSTFC 清),此后任何复位 BL 都直进升级模式、不跳 APP——表现为"设备沉默,
M999 无响应,但 0x30 能回 ok"。排障/测试时须 SWD `mww 0x40021024 0x01000000`
清标志后再放跑(`swd.py boot` 已内置)。
- **(历史)误烧 MB 分支 BL 会让 SWD 在 0x30 后失联**:MB 分支 BL 的 LED=PA14
(SWCLK),0x30 应答后切输出 ⇒ SWD 死。**本板专用 `bootloader_CeramixNano_EDDY`
分支 LED=PB1,PA14 不占用,升级会话期间 SWD 全程可用**(2026-10-24 起所有镜像
均为 EDDY BL,此坑仅存档)。若真误烧:`M9999` 复位(有 APP 时)或用
connect_assert_srst 变体 cfg(`Toolchain/eddy_test/daplink_srst.cfg`)脉冲 SRST
复位恢复——SRST 线已验证接通,但该 DAPLink 下 examination 必失败属正常,
复位本身生效。
- **Ymodem 文件头 ACK 后是 ACK+'C' 连发**:发送器若分两次等会永远等不到第二个 'C'
(首次 OTA 调试即栽在此,BL 无超时会卡死;随后发 5×CA 可解)。
- OpenOCD `program`/`dump_image` 的文件路径必须转正斜杠,否则 Tcl 吃掉 `\l` `\t`。
- legacy 路径(锚点擦除态+flag 在)**没有 CRC 安全网**:实测毁 A 体后仍会跳进坏 A
导致 HardFault(xPSR=0x41000003)——这正是 v5 记录机制存在的意义,量产镜像必须
带锚点(FULL/OTA 正常路径均带)。
+17 -2
View File
@@ -6,6 +6,10 @@ include(${CMAKE_SOURCE_DIR}/cmake/project.cmake)
project(${PROJECT_NAME} LANGUAGES C CXX ASM) project(${PROJECT_NAME} LANGUAGES C CXX ASM)
# Generate version header from CMake version variables
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
configure_file(${CMAKE_SOURCE_DIR}/cmake/version.h.in ${CMAKE_BINARY_DIR}/generated/version.h @ONLY)
# 添加SDK库 # 添加SDK库
add_subdirectory(SDK/CMSIS) add_subdirectory(SDK/CMSIS)
add_subdirectory(SDK/GD32E23x_standard_peripheral) add_subdirectory(SDK/GD32E23x_standard_peripheral)
@@ -27,6 +31,12 @@ set(TARGET_SRC
# Add new source files here # Add new source files here
Src/uart.c Src/uart.c
Src/led.c Src/led.c
Src/uart_ring_buffer.c
Src/command.c
Src/i2c.c
Src/ldc1612.c
Src/tmp112.c
Src/board_config.c
) )
# 设置输出目录 # 设置输出目录
@@ -41,6 +51,7 @@ project_add_target_properties(${PROJECT_NAME})
# 头文件路径 # 头文件路径
target_include_directories(${PROJECT_NAME} PRIVATE target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/Inc ${CMAKE_SOURCE_DIR}/Inc
${CMAKE_BINARY_DIR}/generated
# Add new include directories here, e.g. ${CMAKE_SOURCE_DIR}/Application/User/Inc # Add new include directories here, e.g. ${CMAKE_SOURCE_DIR}/Application/User/Inc
@@ -51,6 +62,9 @@ target_link_options(${PROJECT_NAME} PRIVATE
-T${CMAKE_SOURCE_DIR}/LD/gd32e23x_flash.ld -Xlinker -T${CMAKE_SOURCE_DIR}/LD/gd32e23x_flash.ld -Xlinker
-L${CMAKE_SOURCE_DIR}/LD -L${CMAKE_SOURCE_DIR}/LD
) )
# LD 变更时触发重链接(改布局不重编也要重新生成 bin)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_DEPENDS
${CMAKE_SOURCE_DIR}/LD/gd32e23x_flash.ld)
# 生成 map 文件等 # 生成 map 文件等
target_link_options(${PROJECT_NAME} PRIVATE target_link_options(${PROJECT_NAME} PRIVATE
@@ -62,13 +76,14 @@ target_link_libraries(${PROJECT_NAME} PRIVATE CMSIS)
target_link_libraries(${PROJECT_NAME} PRIVATE GD32E23x_standard_peripheral) target_link_libraries(${PROJECT_NAME} PRIVATE GD32E23x_standard_peripheral)
# 生成 bin/hex/list 文件名格式:[工程名_版本号_编译条件_编译日期] # 生成 bin/hex/list 文件名格式:[工程名_版本号_编译条件_编译日期]
set(OUTPUT_PREFIX "${PROJECT_NAME}_${VERSION}_${IIC_TYPE}_${BUILD_DATE}") set(OUTPUT_PREFIX "${PROJECT_NAME}_${VERSION}_${BUILD_VARIANT}_${BUILD_DATE}")
add_custom_command(TARGET ${PROJECT_NAME} add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD POST_BUILD
COMMAND echo -- Running Post Build Commands COMMAND echo -- Running Post Build Commands
COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.hex COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.hex
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.bin COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${PROJECT_NAME}> $<TARGET_FILE_DIR:${PROJECT_NAME}>/Application.hex
COMMAND ${CMAKE_OBJCOPY} -O binary --gap-fill=0xFF $<TARGET_FILE:${PROJECT_NAME}> ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.bin
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${PROJECT_NAME}> COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${PROJECT_NAME}>
COMMAND ${CMAKE_OBJDUMP} -h -S $<TARGET_FILE:${PROJECT_NAME}> > ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.list COMMAND ${CMAKE_OBJDUMP} -h -S $<TARGET_FILE:${PROJECT_NAME}> > ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.list
COMMAND ${CMAKE_SIZE} --format=berkeley $<TARGET_FILE:${PROJECT_NAME}> > ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.bsz COMMAND ${CMAKE_SIZE} --format=berkeley $<TARGET_FILE:${PROJECT_NAME}> > ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_PREFIX}.bsz
+1
View File
@@ -12,6 +12,7 @@
"type": "FILEPATH", "type": "FILEPATH",
"value": "${sourceDir}/cmake/arm-none-eabi-gcc.cmake" "value": "${sourceDir}/cmake/arm-none-eabi-gcc.cmake"
} }
,"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}, },
"architecture": { "architecture": {
"value": "unspecified", "value": "unspecified",
+85
View File
@@ -0,0 +1,85 @@
# 电涡流传感器模块通信协议
## 电涡流传感器模块通信协议
| **序号** | **修改内容** | **版本** | **日期** | **修改人** |
|:------:|:--------:|:------:|:----------:|:-------:|
| 1 | 初版 | V1.0 | 2024-12-25 | Hulk |
| 2 | 修改指令含义 | V1.1 | 20250822 | Hulk |
| | | | | |
| | | | | |
### 发包格式
| **包头** | **类型** | **数据长度** | **数据** | **校验** |
|:------:|:------:|:-----------:|:------:|:------:|
| D5 | 0x03 | Data Length | Data | CRC |
- 数据长度只包含数据部分,不包含包头、类型、数据长度、校验
- CRC求和校验,包含类型、数据长度、数据
- 数据部分为ascii码
### 回包格式
| **包头** | **状态码** | **数据长度** | **数据** | **校验** |
|:------:|:----------:|:-----------:|:------:|:------:|
| B5 | 0xF0 正常包 | Data Length | Data | CRC |
| B5 | 0xF1 CRC错误 | Data Length | Data | CRC |
| B5 | 0xF2 包头错误 | Data Length | Data | CRC |
| B5 | 0xF3 类型错误 | Data Length | Data | CRC |
| B5 | 0xF4 包长度错误 | Data Length | Data | CRC |
- 数据长度仅包含数据部分,不包含包头状态码等
- CRC求和校验,包含状态码数据长度和数据部分
-------------------
## 电涡流传感器模块功能
### 1. 开启自动读取并发送电涡流传感器模块数据
- M1指令 开启自动读取并发送涡流传感器数据,间隔10ms左右
- `D5 03 02 4D 31 83`
- 电涡流传感器模块涡流回复数据
- `B5 F0 04 01 AE 1B E4 A2`, 有效数据为 `0x01AE1BE4`,转换为`28187620`
- `B5 F0 04 04 19 C1 FA CC`, 有效数据为 `0x0419C1FAD2`,转换为`17612012242`
### 2. 关闭自动读取并发送电涡流传感器模块数据
- M2 指令 关闭自动读取并发送涡流传感器数据
- `D5 03 02 4D 32 84`
> 注:因为485总线为半双工,M1命令开启后持续自动发送数据,M2指令发送停止命令可能无法一次成功,可持续发送几次
### 3. 单次读取并发送电涡流传感器数据
- M3 指令 单次读取并发送涡流传感器数据
- `D5 03 02 4D 33 85`
### 4. 单次读取并发送板载温度传感器数据
- M4 指令 单次读取并发送板载温度传感器数据(TMP112,返回温度值×10000,4 字节有符号大端)
- `D5 03 02 4D 34 86`
- 示例回复:`B5 F0 04 00 04 28 74 94`,有效数据 `0x00042874` = 272500,即 `27.25°C`
### 5. 单次读取并发送温度补偿后的涡流传感器数据
- M5 指令 单次读取并发送温度补偿后的涡流传感器数据(补偿系数默认为 0,与 M3 等价)
- `D5 03 02 4D 35 87`
### 6. 单次读取并发送标定数据(涡流原始值 + 温度值)
- M6 指令 单次读取涡流原始值和板载温度值,用于温度标定
- `D5 03 02 4D 36 88`
- 回复 9 字节:涡流原始值(4字节大端) + 0xFF + 温度值×10000(4字节大端)
- 示例回复:`B5 F0 09 01 AD BA DC FF 00 04 28 74 DC`
### 7. 查询固件版本号
- M999 指令 返回固件版本号字符串,如 `v23.1.0.1`(板卡类型.主版本.次版本.修订号)
- `D5 03 04 4D 39 39 39 FF`
### 8. 复位 MCU
- M9999 指令 立即复位 MCU(无回复)
- `D5 03 05 4D 39 39 39 39 39`
+84 -4
View File
@@ -1,12 +1,92 @@
#ifndef BOARD_CONFIG_H #ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H #define BOARD_CONFIG_H
/******************************************************************************/ #include "version.h"
#define LED_PORT GPIOA #define GD32E23XF4 0x10
#define LED_PIN GPIO_PIN_7 #define GD32E23XF6 0x20
#define LED_RCU RCU_GPIOA #define GD32E23XF8 0x40
/* >>>>>>>>>>>>>>>>>>>>[RS485 PHY DEFINE]<<<<<<<<<<<<<<<<<<<< */
// #define RS485_MAX13487 // RS485 PHY : MAX13487 (AutoDir)
#undef RS485_MAX13487 // RS485 PHY : SP3487 (no AutoDir)
/* >>>>>>>>>>>>>>>>>>>>[IIC TYPE DEFINE]<<<<<<<<<<<<<<<<<<<< */
// #define SOFTWARE_IIC // IIC Type : Software IIC
#undef SOFTWARE_IIC // IIC Type : Hardware IIC
/* >>>>>>>>>>>>>>>>>>>>[DEBUG ASSERTIONS DEFINE]<<<<<<<<<<<<<<<<<<<< */
// #define DEBUG_VERBOSE // Debug Assertions Status : Debug Verbose Information
#undef DEBUG_VERBOSE // Debug Assertions Status : No Debug Verbose Information
/* >>>>>>>>>>>>>>>>>>>>[EDDY DRIVE CURRENT DETECTION]<<<<<<<<<<<<<<<<<<<< */
// #define EDDY_DRIVE_CURRENT_DETECTION // Eddy Drive Current Detection : Enable
#undef EDDY_DRIVE_CURRENT_DETECTION // Eddy Drive Current Detection : Disable
/* >>>>>>>>>>>>>>>>>>>>[COMMAND DEBUG]<<<<<<<<<<<<<<<<<<<< */
// #define COM_DEBUG // Enable Command Debug Information
#undef COM_DEBUG // Disable Command Debug Information
/* >>>>>>>>>>>>>>>>>>>>>[LDC1612 DEBUG]<<<<<<<<<<<<<<<<<<<< */
// #define LDC_DEBUG // LDC1612 Driver Debug : Enable
#undef LDC_DEBUG // LDC1612 Driver Debug : Disable
/******************************************************************************/ /******************************************************************************/
/* Dynamic USART Configuration Structure */
typedef struct {
uint32_t rcu_usart;
uint32_t usart_periph;
IRQn_Type irq_type;
void (*irq_handler)(void); // 函数指针:指向中断处理函数
} usart_config_t;
extern usart_config_t g_usart_config;
extern uint8_t g_mcu_flash_size;
/* USART中断处理函数声明 */
void usart0_irq_handler(void);
void usart1_irq_handler(void);
/******************************************************************************/
#define RCU_GPIO_I2C RCU_GPIOF
#define RCU_I2C RCU_I2C0
#define I2C_SCL_PORT GPIOF
#define I2C_SCL_PIN GPIO_PIN_1
#define I2C_SDA_PORT GPIOF
#define I2C_SDA_PIN GPIO_PIN_0
#define I2C_GPIO_AF GPIO_AF_1
#define I2C_DEBUG_UART USART0
/******************************************************************************/
#define LED_RCU RCU_GPIOB
#define LED_PORT GPIOB
#define LED_PIN GPIO_PIN_1
/******************************************************************************/
#define RS485_RCU (g_usart_config.rcu_usart)
#define RS485_PHY (g_usart_config.usart_periph)
#define RS485_IRQ (g_usart_config.irq_type)
#define RS485_GPIO_RCU RCU_GPIOA
#define RS485_GPIO_PORT GPIOA
#define RS485_EN_PIN GPIO_PIN_1
#define RS485_TX_PIN GPIO_PIN_2
#define RS485_RX_PIN GPIO_PIN_3
#define RS485_BAUDRATE 115200U
/******************************************************************************/
void mcu_detect_and_config(void);
uint8_t get_flash_size(void);
#endif //BOARD_CONFIG_H #endif //BOARD_CONFIG_H
+112
View File
@@ -0,0 +1,112 @@
/**
* @file command.h
* @brief 串口命令解析与处理模块接口声明。
* @details 提供基于环形缓冲区的串口协议解析、命令处理及状态管理功能,
* 支持格式为 D5 03 LEN [cmd] CRC 的命令帧解析与响应。
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <stdint.h>
#include <stdbool.h>
/**
* @defgroup Command 命令处理模块
* @brief 串口命令解析与处理
* @{
*/
/** @brief 传感器周期上报使能标志 */
extern volatile bool g_eddy_current_sensor_report_enabled;
/**
* @section Command_Protocol 协议格式
* 接收命令帧格式:
* @code
* [0] HEADER = 0xD5 // 包头标识
* [1] BOARD_TYPE = 0x03 // 板卡类型标识
* [2] LEN = 数据区字节数 // 有效载荷长度
* [3..(3+LEN-1)] 数据 // 命令数据
* [last] CRC // 校验码(从索引1累加到len-2的低8位)
* @endcode
*
* 响应帧格式:
* @code
* [0] HEADER = 0xB5 // 响应包头
* [1] TYPE // 响应类型(0xF0=成功,0xF1..=错误类型)
* [2] LEN // 响应数据长度
* [3..(3+LEN-1)] 数据 // 响应数据
* [last] CRC // 校验码
* @endcode
*
* @section Command_Usage 使用说明
* 1) 初始化环形缓冲区:
* @code{.c}
* uart_ring_buffer_init();
* @endcode
*
* 2) 在主循环中调用命令处理:
* @code{.c}
* while(1) {
* command_process(); // 处理接收到的命令
* // 其他业务逻辑
* }
* @endcode
*
* 3) 查询传感器上报状态:
* @code{.c}
* if(get_sensor_report_enabled()) {
* // 执行传感器数据上报
* }
* @endcode
*/
/**
* @brief 获取电涡流传感器周期上报使能状态。
* @return bool 上报状态。
* @retval true 传感器周期上报已启用。
* @retval false 传感器周期上报已禁用。
* @ingroup Command
*/
bool get_eddy_sensor_report_enabled(void);
/**
* @brief 设置电涡流传感器周期上报使能状态。
* @param enabled 上报使能标志。
* @arg true 启用传感器周期上报。
* @arg false 禁用传感器周期上报。
* @note 推荐通过此函数修改状态,便于后续功能扩展。
* @ingroup Command
*/
void set_eddy_sensor_report_status(bool enabled);
/**
* @brief 处理串口环形缓冲区中的命令数据。
* @details 基于状态机的非阻塞协议解析器,处理完整的命令帧并自动响应。
* 每次调用处理缓冲区中所有可用数据,遇到错误时自动重置状态机。
* @note 使用静态变量维护解析状态,函数不可重入。
* @warning 依赖环形缓冲区正确实现,建议在主循环中周期调用。
* @ingroup Command
*/
void command_process(void);
/**
* @brief 解析并处理完整的命令帧。
* @param cmd 指向完整命令帧的缓冲区(从包头0xD5开始)。
* @param len 命令帧总长度(字节)。
* @note 内部函数,由 command_process() 调用,一般不直接使用。
* @ingroup Command
*/
void handle_command(const uint8_t *cmd, uint8_t len);
/** @} */ // end of Command group
void eddy_current_report(void);
void temperature_raw_value_report(void);
void eddy_current_compensated_report(void);
void calibration_data_report(void);
#endif // COMMAND_H
+152
View File
@@ -0,0 +1,152 @@
//
// Created by dell on 24-12-20.
//
#ifndef I2C_H
#define I2C_H
#include "gd32e23x_it.h"
#include "gd32e23x.h"
#include "systick.h"
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "board_config.h"
/******************************************************************************/
#define I2C_SPEED 100000U /* 100kHz */
#define I2C_TIME_OUT 5000U /* 5000 loops timeout */
#define I2C_MAX_RETRY 3U /* Maximum retry attempts */
#define I2C_DELAY_10US 10U /* Delay in microseconds for bus reset */
#define I2C_RECOVERY_CLOCKS 9U /* Clock pulses for bus recovery */
#define I2C_MASTER_ADDRESS 0x00U /* Master address (not used) */
/* Legacy compatibility */
#define I2C_OK 1
#define I2C_FAIL 0
#define I2C_END 1
/******************************************************************************/
/* I2C result enumeration */
typedef enum {
I2C_RESULT_SUCCESS = 0, /* Operation successful */
I2C_RESULT_TIMEOUT, /* Timeout occurred */
I2C_RESULT_NACK, /* No acknowledge received */
I2C_RESULT_BUS_BUSY, /* Bus is busy */
I2C_RESULT_ERROR, /* General error */
I2C_RESULT_INVALID_PARAM, /* Invalid parameter */
I2C_RECOVERY_OK,
I2C_RECOVERY_SDA_STUCK_LOW,
I2C_RECOVERY_SCL_STUCK_LOW
} i2c_result_t;
/* I2C state machine enumeration */
typedef enum {
I2C_STATE_IDLE = 0, /* Idle state */
I2C_STATE_START, /* Generate start condition */
I2C_STATE_SEND_ADDRESS, /* Send slave address */
I2C_STATE_CLEAR_ADDRESS, /* Clear address flag */
I2C_STATE_TRANSMIT_REG, /* Transmit register address */
I2C_STATE_TRANSMIT_DATA, /* Transmit data */
I2C_STATE_RESTART, /* Generate restart condition */
I2C_STATE_RECEIVE_DATA, /* Receive data */
I2C_STATE_STOP, /* Generate stop condition */
I2C_STATE_ERROR, /* Error state */
I2C_STATE_END
} i2c_state_t;
/******************************************************************************/
/* Function declarations */
/*!
\brief configure the I2C interface
\param[in] none
\param[out] none
\retval i2c_result_t
*/
i2c_result_t i2c_config(void);
/*!
\brief reset I2C bus with proper recovery
\param[in] none
\param[out] none
\retval i2c_result_t
*/
i2c_result_t i2c_bus_reset(void);
/*!
\brief scan I2C bus for devices
\param[in] none
\param[out] none
\retval none
*/
void i2c_scan(void);
/*!
\brief write 16-bit data to I2C device (8-bit register address + 2 data bytes)
\param[in] slave_addr: 7-bit slave address
\param[in] reg_addr: register address (8-bit)
\param[in] data: pointer to 2-byte data array
\param[out] none
\retval i2c_result_t
*/
i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]);
/*!
\brief read 16-bit data from I2C device (8-bit register address + 2 data bytes)
\param[in] slave_addr: 7-bit slave address
\param[in] reg_addr: register address (8-bit)
\param[out] data: pointer to 2-byte data buffer
\retval i2c_result_t
*/
i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data);
/* Generic read/write functions with configurable length */
/*!
\brief write data to I2C device with configurable length
\param[in] slave_addr: slave device address (7-bit)
\param[in] reg_addr: register address (16-bit, high byte first)
\param[in] data: pointer to data buffer
\param[in] length: number of bytes to write (1-255)
\param[out] none
\retval i2c_result_t: operation result
*/
i2c_result_t i2c_write(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length);
/*!
\brief read data from I2C device with configurable length
\param[in] slave_addr: slave device address (7-bit)
\param[in] reg_addr: register address (16-bit, high byte first)
\param[out] data: pointer to data buffer
\param[in] length: number of bytes to read (1-255)
\retval i2c_result_t: operation result
*/
i2c_result_t i2c_read(uint8_t slave_addr, uint16_t reg_addr, uint8_t *data, uint8_t length);
/* Pure read response without sub-address phase (for commands that already sent read request parameters) */
/*!
\brief read data from I2C device without register/sub-address phase
\param[in] slave_addr: slave device address (7-bit)
\param[out] data: pointer to data buffer
\param[in] length: number of bytes to read (1-255)
\retval i2c_result_t: operation result
*/
i2c_result_t i2c_read_raw(uint8_t slave_addr, uint8_t *data, uint8_t length);
#ifdef DEBUG_VERBOSE
/*!
\brief get status string for debugging
\param[in] status: i2c_result_t value
\param[out] none
\retval const char* status string
*/
const char* i2c_get_status_string(i2c_result_t status);
#endif
#endif //I2C_H
+488
View File
@@ -0,0 +1,488 @@
//
// Created by dell on 24-12-3.
//
#ifndef LDC1612_H
#define LDC1612_H
#include "gd32e23x_it.h"
#include "gd32e23x.h"
#include "systick.h"
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "board_config.h"
#include "i2c.h"
/***************************************************************************/
/* IIC Interface Selection */
#ifdef SOFTWARE_IIC
#define LDC1612_IIC_WRITE_16BITS(addr, reg, data) soft_i2c_write_16bits(addr, reg, data)
#define LDC1612_IIC_READ_16BITS(addr, reg, data) soft_i2c_read_16bits(addr, reg, data)
#define LDC1612_IIC_TYPE_STR "Software IIC"
#else
#define LDC1612_IIC_WRITE_16BITS(addr, reg, data) i2c_write_16bits(addr, reg, data)
#define LDC1612_IIC_READ_16BITS(addr, reg, data) i2c_read_16bits(addr, reg, data)
#define LDC1612_IIC_TYPE_STR "Hardware IIC"
#endif
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>[EXT CLK(MHz)]<<<<<<<<<<<<<<<<<<<< */
#define LDC1612_EXT_CLK_MHZ 40
/***************************************************************************/
#define LDC1612_ADDR (0x2B)
/******************************************************************************/
#define COIL_L_UH 40.9
#define COIL_C_PF 180
/************************Register Addr***************************************/
#define CONVERSION_RESULT_REG_START 0X00
#define SET_CONVERSION_TIME_REG_START 0X08
#define SET_CONVERSION_OFFSET_REG_START 0X0C
#define SET_SETTLECOUNT_REG_START 0X10
#define SET_FREQ_REG_START 0X14
#define SENSOR_STATUS_REG 0X18
#define ERROR_CONFIG_REG 0X19
#define SENSOR_CONFIG_REG 0X1A
#define MUX_CONFIG_REG 0X1B
#define SENSOR_RESET_REG 0X1C
#define SET_DRIVER_CURRENT_REG 0X1E
#define READ_MANUFACTURER_ID 0X7E
#define READ_DEVICE_ID 0X7F
/**********************Sensor Channel****************************************/
#define CHANNEL_0 0
#define CHANNEL_1 1
/**************************DATA (0x00-0x03)*******************************************/
/*
* 作用: 存储28位的传感器转换结果。结果分为高字节(MSB)和低字节(LSB)两个寄存器。
*
* 结构说明:
* - DATA_CHx_MSB: 包含错误标志和数据的高12位 [27:16]。
* - DATA_CHx_LSB: 包含数据的低16位 [15:0]。
*
* MSB寄存器位域:
* [15] ERR_UR: 转换下溢错误标志 (1 = 发生错误)
* [14] ERR_OR: 转换上溢错误标志 (1 = 发生错误)
* [13] ERR_WD: 看门狗超时错误标志 (1 = 发生错误)
* [12] ERR_AE: 振幅错误标志 (高或低) (1 = 发生错误)
* [11:0] DATA[27:16]: 数据的高12位
*
* 注意:
* - 仅当ERROR_CONFIG寄存器中对应的ERR2OUT位置1时,这些错误标志才会在MSB寄存器中被设置。
* - 读取数据时,应先读取LSB,再读取MSB,以确保数据的一致性。
*/
/* --- 数据寄存器错误标志位掩码 --- */
#define LDC1612_DATA_ERR_UR (1 << 15)
#define LDC1612_DATA_ERR_OR (1 << 14)
#define LDC1612_DATA_ERR_WD (1 << 13)
#define LDC1612_DATA_ERR_AE (1 << 12)
#define LDC1612_DATA_ERR_MASK (0xF000)
#define LDC1612_DATA_MSB_MASK (0x0FFF)
/**************************RCOUNT (0x08, 0x09)****************************************/
/*
* 作用: 设置参考计数器值,决定了传感器的转换时间,从而影响测量分辨率。
*
* 位域说明:
* [15:0] RCOUNT: 参考计数值。
*
* 计算公式:
* t_CONVERSION = (RCOUNT * 16) / f_REF
*
* 注意:
* - RCOUNT值必须 ≥ 0x0004。
* - 该寄存器在复位后值为 0x0080。
*
* 配置建议:
* - 需要高采样率: 使用较小的RCOUNT值。
* - 需要高分辨率: 使用较大的RCOUNT值。
*/
/* --- 预设配置示例 --- */
// 高速采样配置 (分辨率较低)
#define LDC1612_RCOUNT_HIGH_SPEED (0x04D6) // 1238, 约 1kSPS @ 40MHz/2
// 平衡配置 (常用)
#define LDC1612_RCOUNT_BALANCED (0x1000) // 4096, 约 380SPS @ 40MHz/2
// 高分辨率配置 (采样率较低)
#define LDC1612_RCOUNT_HIGH_RESOLUTION (0xFFFF) // 65535, 约 24SPS @ 40MHz/2
// 默认配置
#define LDC1612_RCOUNT_TIME_CH0 LDC1612_RCOUNT_BALANCED // 0x1000=4096个时钟周期
/**************************OFFSET (0x0C, 0x0D)****************************************/
/*
* 作用: 设置一个16位的数字偏移量,该值会从原始转换结果中减去。
*
* 位域说明:
* [15:0] OFFSET: 数据偏移值。
*
* 计算公式:
* 最终数据 = 原始转换数据 - OFFSET
*
* 注意:
* - 如果减法结果为负,将触发下溢错误 (ERR_UR)。
* - 该寄存器在复位后值为 0x0000。
*
* 应用场景:
* - 消除传感器或环境的固有基线偏移。
* - 实现“去皮”(Tare)功能,将当前读数设为新的零点。
*/
// 默认配置: 不设置偏移
#define SET_CONVERSION_OFFSET_CH0 0x0000
/**************************SETTLECOUNT (0x10, 0x11)***********************************/
/*
* 作用: 设置传感器振荡器在开始转换前所需的建立时间。
*
* 位域说明:
* [15:0] SETTLECOUNT: 建立时间计数值。
*
* 计算公式:
* t_SETTLE 的计算方式取决于SETTLECOUNT的值:
* - 当 SETTLECOUNT = 0x0000 或 0x0001 时, t_SETTLE = 32 / f_REF
* - 当 SETTLECOUNT ≥ 0x0002 时, t_SETTLE = (SETTLECOUNT * 16) / f_REF
*
* 配置建议:
* - 传感器的Q值越高,所需的建立时间越短 (SETTLECOUNT值可以越小)。
* - 值过小可能导致传感器未充分稳定,数据不准确。
* - 值过大则会不必要地增加总转换时间,降低采样率。
* - 对于大多数应用,0x0100 (256) 是一个很好的起始值。
*/
/* --- 预设配置示例 --- */
// 适用于高Q值传感器 (建立时间短)
#define LDC1612_SETTLECOUNT_HIGH_Q (0x000A) // 约 4µs @ 40MHz/2
// 适用于中等Q值传感器 (通用)
#define LDC1612_SETTLECOUNT_MEDIUM_Q (0x0100) // 约 102µs @ 40MHz/2
// 适用于低Q值传感器 (建立时间长)
#define LDC1612_SETTLECOUNT_LOW_Q (0x0400) // 约 410µs @ 40MHz/2
// 默认配置
#define LDC1612_SETTLECOUNT_CH0 LDC1612_SETTLECOUNT_MEDIUM_Q
/**************************CLOCK_DIVIDER (0x14, 0x15)***********************************/
/*
* 作用: 配置传感器输入频率(f_sensor)和参考时钟(f_ref)的分频器。
*
* 位域说明:
* [15:12] FIN_DIVIDER: 传感器输入分频器。
* [11:10] RESERVED: 必须为00。
* [9:0] FREF_DIVIDER: 参考时钟分频器。
*
* 配置逻辑:
* 1. FIN_DIVIDER: 根据传感器的谐振频率 f_sensor 选择。
* - 目标是使 f_sensor / FIN_DIVIDER <= 8.75MHz。
* - 例如: 如果 f_sensor = 15MHz, 则 FIN_DIVIDER 必须 >= 2。
*
* 2. FREF_DIVIDER: 根据外部时钟 f_clk 和工作模式选择。
* - 目标是使 f_ref = f_clk / FREF_DIVIDER。
* - 单通道模式且 f_clk <= 35MHz: FREF_DIVIDER = 1。
* - 双通道模式或 f_clk > 35MHz: FREF_DIVIDER = 2。
*
* 最终寄存器值 = (FIN_DIVIDER << 12) | FREF_DIVIDER;
*/
/* --- 位域选项宏 --- */
// [15:12] Sensor Input Divider (FIN_DIVIDER)
#define LDC1612_FIN_DIV_1 (0x1 << 12) // for f_sensor <= 8.75MHz
#define LDC1612_FIN_DIV_2 (0x2 << 12) // for 8.75MHz < f_sensor <= 17.5MHz
#define LDC1612_FIN_DIV_4 (0x3 << 12) // for 17.5MHz < f_sensor <= 35MHz
// [9:0] Reference Clock Divider (FREF_DIVIDER)
#define LDC1612_FREF_DIV_1 (0x001)
#define LDC1612_FREF_DIV_2 (0x002)
/* --- 组合宏 --- */
#define LDC1612_CLOCK_DIVIDER_GEN(fin_div, fref_div) ((fin_div) | (fref_div))
/* --- 预设配置示例 (基于40MHz外部时钟) --- */
// 适用于 f_sensor <= 8.75MHz
#define LDC1612_CLOCK_DIVIDER_DEFAULT LDC1612_CLOCK_DIVIDER_GEN(LDC1612_FIN_DIV_1, LDC1612_FREF_DIV_2) // 0x1002
/**************************STATUS (0x18) MACROS****************************************
*
* 作用: 定义STATUS寄存器的位掩码,用于解析设备状态。
*
*/
#define LDC1612_STATUS_DRDY (1 << 6) // 数据就绪
#define LDC1612_STATUS_UNREAD_CH0 (1 << 3) // 通道0有未读数据
#define LDC1612_STATUS_UNREAD_CH1 (1 << 2) // 通道1有未读数据
#define LDC1612_STATUS_ERR_ZC (1 << 8) // 零计数错误
#define LDC1612_STATUS_ERR_ALE (1 << 9) // 振幅过低
#define LDC1612_STATUS_ERR_AHE (1 << 10) // 振幅过高
#define LDC1612_STATUS_ERR_WD (1 << 11) // 看门狗超时
#define LDC1612_STATUS_ERR_OR (1 << 12) // 转换上溢
#define LDC1612_STATUS_ERR_UR (1 << 13) // 转换下溢
#define LDC1612_STATUS_ERR_CHAN_MASK (3 << 14) // 错误通道掩码
/**************************ERROR_CONFIG (0x19)****************************************/
/*
* 作用: 配置状态或错误输出或者触发INTB引脚中断。
*
* 位域说明:
* [15] UR_ERR2OUT: 1 = 转换下溢错误输出到DATA_CHx寄存器
* [14] OR_ERR2OUT: 1 = 转换上溢错误输出到DATA_CHx寄存器
* [13] WD_ERR2OUT: 1 = 看门狗超时错误输出到DATA_CHx寄存器
* [12] AH_ERR2OUT: 1 = 振幅过高错误输出到DATA_CHx寄存器
* [11] AL_ERR2OUT: 1 = 振幅过低错误输出到DATA_CHx寄存器
* [10:8] RESERVED
* [7] UR_ERR2INT: 1 = 转换下溢错误触发INTB
* [6] OR_ERR2INT: 1 = 转换上溢错误触发INTB
* [5] WD_ERR2INT: 1 = 看门狗超时错误触发INTB
* [4] AH_ERR2INT: 1 = 振幅过高错误触发INTB
* [3] AL_ERR2INT: 1 = 振幅过低错误触发INTB
* [2] ZC_ERR2INT: 1 = 零计数错误触发INTB
* [1] RESERVED
* [0] DRDY_2INT: 1 = 数据就绪标志触发INTB
*/
/* --- 位域选项宏 --- */
// --- 中断触发 (ERR2INT) ---
#define LDC1612_ERR_CFG_DRDY_INT_EN (1 << 0) // 数据就绪中断使能
#define LDC1612_ERR_CFG_ZC_INT_EN (1 << 2) // 零计数错误中断使能
#define LDC1612_ERR_CFG_AL_INT_EN (1 << 3) // 振幅过低错误中断使能
#define LDC1612_ERR_CFG_AH_INT_EN (1 << 4) // 振幅过高错误中断使能
#define LDC1612_ERR_CFG_WD_INT_EN (1 << 5) // 看门狗超时中断使能
#define LDC1612_ERR_CFG_OR_INT_EN (1 << 6) // 转换上溢中断使能
#define LDC1612_ERR_CFG_UR_INT_EN (1 << 7) // 转换下溢中断使能
// --- 错误报告至数据寄存器 (ERR2OUT) ---
#define LDC1612_ERR_CFG_AL_OUT_EN (1 << 11) // 振幅过低错误报告使能
#define LDC1612_ERR_CFG_AH_OUT_EN (1 << 12) // 振幅过高错误报告使能
#define LDC1612_ERR_CFG_WD_OUT_EN (1 << 13) // 看门狗超时错误报告使能
#define LDC1612_ERR_CFG_OR_OUT_EN (1 << 14) // 转换上溢错误报告使能
#define LDC1612_ERR_CFG_UR_OUT_EN (1 << 15) // 转换下溢错误报告使能
// 常用配置: 仅使能 "数据就绪" 中断
#define LDC1612_ERROR_CONFIG_DRDY_ONLY (LDC1612_ERR_CFG_DRDY_INT_EN) // 结果: 0x0001
// 常用配置: 使能所有错误报告
#define LDC1612_ERROR_CONFIG_OUT_ONLY (LDC1612_ERR_CFG_AL_OUT_EN | \
LDC1612_ERR_CFG_AH_OUT_EN | \
LDC1612_ERR_CFG_WD_OUT_EN | \
LDC1612_ERR_CFG_OR_OUT_EN | \
LDC1612_ERR_CFG_UR_OUT_EN) // 结果: 0xF800
// 调试配置: 使能所有错误中断和错误报告
#define LDC1612_ERROR_CONFIG_DEBUG_ALL (LDC1612_ERR_CFG_DRDY_INT_EN | \
LDC1612_ERR_CFG_ZC_INT_EN | \
LDC1612_ERR_CFG_AL_INT_EN | \
LDC1612_ERR_CFG_AH_INT_EN | \
LDC1612_ERR_CFG_WD_INT_EN | \
LDC1612_ERR_CFG_OR_INT_EN | \
LDC1612_ERR_CFG_UR_INT_EN | \
LDC1612_ERR_CFG_AL_OUT_EN | \
LDC1612_ERR_CFG_AH_OUT_EN | \
LDC1612_ERR_CFG_WD_OUT_EN | \
LDC1612_ERR_CFG_OR_OUT_EN | \
LDC1612_ERR_CFG_UR_OUT_EN) // 结果: 0xF8FD
// 默认配置: 所有功能都禁用
#define LDC1612_ERROR_CONFIG_DEFAULT (0x0000)
/**************************SENSOR_CONFIG (0x1A) MACROS***************************************/
/*
* CONFIG寄存器位域宏定义,用于灵活组合生成配置值。
* 使用方法: LDC1612_CONFIG_GEN(ACTIVE_CHAN, SLEEP_MODE, RP_OVERRIDE, AUTO_AMP, CLK_SRC, INTB, CURRENT_DRV)
*
* 位域说明 (根据 LDC1612_REG_LIST.md):
* [15:14] ACTIVE_CHAN: 激活通道选择 (仅在 AUTOSCAN_EN=0 时有效)
* [13] SLEEP_MODE_EN: 1 = 睡眠模式使能
* [12] RP_OVERRIDE_EN: 1 = 禁用自动校准 (使用手动的IDRIVE设置)
* [11] SENSOR_ACTIVATE_SEL: 传感器激活电流选择 (0:低电流, 1:高电流)
* [10] AUTO_AMP_DIS: 1 = 禁用自动幅度校正
* [9] REF_CLK_SRC: 1 = 使用外部CLKIN时钟
* [8] RESERVED: 必须为0
* [7] INTB_DIS: 1 = 禁用INTB中断引脚
* [6] HIGH_CURRENT_DRV: 1 = 通道0高电流驱动模式
* [5:0] RESERVED: 必须写入 0x01
*/
/* --- 位域选项宏 --- */
// [15:14] Active Channel Selection
#define LDC1612_CONFIG_ACTIVE_CHAN_CH0 (0x00 << 14)
#define LDC1612_CONFIG_ACTIVE_CHAN_CH1 (0x01 << 14)
// [13] Sleep Mode Enable
#define LDC1612_CONFIG_SLEEP_MODE_DISABLE (0x00 << 13)
#define LDC1612_CONFIG_SLEEP_MODE_ENABLE (0x01 << 13)
// [12] RP Override Enable (Auto-Calibration Disable)
#define LDC1612_CONFIG_RP_OVERRIDE_DISABLE (0x00 << 12) // 启用自动校准
#define LDC1612_CONFIG_RP_OVERRIDE_ENABLE (0x01 << 12) // 禁用自动校准
// [11] Sensor Activation Current Selection
#define LDC1612_CONFIG_SENSOR_ACT_LOW_I (0x00 << 11) // 低电流激活
#define LDC1612_CONFIG_SENSOR_ACT_HIGH_I (0x01 << 11) // 高电流激活
// [10] Auto Amplitude Correction Disable
#define LDC1612_CONFIG_AUTO_AMP_ENABLE (0x00 << 10) // 启用自动幅度校正
#define LDC1612_CONFIG_AUTO_AMP_DISABLE (0x01 << 10) // 禁用自动幅度校正
// [9] Reference Clock Source
#define LDC1612_CONFIG_CLK_SRC_INTERNAL (0x00 << 9)
#define LDC1612_CONFIG_CLK_SRC_EXTERNAL (0x01 << 9)
// [7] INTB Pin Disable
#define LDC1612_CONFIG_INTB_ENABLE (0x00 << 7)
#define LDC1612_CONFIG_INTB_DISABLE (0x01 << 7)
// [6] High Current Drive (Channel 0)
#define LDC1612_CONFIG_HIGH_CURRENT_DISABLE (0x00 << 6)
#define LDC1612_CONFIG_HIGH_CURRENT_ENABLE (0x01 << 6)
/* --- 组合宏 --- */
// 将所有位域组合成一个16位值。注意,保留位0x01被固定添加。
#define LDC1612_CONFIG_GEN(active_chan, sleep, rp_override, sensor_act, auto_amp, clk_src, intb, high_current) \
( (active_chan) | (sleep) | (rp_override) | (sensor_act) | (auto_amp) | (clk_src) | (intb) | (high_current) | 0x0001 )
/* --- 预设配置示例 --- */
// CH0连续转换, 外部时钟, 高驱动电流, 禁用自动幅度修正(适用于电流检测)
#define LDC1612_SENSOR_CONFIG_CH0 LDC1612_CONFIG_GEN( \
LDC1612_CONFIG_ACTIVE_CHAN_CH0, \
LDC1612_CONFIG_SLEEP_MODE_DISABLE, \
LDC1612_CONFIG_RP_OVERRIDE_ENABLE, /* Rp覆盖开启 */ \
LDC1612_CONFIG_SENSOR_ACT_LOW_I, /* 低功耗启动 */ \
LDC1612_CONFIG_AUTO_AMP_DISABLE, /* 禁用自动幅度校正 */ \
LDC1612_CONFIG_CLK_SRC_EXTERNAL, /* 外部时钟 */ \
LDC1612_CONFIG_INTB_ENABLE, /* 启用INTB引脚 */ \
LDC1612_CONFIG_HIGH_CURRENT_ENABLE /* 大电流模式 */ ) // 结果: 0x1641
// TODO 对比1601的不同(大电流与标准电流)
// 睡眠模式, 外部时钟
#define LDC1612_SLEEP_MODE LDC1612_CONFIG_GEN( \
LDC1612_CONFIG_ACTIVE_CHAN_CH0, \
LDC1612_CONFIG_SLEEP_MODE_ENABLE, \
LDC1612_CONFIG_RP_OVERRIDE_DISABLE, \
LDC1612_CONFIG_SENSOR_ACT_HIGH_I, \
LDC1612_CONFIG_AUTO_AMP_ENABLE, \
LDC1612_CONFIG_CLK_SRC_INTERNAL, \
LDC1612_CONFIG_INTB_ENABLE, \
LDC1612_CONFIG_HIGH_CURRENT_DISABLE ) // 结果: 0x2801
/*************************MUX_CONFIG (0x1B) MACROS***************************************/
/*
* MUX_CONFIG寄存器位域宏定义,用于灵活组合生成配置值。
*
* 位域说明:
* [15] AUTOSCAN_EN: 1 = 自动顺序扫描模式使能
* [14:13] RR_SEQUENCE: 扫描序列 (00: CH0, CH1)
* [12:3] RESERVED: 必须写入 0x041
* [2:0] DEGLITCH: 输入消抖滤波器带宽
*/
/* --- 位域选项宏 --- */
// [15] Auto Scan Mode
#define LDC1612_MUX_AUTOSCAN_DISABLE (0x00 << 15) // 单通道连续模式
#define LDC1612_MUX_AUTOSCAN_ENABLE (0x01 << 15) // 自动扫描模式
// [14:13] Round Robin Sequence
#define LDC1612_MUX_RR_SEQ_CH0_CH1 (0x00 << 13) // 扫描 CH0, CH1
// [2:0] Deglitch Filter Bandwidth
#define LDC1612_MUX_DEGLITCH_1MHZ (0x01)
#define LDC1612_MUX_DEGLITCH_3_3MHZ (0x04)
#define LDC1612_MUX_DEGLITCH_10MHZ (0x05)
#define LDC1612_MUX_DEGLITCH_33MHZ (0x07)
/* --- 组合宏 --- */
// 将所有位域组合成一个16位值。注意,保留位0x0208 (0x041 << 3)被固定添加。
#define LDC1612_MUX_CONFIG_GEN(autoscan, sequence, deglitch) \
( (autoscan) | (sequence) | (deglitch) | 0x0208 )
/* --- 预设配置示例 --- */
// 单通道模式, 3.3MHz 滤波
#define LDC1612_MUX_CONFIG LDC1612_MUX_CONFIG_GEN( \
LDC1612_MUX_AUTOSCAN_DISABLE, \
LDC1612_MUX_RR_SEQ_CH0_CH1, /* 此模式下无效,但保持定义 */ \
LDC1612_MUX_DEGLITCH_3_3MHZ ) // 0x020C
/***********************RESET DEVICE (0x1C)***********************************/
/*
* 向RESET_DEV寄存器写入 LDC1612_RESET_CMD 会触发软件复位。
* 复位后,所有寄存器将恢复为默认值,设备进入睡眠模式。
* 需要大约10ms的稳定时间后才能重新配置。
*/
#define LDC1612_RESET_DEV 0x8000
/**************************DRIVE_CURRENT (0x1E, 0x1F)****************************************/
/*
* 作用: 设置传感器的驱动电流,以确保振荡幅度(Vosc)在1.2V到1.8V之间。
*
* 位域说明:
* [15:11] IDRIVE: 当前驱动电流设置值 (0-31)。
* [10:6] INIT_IDRIVE: 初始驱动电流设置值 (0-31)。
* [5:0] RESERVED: 必须为0。
*
* 配置建议:
* 1. 初始阶段可启用自动校准 (CONFIG.RP_OVERRIDE_EN = 0),让芯片自动寻找合适的IDRIVE值。
* 2. 读取DRIVE_CURRENT寄存器,获得自动校准后的IDRIVE值。
* 3. 在最终代码中,禁用自动校准 (CONFIG.RP_OVERRIDE_EN = 1),并手动写入这个调试好的IDRIVE值。
*
* CH_INIT_IDRIVE will update when every conversion systick ==>AutoAmpDis is 0
* CH_INIT_IDRIVE will store init drive current calculated ==> AutoAmpDis is 1
*/
/* --- 驱动电流值生成宏 --- */
// 参数 idrive: 0-31之间的整数
#define LDC1612_DRIVE_CURRENT_GEN(idrive) ( (uint16_t)(idrive) << 11 )
#define LDC1612_DRIVE_CURRENT LDC1612_DRIVE_CURRENT_GEN(18) // 0x9000
/**************************IDs (Read Only 0x7E 0x7F)***********************************/
#define LDC1612_MANUFACTURER_ID 0x5449
#define LDC1612_DEVICE_ID 0x3055
/******************************************************************************/
typedef enum {
LDC1612_STATUS_SUCCESS = 0,
LDC1612_STATUS_ERROR,
LDC1612_STATUS_TIMEOUT,
LDC1612_STATUS_INVALID_PARAM,
LDC1612_STATUS_NO_COIL,
LDC1612_STATUS_UNDER_RANGE,
LDC1612_STATUS_OVER_RANGE
} ldc1612_status_t;
/******************************************************************************/
ldc1612_status_t ldc1612_init(void);
ldc1612_status_t ldc1612_reset_sensor(void);
ldc1612_status_t ldc1612_config_single_channel(uint8_t channel);
uint16_t ldc1612_get_manufacturer_id(void);
uint16_t ldc1612_get_deveice_id(void);
uint32_t ldc1612_get_raw_channel_result(uint8_t channel);
void ldc1612_drvie_current_detect(uint8_t channel);
uint16_t ldc1612_get_sensor_status(void);
bool ldc1612_is_data_ready(uint8_t channel);
uint16_t ldc1612_check_status_and_log_errors(void);
#endif //LDC1612_H
+1
View File
@@ -8,5 +8,6 @@ void led_init(void);
void led_on(void); void led_on(void);
void led_off(void); void led_off(void);
void led_toggle(void); void led_toggle(void);
void led_heart_beat(void);
#endif // LED_H #endif // LED_H
+52
View File
@@ -0,0 +1,52 @@
//
// Created by dell on 24-12-28.
//
#ifndef SOFT_I2C_H
#define SOFT_I2C_H
#include "gd32e23x_it.h"
#include "gd32e23x.h"
#include "systick.h"
#include "board_config.h"
/******************************************************************************/
#define I2C_SCL_HIGH() gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN)
#define I2C_SCL_LOW() gpio_bit_reset(I2C_SCL_PORT, I2C_SCL_PIN)
#define I2C_SDA_HIGH() gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN)
#define I2C_SDA_LOW() gpio_bit_reset(I2C_SDA_PORT, I2C_SDA_PIN)
#define I2C_SDA_READ() gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN)
/******************************************************************************/
#define SOFT_I2C_OK 1
#define SOFT_I2C_FAIL 0
#define SOFT_I2C_END 1
/******************************************************************************/
void soft_i2c_delay(void);
void soft_i2c_config(void);
void soft_i2c_start(void);
void soft_i2c_stop(void);
void soft_i2c_send_ack(void);
void soft_i2c_send_nack(void);
uint8_t soft_i2c_wait_ack(void);
void soft_i2c_send_byte(uint8_t data);
uint8_t soft_i2c_receive_byte(uint8_t ack);
uint8_t soft_i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]);
uint8_t soft_i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data);
#endif //SOFT_I2C_H
+25 -36
View File
@@ -1,47 +1,36 @@
/*! /**
\file systick.h * ************************************************************************
\brief the header file of systick *
* @file systick.h
\version 2025-02-10, V2.4.0, demo for GD32E23x * @author GD32
*/ * @brief
*
/* * ************************************************************************
Copyright (c) 2025, GigaDevice Semiconductor Inc. * @copyright Copyright (c) 2024 GD32
* ************************************************************************
Redistribution and use in source and binary forms, with or without modification, */
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef SYS_TICK_H #ifndef SYS_TICK_H
#define SYS_TICK_H #define SYS_TICK_H
#include <stdint.h> #include <stdint.h>
/* function declarations */
/* configure systick */ /* configure systick */
void systick_config(void); void systick_config(void);
/* delay a time in 10 microseconds */
void delay_10us(uint32_t count);
/* delay a time in milliseconds */ /* delay a time in milliseconds */
void delay_ms(uint32_t count); void delay_ms(uint32_t count);
/* delay decrement */
/* decrement delay counters */
void delay_decrement(void); void delay_decrement(void);
#endif /* SYS_TICK_H */ // /* delay function that doesn't interfere with SysTick interrupt */
// void delay_ms_safe(uint32_t count);
// /* delay a time in microseconds (safe version) */
// void delay_us_safe(uint32_t count);
#endif /* SYS_TICK_H */
+157
View File
@@ -0,0 +1,157 @@
//
// Created by dell on 24-12-20.
// TMP112A Temperature Sensor Driver Header
//
#ifndef TMP112_H
#define TMP112_H
#include "gd32e23x_it.h"
#include "gd32e23x.h"
#include "systick.h"
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "board_config.h"
#include "i2c.h"
/******************************************************************************/
/* TMP112A I2C Address */
#define TMP112A_ADDR (0x49) // 7-bit address (ADD0=GND)
/* Register Addresses */
/******************************************************************************/
#define TMP112A_TEMP_REG 0x00 // 温度寄存器
#define TMP112A_CONFIG_REG 0x01 // 配置寄存器
#define TMP112A_TLOW_REG 0x02 // 低温阈值寄存器
#define TMP112A_THIGH_REG 0x03 // 高温阈值寄存器
/* Configuration Register Bits */
/******************************************************************************/
#define TMP112A_CONFIG_OS (1 << 15) // One-shot
#define TMP112A_CONFIG_R1 (1 << 14) // Converter resolution bit 1
#define TMP112A_CONFIG_R0 (1 << 13) // Converter resolution bit 0
#define TMP112A_CONFIG_F1 (1 << 12) // Fault queue bit 1
#define TMP112A_CONFIG_F0 (1 << 11) // Fault queue bit 0
#define TMP112A_CONFIG_POL (1 << 10) // Polarity
#define TMP112A_CONFIG_TM (1 << 9) // Thermostat mode
#define TMP112A_CONFIG_SD (1 << 8) // Shutdown
#define TMP112A_CONFIG_CR1 (1 << 7) // Conversion rate bit 1
#define TMP112A_CONFIG_CR0 (1 << 6) // Conversion rate bit 0
#define TMP112A_CONFIG_AL (1 << 5) // Alert
#define TMP112A_CONFIG_EM (1 << 4) // Extended mode
/* Resolution Settings */
/******************************************************************************/
#define TMP112A_RESOLUTION_9BIT 0x0000 // 9-bit (0.5°C)
#define TMP112A_RESOLUTION_10BIT 0x2000 // 10-bit (0.25°C)
#define TMP112A_RESOLUTION_11BIT 0x4000 // 11-bit (0.125°C)
#define TMP112A_RESOLUTION_12BIT 0x6000 // 12-bit (0.0625°C)
/* Conversion Rate Settings */
/******************************************************************************/
#define TMP112A_RATE_0_25HZ 0x0000 // 0.25 Hz (4s)
#define TMP112A_RATE_1HZ 0x0040 // 1 Hz (1s)
#define TMP112A_RATE_4HZ 0x0080 // 4 Hz (250ms)
#define TMP112A_RATE_8HZ 0x00C0 // 8 Hz (125ms)
/* Default Configuration */
/******************************************************************************/
#define TMP112A_CONFIG_DEFAULT (TMP112A_RESOLUTION_12BIT | TMP112A_RATE_8HZ)
/* Temperature Conversion Constants */
/******************************************************************************/
#define TMP112A_TEMP_RESOLUTION 0.0625f // 12-bit resolution (°C/LSB)
#define TMP112A_TEMP_MIN -55.0f // 最低温度 (°C)
#define TMP112A_TEMP_MAX 125.0f // 最高温度 (°C)
/* Status Definitions */
/******************************************************************************/
typedef enum {
TMP112A_STATUS_SUCCESS = 0,
TMP112A_STATUS_ERROR,
TMP112A_STATUS_TIMEOUT,
TMP112A_STATUS_INVALID_PARAM,
TMP112A_STATUS_OUT_OF_RANGE
} tmp112a_status_t;
typedef struct {
uint16_t raw_data;
float temperature_c;
float temperature_f;
bool alert_flag;
} tmp112a_result_t;
/******************************************************************************/
/* Function Declarations */
/*!
\brief 初始化TMP112A传感器
\param[in] none
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_init(void);
/*!
\brief 配置TMP112A传感器
\param[in] config: 配置值
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_config(uint16_t config);
/*!
\brief 读取温度
\param[in] none
\param[out] result: 结果结构体指针
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_read_temperature(tmp112a_result_t *result);
void tmp112a_get_raw_temperature_value(uint8_t *value);
/*!
\brief 设置温度阈值
\param[in] low_temp: 低温阈值 (°C)
\param[in] high_temp: 高温阈值 (°C)
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_set_thresholds(float low_temp, float high_temp);
/*!
\brief 进入关机模式
\param[in] none
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_shutdown(void);
/*!
\brief 退出关机模式
\param[in] none
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_wakeup(void);
/*!
\brief 单次转换
\param[in] none
\param[out] result: 结果结构体指针
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_one_shot(tmp112a_result_t *result);
/*!
\brief 获取状态字符串
\param[in] status: 状态码
\param[out] none
\retval const char* 状态字符串
*/
const char* tmp112a_get_status_string(tmp112a_status_t status);
#endif //TMP112_H
+1 -9
View File
@@ -3,14 +3,6 @@
#include "gd32e23x.h" #include "gd32e23x.h"
typedef enum { void rs485_init(void);
UART_PRINTF_USART0 = 0,
UART_PRINTF_USART1 = 1,
UART_PRINTF_BOTH = 2
} uart_printf_port_t;
void uart0_init(uint32_t baudrate);
void uart1_init(uint32_t baudrate);
void uart_set_printf_port(uart_printf_port_t port);
#endif // UART_H #endif // UART_H
+119
View File
@@ -0,0 +1,119 @@
/**
* @file uart_ring_buffer.h
* @brief 简单高效的环形接收缓冲区(字节队列)接口声明。
* @details 提供字节写入/读取、可读长度查询、清空与丢弃统计等 API,
* 适用于中断接收(写)与主循环解析(读)的典型嵌入式串口场景。
*/
#ifndef UART_RING_BUFFER_H
#define UART_RING_BUFFER_H
#include <stdint.h>
#include <stdbool.h>
/**
* @def UART_RX_BUFFER_SIZE
* @brief 接收环形缓冲区容量(单位:字节)。
* @note 采用“预留一格”区分空/满策略,最大可用容量为 UART_RX_BUFFER_SIZE-1。
*/
#define UART_RX_BUFFER_SIZE 64
/**
* @defgroup RingBuffer 环形缓冲区
* @brief 字节环形缓冲区(接收端)
* @{
*/
/**
* @section RingBuffer_Usage 使用说明
* 典型用法:中断接收(写入环形缓冲)、主循环解析(读取环形缓冲)。
*
* 1) 初始化
* @code{.c}
* uart_ring_buffer_init();
* @endcode
*
* 2) 使能串口接收非空中断(RBNE)并开启中断(以 USART0 为例)
* @code{.c}
* usart_interrupt_enable(USART0, USART_INT_RBNE);
* nvic_irq_enable(USART0_IRQn, 2, 0); // 根据工程需要设置优先级
* @endcode
*
* 3) 在中断服务函数中写入环形缓冲(参考你当前工程的写法)
* @code{.c}
* void USART0_IRQHandler(void) {
* if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
* uint8_t data = usart_data_receive(USART0);
* (void)uart_ring_buffer_put(data); // 缓冲满时丢弃并计数
* }
* }
* @endcode
*
* 4) 在主循环中读取处理
* @code{.c}
* while (uart_ring_buffer_available() > 0) {
* int b = uart_ring_buffer_get();
* if (b >= 0) {
* // 处理字节 b
* }
* }
* @endcode
*
* @note 缓冲区满时新字节会被丢弃,可用 uart_ring_buffer_drop_count() 查看累计丢弃数。
* @note 采用“预留一格”区分空/满,最大可用容量为 UART_RX_BUFFER_SIZE-1。
*/
/**
* @brief 初始化环形缓冲区。
* @details 复位读/写索引与丢弃计数,准备接收数据。
* @note 若在中断环境使用,初始化前建议关闭相关接收中断以避免并发竞争。
* @ingroup RingBuffer
*/
void uart_ring_buffer_init(void);
/**
* @brief 获取当前可读的字节数。
* @details 返回范围为 [0, UART_RX_BUFFER_SIZE-1]。
* @return 可读字节数(uint8_t)。
* @note 预留一个空槽区分“空/满”,因此满时返回 UART_RX_BUFFER_SIZE-1。
* @ingroup RingBuffer
*/
uint8_t uart_ring_buffer_available(void);
/**
* @brief 从环形缓冲区读取一个字节。
* @details 若缓冲区非空,返回队头字节并推进读指针;若为空,返回 -1。
* @return 读取到的字节(0..255),或 -1 表示缓冲区为空。
* @retval -1 缓冲区为空,无数据可读。
* @ingroup RingBuffer
*/
int uart_ring_buffer_get(void);
/**
* @brief 向环形缓冲区写入一个字节。
* @param data 待写入的字节。
* @return 是否写入成功。
* @retval true 写入成功。
* @retval false 写入失败(缓冲区已满,数据被丢弃并计数)。
* @note 如需改为“覆盖写入”策略,可在满时先推进读指针再写入。
* @ingroup RingBuffer
*/
bool uart_ring_buffer_put(uint8_t data);
/**
* @brief 清空环形缓冲区。
* @details 复位读/写索引与丢弃计数,相当于逻辑上丢弃所有已接收数据,不擦除数据区内容。
* @ingroup RingBuffer
*/
void uart_ring_buffer_clear(void);
/**
* @brief 获取因缓冲区满而被丢弃的字节累计数量。
* @details 该计数在 init/clear 时清零。
* @return 丢弃的累计字节数。
* @ingroup RingBuffer
*/
uint32_t uart_ring_buffer_drop_count(void);
/** @} */
#endif // UART_RING_BUFFER_H
+4 -2
View File
@@ -10,10 +10,12 @@ _Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */ _Min_Stack_Size = 0x400; /* required amount of stack */
/* Memories definition */ /* Memories definition */
/* 双固件布局:FW_A 运行槽 0x08002000~0x0800AFFF(36K,OTA/Ymodem 接收区),
* 0x0800B000 起为 FW_B 出厂保底槽(OTA 不可擦写),LENGTH 上限防止溢出 B 槽 */
MEMORY MEMORY
{ {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 16K FLASH (rx) : ORIGIN = 0x08002000, LENGTH = 36K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 8K
} }
/* Sections */ /* Sections */
+635
View File
@@ -0,0 +1,635 @@
#
```c
#define DATA_MSB_CH0 0X00
#define DATA_LSB_CH0 0X01
#define DATA_MSB_CH1 0X02
#define DATA_LSB_CH1 0X03
#define DATA_MSB_CH2 0X04 /* LDC1614 Only */
#define DATA_LSB_CH2 0X05 /* LDC1614 Only */
#define DATA_MSB_CH3 0X06 /* LDC1614 Only */
#define DATA_LSB_CH3 0X07 /* LDC1614 Only */
#define RCOUNT_CH0 0X08
#define RCOUNT_CH1 0X09
#define RCOUNT_CH2 0X0A /* LDC1614 Only */
#define RCOUNT_CH3 0X0B /* LDC1614 Only */
#define OFFSET_CH0 0X0C
#define OFFSET_CH1 0X0D
#define OFFSET_CH2 0X0E /* LDC1614 Only */
#define OFFSET_CH3 0X0F /* LDC1614 Only */
#define SETTLECOUNT_CH0 0X10
#define SETTLECOUNT_CH1 0X11
#define SETTLECOUNT_CH2 0X12 /* LDC1614 Only */
#define SETTLECOUNT_CH3 0X13 /* LDC1614 Only */
#define CLOCK_DIVIDER_CH0 0X14
#define CLOCK_DIVIDER_CH1 0X15
#define CLOCK_DIVIDER_CH2 0X16 /* LDC1614 Only */
#define CLOCK_DIVIDER_CH3 0X17 /* LDC1614 Only */
#define STATUS 0X18
#define ERROR_CONFIG 0X19
#define CONFIG 0X1A
#define MUX_CONFIG 0X1B
#define RESET_DEVICE 0X1C
#define DRIVE_CURRENT_CH0 0x1E
#define DRIVE_CURRENT_CH1 0x1F
#define DRIVE_CURRENT_CH2 0x20 /* LDC1614 Only */
#define DRIVE_CURRENT_CH3 0x21 /* LDC1614 Only */
#define MANUFACTURER_ID 0x7E
#define DEVICE_ID 0x7F
```
## 数据寄存器(只读)
### DATA_CH0_MSB 0x00
通道0数据高16位
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├────┬────┬────┬────┬─────────────────────────────────────┤
│ 15 │ 14 │ 13 │ 12 │ 11:0 │
├────┼────┼────┼────┼─────────────────────────────────────┤
│ERR │ERR │ERR │ERR │ DATA0[27:16] │
│_UR │_OR │_WD │_AE │ (数据高12位) │
└────┴────┴────┴────┴─────────────────────────────────────┘
位域说明:
┌──────────┬────────┬────────────────────────────────────────┐
│ 位 │ 名称 │ 说明 │
├──────────┼────────┼────────────────────────────────────────┤
│ 15 │ ERR_UR │ 转换下溢错误标志 │
│ │ │ 0: 无下溢错误 │
│ │ │ 1: 通道0发生下溢错误 │
├──────────┼────────┼────────────────────────────────────────┤
│ 14 │ ERR_OR │ 转换上溢错误标志 │
│ │ │ 0: 无上溢错误 │
│ │ │ 1: 通道0发生上溢错误 │
├──────────┼────────┼────────────────────────────────────────┤
│ 13 │ ERR_WD │ 看门狗超时错误标志 │
│ │ │ 0: 无看门狗错误 │
│ │ │ 1: 通道0发生看门狗超时 │
├──────────┼────────┼────────────────────────────────────────┤
│ 12 │ ERR_AE │ 振幅错误标志 │
│ │ │ 0: 无振幅错误 │
│ │ │ 1: 通道0发生振幅错误(高或低) │
├──────────┼────────┼────────────────────────────────────────┤
│ 11:0 │ DATA0 │ 28位数据的高12位 │
│ │[27:16] │ │
└──────────┴────────┴────────────────────────────────────────┘
### DATA_CH0_LSB 0x01
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├─────────────────────────────────────────────────────────┤
│ 15:0 │
├─────────────────────────────────────────────────────────┤
│ DATA0[15:0] │
│ (数据低16位) │
└─────────────────────────────────────────────────────────┘
### DATA_CH1_MSB 0x02
> 同DATA_CH0_MSB
### DATA_CH1_LSB 0x03
> 同DATA_CH0_LSB
### DATA_CH2_MSB 0x04
> LDC1614专用
### DATA_CH2_LSB 0x05
> LDC1614专用
### DATA_CH3_MSB 0x06
> LDC1614专用
### DATA_CH3_LSB 0x07
## 转换时间寄存器(RCOUNT)
### RCOUNT_CH0 0x08
通道0转换时间计数
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├─────────────────────────────────────────────────────────┤
│ 15:0 │
├─────────────────────────────────────────────────────────┤
│ RCOUNT0[15:0] │
└─────────────────────────────────────────────────────────┘
复位值:0x0080
取值范围:3 ~ 65535 (0x0003 ~ 0xFFFF)
设置通道0的转换时间,决定测量分辨率
转换时间计算公式 t_CONVERSION = (RCOUNT × 16) / f_REF
其中:
- t_CONVERSION:单次转换时间(秒)
- RCOUNT:寄存器值
- f_REF:参考时钟频率(Hz)
常用配置参考:
| RCOUNT值 | 40MHz时钟转换时间 | 典型采样率 |
| -------------- | ----------- | ------- |
| 0x04D6 (1238) | 495µs | ~1kSPS |
| 0x0800 (2048) | 819µs | ~600SPS |
| 0xFFFF (65535) | 26.2ms | ~38SPS |
配置建议:
需要高采样率:使用较小的RCOUNT值
需要高分辨率:使用较大的RCOUNT值
最小值必须≥3
### RCOUNT_CH1 0x09
> 同RCOUNT_CH0
### RCOUNT_CH2 0x0A
> LDC1614专用
### RCOUNT_CH3 0x0B
> LDC1614专用
## 偏移寄存器(OFFSET)
### OFFSET_CH0 0x0C
通道0数据偏移
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├─────────────────────────────────────────────────────────┤
│ 15:0 │
├─────────────────────────────────────────────────────────┤
│ OFFSET0[15:0] │
└─────────────────────────────────────────────────────────┘
复位值:0x0000
作用:设置通道0的偏移值,用于从测量结果中减去固定偏移。
计算公式:实际输出 = 原始测量值 - OFFSET
> 如果结果为负,则报告下溢错误(ERR_UR)
应用场景:
消除传感器固有偏移
校准零点
调整测量范围
### OFFSET_CH1 0x0D
> 同OFFSET_CH0
### OFFSET_CH2 0x0E
> LDC1614专用
### OFFSET_CH3 0x0F
> LDC1614专用
## 建立时间寄存器(SETTLECOUNT)
### SETTLECOUNT_CH0 0x10
通道0建立时间计数
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├─────────────────────────────────────────────────────────┤
│ 15:0 │
├─────────────────────────────────────────────────────────┤
│ SETTLECOUNT0[15:0] │
└─────────────────────────────────────────────────────────┘
复位值:0x0000
取值范围:0 ~ 65535(0x0000 ~ 0xFFFF)
作用:设置通道0传感器振荡器建立稳定振荡所需的时间。
建立时间计算公式:t_SETTLE = (SETTLECOUNT × 16) / f_REF
其中:
- t_SETTLE:建立时间(秒)
- SETTLECOUNT:寄存器值
- f_REF:参考时钟频率(Hz)
常用配置值:
| SETTLECOUNT值 | 40MHz时钟建立时间 | 典型应用 |
| ------------- | ----------- | ------- |
| 0x000A (10) | 4µs | 高Q值传感器 |
| 0x0040 (64) | 25.6µs | 中等Q值传感器 |
| 0x0400 (1024) | 409.6µs | 低Q值传感器 |
配置建议:
- 值过小:传感器未充分稳定,数据不准确
- 值过大:增加转换周期,降低采样率
- 需根据实际Q值计算
### SETTLECOUNT_CH1 0x11
> 同SETTLECOUNT_CH0
### SETTLECOUNT_CH2 0x12
> LDC1614专用
### SETTLECOUNT_CH3 0x13
> LDC1614专用
## 时钟分频器寄存器(CLOCK_DIVIDERS)
### CLOCK_DIVIDERS0 0x14
通道0时钟分频器
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├────────┬────────┬───────────────────────────────────────┤
│ 15:12 │ 11:10 │ 9:0 │
├────────┼────────┼───────────────────────────────────────┤
│FIN_DIV │RESERVED│ FREF_DIVIDER │
│ 0 │ │ │
└────────┴────────┴───────────────────────────────────────┘
复位值:0x0000
位域说明:
| 位 | 名称 | 说明 |
| ----- | -------------- | --------- |
| 15:12 | FIN_DIVIDER0 | 通道0输入分频器 |
| 11:10 | RESERVED | 保留,必须设为00 |
| 9:0 | FREF_DIVIDER0 | 通道0参考分频器 |
FIN_DIVIDER设置:
| FIN_DIVIDER值 | 分频比 | 传感器频率范围 |
| ------------- | ------ | ----------------------------- |
| 0x0 (0000) | 保留,不使用 | - |
| 0x1 (0001) | 1 | f_SENSOR ≤ 8.75MHz |
| 0x2 (0010) | 2 | 8.75MHz < f_SENSOR ≤ 17.5MHz |
| 0x3 (0011) | 4 | 17.5MHz < f_SENSOR ≤ 35MHz |
| 0x4~0xF | 保留 | - |
FREF_DIVIDER设置:
- 值≥1有效
- 典型值:1或2
- 计算公式:f_REF_effective = f_CLKIN / FREF_DIVIDER
常用配置:
| 配置值 | FIN_DIV | FREF_DIV | 说明 |
| ------ | -------- | --------- | --------------------------- |
| 0x1001 | 1 | 1 | FIN=1, FREF=1 |
| 0x1002 | 1 | 2 | FIN=1, FREF=2(推荐用于40MHz双通道) |
| 0x2001 | 2 | 1 | FIN=2, FREF=1 |
配置建议:
- 单通道模式,f_CLKIN≤35MHz:FREF_DIV=1
- 双通道模式,f_CLKIN≤40MHz:FREF_DIV=2
- 单通道模式,35MHz<f_CLKIN≤40MHz:FREF_DIV=2
### CLOCK_DIVIDERS1 0x15
> 同CLOCK_DIVIDERS0
### CLOCK_DIVIDERS2 0x16
> LDC1614专用
### CLOCK_DIVIDERS3 0x17
> LDC1614专用
## 状态寄存器(STATUS)
### STATUS 0x18
设备状态寄存器
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ 15 │ 14 │ 13 │ 12 │ 11 │ 10 │ 9 │ 8 │ 7 │ 6 │ 5:4 │ 3 │ 2 │ 1:0 │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│ ERR_CHAN │ ERR_CHAN │ ERR_UR │ ERR_OR │ ERR_WD │ ERR_AHE │ ERR_ALE │ ERR_ZC │ RESERVED │ DRDY │ RESERVED │UNREAD_CH0│UNREAD_CH1│ RESERVED │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
位域说明:
┌──────────┬─────────────┬────────────────────────────────────────┐
│ 位 │ 名称 │ 说明 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 15:14 │ ERR_CHAN │ 错误通道指示 │
│ │ │ 00: 通道0发生错误 │
│ │ │ 01: 通道1发生错误 │
│ │ │ 10/11: LDC1614专用 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 13 │ ERR_UR │ 转换下溢错误 │
│ │ │ 0: 无下溢错误 │
│ │ │ 1: 有通道发生下溢错误 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 12 │ ERR_OR │ 转换上溢错误 │
│ │ │ 0: 无上溢错误 │
│ │ │ 1: 有通道发生上溢错误 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 11 │ ERR_WD │ 看门狗超时错误 │
│ │ │ 0: 无看门狗错误 │
│ │ │ 1: 有通道发生看门狗超时 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 10 │ ERR_AHE │ 振幅过高错误 │
│ │ │ 0: 无振幅过高错误 │
│ │ │ 1: 有通道振幅>1.8V │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 9 │ ERR_ALE │ 振幅过低警告 │
│ │ │ 0: 无振幅过低警告 │
│ │ │ 1: 有通道振幅<1.2V │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 8 │ ERR_ZC │ 零计数错误 │
│ │ │ 0: 无零计数错误 │
│ │ │ 1: 有通道发生零计数错误 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 7 │ RESERVED │ 保留 │
│ │ │ 0: 读为0 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 6 │ DRDY │ 数据就绪标志 │
│ │ │ 0: 无新转换结果 │
│ │ │ 1: 新转换结果已就绪 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 5:4 │ RESERVED │ 保留 │
│ │ │ 00: 读为00 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 3 │ UNREADCONV0 │ 通道0未读转换标志 │
│ │ │ 0: 通道0无未读数据 │
│ │ │ 1: 通道0有未读数据 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 2 │ UNREADCONV1 │ 通道1未读转换标志 │
│ │ │ 0: 通道1无未读数据 │
│ │ │ 1: 通道1有未读数据 │
├──────────┼─────────────┼────────────────────────────────────────┤
│ 1:0 │ RESERVED │ 保留 (LDC1614中用于CH2/CH3) │
└──────────┴─────────────┴────────────────────────────────────────┘
作用:提供设备运行状态的完整信息,包括错误状态、数据就绪状态等。
读取特性:
- 读取STATUS寄存器会清除错误标志
- 读取STATUS会解除INTB引脚的断言
- 错误标志在被读取前保持锁存状态
## 错误配置寄存器(ERROR_CONFIG)
### ERROR_CONFIG 0x19
错误报告配置
┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ 15 │ 14 │ 13 │ 12 │ 11 │ 10:8 │ 7 │ 6 │ 5 │ 4 │ 3 │ 2 │ 1 │ 0 │
├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
│UR_ERR2OUT│OR_ERR2OUT│WD_ERR2OUT│AH_ERR2OUT│AL_ERR2OUT│ RESERVED │UR_ERR2INT│OR_ERR2INT│WD_ERR2INT│AH_ERR2INT│AL_ERR2INT│ZC_ERR2INT│ RESERVED │DRDY_2INT │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
位域说明(输出寄存器报告):
┌────────┬─────────────┬────────────────────────────────────────┐
│ 位 │ 名称 │ 说明 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 15 │ UR_ERR2OUT │ 下溢错误报告到数据寄存器 │
│ │ │ 0: 不在DATAx_MSB中报告 │
│ │ │ 1: 在DATAx_MSB中报告下溢错误 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 14 │ OR_ERR2OUT │ 上溢错误报告到数据寄存器 │
│ │ │ 0: 不在DATAx_MSB中报告 │
│ │ │ 1: 在DATAx_MSB中报告上溢错误 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 13 │ WD_ERR2OUT │ 看门狗错误报告到数据寄存器 │
│ │ │ 0: 不在DATAx_MSB中报告 │
│ │ │ 1: 在DATAx_MSB中报告看门狗错误 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 12 │ AH_ERR2OUT │ 振幅过高错误报告到数据寄存器 │
│ │ │ 0: 不在DATAx_MSB中报告 │
│ │ │ 1: 在DATAx_MSB中报告振幅过高错误 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 11 │ AL_ERR2OUT │ 振幅过低警告报告到数据寄存器 │
│ │ │ 0: 不在DATAx_MSB中报告 │
│ │ │ 1: 在DATAx_MSB中报告振幅过低警告 │
└────────┴─────────────┴────────────────────────────────────────┘
位域说明(INTB中断报告):
┌────────┬─────────────┬────────────────────────────────────────┐
│ 位 │ 名称 │ 说明 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 7 │ UR_ERR2INT │ 下溢错误触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 触发INTB中断 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 6 │ OR_ERR2INT │ 上溢错误触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 触发INTB中断 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 5 │ WD_ERR2INT │ 看门狗错误触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 触发INTB中断 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 4 │ AH_ERR2INT │ 振幅过高错误触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 触发INTB中断 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 3 │ AL_ERR2INT │ 振幅过低警告触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 触发INTB中断 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 2 │ ZC_ERR2INT │ 零计数错误触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 触发INTB中断 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 1 │ RESERVED │ 保留,设为0 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 0 │ DRDY_2INT │ 数据就绪触发INTB中断 │
│ │ │ 0: 不触发INTB │
│ │ │ 1: 新数据就绪时触发INTB中断 │
└────────┴─────────────┴────────────────────────────────────────┘
作用:配置各种错误和状态条件的报告方式。
常用配置值:
- `0x0000`:禁用所有错误报告(默认)
- `0x00FF`:所有错误都触发INTB中断
- `0x0001`:仅数据就绪触发INTB
- `0x00FD`:启用大部分错误中断(实际应用常用)
## 主配置寄存器(CONFIG)
### CONFIG 0x1A
转换配置寄存器
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├───────┬────┬────┬────┬────┬────┬────┬────┬────────────────┤
│ 15:14 │ 14 │ 13 │ 12 │ 11 │ 10 │ 9 │ 8 │ 7:0 │
├───────┼────┼────┼────┼────┼────┼────┼────┼────────────────┤
│ACTIVE │SLEE│RP_O│SENS│AUTO│REF_│INTB│HIGH│ RESERVED │
│ │P_ │VER_│OR_ │_AMP│CLK_│_DIS│_CUR│ │
│CHAN │MODE│RIDE│ACTI│_DIS│SRC │ │RENT│ │
│ │_EN │_EN │VATE│ │ │ │_DRV│ │
│ │ │ │_SEL│ │ │ │ │ │
└───────┴────┴────┴────┴────┴────┴────┴────┴────────────────┘
复位值:0x2801
位域说明:
┌────────┬──────────────────┬────────────────────────────────────────┐
│ 位 │ 名称 │ 说明 │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 15:14│ ACTIVE_CHAN │ 激活通道选择 │
│ │ │ 0: 通道0激活 │
│ │ │ 1: 通道1激活 │
│ │ │ (仅在单通道模式AUTOSCAN_EN=0时有效) │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 13 │ SLEEP_MODE_EN │ 睡眠模式使能 │
│ │ │ 0: 正常工作模式 │
│ │ │ 1: 进入睡眠模式(低功耗配置模式) │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 12 │ RP_OVERRIDE_EN │ 自动校准禁用 │
│ │ │ 0: 启用自动校准(推荐用于未知传感器) │
│ │ │ 1: 禁用自动校准(使用手动IDRIVE设置) │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 11 │ SENSOR_ACTIVATE_ │ 传感器激活电流选择 │
│ │ SEL │ 0: 低电流激活模式 │
│ │ │ 1: 高电流激活模式 │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 10 │ AUTO_AMP_DIS │ 自动幅度校正禁用 │
│ │ │ 0: 启用自动幅度校正 │
│ │ │ 1: 禁用自动幅度校正 │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 9 │ REF_CLK_SRC │ 参考时钟源选择 │
│ │ │ 0: 使用内部参考时钟(约35MHz) │
│ │ │ 1: 使用外部CLKIN时钟 │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 8 │ IRESERVED │ 保留,必须设为0x00 │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 7 │ INTB_DIS │ INTB中断禁用 │
│ │ │ 0: 启用INTB中断功能 │
│ │ │ 1: 禁用INTB(引脚保持高电平) │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 6 │ HIGH_CURRENT_DRV │ 高电流驱动模式 │
│ │ │ 0: 正常电流驱动 │
│ │ │ 1: 通道0高电流驱动模式(特殊应用) │
├────────┼──────────────────┼────────────────────────────────────────┤
│ 5:0 │ RESERVED │ 保留,必须设为0x01 │
└────────┴──────────────────┴────────────────────────────────────────┘
作用:LDC1612的主配置寄存器,控制工作模式、时钟源、通道选择等核心功能。
⚠️ 重要提示:CONFIG寄存器必须在所有其他配置完成后最后写入,写入后设备开始转换。
常用配置值:
| 配置值 | 说明 |
| ------ | ---------------- |
| 0x1601 | 外部时钟,连续转换,禁用自动校准 |
| 0x1481 | 内部时钟,连续转换 |
| 0x8000 | 软件复位 |
| 0x2801 | 进入睡眠模式 |
## 多路复用器配置寄存器(MUX_CONFIG)
### MUX_CONFIG 0x1B
通道多路复用配置
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├────┬───────┬─────────────────┬─────────────────────────┤
│ 15 │ 14:13 │ 12:3 │ 2:0 │
├────┼───────┼─────────────────┼─────────────────────────┤
│AUTO│RR_ │ RESERVED │ DEGLITCH │
│SCAN│SEQUENCE│ (必须设为 │ (消抖滤波器带宽) │
│_EN │ │ 00 0100 0001) │ │
└────┴───────┴─────────────────┴─────────────────────────┘
复位值:0x020F
位域说明:
┌────────┬─────────────┬────────────────────────────────────────┐
│ 位 │ 名称 │ 说明 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 15 │ AUTOSCAN_EN │ 自动扫描模式使能 │
│ │ │ 0: 单通道连续模式(由ACTIVE_CHAN选择) │
│ │ │ 1: 顺序扫描模式(由RR_SEQUENCE选择) │
├────────┼─────────────┼────────────────────────────────────────┤
│ 14:13 │ RR_SEQUENCE │ 扫描序列配置 │
│ │ │ 00: CH0, CH1 │
│ │ │ 01: CH0, CH1, CH2 (LDC1614) │
│ │ │ 10: CH0, CH1, CH2, CH3 (LDC1614) │
│ │ │ 11: CH0, CH1 │
├────────┼─────────────┼────────────────────────────────────────┤
│ 12:3 │ RESERVED │ 保留,必须设为00 0100 0001 (0x041) │
├────────┼─────────────┼────────────────────────────────────────┤
│ 2:0 │ DEGLITCH │ 输入消抖滤波器带宽 │
│ │ │ 001: 1.0 MHz │
│ │ │ 100: 3.3 MHz │
│ │ │ 101: 10 MHz │
│ │ │ 111: 33 MHz │
└────────┴─────────────┴────────────────────────────────────────┘
作用:配置通道扫描模式和输入消抖滤波器。
DEGLITCH选择建议:
- 选择大于传感器频率的最低设置
- 例如:f_SENSOR=7.8MHz,选择10MHz (101)
常用配置值:
| 配置值 | 说明 |
| ------ | ---------------- |
| 0x020C | 单通道模式,3.3MHz滤波 |
| 0x820C | 双通道顺序模式,3.3MHz滤波 |
| 0x820D | 双通道顺序模式,10MHz滤波 |
## 复位寄存器(RESET_DEV)
### RESET_DEV 0x1C
设备复位
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├─────────────────────────────────────────────────────────┤
│ 15:0 │
├─────────────────────────────────────────────────────────┤
│ RESET_DEV[15:0] │
└─────────────────────────────────────────────────────────┘
作用:软件复位设备。
复位操作:
- 写入0x8000:触发软件复位
- 复位后所有寄存器恢复默认值
- 复位完成后设备进入睡眠模式
复位完成后,delay 10ms,然后重新配置全部寄存器
## 驱动电流寄存器(DRIVE_CURRENT)
### DRIVE_CURRENT0 0x1E
通道0驱动电流
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├────────────────┬──────────────────┬─────────────────────┤
│ 15:11 │ 10:6 │ 5:0 │
├────────────────┼──────────────────┼─────────────────────┤
│ IDRIVE0 │ INIT_IDRIVE0 │ RESERVED │
│ (当前驱动电流) │ (初始驱动电流) │ (设为0x00) │
└────────────────┴──────────────────┴─────────────────────┘
复位值:0x0000
作用:设置通道0的传感器驱动电流。INIT_IDRIVE0可以用来自动幅度校正关闭时,自动确定合适的驱动电流。
IDRIVE值与Rp关系:
| Rp (kΩ) | 推荐IDRIVE | DRIVE_CURRENT值 | Nominal Current (µA) |
| ------- | -------- | --------------- | ------------------ |
| 0.90 | 31 | 0xF800 | 1563 |
| 1.05 | 30 | 0xF000 | 1355 |
| 1.21 | 29 | 0xE800 | 1173 |
| 1.40 | 28 | 0xE000 | 1017 |
| 1.63 | 27 | 0xD800 | 880 |
| 1.89 | 26 | 0xD000 | 763 |
| 2.20 | 25 | 0xC800 | 635 |
| 2.55 | 24 | 0xC000 | 551 |
| 2.95 | 23 | 0xB800 | 489 |
| 3.43 | 22 | 0xB000 | 424 |
| 3.98 | 21 | 0xA800 | 392 |
| 4.61 | 20 | 0xA000 | 297 |
| 5.35 | 19 | 0x9800 | 244 |
| 6.21 | 18 | 0x9000 | 212 |
| 7.20 | 17 | 0x8800 | 195 |
| 8.36 | 16 | 0x8000 | 169 |
| 9.69 | 15 | 0x7800 | 146 |
| 11.2 | 14 | 0x7000 | 127 |
| 13.0 | 13 | 0x6800 | 110 |
| 15.1 | 12 | 0x6000 | 95 |
| 17.6 | 11 | 0x5800 | 82 |
| 20.4 | 10 | 0x5000 | 72 |
| 23.6 | 9 | 0x4800 | 59 |
| 27.4 | 8 | 0x4000 | 52 |
| 31.8 | 7 | 0x3800 | 46 |
| 36.9 | 6 | 0x3000 | 40 |
| 42.8 | 5 | 0x2800 | 32 |
| 49.7 | 4 | 0x2000 | 28 |
| 57.6 | 3 | 0x1800 | 23 |
| 66.9 | 2 | 0x1000 | 20 |
| 77.6 | 1 | 0x0800 | 18 |
| 90.0 | 0 | 0x0000 | 16 |
配置建议:
- 先用自动校准模式确定合适的IDRIVE
- 再用示波器确认Vosc在1.2V~1.8V之间
- 固定IDRIVE后设置RP_OVERRIDE_EN=1
### DRIVE_CURRENT1 0x1F
> 同DRIVE_CURRENT0
### DRIVE_CURRENT2 0x20
> 同DRIVE_CURRENT0
### DRIVE_CURRENT3 0x21
> 同DRIVE_CURRENT0
## 自动幅度校正寄存器(AUTO_AMPGAIN)
### AUTO_AMPGAIN 0x22
┌─────────────────────────────────────────────────────────┐
│ 位域分布 (16位) │
├─────────────────────────────────────────────────────────┤
│ 15:0 │
├─────────────────────────────────────────────────────────┤
## ID寄存器(只读)
### MANUFACTURER_ID 0x7E
制造商ID
┌─────────────────────────────────────────────────────────┐
│ 值:0x5449 ("TI"的ASCII码) │
└─────────────────────────────────────────────────────────┘
作用:验证设备制造商。
### DEVICE_ID 0x7F
设备ID
┌─────────────────────────────────────────────────────────┐
│ LDC1612值:0x3054 │
│ LDC1614值:0x3056 │
└─────────────────────────────────────────────────────────┘
作用:验证设备型号,可用于I2C通信测试。
+78 -18
View File
@@ -1,11 +1,36 @@
# GD32E23x 工程模板 # LDC1612 传感器固件
本仓库为兆易创新 GD32E23x 系列 MCU 的 CMake + VSCode 工程模板,适合嵌入式开发快速上手和团队协作。 本仓库基于 GD32E23x CMake + VSCode 工程模板(`gd32e23x_template_cmake_vscode`),包含 LDC1612 电感采样、TMP112 温度采集及补偿、RS485 命令处理。当前版本为 V1.0.1。固件采用 Bootloader + APP 布局:APP 链接在 `0x08002000`(54KB Flash / 8KB RAM),启动时自设向量表偏移;开发进度与审查资料见 `doc/` 目录。
## 本地编译
需要 CMake 3.20 或更新版本、Ninja 和 ARM GCC。CMake、Ninja 应在 `PATH` 中;ARM GCC 通过 `GD32_GCC_BIN` 指定,也可在配置时传入 `-DTOOLCHAIN_DIRECTORY="D:/toolchain/arm-gcc/bin"`,该参数优先于环境变量。工具链路径支持包根目录或 `bin` 目录。
在工程根目录运行 PowerShell(工具链路径请替换为本机安装位置):
```powershell
$env:GD32_GCC_BIN = 'D:\toolchain\xpack-arm-none-eabi-gcc-15.2.1-1.1\bin'
cmake --preset Debug
cmake --build --preset Debug
cmake --preset Release
cmake --build --preset Release
```
输出位于 `Build/Debug` 或 `Build/Release`:`Application/Application.elf` 用于调试,`LDC1612_V1.0.1_APP_<日期>.bin` / `.hex` 为应用固件;`Application/Application.hex` 供 J-Link 烧录任务使用。应用固件不含 Bootloader:单独烧录需写到 `0x08002000`;整片烧录(Bootloader + APP + 标志页)用配套的固件合并工具(`gd32e230f8_firmware_merge_tool` 仓库)处理。详见[固件布局与 Bootloader](#固件布局与-bootloader)。
如果切换 ARM GCC 版本,请使用新的构建目录,或通过 CMake Tools 的 “Delete Cache and Reconfigure” 重新配置。需要独立验证目录时可运行:
```powershell
cmake --preset Debug -B Build/local-check/Debug
cmake --build Build/local-check/Debug
```
--- ---
## 目录 ## 目录
- [本地编译](#本地编译)
- [固件布局与 Bootloader](#固件布局与-bootloader)
- [适用范围](#适用范围) - [适用范围](#适用范围)
- [默认配置](#默认配置) - [默认配置](#默认配置)
- [工具链准备](#工具链准备) - [工具链准备](#工具链准备)
@@ -16,6 +41,30 @@
--- ---
## 固件布局与 Bootloader(双固件,2026-10-24 起)
GD32E230F8(64KB Flash)采用与 CeramixNano-MB 相同的双固件(出厂救援)布局:
| 区域 | 地址范围 | 说明 |
|---|---|---|
| Bootloader | `0x08000000` - `0x08001FFF`(8KB,页 0~7) | **专属分支 `bootloader_CeramixNano_EDDY`**(V0.1.0,4,040B,LED=PB1,Ymodem 每烧录一包翻转一次;与 MB 分支并列、版本独立编号)——启动判定链 + RS485 Ymodem OTA;PA14 不占用,升级会话期间 SWD 可用 |
| FW_A 运行槽(本仓库 dev/main) | `0x08002000` - `0x0800AFFF`(36KB,页 8~43) | 唯一 OTA/Ymodem 接收区;链接 `ORIGIN=0x08002000, LENGTH=36K`(上限防溢出 B 槽);槽尾锚点 A 记录 @`0x0800AFFC` |
| FW_B 出厂保底槽(release/golden 分支) | `0x0800B000` - `0x0800FBFF`(19KB,页 44~62) | 出厂救援固件(版本冻结 V1.0.2),OTA 永不擦写;B 记录 @`0x0800FBF8` |
| 末页标志页 | `0x0800FC00` - `0x0800FFFF`(1KB,页 63) | legacy 标志 `0xEEEE` @`0x0800FFFC` |
启动判定:A 记录在位 ⇒ trailer 比对 + 固件体 CRC 复算 + 向量校验 ⇒ 跳 A(不过直落 B,防降级);锚点擦除态 ⇒ legacy flag ⇒ 跳 A;B 记录 CRC 复算通过 ⇒ 跳 B;否则留 BL 等 Ymodem。OTA 中途断电/中止 ⇒ 重启落 B 跑出厂固件,**设备不变砖**。
工程侧的适配点:
- `LD/gd32e23x_flash.ld`:FLASH `ORIGIN = 0x08002000, LENGTH = 36K`;
- `Src/system_gd32e23x.c`:`VECT_TAB_OFFSET = 0x2000`——Bootloader 不写 VTOR,APP 必须自己把向量表指到 `0x08002000`,否则中断会落回 Bootloader 向量表(现象:收到第一个 RS485 字节即死机);
- 出厂保底固件在 `release/golden` 分支构建(`ORIGIN=0x0800B000 / LENGTH=0x4BF8`、`VECT_TAB_OFFSET=0xB000`,版本冻结 V1.0.2,判定值 `v26.1.0.2`);
- 打包(`_UPDATE/_FULL/_FACTORY` + MD5)用 merge tool / `hulk-mcu-firmware` skill,profile `CeramixNano_EDDY_Dual_FW`;产物命名 `CeramixNano-EDDY_*`(板卡码 26)。
⚠️ 与旧版 54K 单槽布局不兼容:需整片烧录一次(FACTORY/FULL 镜像)。RS485 协议、命令集(M1-M6/M999/M9999)与回包格式无任何变化。方案完全体文档见 MB 仓库 `doc/双固件方案_完全体说明.md`;开发约定与实测记录见本项目 `AGENTS.md`。
---
## 适用范围 ## 适用范围
- 适用于兆易创新 GD32E23x 系列 Cortex-M23 内核单片机 - 适用于兆易创新 GD32E23x 系列 Cortex-M23 内核单片机
@@ -34,37 +83,49 @@
### 1. xPack GNU Arm Embedded GCC Toolchain ### 1. xPack GNU Arm Embedded GCC Toolchain
- **版本**:xpack-arm-none-eabi-gcc-11.3.1-1.1 - **原模板版本**:xpack-arm-none-eabi-gcc-11.3.1-1.1;本机编译验证使用 15.2.1-1.1。
- **建议解压路径**:Tools/xpack-arm-none-eabi-gcc-11.3.1-1.1 - **安装位置**:由本机统一管理,不提交到仓库。
- **官方下载地址**:https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases - **官方下载地址**:https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases
- **路径自定义说明**: - **路径自定义说明**:
如需自定义工具链路径,请同步修改以下文件: 编译器与 GDB 均通过 `GD32_GCC_BIN` 环境变量定位(或配置时传 `-DTOOLCHAIN_DIRECTORY`),CMake 文件与 `.vscode` 均无需修改路径。
- `Projects/<BoardName>/<ProjectName>/cmake/arm-none-eabi-gcc.cmake`(第2行)
- `Projects/<BoardName>/<ProjectName>/.vscode/launch.json`(第12行)
### 2. OpenOCD ### 2. OpenOCD(GD-Link 支持)
- **版本**:xpack-openocd-0.11.0-3 - **版本**:xpack-openocd-0.11.0-3
- **建议解压路径**:Tools/xpack-openocd-0.11.0-3
- **获取地址**:https://github.com/burakenez/gd32-tools-xpack-openocd/tree/v0.11.0-3 - **获取地址**:https://github.com/burakenez/gd32-tools-xpack-openocd/tree/v0.11.0-3
- **说明**: - **说明**:
- 本版本提取自 Embedded Builder V1.4.1.23782。 - 本版本提取自 Embedded Builder V1.4.1.23782。
- ⚠️ 请勿随意更换版本,因 GD32 MCU 支持有限,推荐严格使用此版本。 - ⚠️ 请勿随意更换版本,因 GD32 MCU 支持有限,推荐严格使用此版本。
- **路径自定义说明**: - **路径自定义说明**:
如需自定义 OpenOCD 路径,请同步修改以下文件: 通过两个环境变量定位,安装位置任意:
- `Projects/<BoardName>/<ProjectName>/.vscode/launch.json`(第14、17行) - `GD32_OPENOCD_BIN`:`openocd.exe` 所在 `bin` 目录;
- `Projects/<BoardName>/<ProjectName>/.vscode/task.json` 中所有相关路径 - `GD32_OPENOCD_SCRIPTS`:OpenOCD `scripts` 目录。
`.vscode` 的调试配置与烧录任务全部读取这两个变量,无需修改任何文件。
--- ---
## 使用说明 ## 使用说明
1. **准备工具链** 1. **配置环境变量**(用户级,按本机安装位置设置一次即可)
- 按上述说明下载并解压 ARM GCC 和 OpenOCD 到 Tools 目录。
- Toolchain 目录内容不会被 git 跟踪,需自行维护。
2. **烧录固件** | 环境变量 | 指向 | 用途 |
- 可直接使用 VSCode 任务栏的 Flash MCU 任务,或命令行运行 OpenOCD。 |---|---|---|
| `GD32_GCC_BIN` | ARM GCC 的 `bin` 目录 | 编译、GDB 调试 |
| `GD32_OPENOCD_BIN` | OpenOCD 的 `bin` 目录 | 烧录、调试 |
| `GD32_OPENOCD_SCRIPTS` | OpenOCD 的 `scripts` 目录 | 烧录、调试 |
设置后重开终端 / VSCode 生效。`cmd` 示例:`setx GD32_GCC_BIN "D:\toolchain\xpack-arm-none-eabi-gcc-15.2.1-1.1\bin"`。
2. **编译**
命令行见上文[本地编译](#本地编译),或直接用 VSCode 的 CMake Tools 选择 Debug / Release preset 构建。
3. **烧录固件**
- **OpenOCD(GD-Link)**:VSCode 任务 `Flash MCU`(连带编译用 `Build and Flash`);另有 `Reset MCU`、`Mass Erase MCU`、`OpenOCD Server` 任务。
- **J-Link**:任务 `Flash MCU (Jlink)` 等,依赖构建生成的 `Build/<preset>/Application/Application.hex`。
- 器件与接口在 `.vscode/settings.json` 的 `gd32.jlink.*` / `gd32.openocd.*` 中配置,当前为 GD32E230F8 / SWD。
4. **调试**
VSCode 调试配置有两个:`Debug with OpenOCD` 与 `Debug with J-Link`(均使用 cortex-debug,SVD 文件 `GD32E230.svd`,支持外设寄存器查看)。烧录调试器以 GD-Link 为例;J-Link 直接选用对应配置即可。
--- ---
@@ -129,7 +190,6 @@
- **快速上手示例**:如 main.c 的最小点灯/串口输出代码片段。 - **快速上手示例**:如 main.c 的最小点灯/串口输出代码片段。
- **常见问题与解答**:如构建失败、烧录失败的排查建议。 - **常见问题与解答**:如构建失败、烧录失败的排查建议。
- **调试说明**:如何用 VSCode 调试、断点、查看寄存器等。
- **多板卡适配说明**:如有多种硬件,如何切换 BoardName。 - **多板卡适配说明**:如有多种硬件,如何切换 BoardName。
- **贡献指南**:如何提交 PR、代码风格约定等。 - **贡献指南**:如何提交 PR、代码风格约定等。
- **License 说明**:开源协议和版权声明。 - **License 说明**:开源协议和版权声明。
+52
View File
@@ -0,0 +1,52 @@
#include "gd32e23x.h"
#include "board_config.h"
#include "systick.h"
/******************************************************************************/
#define FLASH_SIZE_ADDR (*(const uint8_t *)0x1FFFF7E0) // Flash base address
/******************************************************************************/
/* 前向声明中断处理函数 */
void usart0_irq_handler(void);
void usart1_irq_handler(void);
usart_config_t g_usart_config = {
.rcu_usart = RCU_USART1,
.usart_periph = USART1,
.irq_type = USART1_IRQn,
.irq_handler = usart1_irq_handler // 初始化函数指针
};
uint8_t g_mcu_flash_size = 0;
void mcu_detect_and_config(void) {
g_mcu_flash_size = FLASH_SIZE_ADDR;
switch (g_mcu_flash_size) {
case GD32E23XF4:
g_usart_config.rcu_usart = RCU_USART0;
g_usart_config.usart_periph = USART0;
g_usart_config.irq_type = USART0_IRQn;
g_usart_config.irq_handler = usart0_irq_handler; // 指向USART0处理函数
break;
case GD32E23XF6:
g_usart_config.rcu_usart = RCU_USART1;
g_usart_config.usart_periph = USART1;
g_usart_config.irq_type = USART1_IRQn;
g_usart_config.irq_handler = usart1_irq_handler; // 指向USART1处理函数
break;
default: // Default to GD32E23XF8
g_usart_config.rcu_usart = RCU_USART1;
g_usart_config.usart_periph = USART1;
g_usart_config.irq_type = USART1_IRQn;
g_usart_config.irq_handler = usart1_irq_handler; // 指向USART1处理函数
break;
}
}
uint8_t get_flash_size(void) {
return g_mcu_flash_size;
}
+614
View File
@@ -0,0 +1,614 @@
/**
* @file command.c
* @brief 串口命令解析与处理模块实现
* @details 实现基于状态机的协议解析器,支持 D5 03 LEN [cmd] CRC 格式的命令处理,
* 包含命令帧解析、响应生成和传感器状态管理功能。
* @author Hulk
* @date 2025-08-13
* @version 1.0.0
* @ingroup Command
*/
#include "command.h"
#include "uart_ring_buffer.h"
#include "led.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "board_config.h"
#include "gd32e23x_usart.h"
#include "ldc1612.h"
#include "tmp112.h"
/* ============================================================================
* 协议格式说明
* ============================================================================ */
/**
* @name 协议帧格式
* @{
* @details
* Host -> Device 命令帧格式:
* [0] HEADER = 0xD5 // 包头标识
* [1] BOARD_TYPE = 0x03 // 板卡类型标识
* [2] LEN = 数据区字节数 // 有效载荷长度
* [3..(3+LEN-1)] 数据 // 命令数据,如 "M1", "M2S123"
* [last] CRC = 校验码 // 从索引1到(last-1)的累加和低8位
*
* 最小协议包长度为 6 字节
* 数据示例(两字节命令):"M1" / "M2" / "M3"
*
* Device -> Host 响应帧格式:
* [0] 0xB5 // 响应包头
* [1] TYPE // 响应类型(0xF0=成功,0xF1..=错误类型)
* [2] LEN // 响应数据长度
* [3..(3+LEN-1)] 数据 // 响应数据,如 "ok", "err"
* [last] CRC // 校验码(同命令帧规则)
* @}
*/
/* ============================================================================
* 协议常量定义
* ============================================================================ */
/** @name 协议帧标识符
* @{ */
#define PROTOCOL_PACKAGE_HEADER 0xD5 /**< 命令帧包头标识 */
#define PROTOCOL_BOARD_TYPE 0x03 /**< 板卡类型标识 */
/** @} */
/** @name 命令长度限制
* @{ */
#define COMMAND_MIN_LEN 2 /**< 最小命令长度,如"M1" */
#define PROTOCOL_MIN_FRAME_LEN (3 + COMMAND_MIN_LEN + 1) /**< 最小完整帧长度:header+type+len+payload+crc = 6 */
#define PROTOCOL_MAX_FRAME_LEN 16 /**< 最大完整帧长度 */
/** @} */
/** @name 响应帧标识符
* @{ */
#define RESP_HEADER 0xB5 /**< 响应帧包头标识 */
#define RESP_TYPE_OK 0xF0 /**< 成功响应类型 */
#define RESP_TYPE_CRC_ERR 0xF1 /**< CRC校验错误 */
#define RESP_TYPE_HEADER_ERR 0xF2 /**< 包头错误 */
#define RESP_TYPE_TYPE_ERR 0xF3 /**< 类型错误 */
#define RESP_TYPE_LEN_ERR 0xF4 /**< 长度错误 */
/** @} */
/* 温度补偿参数定义
* TEMP_COMP_REF_TEMP_X10000: 参考温度,单位 0.0001°C
* TEMP_COMP_COEFF_NUMERATOR / TEMP_COMP_COEFF_DENOMINATOR: 温漂系数,默认表示 counts/°C
* 补偿公式: compensated = raw - (temp - ref) * coeff
*/
#define TEMP_COMP_REF_TEMP_X10000 (250000L)
#define TEMP_COMP_COEFF_NUMERATOR (0L)
#define TEMP_COMP_COEFF_DENOMINATOR (10000L)
/* ============================================================================
* 模块内部变量
* ============================================================================ */
/** @brief 传感器周期上报使能标志 */
volatile bool g_eddy_current_sensor_report_enabled = false;
/* Debug output control */
#ifdef COM_DEBUG
#include <stdio.h>
#define COMMAND_DEBUG(fmt, ...) printf("[COMMAND] " fmt "\n", ##__VA_ARGS__)
#else
#define COMMAND_DEBUG(fmt, ...)
#endif
/** @name 预设响应数据
* @{ */
static const uint8_t s_report_status_ok[] = { 'o', 'k' }; /**< 成功响应数据 */
static const uint8_t s_report_status_err[] = { 'e','r','r' }; /**< 错误响应数据 */
/** @} */
static int32_t tmp112_get_temperature_x10000(void);
static int64_t calculate_temperature_compensation_offset(int32_t temperature_x10000);
/* ============================================================================
* 公共接口函数
* ============================================================================ */
/**
* @brief 查询电涡流传感器是否启用周期性传感器上报。
* @return true 表示启用;false 表示禁用。
* @ingroup Command
*/
bool get_eddy_sensor_report_enabled(void)
{
return g_eddy_current_sensor_report_enabled;
}
/**
* @brief 设置电涡流传感器是否启用周期性传感器上报标志。
* @details 本模块内部保存的布尔状态,供其他逻辑决定是否进行周期性数据上报;
* 推荐通过本函数修改而非直接访问全局/静态变量,以便后续扩展(如加锁/回调)。
* @param status true 启用周期上报;false 禁用。
* @ingroup Command
*/
void set_eddy_sensor_report_status(bool status)
{
g_eddy_current_sensor_report_enabled = status;
}
/**
* @brief 计算协议包的 8 位累加校验值(Checksum)。
* @details 对输入缓冲区逐字节累加并取低 8 位,累加范围为 data[1] 至 data[len-2],
* 即不包含包头 HEADER(索引 0)与尾部 CRC 字节(索引 len-1)。
* 当 len 小于最小协议帧长度(PACKAGE_MIN_LENGTH)时返回 0。
* @param data 指向待校验的完整协议包缓冲区。
* @param len 缓冲区总长度(字节),应满足 header + type + len + payload + crc 的最小格式。
* @return uint8_t 计算得到的 8 位校验值。
* @note 本函数实现为简单求和校验(Checksum),非多项式 CRC;与本协议“从索引 1 累加到 len-2”的规则一致。
* @ingroup Command
*/
static uint8_t command_sum_crc_calc(const uint8_t *data, uint8_t len)
{
uint16_t crc = 0;
// 仅在满足协议最小帧长时计算(header + type + len + payload + crc)
if (len < PROTOCOL_MIN_FRAME_LEN) return 0;
// 累加从索引 1 到 len-2 的字节(不含 header 和 crc 字节)
for (uint8_t i = 1; i < (len - 1); i++)
{
crc += data[i];
}
return (uint8_t)(crc & 0xFF);
}
/**
* @brief 发送协议响应帧(使用GD32E230标准库)。
* @details 构造并发送格式为 B5 TYPE LEN [payload] CRC 的响应帧,
* 自动计算CRC校验值并通过串口输出。
* @param type 响应类型码(如 RESP_TYPE_OK, RESP_TYPE_CRC_ERR 等)。
* @param payload 指向响应数据的缓冲区,当len为0时可为NULL。
* @param len 响应数据长度(字节),为0时不复制payload数据。
* @note 内部使用固定大小缓冲区,超长响应将被丢弃。
* @warning 使用GD32E230标准库函数发送,确保串口已正确初始化。
* @ingroup Command
*/
static void send_response(uint8_t type, const uint8_t *payload, uint8_t len)
{
uint8_t buf_len = (uint8_t)(3 + len + 1);
uint8_t buf[16]; // 简单场景足够,必要时可增大
if (buf_len > sizeof(buf)) return; // 防御
buf[0] = RESP_HEADER;
buf[1] = type;
buf[2] = len;
// 简化逻辑:只有当len > 0且payload非空时才复制数据
if (len > 0 && payload != NULL) {
for (uint8_t i = 0; i < len; i++) {
buf[3 + i] = payload[i];
}
}
buf[buf_len - 1] = command_sum_crc_calc(buf, buf_len);
// 使用GD32E230标准库函数逐字节发送(标准库实现)
for (uint8_t i = 0; i < buf_len; i++) {
// 等待发送缓冲区空
while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(RS485_PHY, buf[i]);
}
// 等待发送完成
while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {}
// // 使用printf发送(通过重定向到串口)
// for (uint8_t i = 0; i < buf_len; i++) {
// printf("%c", buf[i]);
// }
// // 刷新缓冲区
// fflush(stdout);
}
/**
* @brief 判断字符是否为十进制数字字符。
* @param c 待检查的字符(ASCII码值)。
* @return bool 判断结果。
* @retval true 字符为 '0' 到 '9' 之间的数字字符。
* @retval false 字符不是十进制数字字符。
* @ingroup Command
*/
static inline bool is_dec_digit(uint8_t c) { return (c >= '0' && c <= '9'); }
/**
* @brief 将一个无符号整数转换为字符串并追加到缓冲区。
* @param value 要转换的数字。
* @param buffer 指向目标缓冲区的指针,转换后的字符串将写入此处。
* @return uint8_t 写入的字符数。
*/
static uint8_t uint_to_str(uint32_t value, char *buffer) {
char temp[10]; // 32位无符号整数最多10位
int i = 0;
if (value == 0) {
buffer[0] = '0';
return 1;
}
// 将数字逆序转换为字符存入临时数组
while (value > 0) {
temp[i++] = (char)((value % 10) + '0');
value /= 10;
}
// 将逆序的字符串反转并存入目标缓冲区
uint8_t len = (uint8_t)i;
for (int j = 0; j < len; j++) {
buffer[j] = temp[--i];
}
return len;
}
/**
* @brief 从缓冲区解析十进制无符号整数。
* @details 从指定位置开始连续读取十进制数字字符,累加构成32位无符号整数。
* 遇到非数字字符或到达长度限制时停止解析。
* @param s 指向待解析字符缓冲区的起始位置。
* @param n 允许解析的最大字符数。
* @param out 输出参数,存储解析结果,可为NULL。
* @return uint8_t 实际消耗的字符数。
* @retval 0 首字符不是数字,解析失败。
* @retval >0 成功解析的数字字符个数。
* @note 不处理符号、空白字符或进制前缀。
* @warning 不进行溢出检查,超出uint32_t范围时按无符号算术溢出处理。
* @ingroup Command
*/
static uint8_t parse_uint_dec(const uint8_t *s, uint8_t n, uint32_t *out)
{
uint8_t i = 0;
uint32_t v = 0;
while (i < n && is_dec_digit(s[i]))
{
v = v * 10u + (uint32_t)(s[i] - '0');
i++;
}
if (i == 0) return 0; // 未读到数字
if (out) *out = v; //
return i;
}
/* ============================================================================
* 命令处理函数
* ============================================================================ */
/**
* @brief 解析并处理完整的命令帧。
* @details 处理经过协议校验的完整命令帧,支持以下命令格式:
* - 无参数命令:M<数字>(如 M1、M2、M10、M201)
* - 带参数命令:M<数字>S<参数>(如 M100S123,参数为十进制)
*
* 支持的命令:
* - M1: 开启LED,启用传感器上报
* - M2: 关闭LED,禁用传感器上报
* - M100S<value>: 设置PWM值(示例)
*
* @param frame 指向完整命令帧的缓冲区(从包头0xD5开始)。
* @param len 命令帧总长度(字节)。
* @note 函数内部进行帧格式校验,格式错误时自动发送错误响应。
* @warning 假设输入帧已通过基本协议校验(包头、类型、CRC等)。
* @ingroup Command
*/
void handle_command(const uint8_t *frame, uint8_t len) {
// 帧格式:D5 03 LEN [cmd] CRC; cmd 支持变长,如 "M1"、"M10"、"M201"、"M123S400",有最小长度限制和命令长度校验
uint8_t cmd_len = frame[2];
if (len < PROTOCOL_MIN_FRAME_LEN || (uint8_t)(3 + cmd_len + 1) != len) return; // 长度不匹配或者小于最小限制
const uint8_t *cmd = &frame[3]; // 提取命令部分
// 命令必须以 'M' 开头
if (cmd[0] != 'M'){
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
// 从 'M' 后开始解析
uint8_t cmd_index = 1;
// 解析M后的十进制数,即命令本体
uint32_t base_cmd = 0;
uint8_t used_base_cmd = parse_uint_dec(&cmd[cmd_index], (cmd_len - cmd_index), &base_cmd);
if (used_base_cmd == 0)
{
// 'M' 后没有数字,格式错误
send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
cmd_index = (uint8_t)(cmd_index + used_base_cmd); // 更新索引到命令后
// 情况A:无附加参数的基础命令
if (cmd_index == cmd_len) {
// 仅基础命令,如 M1, M2, M3
switch (base_cmd) {
case 1u: // M1: enable sensor report
set_eddy_sensor_report_status(true);
return;
case 2u: // M2: disable sensor report
set_eddy_sensor_report_status(false);
send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
return;
case 3u:
eddy_current_report();
return;
case 4u:
temperature_raw_value_report();
return;
case 5u:
eddy_current_compensated_report();
return;
case 6u:
calibration_data_report();
return;
// case 201u: // M201命令
// send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
// return;
/* ==========================================
* M999 输出固件版本号命令
* ========================================== */
case 999u: //M999: 输出固件版本号
{
char version_str[16];
char *p = version_str;
*p++ = 'v';
p += uint_to_str(BOARD_TYPE_CODE, p);
*p++ = '.';
p += uint_to_str(FW_VERSION_MAJOR, p);
*p++ = '.';
p += uint_to_str(FW_VERSION_MINOR, p);
*p++ = '.';
p += uint_to_str(FW_VERSION_PATCH, p);
*p = '\0'; // null-terminate for printf safety
uint8_t n = (uint8_t)(p - version_str);
send_response(RESP_TYPE_OK, (uint8_t *)version_str, n);
COMMAND_DEBUG("Firmware Version: %s", version_str);
}
return;
case 9999u:
__disable_irq();
NVIC_SystemReset();
return;
default:
// 其它无参数命令在此扩展(示例:M100)处理逻辑该如何待定
// send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok));
// return;
break;
}
// 未在处理列表的无参数基础命令,回复错误
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
// 情况B:有附加参数的命令
if (cmd[cmd_index] == 'S') {
cmd_index++;
uint32_t param_value = 0;
const uint8_t used_param_cmd = parse_uint_dec(&cmd[cmd_index], (uint8_t)(cmd_len - cmd_index), &param_value);
if (used_param_cmd == 0) {
// 'S' 后没有数字,格式错误
send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err));
return;
}
switch (base_cmd)
{
// case 100u:
// // set_pwm(param_value);
// printf("Set PWM to %u\n", param_value);
// return;
default:
break;
}
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
}
}
/**
* @brief 处理串口环形缓冲区中的命令数据,解析完整的协议帧。
* @details 本函数实现一个基于状态机的协议解析器,用于处理格式为 D5 03 LEN [cmd] CRC 的命令帧:
* - 状态1:等待包头字节 PROTOCOL_PACKAGE_HEADER (0xD5)
* - 状态2:接收板卡类型字节 PROTOCOL_BOARD_TYPE (0x03)
* - 状态3:接收长度字段并计算期望的完整帧长度
* - 状态4:继续接收剩余数据直到完整帧
* - 状态5:对完整帧进行校验(包头、板卡类型、CRC)并处理
*
* 函数采用非阻塞方式处理,每次调用处理缓冲区中所有可用数据。
* 遇到格式错误、长度异常或校验失败时自动重置状态机。
*
* @note 本函数使用静态变量维护解析状态,因此不可重入。在中断环境中使用需注意并发安全。
* 协议帧最大长度受 PROTOCOL_MAX_FRAME_LEN 限制,超出范围的帧将被丢弃。
*
* @warning 函数依赖 uart_ring_buffer_available() 和 uart_ring_buffer_get()
* 正确实现,若这些函数有缺陷可能导致死循环或数据丢失。
*
* @see handle_command() 用于处理校验通过的完整命令帧
* @see command_sum_crc_calc() 用于计算和校验 CRC 值
* @see send_response() 用于发送错误响应
*
* @ingroup Command
*/
void command_process(void) {
static uint8_t cmd_buf[PROTOCOL_MAX_FRAME_LEN];
static uint8_t cmd_len = 0;
static uint8_t expected_cmd_len = 0; // 0 表示尚未确定总长度
while (uart_ring_buffer_available() > 0) {
int byte = uart_ring_buffer_get();
if (byte < 0) break;
if (cmd_len == 0) {
if ((uint8_t)byte == PROTOCOL_PACKAGE_HEADER) {
cmd_buf[cmd_len++] = (uint8_t)byte;
expected_cmd_len = 0; // 等待进一步字段以确定长度
} else {
// 丢弃非起始字节
}
continue;
}
if (cmd_len >= PROTOCOL_MAX_FRAME_LEN) {
// 防御:缓冲溢出,复位状态机
cmd_len = 0;
expected_cmd_len = 0;
}
// 缓存后续字节
cmd_buf[cmd_len++] = (uint8_t)byte;
// 当到达长度字段(索引 2)后,确定总长度:3 + LEN + 1
if (cmd_len == 3) {
uint8_t payload_len = cmd_buf[2];
expected_cmd_len = (uint8_t)(3 + payload_len + 1);
if (expected_cmd_len > PROTOCOL_MAX_FRAME_LEN) {
// 异常:长度超界,复位状态机
cmd_len = 0;
expected_cmd_len = 0;
}
continue;
}
if (expected_cmd_len > 0 && cmd_len == expected_cmd_len) {
// 到帧尾,进行各项校验
bool verification_status = true;
#ifdef DEBUG_VERBOSE
if (cmd_buf[0] != PROTOCOL_PACKAGE_HEADER) {
send_response(RESP_TYPE_HEADER_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
#endif
if (verification_status && cmd_buf[1] != PROTOCOL_BOARD_TYPE) {
send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
if (verification_status) {
uint8_t crc_calc = command_sum_crc_calc(cmd_buf, expected_cmd_len);
uint8_t crc_recv = cmd_buf[expected_cmd_len - 1];
if (crc_calc != crc_recv) {
send_response(RESP_TYPE_CRC_ERR, s_report_status_err, sizeof(s_report_status_err));
verification_status = false;
}
}
if (verification_status) {
handle_command(cmd_buf, expected_cmd_len);
}
// 复位,等待下一帧
cmd_len = 0;
expected_cmd_len = 0;
}
}
}
void eddy_current_report(void) {
// if (!g_eddy_current_sensor_report_enabled) return;
uint32_t raw_result = ldc1612_get_raw_channel_result(CHANNEL_0);
uint8_t sensor_data[4];
sensor_data[0] = (uint8_t)(raw_result >> 24);
sensor_data[1] = (uint8_t)(raw_result >> 16);
sensor_data[2] = (uint8_t)(raw_result >> 8);
sensor_data[3] = (uint8_t)(raw_result & 0xFF);
send_response(RESP_TYPE_OK, sensor_data, sizeof(sensor_data));
}
static int32_t tmp112_get_temperature_x10000(void)
{
uint8_t value[2] = {0};
int16_t temp_raw;
tmp112a_get_raw_temperature_value(value);
temp_raw = (int16_t)(((uint16_t)value[0] << 8) | value[1]);
temp_raw >>= 4;
return (int32_t)temp_raw * 625;
}
static int64_t calculate_temperature_compensation_offset(int32_t temperature_x10000)
{
int32_t delta_temp_x10000 = temperature_x10000 - TEMP_COMP_REF_TEMP_X10000;
return ((int64_t)delta_temp_x10000 * TEMP_COMP_COEFF_NUMERATOR) /
TEMP_COMP_COEFF_DENOMINATOR;
}
void temperature_raw_value_report(void) {
uint8_t raw_result[4];
int32_t raw_value = tmp112_get_temperature_x10000();
raw_result[0] = (uint8_t)((uint32_t)raw_value >> 24);
raw_result[1] = (uint8_t)((uint32_t)raw_value >> 16);
raw_result[2] = (uint8_t)((uint32_t)raw_value >> 8);
raw_result[3] = (uint8_t)((uint32_t)raw_value & 0xFF);
send_response(RESP_TYPE_OK, raw_result, sizeof(raw_result));
}
void eddy_current_compensated_report(void)
{
uint8_t sensor_data[4];
uint32_t raw_result = ldc1612_get_raw_channel_result(CHANNEL_0);
if ((raw_result & 0xF0000000u) == 0u) {
int32_t temperature_x10000 = tmp112_get_temperature_x10000();
int64_t compensated_value = (int64_t)raw_result -
calculate_temperature_compensation_offset(temperature_x10000);
if (compensated_value < 0) {
raw_result = 0;
} else if (compensated_value > 0x0FFFFFFFLL) {
raw_result = 0x0FFFFFFFu;
} else {
raw_result = (uint32_t)compensated_value;
}
}
sensor_data[0] = (uint8_t)(raw_result >> 24);
sensor_data[1] = (uint8_t)(raw_result >> 16);
sensor_data[2] = (uint8_t)(raw_result >> 8);
sensor_data[3] = (uint8_t)(raw_result & 0xFF);
send_response(RESP_TYPE_OK, sensor_data, sizeof(sensor_data));
}
void calibration_data_report(void)
{
uint8_t payload[9];
uint32_t eddy_raw_value = ldc1612_get_raw_channel_result(CHANNEL_0);
int32_t temperature_raw_value = tmp112_get_temperature_x10000();
payload[0] = (uint8_t)(eddy_raw_value >> 24);
payload[1] = (uint8_t)(eddy_raw_value >> 16);
payload[2] = (uint8_t)(eddy_raw_value >> 8);
payload[3] = (uint8_t)(eddy_raw_value & 0xFF);
payload[4] = 0xFF;
payload[5] = (uint8_t)((uint32_t)temperature_raw_value >> 24);
payload[6] = (uint8_t)((uint32_t)temperature_raw_value >> 16);
payload[7] = (uint8_t)((uint32_t)temperature_raw_value >> 8);
payload[8] = (uint8_t)((uint32_t)temperature_raw_value & 0xFF);
send_response(RESP_TYPE_OK, payload, sizeof(payload));
}
+20 -2
View File
@@ -34,6 +34,10 @@ OF SUCH DAMAGE.
#include "gd32e23x_it.h" #include "gd32e23x_it.h"
#include "systick.h" #include "systick.h"
#include "uart.h"
#include "uart_ring_buffer.h"
#include "led.h"
#include "board_config.h"
/*! /*!
\brief this function handles NMI exception \brief this function handles NMI exception
@@ -93,7 +97,21 @@ void PendSV_Handler(void)
\param[out] none \param[out] none
\retval none \retval none
*/ */
void SysTick_Handler(void) void SysTick_Handler(void) {
{ led_heart_beat(); // LED心跳指示灯
delay_decrement(); delay_decrement();
} }
void USART0_IRQHandler(void) {
// 检查当前配置是否使用USART0,并且函数指针不为空
if(g_usart_config.usart_periph == USART0 && g_usart_config.irq_handler != 0) {
g_usart_config.irq_handler(); // 通过函数指针调用对应的处理函数
}
}
void USART1_IRQHandler(void) {
// 检查当前配置是否使用USART1,并且函数指针不为空
if(g_usart_config.usart_periph == USART1 && g_usart_config.irq_handler != 0) {
g_usart_config.irq_handler(); // 通过函数指针调用对应的处理函数
}
}
+1331
View File
File diff suppressed because it is too large Load Diff
+347
View File
@@ -0,0 +1,347 @@
//
// Created by dell on 24-12-3.
//
#include "ldc1612.h"
#ifdef LDC_DEBUG
#include <stdio.h>
#define LDC1612_DEBUG(fmt, ...) printf("[LDC1612] " fmt "\n", ##__VA_ARGS__)
#else
#define LDC1612_DEBUG(fmt, ...)
#endif
/*!
\brief 写入寄存器
\param[in] reg_addr: 寄存器地址
\param[in] value: 写入值
\param[out] none
\retval i2c_result_t
*/
static i2c_result_t ldc1612_write_register(uint8_t reg_addr, uint16_t value) {
uint8_t data[2];
data[0] = (value >> 8) & 0xFF;
data[1] = value & 0xFF;
return LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, reg_addr, data);
}
/*!
\brief 读取寄存器
\param[in] reg_addr: 寄存器地址
\param[out] value: 读取值指针
\retval i2c_status_t
*/
static i2c_result_t ldc1612_read_register(uint8_t reg_addr, uint16_t *value) {
uint8_t data[2];
i2c_result_t status;
if (value == NULL) {
return I2C_RESULT_INVALID_PARAM;
}
status = LDC1612_IIC_READ_16BITS(LDC1612_ADDR, reg_addr, data);
if (status == I2C_RESULT_SUCCESS) {
*value = ((uint16_t)data[0] << 8) | data[1];
}
return status;
}
/*!
\brief 计算并获取频率分频值
\param[in] channel: 通道号
\param[out] none
\retval 计算得到的频率分频值
*/
static uint16_t ldc1612_calculate_freq_divider(uint8_t channel) {
uint16_t value;
uint16_t fin_div, freq_div;
float sensor_freq;
sensor_freq = 1 / (2 * 3.14 * sqrt(COIL_L_UH * COIL_C_PF * pow(10, -18))) * pow(10, -6);
if (sensor_freq <= 8.75) {
fin_div = LDC1612_FIN_DIV_1;
} else if (sensor_freq <= 17.5) {
fin_div = LDC1612_FIN_DIV_2;
} else if (sensor_freq <= 35.0) {
fin_div = LDC1612_FIN_DIV_4;
} else {
LDC1612_DEBUG("Error: Sensor frequency (%.2f MHz) exceeds maximum limit!", sensor_freq);
return 0;
}
/*
Fref为参考时钟频率,单位MHz,必须小于35MHz,如果输入时钟为外部时钟40MHz,则需要分频
LDC1612_EXT_CLK_MHZ为外部时钟频率,单位MHz
Fin为传感器谐振频率,单位MHz。
必须满足:Fin < Fref / 4
通常高精度应用,采用外部40MHz,2分频,Fin不应超5MHz。
*/
if (LDC1612_EXT_CLK_MHZ >= 35)
{
freq_div = LDC1612_FREF_DIV_2;
} else {
freq_div = LDC1612_FREF_DIV_1;
}
if (sensor_freq >= (LDC1612_EXT_CLK_MHZ / freq_div) / 4)
{
LDC1612_DEBUG("Warning: Sensor frequency (%.2f MHz) is too high for the given reference clock (%.2f MHz)!\n", sensor_freq, (float)(LDC1612_EXT_CLK_MHZ / freq_div));
}
value = LDC1612_CLOCK_DIVIDER_GEN(fin_div, freq_div);
return value;
}
uint16_t ldc1612_get_manufacturer_id(void) {
uint8_t data[2] = {0};
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, READ_MANUFACTURER_ID, data);
return (data[0] << 8) | data[1];
}
uint16_t ldc1612_get_deveice_id(void) {
uint8_t data[2] = {0};
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, READ_DEVICE_ID, data);
return (data[0] << 8) | data[1];
}
/** @brief reset sensor.
* */
ldc1612_status_t ldc1612_reset_sensor(void) {
i2c_result_t state = ldc1612_write_register(SENSOR_RESET_REG, LDC1612_RESET_DEV);
return (state == I2C_RESULT_SUCCESS) ? LDC1612_STATUS_SUCCESS : LDC1612_STATUS_ERROR;
}
ldc1612_status_t ldc1612_init(void) {
i2c_result_t i2c_status;
uint16_t manufacturer_id, device_id;
/* reset LDC1612 sensor */
i2c_status = ldc1612_reset_sensor();
if (i2c_status != I2C_RESULT_SUCCESS) {
return LDC1612_STATUS_ERROR;
}
delay_ms(100);
manufacturer_id = ldc1612_get_manufacturer_id();
device_id = ldc1612_get_deveice_id();
if (manufacturer_id != 0x5449 || device_id != 0x3055) {
return LDC1612_STATUS_ERROR;
}
return LDC1612_STATUS_SUCCESS;
}
/*!
\brief 配置单通道模式
\param[in] channel: 通道号 (0或1)
\param[out] none
\retval ldc1612_status_t
*/
ldc1612_status_t ldc1612_config_single_channel(uint8_t channel) {
i2c_result_t status;
uint16_t freq_divider;
if (channel > 1) {
return LDC1612_STATUS_INVALID_PARAM;
}
freq_divider = ldc1612_calculate_freq_divider(channel);
if (freq_divider == 0) {
return LDC1612_STATUS_INVALID_PARAM;
}
/* 配置顺序严格按照TI官方文档要求 */
/* Step 1: 确保传感器处于睡眠模式 - 配置前必须 */
status = ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SLEEP_MODE);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
delay_ms(10);
/* Step 2: 配置频率分频 - 必须在其他配置之前 */
status = ldc1612_write_register(SET_FREQ_REG_START + channel, freq_divider);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
delay_ms(5);
/* Step 3: 配置LC稳定时间 - 影响测量精度 */
status = ldc1612_write_register(SET_SETTLECOUNT_REG_START + channel, LDC1612_SETTLECOUNT_CH0);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 4: 配置转换时间 - 影响测量速度和精度 */
status = ldc1612_write_register(SET_CONVERSION_TIME_REG_START + channel, LDC1612_RCOUNT_TIME_CH0);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 5: 配置转换偏移 */
status = ldc1612_write_register(SET_CONVERSION_OFFSET_REG_START + channel, SET_CONVERSION_OFFSET_CH0);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 6: 配置驱动电流 - 影响传感器灵敏度 */
status = ldc1612_write_register(SET_DRIVER_CURRENT_REG + channel, LDC1612_DRIVE_CURRENT);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 7: 配置多路复用器 - 设置通道选择和滤波 */
// ldc1612_configure_mux_register(LDC1612_MUX_AUTOSCAN_DISABLE, LDC1612_MUX_RR_SEQUENCE_0, LDC1612_MUX_FILTER_ALL_LOW, LDC1612_MUX_FILTER_NONE);
status = ldc1612_write_register(MUX_CONFIG_REG, LDC1612_MUX_CONFIG);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 8: 配置错误输出 */
status = ldc1612_write_register(ERROR_CONFIG_REG, LDC1612_ERROR_CONFIG_DEFAULT);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 9: 最后启动传感器 - 必须最后一步 */
status = ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SENSOR_CONFIG_CH0);
if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR;
/* Step 10: 等待传感器稳定 */
delay_ms(50);
return LDC1612_STATUS_SUCCESS;
}
/** @brief read the raw channel result from register.
@param channel LDC1612 has total two channels.
@param result raw data
* */
uint32_t ldc1612_get_raw_channel_result(uint8_t channel) {
uint32_t raw_value = 0;
uint8_t value[2] = {0};
/* Read MSW */
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, CONVERSION_RESULT_REG_START + (channel * 2), value);
raw_value |= (uint32_t)(((uint16_t)value[0] << 8) | value[1]) << 16;
/* Read LSW */
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, CONVERSION_RESULT_REG_START + 1 + (channel * 2), value);
raw_value |= (uint32_t)(((uint16_t)value[0] << 8) | value[1]);
uint32_t calibration_value = raw_value & 0x0FFFFFFF;
if (calibration_value == 0x0FFFFFFF) {
return 0xF0000000; /* No coil */
}
if (LDC1612_ERROR_CONFIG_DEFAULT & 0xF800) {
uint8_t error_code = (uint8_t)(raw_value >> 24);
if (error_code & 0x80) return 0x80000000; /* Under range */
if (error_code & 0x40) return 0x40000000; /* Over range */
if (error_code & 0x20) return 0x20000000; /* Watchdog */
if (error_code & 0x10) return 0x10000000; /* Amplitude error */
}
return raw_value;
}
void ldc1612_drvie_current_detect(uint8_t channel) {
uint8_t data[2] = {0};
uint16_t init_value = 0 , drive_current = 0;
ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SLEEP_MODE);
delay_ms(10);
uint16_t freq_divider = ldc1612_calculate_freq_divider(channel);
ldc1612_write_register(SET_FREQ_REG_START + channel, freq_divider);
delay_ms(5);
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, data);
// ldc1612_set_sensor_config(LDC1612_SLEEP_MODE);
ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SLEEP_MODE);
delay_ms(10);
ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SENSOR_CONFIG_CH0);
delay_ms(10);
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, data);
init_value = (((data[0] << 8) | data[1]) >> 6) & 0x1F;
drive_current = (init_value << 11) | 0x0000;
LDC1612_DEBUG("init value: 0x%x\tdrive current: 0x%x\n", init_value, drive_current);
}
/** @brief Get sensor status register
@return Status register value
* */
uint16_t ldc1612_get_sensor_status(void) {
uint8_t data[2] = {0};
LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SENSOR_STATUS_REG, data);
return (data[0] << 8) | data[1];
}
/** @brief Check if data is ready for specific channel
@param channel Channel to check (0 or 1)
@return true if data is ready, false otherwise
* */
bool ldc1612_is_data_ready(uint8_t channel) {
uint16_t status = ldc1612_get_sensor_status();
if (channel == 0) {
return (status & 0x0040) != 0; // DRDY_0 bit
} else if (channel == 1) {
return (status & 0x0080) != 0; // DRDY_1 bit
}
return false;
}
/*!
\brief 检查并记录LDC1612的状态和错误
\param[in] none
\param[out] none
\retval 读取到的原始状态寄存器值
*/
uint16_t ldc1612_check_status_and_log_errors(void) {
uint16_t status;
i2c_result_t i2c_status = ldc1612_read_register(SENSOR_STATUS_REG, &status);
if (i2c_status != I2C_RESULT_SUCCESS) {
LDC1612_DEBUG("Failed to read STATUS register!");
return 0;
}
LDC1612_DEBUG("--- LDC1612 Status Check (Value: 0x%04X) ---", status);
// 检查数据就绪状态
if (status & LDC1612_STATUS_DRDY) {
LDC1612_DEBUG(" [OK] Data is ready.");
}
if (status & LDC1612_STATUS_UNREAD_CH0) {
LDC1612_DEBUG(" [INFO] Channel 0 has unread data.");
}
if (status & LDC1612_STATUS_UNREAD_CH1) {
LDC1612_DEBUG(" [INFO] Channel 1 has unread data.");
}
// 检查是否有任何错误标志
if ((status & 0x3F00) == 0) { // 检查所有错误位的掩码
LDC1612_DEBUG(" [OK] No errors detected.");
} else {
uint8_t err_chan = (status & LDC1612_STATUS_ERR_CHAN_MASK) >> 14;
LDC1612_DEBUG(" [ERROR] An error occurred on Channel %d.", err_chan);
if (status & LDC1612_STATUS_ERR_UR) {
LDC1612_DEBUG(" - Underflow Error: Conversion result is less than OFFSET.");
}
if (status & LDC1612_STATUS_ERR_OR) {
LDC1612_DEBUG(" - Overflow Error: Conversion result is at maximum.");
}
if (status & LDC1612_STATUS_ERR_WD) {
LDC1612_DEBUG(" - Watchdog Timeout: Sensor failed to complete conversion in time.");
}
if (status & LDC1612_STATUS_ERR_AHE) {
LDC1612_DEBUG(" - Amplitude High Error: Sensor oscillation amplitude > 1.8V.");
}
if (status & LDC1612_STATUS_ERR_ALE) {
LDC1612_DEBUG(" - Amplitude Low Error: Sensor oscillation amplitude < 1.2V.");
}
if (status & LDC1612_STATUS_ERR_ZC) {
LDC1612_DEBUG(" - Zero-Count Error: Reference count is zero, check clock.");
}
}
LDC1612_DEBUG("-------------------------------------------------");
// 读取STATUS寄存器会自动清除错误标志,但不会清除DRDY和UNREADCONV标志
return status;
}
+42 -5
View File
@@ -1,20 +1,57 @@
#include "led.h" #include "led.h"
/**
* @brief LED心跳指示灯功能
* @details 实现类似心跳的LED闪烁模式:快闪两次然后暂停
* 适合在SysTick中断中调用,通过计数器控制闪烁节拍
* @note 假设SysTick中断频率为1ms,心跳周期约为2秒
* 心跳模式:亮200ms->灭200ms->亮200ms->灭1400ms(循环)
*/
void led_heart_beat(void)
{
static uint16_t heart_beat_counter = 0;
// 心跳周期:2000ms (假设SysTick为1ms中断)
// 模式:亮200ms -> 灭200ms -> 亮200ms -> 灭1400ms
heart_beat_counter++;
if (heart_beat_counter <= 200) {
// 第一次亮:0-200ms
led_on();
}
else if (heart_beat_counter <= 400) {
// 第一次灭:200-400ms
led_off();
}
else if (heart_beat_counter <= 600) {
// 第二次亮:400-600ms
led_on();
}
else if (heart_beat_counter <= 2000) {
// 长时间灭:600-2000ms
led_off();
}
else {
// 重置计数器,开始新的心跳周期
heart_beat_counter = 0;
}
}
void led_init(void) { void led_init(void) {
rcu_periph_clock_enable(LED_RCU); rcu_periph_clock_enable(LED_RCU);
gpio_mode_set(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_PIN); gpio_mode_set(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_PIN);
gpio_output_options_set(LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED_PIN); gpio_output_options_set(LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED_PIN);
gpio_bit_reset(LED_PORT, LED_PIN);
}
void led_on(void) {
gpio_bit_set(LED_PORT, LED_PIN); gpio_bit_set(LED_PORT, LED_PIN);
} }
void led_off(void) { void led_on(void) {
gpio_bit_reset(LED_PORT, LED_PIN); gpio_bit_reset(LED_PORT, LED_PIN);
} }
void led_off(void) {
gpio_bit_set(LED_PORT, LED_PIN);
}
void led_toggle(void) { void led_toggle(void) {
gpio_bit_toggle(LED_PORT, LED_PIN); gpio_bit_toggle(LED_PORT, LED_PIN);
} }
+58 -9
View File
@@ -36,7 +36,12 @@ OF SUCH DAMAGE.
#include "systick.h" #include "systick.h"
#include "uart.h" #include "uart.h"
#include "led.h" #include "led.h"
#include "command.h"
#include <stdio.h> #include <stdio.h>
#include "i2c.h"
#include "board_config.h"
#include "ldc1612.h"
#include "tmp112.h"
/*! /*!
\brief main function \brief main function
@@ -46,17 +51,61 @@ OF SUCH DAMAGE.
*/ */
int main(void) int main(void)
{ {
systick_config(); // nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x2000);
uart0_init(115200);
// uart1_init(115200); // 如需使用USART1请初始化
// printf("Hello USART0!\r\n");
// uart_set_printf_port(UART_PRINTF_USART1); // 切换printf到USART1
// uart_set_printf_port(UART_PRINTF_BOTH); // 同时输出到USART0和USART1
led_init(); led_init();
mcu_detect_and_config();
setbuf(stdout, NULL);
systick_config();
rs485_init();
// led_init();
// printf("Flash size: %d Kbytes\n", get_flash_size());
#ifdef DEBUG_VERBOSE
char hello_world[] = {"Hello World!\r\n"};
for (uint8_t i = 0; i < sizeof(hello_world); i++)
{
while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(RS485_PHY, hello_world[i]);
}
while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {}
#endif
i2c_config();
#ifdef DEBUG_VERBOSE
i2c_scan();
i2c_bus_reset();
#endif
ldc1612_init();
ldc1612_config_single_channel(CHANNEL_0);
tmp112a_init();
#ifdef EDDY_DRIVE_CURRENT_DETECTION
ldc1612_drvie_current_detect(CHANNEL_0);
#endif
while(1){ while(1){
led_toggle(); #ifndef EDDY_DRIVE_CURRENT_DETECTION
delay_ms(200); command_process();
delay_ms(10);
if (g_eddy_current_sensor_report_enabled)
eddy_current_report();
#else
ldc1612_drvie_current_detect(CHANNEL_0);
delay_ms(1000);
#endif
} }
} }
+234
View File
@@ -0,0 +1,234 @@
//
// Created by dell on 24-12-28.
//
#include "soft_i2c.h"
/*!
\brief delay
\param[in] none
\param[out] none
\retval none
*/
void soft_i2c_delay(void) {
delay_10us(2); // Adjust delay as needed
/* delay to freq
* 15KHz: delay_us(20);
* 65KHz: delay_us(1);
*/
}
/*!
\brief configure the software IIC GPIO
\param[in] none
\param[out] none
\retval none
*/
void soft_i2c_config(void) {
rcu_periph_clock_enable(RCU_GPIO_I2C);
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN);
I2C_SCL_HIGH();
I2C_SDA_HIGH();
}
/*!
\brief generate I2C start signal
\param[in] none
\param[out] none
\retval none
*/
void soft_i2c_start(void) {
I2C_SDA_HIGH();
I2C_SCL_HIGH();
soft_i2c_delay();
I2C_SDA_LOW();
soft_i2c_delay();
I2C_SCL_LOW();
}
/*!
\brief generate I2C stop signal
\param[in] none
\param[out] none
\retval none
*/
void soft_i2c_stop(void) {
I2C_SCL_LOW(); // 确保时钟为低
I2C_SDA_LOW(); // 拉低数据线
soft_i2c_delay();
I2C_SCL_HIGH(); // 拉高时钟
soft_i2c_delay();
I2C_SDA_HIGH(); // 在时钟高电平时拉高数据线产生停止条件
soft_i2c_delay(); // 添加缺失的延时
}
/*!
\brief send I2C ACK signal
\param[in] none
\param[out] none
\retval none
*/
void soft_i2c_send_ack(void) {
// sda_out();
I2C_SDA_LOW();
soft_i2c_delay();
I2C_SCL_HIGH();
soft_i2c_delay();
I2C_SCL_LOW();
soft_i2c_delay();
I2C_SDA_HIGH();
}
/*!
\brief send I2C NACK signal
\param[in] none
\param[out] none
\retval none
*/
void soft_i2c_send_nack(void) {
I2C_SDA_HIGH();
soft_i2c_delay();
I2C_SCL_HIGH();
soft_i2c_delay();
I2C_SCL_LOW();
soft_i2c_delay();
I2C_SDA_HIGH();
}
/*!
\brief wait I2C ACK signal
\param[in] none
\param[out] none
\retval 0: ACK received, 1: ACK not received
*/
uint8_t soft_i2c_wait_ack(void) {
I2C_SDA_HIGH(); // 释放SDA线,让从设备控制
soft_i2c_delay();
I2C_SCL_HIGH(); // 拉高时钟
soft_i2c_delay();
uint8_t ack = !I2C_SDA_READ(); // 读取ACK信号(低电平为ACK)
I2C_SCL_LOW(); // 拉低时钟
soft_i2c_delay(); // 添加缺失的延时
return ack;
}
/*!
\brief send a byte via I2C
\param[in] byte: byte to be sent
\param[out] none
\retval none
*/
void soft_i2c_send_byte(uint8_t byte) {
// sda_out();
for (int i = 0; i < 8; i++) {
if (byte & 0x80) {
I2C_SDA_HIGH();
} else {
I2C_SDA_LOW();
}
byte <<= 1;
soft_i2c_delay();
I2C_SCL_HIGH();
soft_i2c_delay();
I2C_SCL_LOW();
soft_i2c_delay();
}
}
/*!
\brief receive a byte via I2C
\param[in] ack: 1: send ACK, 0: send NACK
\param[out] none
\retval received byte
*/
uint8_t soft_i2c_receive_byte(uint8_t ack) {
uint8_t byte = 0;
I2C_SDA_HIGH();
for (int i = 0; i < 8; i++) {
byte <<= 1;
I2C_SCL_HIGH();
soft_i2c_delay();
if (I2C_SDA_READ()) {
byte |= 0x01;
}
I2C_SCL_LOW();
soft_i2c_delay();
}
if (ack) {
soft_i2c_send_ack();
} else {
soft_i2c_send_nack();
}
return byte;
}
uint8_t soft_i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) {
/* 参数验证 */
if (data == NULL || slave_addr > 0x7F) {
return SOFT_I2C_FAIL;
}
soft_i2c_start();
soft_i2c_send_byte(slave_addr << 1); // 修复:左移1位,添加写位
if (!soft_i2c_wait_ack()) {
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
soft_i2c_send_byte(reg_addr);
if (!soft_i2c_wait_ack()) {
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
soft_i2c_send_byte(data[0]);
if (!soft_i2c_wait_ack()) {
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
soft_i2c_send_byte(data[1]);
if (!soft_i2c_wait_ack()) { // 修复:添加错误处理
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
soft_i2c_stop();
return SOFT_I2C_OK;
}
uint8_t soft_i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data)
{
/* 参数验证 */
if (data == NULL || slave_addr > 0x7F) {
return SOFT_I2C_FAIL;
}
/* 写阶段:发送寄存器地址 */
soft_i2c_start();
soft_i2c_send_byte(slave_addr << 1); // 修复:左移1位,写操作
if (!soft_i2c_wait_ack()) {
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
soft_i2c_send_byte(reg_addr);
if (!soft_i2c_wait_ack()) {
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
/* 读阶段:重新开始并读取数据 */
soft_i2c_start(); // 重新开始
soft_i2c_send_byte((slave_addr << 1) | 0x01); // 修复:正确的读地址
if (!soft_i2c_wait_ack()) {
soft_i2c_stop();
return SOFT_I2C_FAIL;
}
soft_i2c_delay();
data[0] = soft_i2c_receive_byte(1); // 第一个字节发送ACK
data[1] = soft_i2c_receive_byte(0); // 最后一个字节发送NACK
soft_i2c_stop();
return SOFT_I2C_OK;
}
+3 -2
View File
@@ -16,6 +16,7 @@
#include <unistd.h> #include <unistd.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "gd32e23x_usart.h" #include "gd32e23x_usart.h"
#include "board_config.h"
#undef errno #undef errno
extern int errno; extern int errno;
@@ -164,7 +165,7 @@ int _execve(char *name, char **argv, char **env)
// USART0 printf重定向实现 // USART0 printf重定向实现
int __io_putchar(int ch) { int __io_putchar(int ch) {
// 等待发送缓冲区空 // 等待发送缓冲区空
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {} while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {}
usart_data_transmit(USART0, (uint8_t)ch); usart_data_transmit(RS485_PHY, (uint8_t)ch);
return ch; return ch;
} }
+1 -1
View File
@@ -41,7 +41,7 @@
#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */ #define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */
#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */ #define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */
#define VECT_TAB_OFFSET (uint32_t)0x00 /* vector table base offset */ #define VECT_TAB_OFFSET (uint32_t)0x2000 /* vector table base offset */
/* select a system clock by uncommenting the following line */ /* select a system clock by uncommenting the following line */
//#define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL) //#define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL)
+101 -66
View File
@@ -1,83 +1,118 @@
/*! /**
\file systick.c * ************************************************************************
\brief the systick configuration file *
* @file systick.c
\version 2025-02-10, V2.4.0, demo for GD32E23x * @author GD32
*/ * @brief 通过 SysTick 定时器进行微秒级别和毫秒级别的延时函数
*
/* * ************************************************************************
Copyright (c) 2025, GigaDevice Semiconductor Inc. * @copyright Copyright (c) 2024 GD32
* ************************************************************************
Redistribution and use in source and binary forms, with or without modification, */
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "gd32e23x.h" #include "gd32e23x.h"
#include "systick.h" #include "systick.h"
volatile static uint32_t delay; volatile static uint32_t delay_count = 0;
/*! /**
\brief configure systick * ************************************************************************
\param[in] none * @brief 配置 SysTick 定时器
\param[out] none *
\retval none *
*/ * ************************************************************************
*/
void systick_config(void) void systick_config(void)
{ {
/* setup systick timer for 1000Hz interrupts */ //设置了 SysTick 定时器的时钟源为 HCLK
if (SysTick_Config(SystemCoreClock / 1000U)){ systick_clksource_set(SYSTICK_CLKSOURCE_HCLK);
/* capture error */
while (1){ // 配置SysTick为1ms周期中断
} // 注意:SysTick_Config会自动设置时钟源为HCLK,所以需要使用SystemCoreClock/1000
} SysTick_Config(SystemCoreClock / 1000U); // 1ms中断
/* configure the systick handler priority */
NVIC_SetPriority(SysTick_IRQn, 0x00U); NVIC_SetPriority(SysTick_IRQn, 0x00U);
} }
/*! /**
\brief delay a time in milliseconds * ************************************************************************
\param[in] count: count in milliseconds * @brief delay_ms 毫秒延时函数
\param[out] none *
\retval none * @param[in] count 毫秒值
*/ *
* ************************************************************************
*/
void delay_10us(uint32_t count)
{
// 基于系统时钟的简单循环延时
// 这是一个粗略的估计,实际延时可能有偏差 实测10.2us
uint32_t loops_per_10us = SystemCoreClock / 1700000; // 粗略估计,每10微秒的循环次数
for(uint32_t i = 0; i < count; i++) {
for(volatile uint32_t j = 0; j < loops_per_10us; j++);
}
}
/**
* ************************************************************************
* @brief delay_ms 毫秒延时函数
*
* @param[in] count 毫秒值
*
* ************************************************************************
*/
void delay_ms(uint32_t count) void delay_ms(uint32_t count)
{ {
delay = count; delay_count = count; // 设置延时计数
while (delay_count != 0U);
while(0U != delay){
}
} }
/*! /**
\brief delay decrement * ************************************************************************
\param[in] none * @brief 每个 SysTick 中断调用时,减少延时计数
\param[out] none *
\retval none * @param[in] void
*/ *
* ************************************************************************
*/
void delay_decrement(void) void delay_decrement(void)
{ {
if (0U != delay){ if (delay_count != 0U)
delay--; {
delay_count--;
} }
} }
// /**
// * ************************************************************************
// * @brief delay_ms_safe 毫秒延时函数(不干扰SysTick中断)
// * @details 使用简单循环实现延时,不会重新配置SysTick
// * @param[in] count 毫秒值
// * ************************************************************************
// */
// void delay_ms_safe(uint32_t count)
// {
// // 基于系统时钟的简单循环延时
// // 这是一个粗略的估计,实际延时可能有偏差
// uint32_t loops_per_ms = SystemCoreClock / 14000; // 粗略估计
// for(uint32_t i = 0; i < count; i++) {
// for(volatile uint32_t j = 0; j < loops_per_ms; j++);
// }
// }
// /**
// * ************************************************************************
// * @brief delay_us_safe 微秒延时函数(不干扰SysTick中断)
// * @details 使用简单循环实现延时,不会重新配置SysTick
// * @param[in] count 微秒值
// * ************************************************************************
// */
// void delay_us_safe(uint32_t count)
// {
// // 基于系统时钟的简单循环延时
// // 这是一个粗略的估计,实际延时可能有偏差
// uint32_t loops_per_us = SystemCoreClock / 22000000; // 粗略估计,每微秒的循环次数
// for(uint32_t i = 0; i < count; i++) {
// for(volatile uint32_t j = 0; j < loops_per_us; j++);
// }
// }
+328
View File
@@ -0,0 +1,328 @@
//
// Created by dell on 24-12-20.
// TMP112A Temperature Sensor Driver Implementation
//
#include "tmp112.h"
/* Private function prototypes */
static i2c_result_t tmp112a_write_register(uint8_t reg_addr, uint16_t value);
static i2c_result_t tmp112a_read_register(uint8_t reg_addr, uint16_t *value);
static float tmp112a_raw_to_celsius(uint16_t raw_data);
static uint16_t tmp112a_celsius_to_raw(float temperature);
/*!
\brief 初始化TMP112A传感器
\param[in] none
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_init(void) {
i2c_result_t i2c_status;
/* 配置传感器为默认设置 */
i2c_status = tmp112a_config(TMP112A_CONFIG_DEFAULT);
if (i2c_status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 等待配置生效 */
delay_ms(1);
return TMP112A_STATUS_SUCCESS;
}
/*!
\brief 配置TMP112A传感器
\param[in] config: 配置值
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_config(uint16_t config) {
i2c_result_t status = tmp112a_write_register(TMP112A_CONFIG_REG, config);
return (status == I2C_RESULT_SUCCESS) ? TMP112A_STATUS_SUCCESS : TMP112A_STATUS_ERROR;
}
/*!
\brief 读取温度
\param[in] none
\param[out] result: 结果结构体指针
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_read_temperature(tmp112a_result_t *result) {
uint16_t raw_data;
i2c_result_t status;
if (result == NULL) {
return TMP112A_STATUS_INVALID_PARAM;
}
/* 读取温度寄存器 */
status = tmp112a_read_register(TMP112A_TEMP_REG, &raw_data);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 解析温度数据 */
result->raw_data = raw_data;
result->temperature_c = tmp112a_raw_to_celsius(raw_data);
result->temperature_f = result->temperature_c * 9.0f / 5.0f + 32.0f;
/* 检查温度范围 */
if (result->temperature_c < TMP112A_TEMP_MIN || result->temperature_c > TMP112A_TEMP_MAX) {
return TMP112A_STATUS_OUT_OF_RANGE;
}
/* 检查报警标志 */
uint16_t config_reg;
status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg);
if (status == I2C_RESULT_SUCCESS) {
result->alert_flag = (config_reg & TMP112A_CONFIG_AL) ? true : false;
} else {
result->alert_flag = false;
}
return TMP112A_STATUS_SUCCESS;
}
void tmp112a_get_raw_temperature_value(uint8_t *value) {
i2c_read_16bits(TMP112A_ADDR, TMP112A_TEMP_REG, value);
return;
}
/*!
\brief 设置温度阈值
\param[in] low_temp: 低温阈值 (°C)
\param[in] high_temp: 高温阈值 (°C)
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_set_thresholds(float low_temp, float high_temp) {
uint16_t low_raw, high_raw;
i2c_result_t status;
/* 参数验证 */
if (low_temp < TMP112A_TEMP_MIN || low_temp > TMP112A_TEMP_MAX ||
high_temp < TMP112A_TEMP_MIN || high_temp > TMP112A_TEMP_MAX ||
low_temp >= high_temp) {
return TMP112A_STATUS_INVALID_PARAM;
}
/* 转换温度为原始值 */
low_raw = tmp112a_celsius_to_raw(low_temp);
high_raw = tmp112a_celsius_to_raw(high_temp);
/* 写入低温阈值 */
status = tmp112a_write_register(TMP112A_TLOW_REG, low_raw);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 写入高温阈值 */
status = tmp112a_write_register(TMP112A_THIGH_REG, high_raw);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
return TMP112A_STATUS_SUCCESS;
}
/*!
\brief 进入关机模式
\param[in] none
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_shutdown(void) {
uint16_t config_reg;
i2c_result_t status;
/* 读取当前配置 */
status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 设置关机位 */
config_reg |= TMP112A_CONFIG_SD;
/* 写回配置 */
status = tmp112a_write_register(TMP112A_CONFIG_REG, config_reg);
return (status == I2C_RESULT_SUCCESS) ? TMP112A_STATUS_SUCCESS : TMP112A_STATUS_ERROR;
}
/*!
\brief 退出关机模式
\param[in] none
\param[out] none
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_wakeup(void) {
uint16_t config_reg;
i2c_result_t status;
/* 读取当前配置 */
status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 清除关机位 */
config_reg &= ~TMP112A_CONFIG_SD;
/* 写回配置 */
status = tmp112a_write_register(TMP112A_CONFIG_REG, config_reg);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 等待传感器启动 */
delay_ms(1);
return TMP112A_STATUS_SUCCESS;
}
/*!
\brief 单次转换
\param[in] none
\param[out] result: 结果结构体指针
\retval tmp112a_status_t
*/
tmp112a_status_t tmp112a_one_shot(tmp112a_result_t *result) {
uint16_t config_reg;
i2c_result_t status;
uint8_t timeout = 100; // 100ms超时
if (result == NULL) {
return TMP112A_STATUS_INVALID_PARAM;
}
/* 读取当前配置 */
status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 启动单次转换 */
config_reg |= TMP112A_CONFIG_OS;
status = tmp112a_write_register(TMP112A_CONFIG_REG, config_reg);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
/* 等待转换完成 */
do {
delay_ms(1);
status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg);
if (status != I2C_RESULT_SUCCESS) {
return TMP112A_STATUS_ERROR;
}
timeout--;
} while ((config_reg & TMP112A_CONFIG_OS) && timeout > 0);
if (timeout == 0) {
return TMP112A_STATUS_TIMEOUT;
}
/* 读取转换结果 */
return tmp112a_read_temperature(result);
}
/*!
\brief 获取状态字符串
\param[in] status: 状态码
\param[out] none
\retval const char* 状态字符串
*/
const char* tmp112a_get_status_string(tmp112a_status_t status) {
switch (status) {
case TMP112A_STATUS_SUCCESS:
return "SUCCESS";
case TMP112A_STATUS_ERROR:
return "ERROR";
case TMP112A_STATUS_TIMEOUT:
return "TIMEOUT";
case TMP112A_STATUS_INVALID_PARAM:
return "INVALID_PARAM";
case TMP112A_STATUS_OUT_OF_RANGE:
return "OUT_OF_RANGE";
default:
return "UNKNOWN";
}
}
/* Private Functions Implementation */
/*!
\brief 写入寄存器
\param[in] reg_addr: 寄存器地址
\param[in] value: 写入值
\param[out] none
\retval i2c_result_t
*/
static i2c_result_t tmp112a_write_register(uint8_t reg_addr, uint16_t value) {
uint8_t data[2];
data[0] = (value >> 8) & 0xFF;
data[1] = value & 0xFF;
return i2c_write_16bits(TMP112A_ADDR, reg_addr, data);
}
/*!
\brief 读取寄存器
\param[in] reg_addr: 寄存器地址
\param[out] value: 读取值指针
\retval i2c_result_t
*/
static i2c_result_t tmp112a_read_register(uint8_t reg_addr, uint16_t *value) {
uint8_t data[2];
i2c_result_t status;
if (value == NULL) {
return I2C_RESULT_INVALID_PARAM;
}
status = i2c_read_16bits(TMP112A_ADDR, reg_addr, data);
if (status == I2C_RESULT_SUCCESS) {
*value = ((uint16_t)data[0] << 8) | data[1];
}
return status;
}
/*!
\brief 将原始数据转换为摄氏度
\param[in] raw_data: 原始数据
\param[out] none
\retval float 温度值(°C)
*/
static float tmp112a_raw_to_celsius(uint16_t raw_data) {
int16_t temp_raw;
/* TMP112A使用12位分辨率,数据在高12位 */
temp_raw = (int16_t)(raw_data >> 4);
/* 处理负数 */
if (temp_raw & 0x800) {
temp_raw |= 0xF000; // 符号扩展
}
/* 转换为摄氏度 */
return (float)temp_raw * TMP112A_TEMP_RESOLUTION;
}
/*!
\brief 将摄氏度转换为原始数据
\param[in] temperature: 温度值(°C)
\param[out] none
\retval uint16_t 原始数据
*/
static uint16_t tmp112a_celsius_to_raw(float temperature) {
int16_t temp_raw;
/* 转换为原始值 */
temp_raw = (int16_t)(temperature / TMP112A_TEMP_RESOLUTION);
/* 移位到高12位 */
return (uint16_t)(temp_raw << 4);
}
+91 -50
View File
@@ -2,65 +2,106 @@
#include "gd32e23x_usart.h" #include "gd32e23x_usart.h"
#include "gd32e23x_rcu.h" #include "gd32e23x_rcu.h"
#include "gd32e23x_gpio.h" #include "gd32e23x_gpio.h"
#include "board_config.h"
#include "uart_ring_buffer.h"
void uart0_init(uint32_t baudrate) { void rs485_init(void) {
#ifndef RS485_MAX13487
/* 使能 GPIOA 和 USART0 时钟 */ /* 使能 GPIOA 和 USART0 时钟 */
rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RS485_GPIO_RCU);
rcu_periph_clock_enable(RCU_USART0); rcu_periph_clock_enable(RS485_RCU);
/* 配置 PA9 为 USART0_TX,PA10 为 USART0_RX */ /* 配置 PA2 为 USART0_TX,PA3 为 USART0_RX */
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9 | GPIO_PIN_10); gpio_af_set(RS485_GPIO_PORT, GPIO_AF_1, RS485_TX_PIN | RS485_RX_PIN | RS485_EN_PIN);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9 | GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9 | GPIO_PIN_10); gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_TX_PIN | RS485_RX_PIN);
gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, RS485_EN_PIN);
gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_EN_PIN);
/* 配置波特率、数据位、停止位等 */ /* 配置波特率、数据位、停止位等 */
usart_deinit(USART0); usart_deinit(RS485_PHY);
usart_baudrate_set(USART0, baudrate); usart_word_length_set(RS485_PHY, USART_WL_8BIT);
usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_stop_bit_set(RS485_PHY, USART_STB_1BIT);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_parity_config(RS485_PHY, USART_PM_NONE);
usart_enable(USART0); usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
} usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
void uart1_init(uint32_t baudrate) { usart_driver_assertime_config(RS485_PHY, 0x01);
rcu_periph_clock_enable(RCU_GPIOA); usart_driver_deassertime_config(RS485_PHY, 0x10);
rcu_periph_clock_enable(RCU_USART1);
// USART1 默认引脚为 PA2 (TX), PA3 (RX)
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_2 | GPIO_PIN_3);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2 | GPIO_PIN_3);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2 | GPIO_PIN_3);
usart_deinit(USART1);
usart_baudrate_set(USART1, baudrate);
usart_receive_config(USART1, USART_RECEIVE_ENABLE);
usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
usart_enable(USART1);
}
static uart_printf_port_t g_printf_port = UART_PRINTF_USART0; usart_rs485_driver_enable(RS485_PHY);
void uart_set_printf_port(uart_printf_port_t port) { usart_enable(RS485_PHY);
g_printf_port = port;
}
// printf 重定向,支持多串口 nvic_irq_enable(RS485_IRQ, 0);
int __io_putchar(int ch) { usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
switch (g_printf_port) { // usart_interrupt_enable(RS485_PHY, USART_INT_IDLE);
case UART_PRINTF_USART0:
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {} #else
usart_data_transmit(USART0, (uint8_t)ch); rcu_periph_clock_enable(RS485_GPIO_RCU);
break; rcu_periph_clock_enable(RS485_RCU);
case UART_PRINTF_USART1:
while (usart_flag_get(USART1, USART_FLAG_TBE) == RESET) {} gpio_af_set(RS485_GPIO_PORT, GPIO_AF_1, GPIO_PIN_2 | GPIO_PIN_3);
usart_data_transmit(USART1, (uint8_t)ch);
break; /* configure USART Tx&Rx as alternate function push-pull */
case UART_PRINTF_BOTH: gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN);
while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {} gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, RS485_TX_PIN | RS485_RX_PIN);
usart_data_transmit(USART0, (uint8_t)ch);
while (usart_flag_get(USART1, USART_FLAG_TBE) == RESET) {} /* configure RS485 EN Pin */
usart_data_transmit(USART1, (uint8_t)ch); gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_EN_PIN);
break; gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_EN_PIN);
default: gpio_bit_write(RS485_GPIO_PORT, RS485_EN_PIN, SET);
break;
/* USART configure */
usart_deinit(RS485_PHY);
usart_baudrate_set(RS485_PHY, RS485_BAUDRATE);
usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE);
usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE);
usart_enable(RS485_PHY);
nvic_irq_enable(USART0_IRQn, 0);
usart_interrupt_enable(RS485_PHY, USART_INT_RBNE);
usart_interrupt_enable(RS485_PHY, USART_INT_IDLE);
#endif // RS485_MAX13487
}
/******************************************************************************/
/* 具体的中断处理函数实现 */
/******************************************************************************/
void usart0_irq_handler(void) {
// 处理USART0的接收中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART0);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 处理USART0的空闲中断
if(usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
}
}
void usart1_irq_handler(void) {
// 处理USART1的接收中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) {
uint8_t data = usart_data_receive(USART1);
// 使用原有的环形缓冲区处理逻辑
(void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计
}
// 处理USART1的空闲中断
if(usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) {
usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
// 在这里添加空闲中断处理逻辑
} }
return ch;
} }
+104
View File
@@ -0,0 +1,104 @@
/**
* @file uart_ring_buffer.c
* @brief 字节环形接收缓冲区的实现。
* @details 适用于中断接收(写)与主循环解析(读)的典型串口场景;
* 采用“预留一格”区分空/满,最大可用容量为 UART_RX_BUFFER_SIZE-1。
* @ingroup RingBuffer
*/
#include "uart_ring_buffer.h"
static volatile uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE];
static volatile uint8_t write_index = 0;
static volatile uint8_t read_index = 0;
static volatile uint32_t dropped_bytes = 0;
/**
* @brief 重置环形缓冲区状态。
* @details 将读指针、写指针与丢弃计数清零,不清空数据区内容。
* @note 内部工具函数;对外请优先使用 uart_ring_buffer_init()/uart_ring_buffer_clear()。
* @ingroup RingBuffer
*/
static void uart_ring_buffer_reset_state(void) {
write_index = 0;
read_index = 0;
dropped_bytes = 0;
}
/**
* @brief 初始化环形缓冲区。
* @details 调用内部重置逻辑,复位读写索引与丢弃计数,准备接收数据。
* @note 若在中断环境使用,初始化前建议关闭相关接收中断以避免并发竞争。
* @ingroup RingBuffer
*/
void uart_ring_buffer_init(void) {
uart_ring_buffer_reset_state();
}
/**
* @brief 获取当前可读的字节数。
* @details 通过读/写指针的快照计算可读长度,范围为 [0, UART_RX_BUFFER_SIZE-1]。
* @return uint8_t 可读字节数。
* @note 预留一个空槽区分“空/满”,因此满时返回 UART_RX_BUFFER_SIZE-1。
* @ingroup RingBuffer
*/
uint8_t uart_ring_buffer_available(void) {
/* 使用快照减少并发不一致窗口 */
uint8_t w = write_index;
uint8_t r = read_index;
return (uint8_t)((w + UART_RX_BUFFER_SIZE - r) % UART_RX_BUFFER_SIZE);
}
/**
* @brief 从环形缓冲区读取一个字节。
* @details 若缓冲区非空,返回队头字节并推进读指针;若为空,返回 -1。
* @return int 读取到的字节(0..255),或 -1 表示缓冲区为空。
* @ingroup RingBuffer
*/
int uart_ring_buffer_get(void) {
if (read_index == write_index) return -1; // 空
uint8_t data = uart_rx_buffer[read_index];
read_index = (read_index + 1) % UART_RX_BUFFER_SIZE;
return data;
}
/**
* @brief 向环形缓冲区写入一个字节。
* @details 尝试写入一个新字节;若缓冲区已满则丢弃并计数。
* @param data 待写入的字节。
* @return bool 是否写入成功。
* @retval true 写入成功。
* @retval false 写入失败(缓冲区已满,数据被丢弃并计数)。
* @note 如需“覆盖写入”策略,可在满时先推进读指针再写入。
* @ingroup RingBuffer
*/
bool uart_ring_buffer_put(uint8_t data) {
uint8_t next = (write_index + 1) % UART_RX_BUFFER_SIZE;
if (next != read_index) { // 缓冲区未满
uart_rx_buffer[write_index] = data;
write_index = next;
return true;
} else {
/* 缓冲区已满,丢弃新字节并计数 */
dropped_bytes++;
return false;
}
}
/**
* @brief 清空环形缓冲区。
* @details 复位读写索引与丢弃计数,相当于逻辑上丢弃所有已接收数据,不擦除数据区内容。
* @ingroup RingBuffer
*/
void uart_ring_buffer_clear(void) {
uart_ring_buffer_reset_state();
}
/**
* @brief 获取因满而被丢弃的字节累计数量。
* @details 写入时缓冲区满会丢弃新字节并累加计数;该计数在 init/clear 时清零。
* @return uint32_t 丢弃的累计字节数。
* @ingroup RingBuffer
*/
uint32_t uart_ring_buffer_drop_count(void) {
return dropped_bytes;
}
+94 -15
View File
@@ -1,5 +1,61 @@
# You can change TOOLCHAIN_DIRECTORY if you want to use different toolchain. # The project never stores a compiler package. Each machine supplies a path.
set(TOOLCHAIN_DIRECTORY "${CMAKE_SOURCE_DIR}/Toolchain/xpack-arm-none-eabi-gcc-11.3.1-1.1/bin") # TOOLCHAIN_DIRECTORY (CMake cache) has priority over the environment variable.
# GD32_GCC_ENV_VAR lets a workstation use a different variable name if needed.
set(TOOLCHAIN_DIRECTORY "" CACHE PATH "Path to arm-none-eabi toolchain bin directory")
set(GD32_GCC_ENV_VAR "GD32_GCC_BIN" CACHE STRING "Environment variable containing the ARM GCC path")
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES TOOLCHAIN_DIRECTORY GD32_GCC_ENV_VAR)
if(GD32_GCC_ENV_VAR AND NOT GD32_GCC_ENV_VAR MATCHES "^GD32_[A-Za-z0-9_]+$")
message(FATAL_ERROR "GD32_GCC_ENV_VAR must use the GD32_ prefix: ${GD32_GCC_ENV_VAR}")
endif()
if(NOT TOOLCHAIN_DIRECTORY AND GD32_GCC_ENV_VAR)
if(DEFINED ENV{${GD32_GCC_ENV_VAR}} AND NOT "$ENV{${GD32_GCC_ENV_VAR}}" STREQUAL "")
file(TO_CMAKE_PATH "$ENV{${GD32_GCC_ENV_VAR}}" TOOLCHAIN_DIRECTORY)
endif()
endif()
if(NOT TOOLCHAIN_DIRECTORY)
message(FATAL_ERROR
"Cannot find ARM GCC toolchain. Set ${GD32_GCC_ENV_VAR} in the machine environment "
"or pass -DTOOLCHAIN_DIRECTORY=<toolchain-bin>.")
endif()
file(TO_CMAKE_PATH "${TOOLCHAIN_DIRECTORY}" TOOLCHAIN_DIRECTORY)
get_filename_component(TOOLCHAIN_DIRECTORY "${TOOLCHAIN_DIRECTORY}" ABSOLUTE)
if(WIN32)
set(TOOLCHAIN_SUFFIX ".exe")
else()
set(TOOLCHAIN_SUFFIX "")
endif()
# Accept either the package root or its bin directory.
if(NOT EXISTS "${TOOLCHAIN_DIRECTORY}/arm-none-eabi-gcc${TOOLCHAIN_SUFFIX}" AND
EXISTS "${TOOLCHAIN_DIRECTORY}/bin/arm-none-eabi-gcc${TOOLCHAIN_SUFFIX}")
set(TOOLCHAIN_DIRECTORY "${TOOLCHAIN_DIRECTORY}/bin")
endif()
set(_ARM_GCC_EXECUTABLE "${TOOLCHAIN_DIRECTORY}/arm-none-eabi-gcc${TOOLCHAIN_SUFFIX}")
set(_TOOLCHAIN_BINARIES
arm-none-eabi-gcc
arm-none-eabi-g++
arm-none-eabi-gdb
arm-none-eabi-ar
arm-none-eabi-ranlib
arm-none-eabi-as
arm-none-eabi-ld
arm-none-eabi-objcopy
arm-none-eabi-objdump
arm-none-eabi-size
)
foreach(_binary IN LISTS _TOOLCHAIN_BINARIES)
if(NOT EXISTS "${TOOLCHAIN_DIRECTORY}/${_binary}${TOOLCHAIN_SUFFIX}")
message(FATAL_ERROR
"ARM GCC toolchain is incomplete; missing ${_binary}${TOOLCHAIN_SUFFIX} in ${TOOLCHAIN_DIRECTORY}")
endif()
endforeach()
set(CMAKE_C_FLAGS_DEBUG "") set(CMAKE_C_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS_DEBUG "") set(CMAKE_CXX_FLAGS_DEBUG "")
@@ -12,10 +68,6 @@ set(CMAKE_COLOR_DIAGNOSTICS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
if(WIN32)
set(TOOLCHAIN_SUFFIX ".exe")
endif()
set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_SYSTEM_PROCESSOR arm)
@@ -28,21 +80,48 @@ set(FLAGS "-mcpu=cortex-m23 -std=gnu11 -fstack-usage -
set(ASM_FLAGS "-x assembler-with-cpp") set(ASM_FLAGS "-x assembler-with-cpp")
set(CPP_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics") set(CPP_FLAGS "-fno-rtti -fno-exceptions -fno-threadsafe-statics")
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc${TOOLCHAIN_SUFFIX}) set(_REQUESTED_C_COMPILER "${TOOLCHAIN_PREFIX}gcc${TOOLCHAIN_SUFFIX}")
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++${TOOLCHAIN_SUFFIX}) if(DEFINED CMAKE_C_COMPILER AND CMAKE_C_COMPILER AND
set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}gcc${TOOLCHAIN_SUFFIX}) IS_ABSOLUTE "${CMAKE_C_COMPILER}" AND
NOT CMAKE_C_COMPILER STREQUAL "${_REQUESTED_C_COMPILER}")
message(FATAL_ERROR
"The build directory is already configured for a different ARM GCC compiler:\n"
" cached: ${CMAKE_C_COMPILER}\n"
" selected: ${_REQUESTED_C_COMPILER}\n"
"Delete the build directory or run CMake Tools 'Delete Cache and Reconfigure'.")
endif()
set(CMAKE_C_COMPILER "${_REQUESTED_C_COMPILER}")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PREFIX}g++${TOOLCHAIN_SUFFIX}")
set(CMAKE_ASM_COMPILER "${TOOLCHAIN_PREFIX}gcc${TOOLCHAIN_SUFFIX}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS} ${CPP_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS} ${CPP_FLAGS}")
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${FLAGS} ${ASM_FLAGS}") set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${FLAGS} ${ASM_FLAGS}")
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy${TOOLCHAIN_SUFFIX}) set(CMAKE_AR "${TOOLCHAIN_PREFIX}ar${TOOLCHAIN_SUFFIX}")
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size${TOOLCHAIN_SUFFIX}) set(CMAKE_RANLIB "${TOOLCHAIN_PREFIX}ranlib${TOOLCHAIN_SUFFIX}")
set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump${TOOLCHAIN_SUFFIX}) set(CMAKE_OBJCOPY "${TOOLCHAIN_PREFIX}objcopy${TOOLCHAIN_SUFFIX}")
set(CMAKE_AS ${TOOLCHAIN_PREFIX}as${TOOLCHAIN_SUFFIX}) set(CMAKE_SIZE "${TOOLCHAIN_PREFIX}size${TOOLCHAIN_SUFFIX}")
set(CMAKE_LD ${TOOLCHAIN_PREFIX}ld${TOOLCHAIN_SUFFIX}) set(CMAKE_OBJDUMP "${TOOLCHAIN_PREFIX}objdump${TOOLCHAIN_SUFFIX}")
set(CMAKE_AS "${TOOLCHAIN_PREFIX}as${TOOLCHAIN_SUFFIX}")
set(CMAKE_LD "${TOOLCHAIN_PREFIX}ld${TOOLCHAIN_SUFFIX}")
set(CMAKE_LINKER "${TOOLCHAIN_PREFIX}ld${TOOLCHAIN_SUFFIX}")
execute_process(
COMMAND "${_ARM_GCC_EXECUTABLE}" --version
OUTPUT_VARIABLE _ARM_GCC_VERSION_OUTPUT
ERROR_VARIABLE _ARM_GCC_VERSION_ERROR
RESULT_VARIABLE _ARM_GCC_VERSION_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT _ARM_GCC_VERSION_RESULT EQUAL 0)
message(FATAL_ERROR "Unable to execute ${_ARM_GCC_EXECUTABLE}: ${_ARM_GCC_VERSION_ERROR}")
endif()
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" _ARM_GCC_VERSION "${_ARM_GCC_VERSION_OUTPUT}")
message(STATUS "Using ARM GCC ${_ARM_GCC_VERSION} from ${TOOLCHAIN_DIRECTORY}")
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf") set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf") set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+8 -5
View File
@@ -1,16 +1,19 @@
# Project basic info # Project basic info
set(PROJECT_NAME "LDC1612") set(PROJECT_NAME "CeramixNano-EDDY")
set(BOARD_TYPE_CODE 26)
set(VERSION_MAJOR 1) set(VERSION_MAJOR 1)
set(VERSION_MINOR 0) set(VERSION_MINOR 0)
set(VERSION_PATCH 0) set(VERSION_PATCH 4)
set(VERSION "V${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") set(VERSION "V${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
string(TIMESTAMP BUILD_DATE "%Y-%m-%d") string(TIMESTAMP BUILD_DATE "%Y-%m-%d")
# 编译条件(如IIC类型等) # 编译条件(如IIC类型等)
set(IIC_TYPE "AutoDetectDriveCurrent") # Example: HW-IIC_APP / HW-IIC_Bootloader
# set(IIC_TYPE "HW-IIC") # set(BUILD_VARIANT "AutoDetectDriveCurrent")
# set(BUILD_VARIANT "HW-IIC")
set(BUILD_VARIANT "APP")
# 其它自定义宏 # 其它自定义宏
add_definitions(-DIIC_TYPE=${IIC_TYPE}) add_definitions(-DBUILD_VARIANT=${BUILD_VARIANT})
add_definitions(-DPROJECT_VERSION="${VERSION}") add_definitions(-DPROJECT_VERSION="${VERSION}")
add_definitions(-DBUILD_DATE="${BUILD_DATE}") add_definitions(-DBUILD_DATE="${BUILD_DATE}")
+7
View File
@@ -0,0 +1,7 @@
#pragma once
/* Auto-generated from CMake VERSION_* variables. Do not edit directly. */
#define BOARD_TYPE_CODE @BOARD_TYPE_CODE@
#define FW_VERSION_MAJOR @VERSION_MAJOR@
#define FW_VERSION_MINOR @VERSION_MINOR@
#define FW_VERSION_PATCH @VERSION_PATCH@
Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
@echo off
set OPENOCD_SCRIPTS=
start "" "D:\Microsoft VS Code\Code.exe"