Compare commits

..
31 Commits
Author SHA1 Message Date
cbb f3580f38e4 docs(review): 专家评审 2026-10-19 2026-10-20 21:11:29 +08:00
cbb 86ca7666fa feat(motor): 引入运行禁止机制重构电机控制安全系统
- 添加 run_inhibit.h/cpp 模块作为全局运行闸门,统一管理所有停机/唤醒逻辑
- 移除 FOC 控制器中的 5 个相互引用的开关机函数(shut_down/resume 等)
- 将 nFAULT 硬件故障检测从仅上报改为运行门控制,连续采样故障时申请禁止位
- 改进电压检测逻辑,欠压判定现在通过 run_inhibit 统一执行停机动作
- 修正 PB2(nFAULT) 引脚配置为内部上拉输入,避免浮空误读导致系统锁死
- 在 CAN 状态帧中新增 RunInhibit 位图(字节 36-39),实时反映禁止运行的故障源
- 电流采样失效、过热故障现在通过禁止位管理而非直接操作驱动状态
- 校准函数增加运行许可检查,防止在停机状态下意外唤醒驱动
2026-10-13 00:47:52 +08:00
cbb 72cc10b40d chore(gitignore): 忽略 worktree/评审过程稿与 IDE 缓存
变更点:
- 忽略 .worktrees/、.zcode/、SECURITY_AUDIT_REPORT.*、*.html 过程稿
- 本仓代码无业务变更(最近功能改动已在 40fea32 等提交中)
2026-09-20 22:58:17 +08:00
cbb 40fea328b0 feat(can): 反馈报文刷新率逐帧可配置并持久化
STATUS/CONFIG 反馈帧各自独立按 period_ms(1ms 节拍分频,0=停发)发送,
默认 STATUS 1kHz、CONFIG 250Hz,可运行时修改:

- 0x0400 扩展格式(DLC=4):[选择器 u16][值 u16],选择器 0x0000=节点 ID
  (仅单播),0x0100/0x0101=对应帧刷新周期;回 5B ACK,广播只应用不应答
- 0x0401 扩展格式(DLC=2):查询选择器对应值,回 [sel][val](4B);
  DLC<=1 及 SET DLC 1~2 保持旧格式行为完全不变
- 刷新率经 FlashEEPROM 独立 u16 记录(虚拟地址 0x08/0x09)持久化,
  不并入 6 参数 CRC 组,避免老设备升级后整组参数回退默认值
- save_system_config 追加从 RAM 副本回写两条记录,页满 format()
  重建后配置不丢失;read() 增加保留字节校验防 CRC 头部字误匹配
- CANFD_all_Send 拆分为 send_status_frame/send_config_frame,
  初始化失败上报路径直调 STATUS 帧不受分频影响

GATE_DRIVER_DRV8323 0/1 两种板型均编译通过
2026-09-12 19:00:06 +08:00
cbb 0d3bd9cbc6 chore: 忽略 Qoder IDE 生成的 repowiki 缓存目录 2026-09-09 02:54:39 +08:00
cbb 6bdae905d3 chore(rte): 同步 Debug 上下文 RTE_Components.h 生成结果
由 csolution 重新生成,外设组件版本由陈旧的 1.0.0 更正为 1.3.0,与
Release 上下文及构建实际链接的包版本一致(构建目标名 ..._1_3_0)。

该文件头部标注 "CSOLUTION generated file: DO NOT EDIT",此前手工添加的
Doxygen 注释块在重新生成时被覆盖移除,属预期行为。生成器版本戳为 2.12.0,
低于 Release 的 2.13.0,仅记录最后一次生成所用工具版本,不影响构建。
2026-09-09 02:54:20 +08:00
cbb 42dd1adc06 feat(can): CAN-FD 数据段波特率 2Mbps 提升至 5Mbps
数据段改为 prescaler=2、BS1=4TQ、BS2=1TQ:60MHz / (2 × 6TQ) = 5Mbps,
采样点 (1+4)/6 = 83.3%。仲裁段保持 1Mbps(prescaler=6、7TQ+2TQ)不变。

原注释称"数据段 prescaler 必须与非数据段相同",实际 CAN_DBT 的 DBAUDPSC
独立于仲裁段 BAUDPSC,数据段可自取分频,该约束不成立。

TDC 参数维持 filter=4 / offset=4 不变并补注说明:TDCMOD=CALC_AND_OFFSET
下硬件自动测得回路延时 TDCV,TDCO 仅作微调,经验取值约等于数据段 BS1 的
tq 数,不随波特率等比缩放;放大到 12 会使本节点回采错开约 6 个数据位,
自发帧被判为错误帧。
2026-09-09 02:53:34 +08:00
cbb 9fbd9c3d30 refactor(FOC): 移除齿槽转矩补偿功能
对硬编码的两张 128 点补偿表做谐波分解后确认该补偿无效:
- 能量集中在 k=1(11次/转) 16.1% 与 k=2(22次/转) 82.2%,合计 98.3%;
  而 22 极电机的真实齿槽基频应落在槽极 LCM 处(12槽=132次/转 k=12、
  18槽 k=18、24槽 k=24),实测幅值仅 0.46/0.12/0.08 mA,在噪声地板内。
  即表中属于齿槽的成分不足总能量 0.1%,k=1/k=2 实为电角度零位偏差与
  电流采样通道增益/零点失配的特征。
- 表按电角度查表,而电角度含每台独立的 calibration_angle(Flash 读回)。
  相位偏 φ 时残差比为 2sin(kφ/2):零点差 30° 电角度时 k=2 残差即等于
  原幅值,补偿由减小扰动变为增大扰动。22 极下 2.7° 机械偏差即达此量级,
  硬编码全局共用一张表不成立。
- 表采集于 bec7c11(2026-05),其电流链路为改版前方案,此后陆续修正了
  采样时刻落在上管导通窗、PA3/4/5 未配 AIN、零点配平未应用等问题。
  用当时的链路去测量比其自身误差小三个数量级的量,采到的是链路误差。

删除内容:get_cogging_compensation 查表插值、FOC_DEBUG 校准状态机及
其打印函数与静态缓冲、init() 中两张硬编码表、COGGING_TABLE_SIZE/
COGGING_COMP_SCALE、tasks.cpp 调度 tick、RESET 帧 bit4 触发(该位保留)。

六个控制模式的 Uq 输出去掉补偿项;angle_el_cog 仍供 update_el_sincos
使用,改名回 angle_el。mode3 注释中"反转阻力差异由 REV 齿槽补偿表承担"
的描述一并订正(该依赖本已失效)。
2026-09-09 02:53:11 +08:00
cbb 25ed2a2bde feat(adc): 添加三相电流零点配平并扩展初始化错误上报
- ADC: 新增 zero_calibrate() 函数,在上电后采集 200 帧计算各通道零点偏差
- ADC: 修改 current_adc_to_ma() 支持逐通道扣除配平后的零点偏移
- ADC: 调整电流采样引脚为模拟输入模式并优化采样时间至 13.5 周期
- CAN: STATUS 帧字节 32~35 新增 InitStatus 字段,反馈初始化错误位图
- main: 细化初始化错误检测,分别标记角度/ADC/CAN/Kalman/FOC/门驱动故障
- config.h: 新增 INIT_ERR_* 错误码定义及 CURRENT_ZERO_CAL_LIMIT 阈值
2026-09-07 03:34:47 +08:00
cbb 7c4aaa5ee6 feat(FOC): 优化FOC控制算法并修复多个控制环路问题
- 重构cal_Iq_Id函数,移除电角度参数传递,改用预计算的sin/cos值
  以避免重复三角函数计算,提升性能约15-300周期
- 修复角度同步Uq前馈学习中的运动方向判断逻辑,解决反转时的
  震荡问题和正转正常反转震荡的根本原因
- 实现低速缩放因子函数get_mode3_velocity_scale,改进静摩擦
  hunting抑制效果
- 优化电压检测逻辑,将阻塞式ADC读取改为连续模式零等待,
  提升实时性能
- 修复初始化期间电压分母为零的问题,添加下限保护防止P增益
  无穷大
- 实现PID控制器测量微分功能,消除目标阶跃时的微分冲击
- 改进积分抗饱和策略,添加外部饱和泄放功能,优化到位稳定
  性能

perf(Filter): 优化中值滤波算法性能

- 针对ADC_FILTER_SIZE=6的固定场景实现最优排序网络,
  用12次比较替代通用std::nth_element算法,显著提升性能
- 保留通用回退路径处理其他尺寸情况
2026-09-01 02:46:25 +08:00
cbb 76f7987238 feat(FOC): 实现电角度零位自动校准并修复ADC采样干扰问题
- 新增FlashEEPROM用于保存零位校准角,掉电不丢失
- 实现安全的零位校准算法:运行中禁止校准、对齐后重新采样、保存到Flash
- 重构ADC架构:分离ADC0(PWM同步电流采样)和ADC1(VBUS/DRV温度轮询)
- 解决ADC采样冲突:移除ADC0的regular连续扫描,避免与注入组抢占资源
- 修复VBUS电压读取错误:从DMA缓冲区改为直接ADC1单次转换
- 修正开环控制Uq幅值限幅逻辑,保留符号避免反转时抖动
- 更新CAN协议:电流数据改为有符号16位打包,支持负电流传输
- 调整版本号至v26.8.16.7
2026-08-16 02:13:37 +08:00
cbb 42c9f2f669 feat(can): 添加节点ID设置命令的安全防护和改进闪存写入逻辑
- 添加目标节点验证机制,防止SET_NODE_ID命令被错误处理
- 修改节点ID范围验证逻辑,限制为1到0xFF
- 调整闪存写入顺序,在确认写入成功后再发送响应
- 修复SET_NODE_ID消息处理中的边界条件检查

fix(can): 启用自动总线离线恢复功能

- 将CAN参数auto_bus_off_recovery从DISABLE改为ENABLE
- 提高CAN通信的稳定性和自动恢复能力

chore(config): 更新软件版本号并添加编译时间信息

- 将软件补丁版本从24更新到25
- 添加APP_BUILD_DATE和APP_BUILD_TIME宏定义
- 新增getAppBuildTime函数获取编译时间信息
- 添加Flash页大小相关注释说明

perf(debug): 调整ST-Link调试器时钟频率

- 将调试器时钟从10MHz降低到4MHz
- 提高调试连接的稳定性

refactor(static_test): 移除静态测试文件列表

- 删除static_test_automotive_business_20260523/files.csv文件内容
2026-08-13 01:14:07 +08:00
cbb b71fba2016 feat(FOC): 添加角度同步Uq前馈补偿和速度环优化
- 实现update_angle_sync_uq_feedforward函数,支持mode2和mode3的角度同步  Uq前馈补偿,按物理360°查表学习负载/阻力差异
- 实现get_mode3_velocity_p函数,mode3低速时降低速度环P参数减少抖动
- 在velocity_control和angle_control_with_velocity_limit中集成Uq前馈补偿
- 优化normalizeAngle360函数,大角度时先扣整圈数避免while循环过多
- 调整配置参数,降低DEFAULT_vel_loop_p并添加详细的Uq前馈补偿配置
- 更新电源电压参数使用本周期采样值,避免电压滞后影响
2026-05-24 04:56:33 +08:00
cbb e646544f7f refactor(FOC): 重构齿槽转矩校准状态机为独立函数
- 将原本在 calibrate_cogging_tick 中的大函数拆分为多个职责单一的函数
- 新增 start_cogging_calibration、cogging_settling_tick、cogging_sampling_tick、
  cogging_stopping_tick、cogging_done_tick 等状态处理函数
- 添加 process_cogging_run_data、start_next_cogging_run、build_cogging_tables、
  print_cogging_table、print_cogging_c_array 等辅助函数
- 移动全局 FOC 控制器单例注释到更合适位置
- 添加齿槽转矩校准参数常量定义和详细注释
- 添加防御性编程处理默认状态分支,避免状态机卡死
- 提高代码可读性和维护性

feat(tasks): 添加任务头文件到项目配置

- 在 Robot_V2.cproject.yml 中添加 tasks.h 和 tasks.cpp 文件引用

docs(ADC): 添加ADC转换常量说明注释

- 在 ADC.cpp 中添加关于ADC换算常量的详细注释
- 说明全局采样器单例的初始化和使用方式
2026-05-24 01:21:41 +08:00
cbb 4c4e47e9e3 refactor(FOC): 添加Doxygen文档注释并优化SVPWM算法性能
添加了完整的Doxygen文档注释到FOC算法模块的所有函数、类和变量,提升了代码可读性和维护性。同时优化了SVPWM算法实现,将atan2f替换为符号法扇区判断,显著提升计算效率(约15周期vs atan2f 300周期),并改进了arm_sin_cos_f32替代sqrtf提高性能。

feat(FOC): 统一电气角度计算避免重复运算

重构代码以统一电气角度计算,在各个控制循环中只计算一次电气角度并传递给需要的函数,避免了多次重复的fmodf运算和角度归一化操作,提升了实时控制性能。

refactor(Filter): 改进α-β观测器角度处理精度

修改角度卡尔曼滤波器在归一化角度空间[0,360)内工作,避免大累积角度的浮点精度损失问题,确保静止状态下不会产生虚假速度,同时保持与下游代码的兼容性。

refactor: 为算法模块添加完整文档注释

为所有算法模块(FOC、Filter、pid、thermal)添加了详细的Doxygen文档注释,包括函数说明、参数描述、返回值说明等,便于代码理解和API文档生成。
2026-05-23 22:46:32 +08:00
cbb bec7c11b87 feat(FOC): 添加齿槽转矩补偿功能
- 实现齿槽转矩补偿算法,通过查表+线性插值方式计算补偿电压值
- 添加齿槽转矩校准功能,支持正转3次+反转3次的非阻塞校准状态机
- 在所有控制模式中集成齿槽转矩补偿,包括开环、角度、速度、电流和MIT模式
- 硬编码预校准的正反转补偿表数据
- 通过CAN命令支持运行时触发校准功能
- 添加相关配置参数和调试开关
2026-05-19 01:26:42 +08:00
cbb 9955132e0e feat(FOC): 添加速度目标突变检测和积分清零机制
- 在velocity_control()函数中添加目标速度突变检测,当速度变化
  超过5.0f时自动清零积分项
- 在angle_control_with_velocity_limit()函数中添加相同的速度
  突变检测逻辑
- 在MIT_Mode()函数中实现速度环积分清零功能
- 添加reset_integral()方法到PID控制器用于清零积分项

refactor(Filter): 将角度卡尔曼滤波器替换为α-β观测器

- 重构EncoderAngleKalman类,将内部卡尔曼滤波器改为α-β观
  测器算法
- 保留原有的接口名称和参数以维持兼容性
- 添加ANGLE_OBSERVER_ALPHA和ANGLE_OBSERVER_BETA配置参数
- 修改速度获取逻辑,直接从观测器状态获取速度估计值

perf(systick): 实现基于DWT的CPU使用率监测

- 使用DWT周期计数器精确测量任务执行周期
- 添加busy_task_enter()和busy_task_exit()函数追踪忙碌周期
- 实现每秒统计CPU占用率并提供get_cpu_usage()接口
- 在CAN数据包中添加CPU使用率传输字段

fix(Can): 修复CANFD数据包中的整数打包问题

- 将16位整数正确拆分为两个字节进行传输
- 修正了test_u16数据的字节序打包方式

chore(config): 更新软件版本号并优化配置参数

- 将版本号从26.4.26更新到26.5.17
- 调整角度/速度α-β观测器的相关增益参数
2026-05-17 23:40:53 +08:00
cbb e95673fc4e fix(algorithm): 修改FOC控制器中的角度计算和校准参数
修正了FOC控制器中电气角度计算的符号错误(减法改为加法),
移除了不必要的空行,并优化了velocity_open_loop函数中的注释
格式。同时修正了角度控制中PID计算的目标角度差值顺序。

BREAKING CHANGE: 角度校准参数从硬编码值改为配置常量。
2026-05-17 03:54:26 +08:00
cbb d6f784db60 fix(FOC): 修复SVPWM相电压计算中的角度单位转换错误
SVPWM相电压计算中,arm_sin_cos_f32函数期望接收度数而非弧度,
需要将local_angle乘以RAD2DEG进行单位转换。

同时修正了开环速度控制中的电压计算公式,
将固定电压改为基于目标速度的约束电压,
并调整了电机控制频率配置。
2026-05-16 21:22:14 +08:00
cbb c6e00e1a49 feat(ADC): 重构ADC采样系统以支持同步电流采集
- 将ADC缓冲区从6通道减少到3通道,专门用于MCU温度、DRV温度和VBUS监测
- 实现了注入组ADC同步采样,通过TIMER1 TRGO触发三相电流同时采样
- 添加了PWM中心对齐模式以提供更好的采样窗口
- 在FOC控制器中增加了ADC错误检查和三相公共模偏置消除
- 优化了电流采样更新机制,使用中断驱动的数据搬运
- 调整了ADC通道配置,规则组用于后台温度/VBUS采样,注入组用于PWM同步电流采样
2026-05-16 13:09:57 +08:00
cbb 12b2577d6b feat(can): 实现CANFD协议栈及DMA优化
- 添加CANFD DMA接收功能,使用32位宽数据传输提升效率
- 实现CANFD收发队列管理,支持异步消息处理防止丢帧
- 重构CAN中断处理机制,采用FIFO0中断配合DMA完成数据接收
- 添加CANFD DLC长度转换表,规范化数据长度处理
- 实现临界区保护机制,确保多线程环境下的数据安全
- 优化发送队列机制,使用邮箱空闲中断进行消息调度
- 添加DMA错误处理,增强系统稳定性
- 移除原有8字节限制,支持CANFD最大64字节数据传输
2026-05-15 23:04:04 +08:00
cbb 94d5fda0b6 feat: 更新CMSIS和GD32C10x_DFP包版本并优化代码
- 升级ARM::CMSIS从4.5.0到6.1.0版本
- 添加ARM::CMSIS-DSP@1.16.2依赖包
- 升级GigaDevice::GD32C10x_DFP从1.0.1到1.5.0版本
- 移除FOC.cpp中未使用的systick.h头文件
- 移除pid.cpp中未使用的<algorithm>头文件
- 更新GD32C10x固件库至V1.7.0版本(2026-01-30)
- 修复ADC驱动中的无符号整数运算问题
- 优化BKP、CAN、CTC等外设驱动中的FlagStatus返回逻辑
- 改进CAN驱动的消息发送和传输停止函数实现
- 修复CRC、DMA、FMC等驱动中的数据类型和变量声明问题
- 清理和修正各驱动文件中的文档注释
2026-05-06 00:14:16 +08:00
cbb f4fb23d640 feat(FOC): 添加快速电压检测功能和PWM调试模式
新增基于跌幅检测的快速电压保护机制,在100us周期内响应电压异常,
当电压跌幅超过阈值或低于绝对下限时立即关机保护。同时添加PWM输出
调试模式便于硬件测试。

BREAKING CHANGE: 重构了电压检测逻辑,采用峰值跟踪算法替代固
定阈值检测,提高在动态供电环境下的可靠性
2026-04-26 03:36:21 +08:00
cbb bdf03517b3 feat(CAN): 添加静默模式支持并优化节点ID处理
- 添加静默模式功能,在接收到base_id=0x0000消息时停止发送10秒
- 修正节点ID加载逻辑,区分有效节点ID(0-65534)和Flash擦除状态(0xFFFF)
- 添加静默模式相关的状态检查和时间管理功能
- 在CAN通信检查中集成静默模式处理流程

fix(FOC): 修复速度控制中的方向参数应用
- 在velocity_control、angle_control_with_velocity_limit和MIT_Mode函数中
 正确应用DIR参数调整速度误差的符号
- 添加详细注释说明DIR参数对速度误差计算的影响

refactor(systick): 添加毫秒级系统时间功能
- 实现get_systick_ms()函数提供毫秒级系统时间
- 添加SysTick中断处理程序更新系统时间计数器

chore(init): 调整初始化顺序优化系统启动流程
- 将EEPROM初始化移至CAN初始化之前,确保正确的依赖关系
2026-03-19 23:49:08 +08:00
cbb 9f56ab394e feat(CAN): 实现多节点CAN总线通信与节点ID管理功能
- 添加CAN调试宏定义,支持条件编译调试信息输出
- 实现节点ID相关功能,包括构建CAN ID、消息过滤、获取/设置节点ID
- 支持Flash存储节点ID,提供加载和保存功能
- 实现节点ID设置和查询命令处理
- 更新CANFD发送函数使用节点ID构建发送ID
- 添加消息过滤逻辑,只处理目标节点的消息
- 重构DMA中断服务程序,优化数据传输处理
- 替换printf为调试宏,便于生产环境控制调试输出
2026-03-15 02:38:50 +08:00
cbb 2b93a4537c refactor(FOC): 移除代码冲突标记并优化FOC控制器实现
- 移除了所有git冲突标记(<<<<<<< HEAD, =======, >>>>>>>)
- 清理了校准步骤相关注释代码
- 修复了Iq,Id计算中的角度处理逻辑
- 优化了PID控制器参数设置和初始化流程
- 重构了SVPWM相电压设置方法,移除了重复的相电压设置函数
- 修复了关闭功能和电源电压安全限制的计算

feat(CAN): 增强CAN通信协议支持多种控制模式

- 实现了目标电流、速度、角度的多参数控制
- 添加了PID参数动态配置功能
- 支持7种不同控制模式的切换
- 实现了控制模式切换时PID状态重置机制

refactor(pid): 扩展PID控制器功能接口

- 添加了set_P方法替代原有的set_P_percent
- 新增set_limit方法用于输出限幅控制
- 实现了reset方法用于PID状态重置
- 优化了PID参数设置接口

fix(timer): 优化定时器任务调度机制

- 重构了任务调度的时间片管理逻辑
- 实现了基于查找表的任务分配方式
- 优化了100us、1ms、10ms、100ms等不同周期的任务调度
- 提高了系统实时性表现

perf(config): 优化系统配置和数学常量定义

- 添加了必要的GD32头文件包含
- 重新组织了PID参数命名规范
- 定义了常用数学常量如三角函数相关数值
- 优化了定时器频率和周期计算公式
2026-03-09 01:48:12 +08:00
cbb 1ee2f68a89 Merge branch 'master' of http://172.30.0.1:8418/cbb/Robot_v2 2026-03-09 01:47:30 +08:00
cbb a26dd1cc8e feat(can): 增强CAN通信缓冲区管理机制
- 实现循环缓冲区队列管理CAN消息接收
- 添加缓冲区满/空状态检查函数
- 实现消息出队列dequeue_rx_message功能
- 在中断处理程序中添加消息入队列机制
- 优化CAN消息处理流程,支持批量处理

refactor(motor): 优化电机控制类结构和安全机制
- 重命名PID控制器参数变量名以提高可读性
- 添加过热关机标志位thermal_shutdown和相关处理函数
- 实现过热关机shut_down_thermal和过热恢复resume_from_thermal功能
- 调整关机逻辑,添加drv_stop调用确保电机完全停止
- 修正电流环控制中的占空比设置,改为单相保持

feat(motor): 实现FOC控制算法改进和零点校准功能
- 实现电机零点校准函数calibrate_zero_position用于位置补偿
- 修正FOC控制器中的电气角度计算,集成KTH7823校准角度补偿
- 新增d轴电流计算功能
- 重构PID参数初始化方式,使用统一的电压限制配置
- 更新SVPWM相电压设置算法,改进角度归一化处理
- 修改缺省扇区输出占空比从0.0f改为0.5f以提高稳定性

fix(control): 修复控制回路方向和频率计算问题
- 修正角度控制回路中的误差计算方向
- 修正开环速度控制中的频率参数使用
- 修正速度环控制中的误差计算方向
- 修正MIT模式下多级PID控制器的参数设置和误差计算

refactor(filter): 修正编码器速度计算算法
- 移除速度计算中的冗余乘法因子2

feat(pid): 扩展PID控制器功能
- 添加reset方法用于重置PID内部状态

chore(build): 更新开发工具版本和ADC配置
- 更新csolution版本至2.13.0
- 调整ADC采样时间配置以优化性能
2026-03-09 01:35:26 +08:00
cbb 9689841148 feat(FOC): 添加电流卡尔曼滤波器并优化MIT控制模式
- 优化MIT控制模式的角度和速度计算
- 调整多个PID控制器的参数配置
- 增强卡尔曼滤波器的数值稳定性
2026-02-08 18:53:55 +08:00
cbb 173fadadd2 feat(FOC): 重构FOC控制器实现并修复SVPWM算法支持
- 修复类名拼写错误:FOC_Controlroller -> FOC_Controller
- 移除不必要的角度归一化函数,直接使用fmod优化性能
- 添加完整的SVPWM算法实现,提高电机控制效率
- 重构PID参数配置,使用动态参数调整机制
- 增加电流反馈功能,实时监测电机运行状态
- 优化电压安全限制,提高系统稳定性
- 更新控制模式映射,支持更多控制策略
- 改进CAN通信协议,增加电流反馈传输
2026-02-02 02:23:16 +08:00
cbb c9934849c5 feat(FOC): 重构FOC控制器实现并修复SVPWM算法支持
- 修复类名拼写错误:FOC_Controlroller -> FOC_Controller
- 移除不必要的角度归一化函数,直接使用fmod优化性能
- 添加完整的SVPWM算法实现,提高电机控制效率
- 重构PID参数配置,使用动态参数调整机制
- 增加电流反馈功能,实时监测电机运行状态
- 优化电压安全限制,提高系统稳定性
- 更新控制模式映射,支持更多控制策略
- 改进CAN通信协议,增加电流反馈传输
2026-02-02 02:12:14 +08:00
68 changed files with 7695 additions and 2088 deletions
+15
View File
@@ -80,3 +80,18 @@ uv2csolution.log
# legacy intermediate project file
*.cprj
# Qoder IDE generated repowiki cache
.qoder/
# Compose worktrees
.worktrees/
# Multi-agent review process files
.agents/
ORIGINAL_REQUEST.md
.zcode/
# 安全评审过程稿(不入库)
SECURITY_AUDIT_REPORT.*
*.html
+5 -2
View File
@@ -1,8 +1,11 @@
cbuild-pack:
resolved-packs:
- resolved-pack: ARM::CMSIS@4.5.0
- resolved-pack: ARM::CMSIS@6.1.0
selected-by-pack:
- ARM::CMSIS
- resolved-pack: GigaDevice::GD32C10x_DFP@1.0.1
- resolved-pack: ARM::CMSIS-DSP@1.16.2
selected-by-pack:
- ARM::CMSIS-DSP
- resolved-pack: GigaDevice::GD32C10x_DFP@1.5.0
selected-by-pack:
- GigaDevice::GD32C10x_DFP
+1 -1
View File
@@ -33,7 +33,7 @@ solution:
- project-context: Robot_V2.Release
debugger:
name: ST-Link@pyOCD
clock: 10000000
clock: 4000000
protocol: swd
# List of different build configurations.
+631 -167
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/FOC.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "FOC.h"
#include "Can.h"
#include "Filter.h"
@@ -5,59 +11,233 @@
#include "adc.h"
#include "ang_sensor.h"
#include "config.h"
#include "flash.h"
#include "motor.h"
#include "pid.h"
#include "run_inhibit.h"
#include "systick.h"
#include <arm_math.h>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <gd32c10x.h>
FOC_Controlroller FOC; // FOC控制初始化
// 全局 FOC 控制器单例:系统只有一个电机控制对象。
// 初始化顺序由 main::hardware_init() 明确调用 FOC.init() 完成;
// 运行期主要由主循环控制任务访问,CAN 命令只更新目标/参数。
FOC_Controller FOC; // FOC控制初始化
extern sampler sampler;
extern angle_sensor KTH7823;
extern CAN_transmiter CAN0_transmiter;
extern EncoderVelocityKalman Vel_kalman; // 速度滤波
extern CurrentKalman Current_kalman;
extern FlashEEPROM flashEEP;
// 初始变量及函数定义
#define _constrain(amt, low, high) \
((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
/** @brief constrain 宏定义 */
#define constrain(value, min, max, target_min, target_max) \
((value - min) / (max - min) * (target_max - target_min))
/**@
* @brief 归一化角度
*
* @param angle
* @return float
// 校准步骤(代码模板)
/**
* @brief calibrate_zero_position 函数
* @return void
*/
inline float _normalizeAngle(float angle) {
float a = fmod(angle, 360.0f);
// 取余运算可以用于归一化,列出特殊值例子算便知
return a >= 0 ? a : (a + 360.0f);
void calibrate_zero_position(void) {
// 运行中禁止校准:要求控制模式为停止(0)且基本静止,
// 否则转子无法对齐到电气 0°,标定结果错误,甚至因电角度突变导致失控。
// 被禁止运行时同样拒绝:本函数会直接 motor_resume() 唤醒驱动,
// 不判闸门就成了停机态下的旁路通道。
if (FOC.control_mode != 0 || !run_inhibit.permitted() ||
fabsf(FOC.rt_velocity) > CALIB_MAX_SPEED) {
return;
}
// 使能电机驱动器,准备对齐
motor_resume();
// 施加固定电压矢量(A 相高电平),让转子对齐到电气 0° 位置
Set_3Pwm(ALIGN_PWM_DUTY, 0.0f, 0.0f);
delay_1ms(ALIGN_SETTLE_MS);
// 对齐稳定后重新采样传感器角度,再计算零位补偿角,使当前位置对应电气 0°
KTH7823.update();
float current_sensor_angle = KTH7823.getcurrent_sensorangle();
float electrical_angle = fmodf(current_sensor_angle * PP, 360.0f);
KTH7823.set_calibration(-electrical_angle);
// 停止施加电压
Set_3Pwm(0.0f, 0.0f, 0.0f);
// 保存校准角到 Flash,掉电不丢
flashEEP.save_calibration_angle(KTH7823.calibration_angle);
}
// 通过Ia,Ib,Ic计算Iq,Id(目前仅输出Iq)
inline float FOC_Controlroller::cal_Iq_Id(float current_a, float current_b,
/**
* @brief cal_Iq_Id 函数
* @param[in] current_a 参数
* @param[in] current_b 参数
* @param[in] current_c 参数
* @param[in] angle_el 参数
* @return float 返回值
*/
inline float FOC_Controller::cal_Iq_Id(float current_a, float current_b,
float current_c) {
// 计算电气角度
float angle_el = _normalizeAngle(shaft_angle * PP);
if (current_a == ERROR_ADC_VAL || current_b == ERROR_ADC_VAL ||
current_c == ERROR_ADC_VAL) {
return Current_kalman.getCurrent();
}
// 去除三相公共模偏置,使 Ia + Ib + Ic 更接近 0
const float current_avg = (current_a + current_b + current_c) / 3.0f;
current_a -= current_avg;
current_b -= current_avg;
current_c -= current_avg;
float I_alpha, I_beta;
// Clarke变换 (使用三相电流)
// arm_clarke_f32(current_a, current_b, &I_alpha, &I_beta);
float I_alpha, I_beta;
I_alpha = (2.0f / 3.0f) * current_a - (1.0f / 3.0f) * current_b -
(1.0f / 3.0f) * current_c;
I_beta = _1_SQRT3 * current_b - _1_SQRT3 * current_c;
// Park变换
float sin_el, cos_el;
arm_sin_cos_f32(angle_el, &sin_el, &cos_el);
// Park变换:sin/cos 由 update_el_sincos() 每周期算一次,
// 与 setSVPWM_PhaseVoltage 的逆 Park 共用,省一次三角函数
float I_q, I_d;
I_q = I_beta * cos_el - I_alpha * sin_el;
// arm_park_f32(I_alpha, I_beta, &I_d, &I_q, sin_el, cos_el);
I_q = I_beta * el_cos - I_alpha * el_sin;
I_d = I_beta * el_sin + I_alpha * el_cos;
return I_q * 1000.0f -
KE_FACTOR * rt_velocity; // 返回Iq值,乘以100以转换为mA
// 返回Iq值,乘以1000以转换为mA
return Current_kalman.update(I_q * 1000 - KE_FACTOR * rt_velocity);
// return I_q * 1000 - KE_FACTOR * rt_velocity;
}
/**
* @brief 角度同步 Uq 前馈更新/输出。
* @note 只在稳定同向巡航时输出和学习;停止、低速、反向、明显刹车/超速时立即退出。
*/
float FOC_Controller::update_angle_sync_uq_feedforward(
u8 mode, float table[], u16 table_size, float mech_angle,
float target_vel, float actual_vel, float velocity_error) {
if (table_size == 0U) return 0.0f;
bool enable = false;
bool learn_enable = false;
float min_target = 0.0f;
float stop_speed = 0.0f;
float brake_err = 0.0f;
float learn_err_max = 0.0f;
float learn_alpha = 0.0f;
float gain = 0.0f;
float max_ff = 0.0f;
float dir_sign = 1.0f;
switch (mode) {
case 2:
enable = MODE2_UQ_FF_ENABLE;
learn_enable = MODE2_UQ_FF_LEARN_ENABLE;
min_target = MODE2_UQ_FF_MIN_TARGET;
stop_speed = MODE2_UQ_FF_STOP_SPEED;
brake_err = MODE2_UQ_FF_BRAKE_ERR;
learn_err_max = MODE2_UQ_FF_LEARN_ERR_MAX;
learn_alpha = MODE2_UQ_FF_LEARN_ALPHA;
gain = MODE2_UQ_FF_GAIN;
max_ff = MODE2_UQ_FF_MAX;
dir_sign = 1.0f;
break;
case 3:
enable = MODE3_UQ_FF_ENABLE;
learn_enable = MODE3_UQ_FF_LEARN_ENABLE;
min_target = MODE3_UQ_FF_MIN_TARGET;
stop_speed = MODE3_UQ_FF_STOP_SPEED;
brake_err = MODE3_UQ_FF_BRAKE_ERR;
learn_err_max = MODE3_UQ_FF_LEARN_ERR_MAX;
learn_alpha = MODE3_UQ_FF_LEARN_ALPHA;
gain = MODE3_UQ_FF_GAIN;
max_ff = MODE3_UQ_FF_MAX;
dir_sign = DIR;
break;
default:
return 0.0f;
}
// 超速/制动判定必须沿"运动方向":velocity_error×dir_sign 是 PID 系误差,
// 再乘运动方向符号后,< -brake_err 才表示"沿运动方向明显超速"。
// 原实现只乘 dir_sign:反转(负速度)时判定极性恰好相反——反转超速时
// 前馈继续输出、反转欠速时前馈反而被切断,已学到的电压(可达±MAX)
// 随误差符号通断,形成正转没有、反转独有的 bang-bang 震荡。
const float motion_sign = (target_vel * dir_sign >= 0.0f) ? 1.0f : -1.0f;
const bool active = (fabsf(target_vel) > stop_speed) &&
(fabsf(actual_vel) > stop_speed) &&
((target_vel * actual_vel) > 0.0f) &&
((velocity_error * dir_sign * motion_sign) > -brake_err);
float angle = normalizeAngle360(mech_angle);
float index_f = angle / 360.0f * (float)table_size;
u16 idx0 = (u16)index_f;
if (idx0 >= table_size) idx0 = table_size - 1U;
u16 idx1 = (idx0 + 1U) % table_size;
float frac = index_f - (float)idx0;
float ff = table[idx0] * (1.0f - frac) + table[idx1] * frac;
if (ff > max_ff) ff = max_ff;
else if (ff < -max_ff) ff = -max_ff;
// 学习仅在误差带内进行:启停/换向瞬态误差可达上百 °/s,直接学习会把
// ±MAX 钳位值写进途经的角度桶,下一轮运动经过该角度时突加满幅前馈
// 电压,本身就是一次转矩扰动。
if (learn_enable && active && fabsf(target_vel) > min_target &&
fabsf(velocity_error * dir_sign) < learn_err_max) {
float ff_target = velocity_error * dir_sign * gain;
if (ff_target > max_ff) ff_target = max_ff;
else if (ff_target < -max_ff) ff_target = -max_ff;
table[idx0] += learn_alpha * (ff_target - table[idx0]);
}
return (enable && active && fabsf(target_vel) > min_target) ? ff : 0.0f;
}
/**
* @brief mode3 低速缩放因子:静止附近降到 min_scale,速度升高后线性恢复到 1。
* @note P/I 使用各自的地板(I 地板更低):低速段保持"比例主导",
* 积分充电足够慢,压制静摩擦 hunting(停下瞬间小幅来回抖动)。
* D 不衰减,保持阻尼。
*/
float FOC_Controller::get_mode3_velocity_scale(float target_vel,
float actual_vel,
float min_scale) {
if (!MODE3_LOW_SPEED_P_SCALE_ENABLE) {
return 1.0f;
}
float abs_target = fabsf(target_vel);
float abs_speed = fabsf(actual_vel);
float speed_ref = (abs_target > abs_speed) ? abs_target : abs_speed;
if (speed_ref <= MODE3_LOW_SPEED_P_LOW) {
return min_scale;
}
if (speed_ref < MODE3_LOW_SPEED_P_FULL) {
return min_scale +
(1.0f - min_scale) * (speed_ref - MODE3_LOW_SPEED_P_LOW) /
(MODE3_LOW_SPEED_P_FULL - MODE3_LOW_SPEED_P_LOW);
}
return 1.0f;
}
/**
* @brief mode3 速度内环 P:低速缩放因子(方向无关)。
* @note 历史上曾有"反转 P 提升到 ×2"的增益调度(先是 −120/−30 硬台阶,
* 后改为连续线性过渡):连续化后反转仍剧烈震荡,说明问题不在
* 台阶突跳,而在增益本身——×2 只对反转生效,把反转环路增益整体
* 翻倍而正转不变,正是"正转正常、反转震荡"的直接来源之一。
* 已移除。反转阻力差异由 Uq 前馈学习承担;
* 若实测反转欠速/偏慢,优先增大前馈学习限幅,不要恢复非对称 P。
*/
float FOC_Controller::get_mode3_velocity_p(float target_vel, float actual_vel) {
return vel_angloop_p * get_mode3_velocity_scale(target_vel, actual_vel,
MODE3_LOW_SPEED_P_MIN_SCALE);
}
/**
@@ -65,20 +245,26 @@ inline float FOC_Controlroller::cal_Iq_Id(float current_a, float current_b,
*
* @return u8 0表示初始化成功,1表示初始化失败
*/
u8 FOC_Controlroller::init() {
u8 FOC_Controller::init() {
motor_init(); // 电机驱动初始化
vel_loop.set_PID(vel_loop_p * 100 / limit_voltage, 0.20f, 5e-5); // 设置电机速度控制参数
angle_loop.set_PID(angle_loop_p * 100 / limit_voltage, 100.0f,
0.2f); // 设置电机角度控制参数
current_loop.set_PID(current_loop_p * 100 / limit_voltage, 5e-7f,
7e-8); // 设置电机电流控制参数
vel_inVelAngloop.set_PID(vel_angloop_p * 100 / limit_voltage, 3.0f,
5e-4); // 设置电机速度控制参数
ang_inVelAngloop.set_PID(ang_angloop_p * 100 / limit_voltage, 0.0f,
5e-4); // 设置电机角度控制参数
// limit_voltage 从 Flash/CAN 恢复,可能为 0:分母下限保护,防止 P=inf
const float denom = (limit_voltage > MIN_LIMIT_VOLTAGE)
? limit_voltage
: voltage_power_supply;
FOC.cur_loop_p = DEFAULT_current_loop_p * 100 / denom;
FOC.cur_MIT_p = DEFAULT_current_MIT_p * 100 / denom;
FOC.vel_loop_p = DEFAULT_vel_loop_p * 100 / denom;
FOC.vel_angloop_p = DEFAULT_vel_angloop_p * 100;
FOC.vel_MIT_p = DEFAULT_vel_MIT_p * 100;
FOC.ang_loop_p = DEFAULT_angle_loop_p * 100 / denom;
FOC.ang_angloop_p = DEFAULT_angle_angloop_p * 100 / denom;
FOC.ang_MIT_p = DEFAULT_angle_MIT_p * 100 / denom;
// 运行期不每周期 set_P 的环路(mode1/mode5)在此同步一次有效增益,
// 避免沿用构造期的临时值
angle_loop.set_P(ang_loop_p);
current_loop.set_P(cur_loop_p);
return 0;
}
@@ -86,90 +272,229 @@ u8 FOC_Controlroller::init() {
* @brief 电机控制类构造函数
*
*/
FOC_Controlroller::FOC_Controlroller() { ; }
FOC_Controller::FOC_Controller() { ; }
/**
* @brief 设置电机输出,使用优化的SVPWM算法
* @brief 每控制周期调用一次:计算当前电角度的 sin/cos。
* @note cal_Iq_Id 的 Park 变换与 setSVPWM_PhaseVoltage 的逆 Park 变换
* 使用同一电角度,三角函数每周期只算这一次,两处共用。
*/
inline void FOC_Controller::update_el_sincos(float angle_el) {
arm_sin_cos_f32(angle_el, &el_sin, &el_cos);
}
/**
* @brief 使用SVPWM算法设置电机输出
*
* @param Uq: q轴电压
* @param Ud: d轴电压
* @param angle_el: 电机轴角度
*
* 优化:atan2f → X/Y/Z 符号法判扇区(~15周期 vs atan2f ~300周期)
* 边界上扇区编号可能与 atan2f 差1,但 duty 输出完全一致(已数值验证)
* 逆 Park 所需 sin/cos 由 update_el_sincos() 预计算,不再重复计算
*/
inline void FOC_Controlroller::setPhaseVoltage(float Uq, float Ud) {
// 计算电气角度
float angle_el = _normalizeAngle(shaft_angle * PP);
inline void FOC_Controller::setSVPWM_PhaseVoltage(float Uq, float Ud) {
// ① 逆Park变换(sin/cos 为本周期预计算值)
float Ualpha = Ud * el_cos - Uq * el_sin;
float Ubeta = Ud * el_sin + Uq * el_cos;
// 帕克逆变换 (直接计算,避免函数调用开销)
float sin_el, cos_el;
arm_sin_cos_f32(angle_el, &sin_el, &cos_el);
float Ualpha = Ud * cos_el - Uq * sin_el;
float Ubeta = Ud * sin_el + Uq * cos_el;
// ② 扇区判定(替代 atan2f)
float X = Ubeta;
float Y = SQRT3DV2 * Ualpha - 0.5f * Ubeta;
float Z = -SQRT3DV2 * Ualpha - 0.5f * Ubeta;
// 克拉克逆变换 (使用预计算常量优化)
Ua = Ualpha + v_half;
Ub = sqrt3dv2 * Ubeta - 0.5f * Ualpha + v_half;
Uc = -0.5f * Ualpha - sqrt3dv2 * Ubeta + v_half;
u8 sector_code = (X > 0.0f ? 1u : 0u)
+ (Y > 0.0f ? 2u : 0u)
+ (Z > 0.0f ? 4u : 0u);
// 计算占空比并限制范围
duty_a = _constrain(Ua / v_safe, 0.0f, 1.0f);
duty_b = _constrain(Ub / v_safe, 0.0f, 1.0f);
duty_c = _constrain(Uc / v_safe, 0.0f, 1.0f);
// 设置PWM输出
float voltage_exchange = limit_voltage / v_safe;
duty_a = duty_a * voltage_exchange;
duty_b = duty_b * voltage_exchange;
duty_c = duty_c * voltage_exchange;
// 编码 → 扇区 查找表(由 atan2f 扇区定义反推,全角度数值验证通过)
static const u8 sector_lut[8] = {0, 2, 6, 1, 4, 3, 5, 0};
u8 sector = sector_lut[sector_code];
if (sector == 0u) {
Set_3Pwm(0.5f, 0.5f, 0.5f);
return;
}
// ③ 模长与调制比(arm_sqrt_f32 替代 sqrtf,~14周期 vs ~100周期)
float magnitude;
arm_sqrt_f32(Ualpha * Ualpha + Ubeta * Ubeta, &magnitude);
float inv_Udc = 1.0f / voltage_power_supply;
float modulation_index = magnitude * inv_Udc;
// 电压限制
if (limit_voltage > voltage_power_supply) {
limit_voltage = voltage_power_supply;
}
float limit_percent = limit_voltage / v_safe;
// ④ T1/T2 计算(替代 atan2f + 第二次 arm_sin_cos_f32)
// 用预计算系数表直接从 Ualpha/Ubeta 算出
static const float svpwm_coeff[6][4] = {
{ SQRT3DV2, -0.5f, 0.0f, 1.0f}, // sector 1
{ SQRT3DV2, 0.5f, -SQRT3DV2, 0.5f}, // sector 2
{ 0.0f, 1.0f, -SQRT3DV2, -0.5f}, // sector 3
{ -SQRT3DV2, 0.5f, 0.0f, -1.0f}, // sector 4
{ -SQRT3DV2, -0.5f, SQRT3DV2, -0.5f}, // sector 5
{ 0.0f, -1.0f, SQRT3DV2, 0.5f}, // sector 6
};
float T1 = (svpwm_coeff[sector-1][0] * Ualpha + svpwm_coeff[sector-1][1] * Ubeta) * inv_Udc;
float T2 = (svpwm_coeff[sector-1][2] * Ualpha + svpwm_coeff[sector-1][3] * Ubeta) * inv_Udc;
// 过调制限制:等比缩放 T1/T2
if (modulation_index > limit_percent) {
float scale = limit_percent / modulation_index;
T1 *= scale;
T2 *= scale;
}
float T0 = 1.0f - T1 - T2;
// ⑤ 占空比计算(不变)
switch (sector) {
case 1:
duty_a = 0.5f * T0 + T1 + T2;
duty_b = 0.5f * T0 + T2;
duty_c = 0.5f * T0;
break;
case 2:
duty_a = 0.5f * T0 + T1;
duty_b = 0.5f * T0 + T1 + T2;
duty_c = 0.5f * T0;
break;
case 3:
duty_a = 0.5f * T0;
duty_b = 0.5f * T0 + T1 + T2;
duty_c = 0.5f * T0 + T2;
break;
case 4:
duty_a = 0.5f * T0;
duty_b = 0.5f * T0 + T1;
duty_c = 0.5f * T0 + T1 + T2;
break;
case 5:
duty_a = 0.5f * T0 + T2;
duty_b = 0.5f * T0;
duty_c = 0.5f * T0 + T1 + T2;
break;
case 6:
duty_a = 0.5f * T0 + T1 + T2;
duty_b = 0.5f * T0;
duty_c = 0.5f * T0 + T1;
break;
default:
duty_a = 0.5f;
duty_b = 0.5f;
duty_c = 0.5f;
break;
}
Set_3Pwm(duty_a, duty_b, duty_c);
}
/**
* @brief 电机关机
*
*/
void FOC_Controlroller::shut_down() {
is_shut_down = 1;
motor_sleep();
}
/**
* @brief 占空比置0
*
*/
void FOC_Controlroller::drv_stop() { Set_3Pwm(0.0f, 0.0f, 0.0f); }
/**
* @brief 电机锁止
*/
float FOC_Controlroller::drv_hold() {
float FOC_Controller::drv_hold() {
float hold_pwm_duty = HOLD_PWM_DUTY;
if (HOLD_PWM_DUTY > 0.95f) {
hold_pwm_duty = 0.95f;
} else if (HOLD_PWM_DUTY < 0.0f) {
hold_pwm_duty = 0.0f;
}
Set_3Pwm(hold_pwm_duty, 0, 0);
return hold_pwm_duty;
}
/**
* @brief PWM模式(调试)
*/
float FOC_Controller::PWM_out() {
float hold_pwm_duty = target_velocity/100;
Set_3Pwm(hold_pwm_duty, hold_pwm_duty, hold_pwm_duty);
return hold_pwm_duty;
}
/**
* @brief 电机恢复
*
*/
void FOC_Controlroller::resume() {
over_current = false;
voltage_error = false;
is_shut_down = false;
motor_resume();
}
/**
* @brief 更新电源电压
*
* @note 读数不在合理区间(含采样链给的 ERROR_ADC_VAL 哨兵值)时保留上次值,
* 不更新。这里不再置"电压故障"标志:欠压判定与停机都在
* fast_voltage_check() → run_inhibit 那条路上做。
*/
void FOC_Controlroller::update_power(void) {
voltage_power_supply = sampler.get_vbus_voltage();
if (voltage_power_supply < 36.0f and voltage_power_supply > 8.0f) {
voltage_error = false;
v_half = voltage_power_supply * 0.5f;
v_safe = voltage_power_supply * 0.95f;
void FOC_Controller::update_power(float voltage) {
if (voltage < 36.0f and voltage > 8.0f) {
voltage_power_supply = voltage;
v_half = voltage * 0.5f;
v_safe = voltage * 1.1f;
}
}
/**
* @brief 快速电压检测(在电机控制循环中每100us调用一次)
*
* 直接从DMA缓冲区读取最新ADC值,检测电压跌幅
* 响应时间 < 200us
*
* 算法:
* 1. 使用峰值检测算法跟踪当前额定电压(vbus_reference)
* - 电压上升时,基准电压跟随
* - 电压下跌时,基准电压保持(不跟随下跌)
* 2. 计算电压跌幅 = 基准电压 - 当前电压
* 3. 关机条件(满足任一即可):
* - 电压跌幅超过 VBUS_DROP_THRESHOLD
* - 电压低于绝对下限 VBUS_ABSOLUTE_MIN
*
* 参数定义在 config.h 中:
* - VBUS_DROP_THRESHOLD: 电压跌幅阈值 (默认5V)
* - VBUS_ABSOLUTE_MIN: 绝对最低电压 (默认8V)
* - VBUS_DEBOUNCE_COUNT: 防抖计数 (默认5次)
*/
/**
* @brief fast_voltage_check 函数
* @return void
*/
void FOC_Controller::fast_voltage_check(void) {
// ADC1 连续模式零等待读最新 VBUS(ch6,11:1 分压)。
// 旧实现 adc1_read_raw 阻塞式"触发+等EOC":10kHz 调用每次 ~1.5μs 死等。
// 通道顺序问题的历史注释已随 ADC1 连续模式改造失效(本函数只读 DR)。
float current_vbus =
adc1_read_vbus_latest() * (3.3f / 4096.0f) * 11.0f;
// 更新基准电压(峰值检测)
// 电压上升时,基准电压跟随;电压下跌时,基准电压保持
if (current_vbus > vbus_reference) {
vbus_reference = current_vbus;
}
// 电压下跌时不更新基准电压,保持之前的峰值
// 更新上次电压值
last_vbus_fast = current_vbus;
// 计算电压跌幅(相对于基准电压)
float voltage_drop = vbus_reference - current_vbus;
// 关机条件(满足任一即可):
// 1. 电压跌幅超过阈值
// 2. 电压低于绝对下限
bool drop_exceeded = (voltage_drop > VBUS_DROP_THRESHOLD);
bool below_absolute = (current_vbus < VBUS_ABSOLUTE_MIN);
// 只看本来源是否已经申请过:不查别的故障源,避免欠压判定被过热/nFAULT 挡住
if ((drop_exceeded || below_absolute) &&
(run_inhibit.sources() & INHIBIT_UNDERVOLTAGE) == 0U) {
// 电压跌落计数(防抖)
vbus_drop_count++;
if (vbus_drop_count >= VBUS_DEBOUNCE_COUNT) {
// 确认需要关机:只申请禁止位,停机动作由 run_inhibit 在控制任务里执行
run_inhibit.request(INHIBIT_UNDERVOLTAGE);
emergency_shutdown = true; // 标记紧急关机,低频任务会执行保存
vbus_drop_count = 0; // 重置计数器
}
} else {
voltage_error = true;
// 电压正常,重置计数器
vbus_drop_count = 0;
}
}
@@ -179,82 +504,56 @@ void FOC_Controlroller::update_power(void) {
* @param target_velocity: 目标速度
* @return float: q轴电压
*/
float FOC_Controlroller::velocity_open_loop() {
// 通过时间间隔和目标速度计算需要转动的机械角度,存储在shaft_angle中,在此之前,还需要对轴角度归一化
shaft_angle = _normalizeAngle(shaft_angle + target_velocity * ANGLE_FACTOR *
REDUCTION_RATIO * 1.0 /
TIMER2_FREQUENCY);
// 因为在开环控制中,Ud始终为0,只控制Uq
const float Uq = voltage_power_supply / 2.0f;
setPhaseVoltage(Uq, 0.0f);
CAN0_transmiter.testfloat_union.f = shaft_angle;
// cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
// sampler.get_current('W'));
float FOC_Controller::velocity_open_loop() {
// 通过时间间隔和目标速度计算需要转动的机械角度,存储在 shaft_angle 中
shaft_angle =
normalizeAngle360(shaft_angle + target_velocity * REDUCTION_RATIO / MOTOR_FREQUENCY);
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
// 因为在开环控制中,Ud 始终为 0,只控制 Uq
// 幅度限幅必须保留符号:原 _constrain 下限为 +2V,负速度会被钳到 +2V,
// 磁场角度反向而 Uq 仍为正,导致反转时电机原地抖动。
float Uq = target_velocity * REDUCTION_RATIO * 0.005f;
const float UQ_MIN_MAG = 2.0f;
const float UQ_MAX_MAG = voltage_power_supply * 0.5f;
// 零速不输出,避免 2V 最小幅值导致静止时缓慢爬行
if (Uq != 0.0f) {
float Uq_mag = fabsf(Uq);
if (Uq_mag < UQ_MIN_MAG) {
Uq_mag = UQ_MIN_MAG;
} else if (Uq_mag > UQ_MAX_MAG) {
Uq_mag = UQ_MAX_MAG;
}
Uq = (Uq < 0.0f) ? -Uq_mag : Uq_mag;
}
setSVPWM_PhaseVoltage(Uq, 0.0f);
return limit_voltage;
}
/**
* @brief 电机初始化
*
* @param _PP: 电机极队数
*/
// void FOC_Controlroller::DFOC_alignSensor(int _PP) {
// u8 PP = _PP;
// setPhaseVoltage(3, 0, _3PI_2);
// delay_1ms(3000);
// zero_electric_angle = _electricalAngle();
// setPhaseVoltage(0, 0, _3PI_2);
// std::printf("0电角度:%f", zero_electric_angle);
// }
/**
* @brief 角度闭环控制
*
* @param target_angle: 目标角度
* @return float: 当前角度
*/
float FOC_Controlroller::angle_control() {
// PID 控制计算
float Uq = angle_loop(target_angle - rt_angle);
float FOC_Controller::angle_control() {
// PID 控制计算(D 项对测量微分+低通,目标阶跃无微分踢)
angle_loop.set_P(ang_loop_p);
float Uq = angle_loop(target_angle - rt_angle, rt_angle);
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
// 设置相电压
setPhaseVoltage(Uq, 0.0f);
return shaft_angle;
}
/**
* @brief 带速度限制的角度闭环控制
*
* @param target_angle: 目标角度
* @param max_velocity: 最大速度限制 (单位: rad/s)
* @return float: 当前角度
*/
float FOC_Controlroller::angle_control_with_velocity_limit() {
// 计算角度误差
float angle_error = target_angle - rt_angle;
// 使用角度PID控制器计算目标速度
float target_velocity = ang_inVelAngloop(angle_error);
// 对目标速度进行限幅
target_velocity =
_constrain(target_velocity, -limit_velocity, limit_velocity);
// 使用速度PID控制器计算最终输出电压
float Uq = vel_inVelAngloop(rt_velocity - target_velocity);
CAN0_transmiter.testfloat_union.f = shaft_angle;
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
// 设置相电压
setPhaseVoltage(Uq, 0.0f);
setSVPWM_PhaseVoltage(Uq, 0.0f);
return shaft_angle;
}
@@ -264,30 +563,195 @@ float FOC_Controlroller::angle_control_with_velocity_limit() {
* @param target_velocity: 目标速度
* @return float: 当前速度
*/
float FOC_Controlroller::velocity_control() {
// PID 控制计算
float Uq = vel_loop((rt_velocity - target_velocity) * 25);
// 获取电机轴角度
float FOC_Controller::velocity_control() {
// 获取电机轴角度:mode2 Uq 前馈补偿需要用物理 360° 角度查表。
shaft_angle = KTH7823.getcurrent_sensorangle();
// mode2 速度环:真实速度波动保留在反馈里,PID 继续看真实 rt_velocity。
float velocity_error = (target_velocity - rt_velocity) * DIR * REDUCTION_RATIO;
// mode2 使用独立速度环参数。
vel_loop.set_PID(vel_loop_p, vel_loop_Ki, vel_loop_Kd);
vel_loop.set_limit(limit_voltage);
// 仅"实质性换向"时清积分:目标速度变号且新幅值够大(真实反向指令)才清。
// 最终贴合阶段误差过零引起的微小变号(|target|=150×0.01°≈1.5°/s)不清——
// 否则积分被反复清空,低速段缩小的 P 项(0.12V@0.01°)推不过静摩擦,
// 永远停在差 ~0.01° 的位置
static float last_mode2_target_vel = 0.0f;
if (last_mode2_target_vel * target_velocity < 0.0f &&
fabsf(target_velocity) > 5.0f) {
vel_loop.reset_integral();
}
last_mode2_target_vel = target_velocity;
float Uq_pid =
vel_loop(velocity_error, rt_velocity * DIR * REDUCTION_RATIO);
static float uq_ff_table[MODE2_UQ_FF_TABLE_SIZE] = {0.0f};
float Uq_ff = update_angle_sync_uq_feedforward(
2, uq_ff_table, MODE2_UQ_FF_TABLE_SIZE, shaft_angle,
target_velocity, rt_velocity, velocity_error);
// PID 输出已内部钳位;前馈叠加后超限时泄放积分,
// 让 PID 感知真实饱和(否则重载下积分继续累积→释载超调振荡)
float Uq = Uq_pid + Uq_ff;
if (Uq > limit_voltage) {
vel_loop.bleed_integral(Uq - limit_voltage);
Uq = limit_voltage;
} else if (Uq < -limit_voltage) {
vel_loop.bleed_integral(Uq + limit_voltage);
Uq = -limit_voltage;
}
CAN0_transmiter.testfloat_union.f = Uq;
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// 设置相电压
setPhaseVoltage(Uq, 0.0f);
CAN0_transmiter.testfloat_union.f =
cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
setSVPWM_PhaseVoltage(Uq, 0.0f);
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
return rt_velocity;
}
float FOC_Controlroller::current_control() {
// PID 控制计算
float Iq = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
float Uq = current_loop(target_current - Iq);
CAN0_transmiter.testfloat_union.f = Iq;
/**
* @brief current_control 函数
* @return float 返回值
*/
float FOC_Controller::current_control() {
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
CAN0_transmiter.testfloat_union.f = shaft_angle;
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// PID 控制计算
float Iq = rt_current =
cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
current_loop.set_P(cur_loop_p);
float Uq = current_loop(target_current - Iq, Iq);
if (Uq > limit_voltage) {
Uq = limit_voltage;
} else if (Uq < -limit_voltage) {
Uq = -limit_voltage;
}
// 设置相电压
setPhaseVoltage(Uq, 0.0f);
setSVPWM_PhaseVoltage(Uq, 0.0f);
return 1;
}
/**
* @brief 带速度限制的角度闭环控制
*
* @param target_angle: 目标角度
* @param max_velocity: 最大速度限制 (单位: rad/s)
* @return float: 当前角度
*/
float FOC_Controller::angle_control_with_velocity_limit() {
// 计算角度误差
float angle_error = target_angle - rt_angle;
ang_inVelAngloop.set_P(ang_angloop_p);
// 使用角度PID控制器计算目标速度(D 项对测量微分+低通)
float target_velocity = ang_inVelAngloop(angle_error, rt_angle);
// 使用速度PID控制器计算最终输出电压
// 低速段 P/I 各自按地板衰减(D 保持满值):I 地板更低(0.15),
// 停止瞬间积分充电速率仅为全速的 15%,进一步压制静摩擦 hunting
float i_scale = get_mode3_velocity_scale(target_velocity, rt_velocity,
MODE3_LOW_SPEED_I_MIN_SCALE);
vel_inVelAngloop.set_PID(get_mode3_velocity_p(target_velocity, rt_velocity),
vel_angloop_Ki * i_scale, vel_angloop_Kd);
float velocity_error = target_velocity - rt_velocity;
// 仅"实质性换向"时清积分(变号且新幅值>5°/s)。制动段不清(防欠程);
// 最终贴合阶段误差过零的微小变号也不清(防积分反复被清、停在差0.01°)
static float last_vang_target_vel = 0.0f;
if (last_vang_target_vel * target_velocity < 0.0f &&
fabsf(target_velocity) > 5.0f) {
vel_inVelAngloop.reset_integral();
}
last_vang_target_vel = target_velocity;
float Uq_pid = vel_inVelAngloop(velocity_error, rt_velocity) * DIR;
// 获取电机轴角度:mode3 Uq 前馈补偿按物理 360° 查表。
shaft_angle = KTH7823.getcurrent_sensorangle();
static float mode3_uq_ff_table[MODE3_UQ_FF_TABLE_SIZE] = {0.0f};
float Uq_ff = update_angle_sync_uq_feedforward(
3, mode3_uq_ff_table, MODE3_UQ_FF_TABLE_SIZE, shaft_angle,
target_velocity, rt_velocity, velocity_error);
// 前馈叠加后超限时泄放积分(×DIR 补偿:Uq = PID输出×DIR + 前馈)
float Uq = Uq_pid + Uq_ff;
if (Uq > limit_voltage) {
vel_inVelAngloop.bleed_integral(DIR * (Uq - limit_voltage));
Uq = limit_voltage;
} else if (Uq < -limit_voltage) {
vel_inVelAngloop.bleed_integral(DIR * (Uq + limit_voltage));
Uq = -limit_voltage;
}
CAN0_transmiter.testfloat_union.f = shaft_angle;
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// 获取当前实际电流值
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
// 设置相电压
setSVPWM_PhaseVoltage(Uq, 0.0f);
return shaft_angle;
}
/**
* @brief MIT控制
*
* @param target_angle: 目标角度
* @param max_velocity: 最大速度限制 (单位: rad/s)
* @return float: 当前角度
*/
float FOC_Controller::MIT_Mode() {
// 第一层:角度环(外环)
float angle_error = target_angle - rt_angle;
ang_inMITloop.set_P(ang_MIT_p);
float target_velocity =
ang_inMITloop(angle_error, rt_angle);
// 第二层:速度环(中环)
float velocity_error = (target_velocity - rt_velocity)* DIR;
// 仅"实质性换向"时清积分(变号且新幅值>5°/s,理由同 mode3)
static float last_mit_target_vel = 0.0f;
if (last_mit_target_vel * target_velocity < 0.0f &&
fabsf(target_velocity) > 5.0f) {
vel_inMITloop.reset_integral();
}
last_mit_target_vel = target_velocity;
vel_inMITloop.set_P(vel_MIT_p);
float target_current = vel_inMITloop(velocity_error, rt_velocity * DIR);
// 获取电机轴角度
shaft_angle = KTH7823.getcurrent_sensorangle();
// 电气角度(算一次,供 cal_Iq_Id / setSVPWM 共用)
float angle_el = normalizeAngle360(shaft_angle * PP + KTH7823.calibration_angle);
update_el_sincos(angle_el);
// 第三层:电流环(内环)
rt_current = cal_Iq_Id(sampler.get_current('U'), sampler.get_current('V'),
sampler.get_current('W'));
float current_error = target_current - rt_current;
current_inMITloop.set_P(cur_MIT_p);
float Uq = current_inMITloop(current_error, rt_current);
// 设置相电压
setSVPWM_PhaseVoltage(Uq, 0.0f);
return shaft_angle;
}
+165 -29
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/FOC.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
// 函数声明
#ifndef _FOC_H
#define _FOC_H
@@ -5,53 +11,151 @@
#include "pid.h"
#include <cstdint>
class FOC_Controlroller {
/**
* @brief FOC_Controller 类或结构体
*/
class FOC_Controller {
public:
FOC_Controlroller();
FOC_Controller();
// 初始化函数
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init();
u8 control_mode = 0;
float limit_voltage = 0.0f;
float limit_velocity = 0.0f;
float target_angle = 0.0f;
float target_velocity = 0.0f;
float target_current = 0.0f;
float rt_angle = 0.0f;
float rt_velocity = 0.0f;
bool over_current = false;
bool voltage_error = false;
bool is_shut_down = false;
float rt_current = 0.0f;
// 关机事件标记:通知低频任务把当前状态落盘(不是"能不能运行"的判据,
// 运行许可一律看 sys/run_inhibit.h)
bool emergency_shutdown = false; // 紧急电压跌落关机标志
float voltage_power_supply = 24.0f; // 电源电压
float v_safe = voltage_power_supply * 0.95f;
float v_safe = voltage_power_supply / 1.05f;
float duty_a = 0, duty_b = 0, duty_c = 0;
float Ua = 0, Ub = 0, Uc = 0;
// 快速电压检测相关成员(基于跌幅检测)
float vbus_reference = 0.0f; // 基准电压(峰值检测,跟踪当前额定电压)
float last_vbus_fast = 0.0f; // 上次电压值(快速检测用)
u32 vbus_drop_count = 0; // 电压跌落计数器(防抖)
float cur_loop_p = DEFAULT_current_loop_p;
float vel_loop_p = DEFAULT_vel_loop_p;
float ang_loop_p = DEFAULT_angle_loop_p;
float vel_angloop_p = DEFAULT_vel_angloop_p;
float ang_angloop_p = DEFAULT_angle_angloop_p;
float ang_MIT_p = DEFAULT_angle_MIT_p;
float vel_MIT_p = DEFAULT_vel_MIT_p;
float cur_MIT_p = DEFAULT_current_MIT_p;
/**
* @brief velocity_open_loop 函数
* @return float 返回值
*/
float velocity_open_loop(void);
/**
* @brief angle_control 函数
* @return float 返回值
*/
float angle_control(void);
/**
* @brief angle_control_with_velocity_limit 函数
* @return float 返回值
*/
float angle_control_with_velocity_limit(void);
/**
* @brief MIT_Mode 函数
* @return float 返回值
*/
float MIT_Mode(void);
/**
* @brief velocity_control 函数
* @return float 返回值
*/
float velocity_control(void);
/**
* @brief current_control 函数
* @return float 返回值
*/
float current_control(void);
void update_power(void);
/**
* @brief 更新 power 相关状态
* @param[in] voltage 参数
* @return void
*/
void update_power(float voltage);
/**
* @brief DFOC_alignSensor 函数
* @param[in] _PP 参数
* @return void
*/
void DFOC_alignSensor(int _PP);
void shut_down(void);
void resume(void);
void drv_stop(void);
/**
* @brief drv_hold 函数
* @return float 返回值
*/
float drv_hold(void);
PIDController vel_loop = PIDController{
vel_loop_p * 100, 0.20f, 5e-5, 1000000, voltage_power_supply}; // 12V
PIDController angle_loop =
PIDController{angle_loop_p * 100, 100.0f, 0.2f, 10000000,
voltage_power_supply}; // 12V
PIDController current_loop = PIDController{
current_loop_p * 100, 5e-7f, 7e-8, 500, 24}; // 12V
PIDController vel_inVelAngloop = PIDController{
vel_angloop_p * 100, 3.0f, 5e-4, 1000000, voltage_power_supply};
PIDController ang_inVelAngloop =
PIDController{ang_angloop_p * 100, 0.0f, 5e-4, 1000000, 999};
/**
* @brief PWM_out 函数
* @return float 返回值
*/
float PWM_out(void);
/**
* @brief fast_voltage_check 函数
* @return void
*/
void fast_voltage_check(void); // 快速电压检测(100us周期调用)
// 此处默认值不作数
// 注意:构造期 limit_voltage 尚为 0,分母一律用 voltage_power_supply(24V),
// 否则 P 初始化为 +inf;运行期各模式每周期 set_P/set_PID 覆盖。
// 内环 output_ramp 保持原值:实测收紧到 2000 V/s 会在制动段引入相位滞后,
// 导致到位抖动,已回退。
PIDController vel_loop =
PIDController{DEFAULT_vel_loop_p * 100 / voltage_power_supply, vel_loop_Ki,
vel_loop_Kd, 10000, voltage_power_supply};
PIDController angle_loop =
PIDController{DEFAULT_angle_loop_p * 100 / voltage_power_supply, 100.0f,
0.02f, 10000000, voltage_power_supply};
PIDController current_loop = PIDController{
DEFAULT_current_loop_p * 100 / voltage_power_supply, current_loop_Ki,
current_loop_Kd, 1500, voltage_power_supply};
PIDController vel_inVelAngloop =
PIDController{DEFAULT_vel_angloop_p * 100, vel_angloop_Ki,
vel_angloop_Kd, 1e6, v_safe};
PIDController ang_inVelAngloop =
PIDController{DEFAULT_angle_angloop_p * 100 / voltage_power_supply,
ang_angloop_Ki, ang_angloop_Kd, 1e4, 100};
PIDController ang_inMITloop =
PIDController{DEFAULT_angle_MIT_p * 100 / voltage_power_supply, angle_MIT_Ki,
angle_MIT_Kd, 1e5, 100};
PIDController vel_inMITloop = PIDController{
DEFAULT_vel_MIT_p * 100, vel_MIT_Ki, vel_MIT_Kd, 1e9, 1000};
PIDController current_inMITloop = PIDController{
DEFAULT_current_loop_p * 100 / voltage_power_supply, current_loop_Ki,
current_loop_Kd, 1500, voltage_power_supply};
/**
* @brief 更新 pid_limits 相关状态
* @return void
*/
void update_pid_limits() {
vel_loop.limit = voltage_power_supply;
angle_loop.limit = voltage_power_supply;
@@ -62,16 +166,48 @@ class FOC_Controlroller {
protected:
float shaft_angle = 0;
float Ualpha, Ubeta = 0;
float zero_electric_angle = 0;
float v_half = voltage_power_supply * 0.5f; // 电源电压的一半
void setPhaseVoltage(float Uq, float Ud);
// 本周期电角度的 sin/cos:update_el_sincos() 每周期算一次,
// Park 变换与逆 Park 变换共用,避免同一角度算两次三角函数
float el_sin = 0.0f;
float el_cos = 0.0f;
/**
* @brief 每周期计算一次电角度 sin/cos 供 Park/逆Park 共用
* @param[in] angle_el 电角度(度)
* @return void
*/
inline void update_el_sincos(float angle_el);
/**
* @brief 设置 setSVPWM_PhaseVoltage 对应的参数或状态
* @param[in] Uq 参数
* @param[in] Ud 参数
* @return void
*/
void setSVPWM_PhaseVoltage(float Uq, float Ud);
/**
* @brief cal_Iq_Id 函数
* @param[in] current_a 参数
* @param[in] current_b 参数
* @param[in] current_c 参数
* @return float 返回值
* @note 电气角度三角函数用 update_el_sincos() 的预计算结果
*/
float cal_Iq_Id(float current_a, float current_b, float current_c);
float update_angle_sync_uq_feedforward(u8 mode, float table[],
u16 table_size, float mech_angle,
float target_vel, float actual_vel,
float velocity_error);
float get_mode3_velocity_p(float target_vel, float actual_vel);
float get_mode3_velocity_scale(float target_vel, float actual_vel,
float min_scale);
};
// 定义函数指针类型
typedef float (FOC_Controlroller::*FOCFunctionPtr)(void);
inline float _normalizeAngle(float angle);
inline float _electricalAngle();
typedef float (FOC_Controller::*FOCFunctionPtr)(void);
/**
* @brief calibrate_zero_position 函数
* @return void
*/
void calibrate_zero_position(void);
#endif
+199 -44
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/Filter.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "Filter.h"
#include "Can.h"
#include "ang_sensor.h"
@@ -8,25 +14,82 @@
extern angle_sensor KTH7823;
extern CAN_transmiter CAN0_transmiter;
extern u32 motor_runtime_50us;
EncoderAngleKalman Angle_kalman(0, 1.0f, 1e-4f, 0.25f);
EncoderVelocityKalman Vel_kalman(0, 1e-5f, 1e-6f, 0.02f);
// 使用配置文件中的参数
/**
* @brief Angle_kalman 函数
* @param[in] ANGLE_KALMAN_INITIAL_STATE 参数
* @param[in] ANGLE_KALMAN_INITIAL_COVARIANCE 参数
* @param[in] ANGLE_KALMAN_PROCESS_NOISE_Q 参数
* @param[in] ANGLE_KALMAN_MEASUREMENT_NOISE_R 参数
* @return EncoderAngleKalman 返回值
*/
EncoderAngleKalman Angle_kalman(ANGLE_KALMAN_INITIAL_STATE,
ANGLE_KALMAN_INITIAL_COVARIANCE,
ANGLE_KALMAN_PROCESS_NOISE_Q,
ANGLE_KALMAN_MEASUREMENT_NOISE_R);
/**
* @brief Vel_kalman 函数
* @param[in] VELOCITY_KALMAN_INITIAL_STATE 参数
* @param[in] VELOCITY_KALMAN_INITIAL_COVARIANCE 参数
* @param[in] VELOCITY_KALMAN_PROCESS_NOISE_Q 参数
* @param[in] VELOCITY_KALMAN_MEASUREMENT_NOISE_R 参数
* @return EncoderVelocityKalman 返回值
*/
EncoderVelocityKalman Vel_kalman(VELOCITY_KALMAN_INITIAL_STATE,
VELOCITY_KALMAN_INITIAL_COVARIANCE,
VELOCITY_KALMAN_PROCESS_NOISE_Q,
VELOCITY_KALMAN_MEASUREMENT_NOISE_R);
/**
* @brief Current_kalman 函数
* @param[in] CURRENT_KALMAN_INITIAL_STATE 参数
* @param[in] CURRENT_KALMAN_INITIAL_COVARIANCE 参数
* @param[in] CURRENT_KALMAN_PROCESS_NOISE_Q 参数
* @param[in] CURRENT_KALMAN_MEASUREMENT_NOISE_R 参数
* @return CurrentKalman 返回值
*/
CurrentKalman Current_kalman( CURRENT_KALMAN_INITIAL_STATE, CURRENT_KALMAN_INITIAL_COVARIANCE,
CURRENT_KALMAN_PROCESS_NOISE_Q,
CURRENT_KALMAN_MEASUREMENT_NOISE_R);
/*!
\brief 中值滤波器函数
\param[in] values 待滤波的数据数组
\param[in] size 数组大小
\retval 滤波后的中值
\note 使用部分排序优化算法提高效率
\note size==6(ADC_FILTER_SIZE,唯一实际调用)走固定 12 次比较的
排序网络:std::nth_element 是通用算法,对 6 个元素的开销
数倍于固定网络,而本函数在 1kHz 慢速任务里被 3 处调用。
语义与旧版一致:返回上中值(完全排序后的 [size/2])。
*/
static inline void median_cswap(float &a, float &b) {
if (a > b) {
float t = a;
a = b;
b = t;
}
}
float medium_filter(float values[], u8 size) {
// 使用局部数组避免动态内存分配
float sorted[size];
if (size == 6) {
float s0 = values[0], s1 = values[1], s2 = values[2];
float s3 = values[3], s4 = values[4], s5 = values[5];
// n=6 最优排序网络(12 个比较器,Knuth AoCP Vol.3)
median_cswap(s1, s2); median_cswap(s4, s5);
median_cswap(s0, s2); median_cswap(s3, s5);
median_cswap(s0, s1); median_cswap(s3, s4); median_cswap(s2, s5);
median_cswap(s0, s3); median_cswap(s1, s4); median_cswap(s2, s4);
median_cswap(s1, s3); median_cswap(s2, s3);
return s3;
}
// 其他尺寸的通用回退路径(当前无调用者)
float sorted[16];
if (size > 16U) size = 16U;
memcpy(sorted, values, size * SIZE_FLOAT);
// 这比完整排序更高效,时间复杂度从O(n log n)降低到O(n)
std::nth_element(sorted, sorted + size / 2, sorted + size);
return sorted[size / 2]; // 返回中值
return sorted[size / 2];
}
// EncoderVelocityKalman类的实现
@@ -40,6 +103,14 @@ float medium_filter(float values[], u8 size) {
*/
EncoderVelocityKalman::EncoderVelocityKalman(float initial_velocity, float T,
float Q, float R)
/**
* @brief kalman 函数
* @param[in] initial_velocity 参数
* @param[in] T 参数
* @param[in] Q 参数
* @param[in] R 参数
* @return : 返回值
*/
: kalman(initial_velocity, T, Q, R) {}
/*!
@@ -83,67 +154,151 @@ void EncoderVelocityKalman::setMeasurementNoise(float r) {
// EncoderAngleKalman类的实现
/*!
\brief 构造函数,初始化编码器角度卡尔曼滤波器参数
\brief 构造函数,初始化编码器角度 α-β 观测器参数
\param[in] initial_angle 初始角度值 (degree)
\param[in] initial_error_covariance 初始误差协方差
\param[in] process_noise 过程噪声协方差
\param[in] measurement_noise 测量噪声协方差
\param[in] initial_error_covariance 保留参数,当前未使用
\param[in] process_noise 保留参数,当前未使用
\param[in] measurement_noise 保留参数,当前未使用
\retval none
*/
EncoderAngleKalman::EncoderAngleKalman(float initial_angle,
float initial_error_covariance,
float process_noise,
float measurement_noise)
: kalman(initial_angle, initial_error_covariance, process_noise,
measurement_noise),
last_angle(initial_angle), initialized(true) {}
: angle_estimate(initial_angle), velocity_estimate(0.0f),
alpha_gain(ANGLE_OBSERVER_ALPHA), beta_gain(ANGLE_OBSERVER_BETA),
last_update_count(0U), initialized(false) {
(void)initial_error_covariance;
(void)process_noise;
(void)measurement_noise;
}
/*!
\brief 更新 α-β 观测器状态
\param[in] measured_angle 测量的角度值 (degree),累积角度
\retval 估计的角度值 (degree),直接返回输入的累积角度(保持下游兼容)
\note 观测器内部在归一化角度 [0, 360) 上工作,避免大累积角度的浮点精度损失。
速度从归一化角度差分推导,静止时不会产生虚假速度。
*/
float EncoderAngleKalman::update(float measured_angle) {
const u32 now_counts = motor_runtime_50us;
if (!initialized) {
// 初始化:归一化角度
angle_estimate = normalizeAngle360(measured_angle);
velocity_estimate = 0.0f;
last_update_count = now_counts;
initialized = true;
return measured_angle;
}
u32 dt_counts = now_counts - last_update_count;
last_update_count = now_counts;
if (dt_counts == 0U) {
dt_counts = 1U;
}
const float dt = TIME_SLICE * static_cast<float>(dt_counts);
// 将输入的累积角度归一化到 [0, 360)
float measured_norm = normalizeAngle360(measured_angle);
// 预测(在归一化空间)
float angle_pred = normalizeAngle360(angle_estimate + velocity_estimate * dt);
const float velocity_pred = velocity_estimate;
// 计算角度误差,归一化到 [-180, 180) 避免绕回跳变
float error = measured_norm - angle_pred;
if (error > 180.0f) error -= 360.0f;
else if (error < -180.0f) error += 360.0f;
// α-β 更新
angle_estimate = normalizeAngle360(angle_pred + alpha_gain * error);
velocity_estimate = velocity_pred + (beta_gain / dt) * error;
// 直接返回原始累积角度,保持下游兼容
return measured_angle;
}
/*!
\brief 获取当前角度估计值
\retval 当前角度估计值 (degree)
*/
float EncoderAngleKalman::getAngle() const { return angle_estimate; }
/*!
\brief 获取当前速度估计值
\retval 当前速度估计值 (degree/s)
*/
float EncoderAngleKalman::getVelocity() const { return velocity_estimate; }
/*!
\brief 设置 α 增益(借用原接口名)
\param[in] q α 增益
\retval none
*/
void EncoderAngleKalman::setProcessNoise(float q) {
alpha_gain = q;
}
/*!
\brief 设置 β 增益(借用原接口名)
\param[in] r β 增益
\retval none
*/
void EncoderAngleKalman::setMeasurementNoise(float r) {
beta_gain = r;
}
// CurrentKalman类的实现
/*!
\brief 构造函数,初始化电流卡尔曼滤波器参数
\param[in] initial_current 初始电流值 (A)
\param[in] initial_error_covariance 初始误差协方差
\param[in] process_noise 过程噪声协方差
\param[in] measurement_noise 测量噪声协方差
\retval none
*/
CurrentKalman::CurrentKalman(float initial_current,
float initial_error_covariance,
float process_noise, float measurement_noise)
/**
* @brief kalman 函数
* @param[in] initial_current 参数
* @param[in] initial_error_covariance 参数
* @param[in] process_noise 参数
* @param[in] measurement_noise 参数
* @return : 返回值
*/
: kalman(initial_current, initial_error_covariance, process_noise,
measurement_noise) {}
/*!
\brief 更新卡尔曼滤波器状态
\param[in] measured_angle 测量的角度值 (degree)
\retval 估计的角度值 (degree)
\note 实现卡尔曼滤波的预测和更新步骤
\param[in] measured_current 测量的电流值 (A)
\retval 估计的电流值 (A)
*/
float EncoderAngleKalman::update(float measured_angle) {
// 更新last_angle以支持速度计算
last_angle = kalman.getState();
// 1. 预测步骤 - 速度估计
kalman.predict(last_angle);
// 2. 更新步骤 - 使用处理后的角度值更新
float filtered_angle = kalman.update(measured_angle);
return filtered_angle;
float CurrentKalman::update(float measured_current) {
return kalman.update(measured_current);
}
/*!
\brief 获取当前角度估计值
\retval 当前角度估计值 (degree)
\brief 获取当前电流估计值
\retval 当前电流估计值 (A)
*/
float EncoderAngleKalman::getAngle() const { return kalman.getState(); }
/*!
\brief 获取当前角度估计值
\retval 当前角度估计值 (degree)
*/
float EncoderAngleKalman::getVelocity() const {
static u32 last_us50_counter = motor_runtime_50us;
float vel = (kalman.getState() - last_angle) * TIMER2_FREQUENCY *
(motor_runtime_50us - last_us50_counter);
last_us50_counter = motor_runtime_50us;
return vel;
}
float CurrentKalman::getCurrent() const { return kalman.getState(); }
/*!
\brief 设置过程噪声协方差
\param[in] q 过程噪声协方差值
\retval none
*/
void EncoderAngleKalman::setProcessNoise(float q) { kalman.setProcessNoise(q); }
void CurrentKalman::setProcessNoise(float q) { kalman.setProcessNoise(q); }
/*!
\brief 设置测量噪声协方差
\param[in] r 测量噪声协方差值
\retval none
*/
void EncoderAngleKalman::setMeasurementNoise(float r) {
void CurrentKalman::setMeasurementNoise(float r) {
kalman.setMeasurementNoise(r);
}
+152 -10
View File
@@ -1,21 +1,36 @@
/**
* @file Algorithm/Filter.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _FILTER_H
#define _FILTER_H
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
// C风格函数声明可以放在这里
// 例如: void some_c_function(float* data, int size);
#ifdef __cplusplus
} // extern "C"
#endif
#ifdef __cplusplus
#include <cstring>
#include "Can.h"
#include <cstdint>
#include <cstring>
extern CAN_transmiter CAN0_transmiter;
/**
* @brief medium_filter 函数
* @param[in] values 参数
* @param[in] size 参数
* @return float 返回值
*/
float medium_filter(float values[], u8 size);
/*!
@@ -75,10 +90,19 @@ template <typename T> class KalmanFilter {
\retval 估计的状态值
*/
T update(T measurement) {
T K = P / (P + R); // 卡尔曼增益
x += K * (measurement - x); // 更新状态估计
P = (1 - K) * P; // 更新误差协方差
// 添加协方差下限保护,防止数值不稳定
const T MIN_COVARIANCE = static_cast<T>(1e-6);
if (P < MIN_COVARIANCE) {
P = MIN_COVARIANCE;
}
return x;
}
@@ -101,6 +125,25 @@ template <typename T> class KalmanFilter {
\retval none
*/
void setMeasurementNoise(T measurement_noise) { R = measurement_noise; }
/*!
\brief 重置估计误差协方差
\param[in] error_covariance 误差协方差值
\retval none
*/
void resetCovariance(T error_covariance) { P = error_covariance; }
/*!
\brief 获取当前误差协方差
\retval 当前误差协方差
*/
T getCovariance() const { return P; }
/*!
\brief 获取当前过程噪声协方差
\retval 当前过程噪声协方差
*/
T getProcessNoise() const { return Q; }
};
/*!
@@ -116,16 +159,36 @@ class EncoderVelocityKalman {
float initial_error_covariance, float process_noise,
float measurement_noise);
/**
* @brief 更新对象内部状态或采样数据
* @param[in] measured_velocity 参数
* @return float 返回值
*/
float update(float measured_velocity);
/**
* @brief 获取 getVelocity 对应的数据或状态
* @return float 返回值
*/
float getVelocity() const;
/**
* @brief 设置 setProcessNoise 对应的参数或状态
* @param[in] q 参数
* @return void
*/
void setProcessNoise(float q);
/**
* @brief 设置 setMeasurementNoise 对应的参数或状态
* @param[in] r 参数
* @return void
*/
void setMeasurementNoise(float r);
u8 init(float init_velocity) {
u8 init(float init_velocity, float initial_error_covariance = 1.0f) {
kalman.x = init_velocity;
kalman.resetCovariance(initial_error_covariance); // 重置估计误差协方差
return 0;
}
};
@@ -136,29 +199,108 @@ class EncoderVelocityKalman {
*/
class EncoderAngleKalman {
private:
KalmanFilter<float> kalman;
float angle_estimate; // 归一化角度估计 [0, 360)
float velocity_estimate; // 速度估计 (deg/s)
float alpha_gain;
float beta_gain;
u32 last_update_count;
bool initialized;
float last_angle;
public:
EncoderAngleKalman(float initial_angle, float initial_error_covariance,
float process_noise, float measurement_noise);
/**
* @brief 更新对象内部状态或采样数据
* @param[in] measured_angle 参数
* @return float 返回值
*/
float update(float measured_angle);
/**
* @brief 获取 getAngle 对应的数据或状态
* @return float 返回值
*/
float getAngle() const;
/**
* @brief 获取 getVelocity 对应的数据或状态
* @return float 返回值
*/
float getVelocity() const;
/**
* @brief 设置 setProcessNoise 对应的参数或状态
* @param[in] q 参数
* @return void
*/
void setProcessNoise(float q);
float getstate() const { return kalman.getState(); };
/**
* @brief 获取 getstate 对应的数据或状态
* @return float 返回值
*/
float getstate() const { return angle_estimate; };
/**
* @brief 设置 setMeasurementNoise 对应的参数或状态
* @param[in] r 参数
* @return void
*/
void setMeasurementNoise(float r);
u8 init(float init_angle) {
kalman.x = init_angle;
u8 init(float init_angle, float initial_error_covariance = 1.0f) {
(void)initial_error_covariance;
angle_estimate = init_angle;
velocity_estimate = 0.0f;
last_update_count = 0U;
initialized = false;
return 0;
}
};
/*!
\brief 电流卡尔曼滤波器类
\note 用于滤除电流测量噪声的卡尔曼滤波器实现
*/
class CurrentKalman {
private:
KalmanFilter<float> kalman;
public:
CurrentKalman(float initial_current, float initial_error_covariance,
float process_noise, float measurement_noise);
/**
* @brief 更新对象内部状态或采样数据
* @param[in] measured_current 参数
* @return float 返回值
*/
float update(float measured_current);
/**
* @brief 获取 getCurrent 对应的数据或状态
* @return float 返回值
*/
float getCurrent() const;
/**
* @brief 设置 setProcessNoise 对应的参数或状态
* @param[in] q 参数
* @return void
*/
void setProcessNoise(float q);
/**
* @brief 设置 setMeasurementNoise 对应的参数或状态
* @param[in] r 参数
* @return void
*/
void setMeasurementNoise(float r);
u8 init(float init_current, float initial_error_covariance = 1.0f) {
kalman.x = init_current;
kalman.resetCovariance(initial_error_covariance); // 重置估计误差协方差
return 0;
}
};
#endif // __cplusplus
#endif // _FILTER_H
+91 -15
View File
@@ -1,40 +1,66 @@
/**
* @file Algorithm/pid.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "pid.h"
#define _constrain(amt, low, high) \
((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
#include "Can.h"
#include <algorithm>
#include "config.h"
#include <cmath>
extern CAN_transmiter CAN0_transmiter;
constexpr float TIME_SLICE = 1e-4;
// 微分项一阶低通离散系数:α = Ts / (τ + Ts)
constexpr float DERIV_LPF_ALPHA = TIME_SLICE / (DERIV_LPF_TAU + TIME_SLICE);
PIDController::PIDController(float P, float I, float D, float ramp, float limit)
: P(P), I(I), D(D), output_ramp(ramp) // PID控制器加速度限幅
,
limit(limit) // PID控制器输出限幅
,
error_prev(0.0f), output_prev(0.0f), integral_prev(0.0f) {}
error_prev(0.0f), output_prev(0.0f), integral_prev(0.0f),
measurement_prev(0.0f), derivative_filt(0.0f), deriv_init(false) {}
// PID 控制器函数
float PIDController::operator()(float error) {
// 测量微分版本:D 项对测量量而非误差微分,目标阶跃不再产生微分踢
float PIDController::operator()(float error, float measurement) {
// 首次调用只建立测量基线,避免 (meas-0)/Ts 的假微分尖峰
if (!deriv_init) {
measurement_prev = measurement;
deriv_init = true;
}
float d_raw = -(measurement - measurement_prev) / TIME_SLICE;
derivative_filt += DERIV_LPF_ALPHA * (d_raw - derivative_filt);
measurement_prev = measurement;
return step(error, D * derivative_filt);
}
// 公共计算路径:Tustin 积分 + 条件积分抗饱和 + 输出斜坡
float PIDController::step(float error, float derivative) {
// P环
float proportional = P * error;
// Tustin 散点积分(I环)
// 添加积分限幅,防止积分饱和
float integral =
integral_prev + I * TIME_SLICE * 0.5f * (error + error_prev);
// 将P,I,D三环的计算值加起来,与限幅比较得到未饱和/饱和输出
float output_unsat = proportional + integral + derivative;
float output = _constrain(output_unsat, -limit, limit);
// 抗积分饱和(条件积分):输出饱和且误差仍在推高饱和方向时冻结积分。
// 不用大增益反算回退——深饱和时(P 项单独超限幅)反算会把积分压成反号,
// 误差一缩小输出瞬间反向,实测表现为到位疯狂抖动/停不到位。
if (output != output_unsat) {
if ((output_unsat > limit && error > 0.0f) ||
(output_unsat < -limit && error < 0.0f)) {
integral = integral_prev; // 冻结:不再向饱和方向累积
}
}
integral = _constrain(integral, -limit, limit);
// D环(微分环节)
float derivative = D * (error - error_prev) / TIME_SLICE;
// 将P,I,D三环的计算值加起来
float output = proportional + integral + derivative;
output = _constrain(output, -limit, limit);
// 输出斜坡限制
// 简化的斜坡限制(单行逻辑)
if (output_ramp > 0) {
float max_delta = output_ramp * TIME_SLICE;
output = output_prev +
@@ -48,6 +74,25 @@ float PIDController::operator()(float error) {
return output;
}
/**
* @brief 外部饱和泄放积分:前馈叠加后超出限幅时调用
* @param[in] excess 超出限幅的部分(带符号)
* @return void
* @note 仅当积分与超限同号(积分在加剧超限)时泄放,且不泄过零点:
* 防止前馈导致的饱和把积分抽干成反号,到位段失去保持力。
*/
void PIDController::bleed_integral(float excess) {
if (excess > 0.0f && integral_prev > 0.0f) {
float bleed = ANTI_WINDUP_GAIN * excess;
integral_prev = (integral_prev > bleed) ? (integral_prev - bleed)
: 0.0f;
} else if (excess < 0.0f && integral_prev < 0.0f) {
float bleed = ANTI_WINDUP_GAIN * (-excess);
integral_prev = (integral_prev < -bleed) ? (integral_prev + bleed)
: 0.0f;
}
}
/**
* @brief 设置PID参数
*
@@ -68,4 +113,35 @@ void PIDController::set_PID(float P, float I, float D) {
* @param P: P参数
* @return void
*/
void PIDController::set_P_percent(float pp) { this->P = pp; }
void PIDController::set_P(float pp) {
this->P = pp;
}
/**
* @brief 设置输出限幅
*
* @param limit: 输出限幅
* @return void
*/
void PIDController::set_limit(float limit) { this->limit = limit; }
/**
* @brief 重置PID控制器状态
*
* @return void
*/
void PIDController::reset() {
error_prev = 0.0f;
output_prev = 0.0f;
integral_prev = 0.0f;
measurement_prev = 0.0f;
derivative_filt = 0.0f;
deriv_init = false;
}
/**
* @brief 仅清除PID积分项,保留输出斜坡和误差历史
*
* @return void
*/
void PIDController::reset_integral() { integral_prev = 0.0f; }
+64 -3
View File
@@ -1,14 +1,64 @@
/**
* @file Algorithm/pid.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef PID_H
#define PID_H
/**
* @brief PIDController 类或结构体
*/
class PIDController {
public:
PIDController(float P, float I, float D, float ramp, float limit);
~PIDController() = default;
float operator()(float error);
/**
* @brief PID 计算(唯一入口)
* @param[in] error 控制误差
* @param[in] measurement 反馈测量量(D 项对它微分,目标阶跃不产生微分踢)
* @return float PID 输出
*/
float operator()(float error, float measurement);
/**
* @brief 设置 set_PID 对应的参数
* @param[in] P 参数
* @param[in] I 参数
* @param[in] D 参数
* @return void
*/
void set_PID(float P, float I, float D);
void set_P_percent(float pp);
/**
* @brief 设置 set_P 对应的参数
* @param[in] pp 参数
* @return void
*/
void set_P(float pp);
/**
* @brief 设置 set_limit 对应的参数
* @param[in] limit 参数
* @return void
*/
void set_limit(float limit);
/**
* @brief 复位对象内部状态
* @return void
*/
void reset(); // 重置PID控制器状态
/**
* @brief reset_integral 函数
* @return void
*/
void reset_integral(); // 仅清除积分项,保留输出斜坡状态
/**
* @brief 外部饱和泄放积分(反算抗饱和的补充入口)
* @param[in] excess 输出超出限幅的部分(带符号),如前馈叠加后超限
* @return void
* @note 调用方向须与 PID 输出到最终 Uq 的符号一致(有 ×DIR 的要乘上 DIR)
*/
void bleed_integral(float excess);
float P; //!< 比例增益(P环增益)
float I; //!< 积分增益(I环增益)
@@ -17,9 +67,20 @@ class PIDController {
float limit;
protected:
/**
* @brief 公共计算路径:积分、抗饱和、输出斜坡
* @param[in] error 控制误差
* @param[in] derivative 已完成低通滤波的微分项(D 已乘入)
* @return float PID 输出
*/
float step(float error, float derivative);
float error_prev; //!< 最后的跟踪误差值
float output_prev; //!< 最后一个 pid 输出值
float integral_prev; //!< 最后一个积分分量值
unsigned long timestamp_prev; //!< 上次执行时间戳
float measurement_prev; //!< 上次测量值(对测量微分用)
float derivative_filt; //!< 微分项一阶低通滤波后的变化率
bool deriv_init; //!< 测量微分是否已完成首次采样
};
#endif
+5
View File
@@ -0,0 +1,5 @@
/**
* @file Algorithm/thermal.cpp
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
+6
View File
@@ -1,3 +1,9 @@
/**
* @file Algorithm/thermal.h
* @brief 算法模块源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef THERMAL_H
#define THERMAL_H
+85 -7
View File
@@ -2,11 +2,11 @@
\file gd32c10x_adc.c
\brief ADC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,25 +35,37 @@ OF SUCH DAMAGE.
#include "gd32c10x_adc.h"
/* discontinuous mode macro*/
/** @brief ADC_CHANNEL_LENGTH_SUBTRACT_ONE 宏定义 */
#define ADC_CHANNEL_LENGTH_SUBTRACT_ONE ((uint8_t)1U)
/* ADC regular channel macro */
/** @brief ADC_REGULAR_CHANNEL_RANK_SIX 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_SIX ((uint8_t)6U)
/** @brief ADC_REGULAR_CHANNEL_RANK_TWELVE 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_TWELVE ((uint8_t)12U)
/** @brief ADC_REGULAR_CHANNEL_RANK_SIXTEEN 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_SIXTEEN ((uint8_t)16U)
/** @brief ADC_REGULAR_CHANNEL_RANK_LENGTH 宏定义 */
#define ADC_REGULAR_CHANNEL_RANK_LENGTH ((uint8_t)5U)
/* ADC sampling time macro */
/** @brief ADC_CHANNEL_SAMPLE_TEN 宏定义 */
#define ADC_CHANNEL_SAMPLE_TEN ((uint8_t)10U)
/** @brief ADC_CHANNEL_SAMPLE_EIGHTEEN 宏定义 */
#define ADC_CHANNEL_SAMPLE_EIGHTEEN ((uint8_t)18U)
/** @brief ADC_CHANNEL_SAMPLE_LENGTH 宏定义 */
#define ADC_CHANNEL_SAMPLE_LENGTH ((uint8_t)3U)
/* ADC inserted channel macro */
/** @brief ADC_INSERTED_CHANNEL_RANK_LENGTH 宏定义 */
#define ADC_INSERTED_CHANNEL_RANK_LENGTH ((uint8_t)5U)
/** @brief ADC_INSERTED_CHANNEL_SHIFT_LENGTH 宏定义 */
#define ADC_INSERTED_CHANNEL_SHIFT_LENGTH ((uint8_t)15U)
/* ADC inserted channel offset macro */
/** @brief ADC_OFFSET_LENGTH 宏定义 */
#define ADC_OFFSET_LENGTH ((uint8_t)3U)
/** @brief ADC_OFFSET_SHIFT_LENGTH 宏定义 */
#define ADC_OFFSET_SHIFT_LENGTH ((uint8_t)4U)
/*!
@@ -97,6 +109,11 @@ void adc_deinit(uint32_t adc_periph)
\param[out] none
\retval none
*/
/**
* @brief adc_mode_config 函数
* @param[in] mode 参数
* @return void
*/
void adc_mode_config(uint32_t mode)
{
ADC_CTL0(ADC0) &= ~(ADC_CTL0_SYNCM);
@@ -286,6 +303,14 @@ void adc_resolution_config(uint32_t adc_periph , uint32_t resolution)
\param[out] none
\retval none
*/
/**
* @brief adc_oversample_mode_config 函数
* @param[in] adc_periph 参数
* @param[in] mode 参数
* @param[in] shift 参数
* @param[in] ratio 参数
* @return void
*/
void adc_oversample_mode_config(uint32_t adc_periph, uint32_t mode, uint16_t shift, uint8_t ratio)
{
if(ADC_OVERSAMPLING_ONE_CONVERT == mode){
@@ -357,6 +382,13 @@ void adc_dma_mode_disable(uint32_t adc_periph)
\param[out] none
\retval none
*/
/**
* @brief adc_discontinuous_mode_config 函数
* @param[in] adc_periph 参数
* @param[in] adc_channel_group 参数
* @param[in] length 参数
* @return void
*/
void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_group, uint8_t length)
{
/* disable discontinuous mode of regular & inserted channel */
@@ -393,6 +425,13 @@ void adc_discontinuous_mode_config(uint32_t adc_periph, uint8_t adc_channel_grou
\param[out] none
\retval none
*/
/**
* @brief adc_channel_length_config 函数
* @param[in] adc_periph 参数
* @param[in] adc_channel_group 参数
* @param[in] length 参数
* @return void
*/
void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t length)
{
switch(adc_channel_group){
@@ -431,6 +470,14 @@ void adc_channel_length_config(uint32_t adc_periph, uint8_t adc_channel_group, u
\param[out] none
\retval none
*/
/**
* @brief adc_regular_channel_config 函数
* @param[in] adc_periph 参数
* @param[in] rank 参数
* @param[in] adc_channel 参数
* @param[in] sample_time 参数
* @return void
*/
void adc_regular_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc_channel , uint32_t sample_time)
{
uint32_t rsq,sampt;
@@ -499,6 +546,14 @@ void adc_regular_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc
\param[out] none
\retval none
*/
/**
* @brief adc_inserted_channel_config 函数
* @param[in] adc_periph 参数
* @param[in] rank 参数
* @param[in] adc_channel 参数
* @param[in] sample_time 参数
* @return void
*/
void adc_inserted_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t adc_channel , uint32_t sample_time)
{
uint8_t inserted_length;
@@ -508,8 +563,8 @@ void adc_inserted_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t ad
inserted_length = (uint8_t)GET_BITS(ADC_ISQ(adc_periph) , 20U , 21U);
/* the channel number is written to these bits to select a channel as the nth conversion in the inserted channel group */
isq = ADC_ISQ(adc_periph);
isq &= ~((uint32_t)(ADC_ISQ_ISQN << (5U * ((3 + rank) - inserted_length))));
isq |= ((uint32_t)adc_channel << (5U * ((3 + rank) - inserted_length)));
isq &= ~((uint32_t)(ADC_ISQ_ISQN << (5U * ((3U + rank) - inserted_length))));
isq |= ((uint32_t)adc_channel << (5U * ((3U + rank) - inserted_length)));
ADC_ISQ(adc_periph) = isq;
/* ADC sampling time config */
@@ -544,6 +599,13 @@ void adc_inserted_channel_config(uint32_t adc_periph , uint8_t rank , uint8_t ad
\param[out] none
\retval none
*/
/**
* @brief adc_inserted_channel_offset_config 函数
* @param[in] adc_periph 参数
* @param[in] inserted_channel 参数
* @param[in] offset 参数
* @return void
*/
void adc_inserted_channel_offset_config(uint32_t adc_periph , uint8_t inserted_channel , uint16_t offset)
{
uint8_t inserted_length;
@@ -592,6 +654,13 @@ void adc_inserted_channel_offset_config(uint32_t adc_periph , uint8_t inserted_c
\param[out] none
\retval none
*/
/**
* @brief adc_external_trigger_source_config 函数
* @param[in] adc_periph 参数
* @param[in] adc_channel_group 参数
* @param[in] external_trigger_source 参数
* @return void
*/
void adc_external_trigger_source_config(uint32_t adc_periph, uint8_t adc_channel_group, uint32_t external_trigger_source)
{
switch(adc_channel_group){
@@ -669,7 +738,6 @@ void adc_software_trigger_enable(uint32_t adc_periph , uint8_t adc_channel_group
/*!
\brief read ADC regular group data register
\param[in] adc_periph: ADCx,x=0,1
\param[in] none
\param[out] none
\retval the conversion value
*/
@@ -818,6 +886,12 @@ void adc_watchdog_threshold_config(uint32_t adc_periph , uint16_t low_threshold
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief adc_flag_get 函数
* @param[in] adc_periph 参数
* @param[in] adc_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus adc_flag_get(uint32_t adc_periph , uint32_t adc_flag)
{
FlagStatus reval = RESET;
@@ -840,6 +914,12 @@ FlagStatus adc_flag_get(uint32_t adc_periph , uint32_t adc_flag)
\param[out] none
\retval none
*/
/**
* @brief adc_flag_clear 函数
* @param[in] adc_periph 参数
* @param[in] adc_flag 参数
* @return void
*/
void adc_flag_clear(uint32_t adc_periph , uint32_t adc_flag)
{
ADC_STAT(adc_periph) &= ~((uint32_t)adc_flag);
@@ -848,7 +928,6 @@ void adc_flag_clear(uint32_t adc_periph , uint32_t adc_flag)
/*!
\brief get the bit state of ADCx software start conversion
\param[in] adc_periph: ADCx, x=0,1, only one among these parameters can be selected
\param[in] none
\param[out] none
\retval FlagStatus: SET or RESET
*/
@@ -864,7 +943,6 @@ FlagStatus adc_regular_software_startconv_flag_get(uint32_t adc_periph)
/*!
\brief get the bit state of ADCx software inserted channel start conversion
\param[in] adc_periph: ADCx, x=0,1 only one among these parameters can be selected
\param[in] none
\param[out] none
\retval FlagStatus: SET or RESET
*/
+13 -6
View File
@@ -2,11 +2,11 @@
\file gd32c10x_bkp.c
\brief BKP driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_bkp.h"
/* BKP register bits offset */
/** @brief BKP_TAMPER_BITS_OFFSET 宏定义 */
#define BKP_TAMPER_BITS_OFFSET ((uint32_t)8U)
/*!
@@ -287,11 +288,14 @@ void bkp_interrupt_disable(void)
*/
FlagStatus bkp_flag_get(void)
{
FlagStatus ret;
if(RESET != (BKP_TPCS & BKP_FLAG_TAMPER)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -313,11 +317,14 @@ void bkp_flag_clear(void)
*/
FlagStatus bkp_interrupt_flag_get(void)
{
FlagStatus ret;
if(RESET != (BKP_TPCS & BKP_INT_FLAG_TAMPER)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
+156 -26
View File
@@ -2,11 +2,11 @@
\file gd32c10x_can.c
\brief CAN driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -33,9 +33,9 @@ OF SUCH DAMAGE.
*/
#include "gd32c10x_can.h"
#include "stdio.h"
#include <stdlib.h>
/** @brief CAN_ERROR_HANDLE 宏定义 */
#define CAN_ERROR_HANDLE(s) do{}while(1)
/* This table can be used to calculate data length in FD mode */
@@ -72,6 +72,12 @@ void can_deinit(uint32_t can_periph)
\param[out] none
\retval none
*/
/**
* @brief can_struct_para_init 函数
* @param[in] type 参数
* @param[in] p_struct 参数
* @return void
*/
void can_struct_para_init(can_struct_type_enum type, void *p_struct)
{
uint8_t i;
@@ -184,6 +190,12 @@ void can_struct_para_init(can_struct_type_enum type, void *p_struct)
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief can_init 函数
* @param[in] can_periph 参数
* @param[in] can_parameter_init 参数
* @return ErrStatus 返回值
*/
ErrStatus can_init(uint32_t can_periph, can_parameter_struct *can_parameter_init)
{
uint32_t timeout = CAN_TIMEOUT;
@@ -297,14 +309,21 @@ ErrStatus can_init(uint32_t can_periph, can_parameter_struct *can_parameter_init
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief can_fd_init 函数
* @param[in] can_periph 参数
* @param[in] can_fdframe_init 参数
* @return ErrStatus 返回值
*/
ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
{
uint32_t timeout = CAN_TIMEOUT;
uint32_t tempreg = 0U;
ErrStatus reval = SUCCESS;
/* check null pointer */
if(0 == can_fdframe_init) {
return ERROR;
reval = ERROR;
}
/* disable sleep mode */
CAN_CTL(can_periph) &= ~CAN_CTL_SLPWMOD;
@@ -316,7 +335,7 @@ ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
}
/* check initialize working success */
if(CAN_STAT_IWS != (CAN_STAT(can_periph) & CAN_STAT_IWS)) {
return ERROR;
reval = ERROR;
} else {
/* set the data bit timing register */
CAN_DBT(can_periph) = (BT_DSJW((uint32_t)can_fdframe_init->data_resync_jump_width) | \
@@ -342,7 +361,7 @@ ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
CAN_FDTDC(can_periph) = (FDTDC_TDCF(can_fdframe_init->p_delay_compensation->tdc_filter) | FDTDC_TDCO(
can_fdframe_init->p_delay_compensation->tdc_offset));
} else {
return ERROR;
reval = ERROR;
}
} else {
/* Transmitter delay compensation mode is disabled */
@@ -366,11 +385,11 @@ ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
}
/* check exit initialize mode */
if(0U == timeout) {
return ERROR;
reval = ERROR;
}
}
return SUCCESS;
return reval;
}
/*!
@@ -388,6 +407,11 @@ ErrStatus can_fd_init(uint32_t can_periph, can_fdframe_struct *can_fdframe_init)
\param[out] none
\retval none
*/
/**
* @brief can_filter_init 函数
* @param[in] can_filter_parameter_init 参数
* @return void
*/
void can_filter_init(can_filter_parameter_struct *can_filter_parameter_init)
{
uint32_t val = 0U;
@@ -470,6 +494,14 @@ void can_filter_init(can_filter_parameter_struct *can_filter_parameter_init)
\param[out] none
\retval none
*/
/**
* @brief can_filter_mask_mode_init 函数
* @param[in] id 参数
* @param[in] mask 参数
* @param[in] format_fifo 参数
* @param[in] filter_number 参数
* @return void
*/
void can_filter_mask_mode_init(uint32_t id, uint32_t mask, can_format_fifo_enum format_fifo, uint16_t filter_number)
{
can_filter_parameter_struct can_filter;
@@ -556,6 +588,12 @@ void can_filter_mask_mode_init(uint32_t id, uint32_t mask, can_format_fifo_enum
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief can_monitor_mode_set 函数
* @param[in] can_periph 参数
* @param[in] mode 参数
* @return ErrStatus 返回值
*/
ErrStatus can_monitor_mode_set(uint32_t can_periph, uint8_t mode)
{
ErrStatus reval = SUCCESS;
@@ -728,6 +766,12 @@ void can_time_trigger_mode_disable(uint32_t can_periph)
\param[out] none
\retval mailbox_number
*/
/**
* @brief can_message_transmit 函数
* @param[in] can_periph 参数
* @param[in] transmit_message 参数
* @return uint8_t 返回值
*/
uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct *transmit_message)
{
uint8_t mailbox_number = CAN_MAILBOX0;
@@ -747,10 +791,9 @@ uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct *t
} else {
mailbox_number = CAN_NOMAILBOX;
}
/* return no mailbox empty */
if(CAN_NOMAILBOX == mailbox_number) {
return CAN_NOMAILBOX;
}
/* got one empty mailbox */
if(CAN_NOMAILBOX != mailbox_number) {
CAN_TMI(can_periph, mailbox_number) &= CAN_TMI_TEN;
if(CAN_FF_STANDARD == transmit_message->tx_ff) {
@@ -830,7 +873,7 @@ uint8_t can_message_transmit(uint32_t can_periph, can_trasnmit_message_struct *t
/* enable transmission */
CAN_TMI(can_periph, mailbox_number) |= CAN_TMI_TEN;
}
return mailbox_number;
}
@@ -901,25 +944,63 @@ can_transmit_state_enum can_transmit_states(uint32_t can_periph, uint8_t mailbox
only one parameter can be selected which is shown as below:
\arg CAN_MAILBOXx(x=0,1,2)
\param[out] none
\retval none
\retval ErrStatus: ERROR or SUCCESS
*/
void can_transmission_stop(uint32_t can_periph, uint8_t mailbox_number)
ErrStatus can_transmission_stop(uint32_t can_periph, uint8_t mailbox_number)
{
ErrStatus reval = SUCCESS;
/* timeout for CAN_TSTAT_MSTx bits */
uint32_t timeout = CAN_TIMEOUT;
uint32_t reg_value0 = 0U;
uint32_t reg_value1 = 0U;
/* get the status of transmit FIFO order */
reg_value0 = CAN_CTL(can_periph) & CAN_CTL_TFO;
if(CAN_MAILBOX0 == mailbox_number) {
reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS0 | CAN_ALL_MAILBOX_EMPTY);
if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS0 == reg_value1)){
reval = ERROR;
} else {
CAN_TSTAT(can_periph) |= CAN_TSTAT_MST0;
while(CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)) {
while((CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)) && (0U != timeout)) {
timeout--;
}
if(CAN_TSTAT_MST0 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST0)){
reval = ERROR;
}
}
} else if(CAN_MAILBOX1 == mailbox_number) {
reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS1 | CAN_ALL_MAILBOX_EMPTY);
if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS1 == reg_value1)){
reval = ERROR;
}else{
CAN_TSTAT(can_periph) |= CAN_TSTAT_MST1;
while(CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)) {
while((CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)) && (0U != timeout)) {
timeout--;
}
if(CAN_TSTAT_MST1 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST1)){
reval = ERROR;
}
}
} else if(CAN_MAILBOX2 == mailbox_number) {
reg_value1 = CAN_TSTAT(can_periph) & (CAN_TSTAT_TMLS2 | CAN_ALL_MAILBOX_EMPTY);
if((CAN_CTL_TFO == reg_value0) && (CAN_TSTAT_TMLS2 == reg_value1)){
reval = ERROR;
}else{
CAN_TSTAT(can_periph) |= CAN_TSTAT_MST2;
while(CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)) {
while((CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)) && (0U != timeout)) {
timeout--;
}
if(CAN_TSTAT_MST2 == (CAN_TSTAT(can_periph) & CAN_TSTAT_MST2)){
reval = ERROR;
}
}
} else {
/* illegal parameters */
}
return reval;
}
/*!
@@ -938,6 +1019,13 @@ void can_transmission_stop(uint32_t can_periph, uint8_t mailbox_number)
\arg rx_fi: 0 - 27
\retval none
*/
/**
* @brief can_message_receive 函数
* @param[in] can_periph 参数
* @param[in] fifo_number 参数
* @param[in] receive_message 参数
* @return void
*/
void can_message_receive(uint32_t can_periph, uint8_t fifo_number, can_receive_message_struct *receive_message)
{
uint32_t canfd_en = 0U;
@@ -976,13 +1064,10 @@ void can_message_receive(uint32_t can_periph, uint8_t fifo_number, can_receive_m
receive_message->rx_data[5] = (uint8_t)(GET_RFIFOMDATA1_DB5(CAN_RFIFOMDATA1(can_periph, fifo_number)));
receive_message->rx_data[6] = (uint8_t)(GET_RFIFOMDATA1_DB6(CAN_RFIFOMDATA1(can_periph, fifo_number)));
receive_message->rx_data[7] = (uint8_t)(GET_RFIFOMDATA1_DB7(CAN_RFIFOMDATA1(can_periph, fifo_number)));
} else {
printf("CAN FD function disabled but FD frame received\r\n");
// CAN_ERROR_HANDLE("CAN FD function disabled \r\n");
CAN_ERROR_HANDLE("CAN FD function disabled \r\n");
}
} else {
/* check FD function has been enabled */
/* get receive data length */
canfd_recv_cnt = (uint8_t)(GET_RFIFOMP_DLENC(CAN_RFIFOMP(can_periph, fifo_number)));
@@ -1180,6 +1265,11 @@ ErrStatus can_wakeup(uint32_t can_periph)
\arg CAN_ERROR_CRC: CRC error
\arg CAN_ERROR_SOFTWARECFG: software configure
*/
/**
* @brief can_error_get 函数
* @param[in] can_periph 参数
* @return can_error_enum 返回值
*/
can_error_enum can_error_get(uint32_t can_periph)
{
can_error_enum error;
@@ -1244,6 +1334,12 @@ uint8_t can_transmit_error_number_get(uint32_t can_periph)
\param[out] none
\retval none
*/
/**
* @brief can_interrupt_enable 函数
* @param[in] can_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void can_interrupt_enable(uint32_t can_periph, uint32_t interrupt)
{
CAN_INTEN(can_periph) |= interrupt;
@@ -1272,6 +1368,12 @@ void can_interrupt_enable(uint32_t can_periph, uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief can_interrupt_disable 函数
* @param[in] can_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void can_interrupt_disable(uint32_t can_periph, uint32_t interrupt)
{
CAN_INTEN(can_periph) &= ~interrupt;
@@ -1320,14 +1422,22 @@ void can_interrupt_disable(uint32_t can_periph, uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief can_flag_get 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus can_flag_get(uint32_t can_periph, can_flag_enum flag)
{
FlagStatus retval;
/* get flag and interrupt enable state */
if(RESET != (CAN_REG_VAL(can_periph, flag) & BIT(CAN_BIT_POS(flag)))) {
return SET;
retval = SET;
} else {
return RESET;
retval = RESET;
}
return retval;
}
/*!
@@ -1358,6 +1468,12 @@ FlagStatus can_flag_get(uint32_t can_periph, can_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief can_flag_clear 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return void
*/
void can_flag_clear(uint32_t can_periph, can_flag_enum flag)
{
CAN_REG_VAL(can_periph, flag) |= BIT(CAN_BIT_POS(flag));
@@ -1388,8 +1504,15 @@ void can_flag_clear(uint32_t can_periph, can_flag_enum flag)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief can_interrupt_flag_get 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum flag)
{
FlagStatus retval;
uint32_t ret1 = RESET;
uint32_t ret2 = RESET;
@@ -1406,10 +1529,11 @@ FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum f
/* get the status of interrupt enable bit */
ret2 = CAN_INTEN(can_periph) & BIT(CAN_BIT_POS1(flag));
if(ret1 && ret2) {
return SET;
retval = SET;
} else {
return RESET;
retval = RESET;
}
return retval;
}
/*!
@@ -1431,6 +1555,12 @@ FlagStatus can_interrupt_flag_get(uint32_t can_periph, can_interrupt_flag_enum f
\param[out] none
\retval none
*/
/**
* @brief can_interrupt_flag_clear 函数
* @param[in] can_periph 参数
* @param[in] flag 参数
* @return void
*/
void can_interrupt_flag_clear(uint32_t can_periph, can_interrupt_flag_enum flag)
{
CAN_REG_VALS(can_periph, flag) |= BIT(CAN_BIT_POS0(flag));
@@ -2,11 +2,11 @@
\file gd32c10x_crc.c
\brief CRC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -34,7 +34,9 @@ OF SUCH DAMAGE.
#include "gd32c10x_crc.h"
/** @brief CRC_DATA_RESET_VALUE 宏定义 */
#define CRC_DATA_RESET_VALUE ((uint32_t)0xFFFFFFFFU)
/** @brief CRC_FDATA_RESET_VALUE 宏定义 */
#define CRC_FDATA_RESET_VALUE ((uint32_t)0x00000000U)
/*!
@@ -117,11 +119,11 @@ uint32_t crc_single_data_calculate(uint32_t sdata)
\param[out] none
\retval 32-bit value calculated by CRC
*/
uint32_t crc_block_data_calculate(const uint32_t *array, uint32_t size)
uint32_t crc_block_data_calculate(uint32_t array[], uint32_t size)
{
uint32_t index;
for(index = 0U; index < size; index++){
CRC_DATA = *(array+index);
CRC_DATA = array[index];
}
return (CRC_DATA);
}
+47 -9
View File
@@ -2,11 +2,11 @@
\file gd32c10x_ctc.c
\brief CTC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -34,12 +34,17 @@ OF SUCH DAMAGE.
#include "gd32c10x_ctc.h"
/** @brief CTC_FLAG_MASK 宏定义 */
#define CTC_FLAG_MASK ((uint32_t)0x00000700U)
/* CTC register bit offset */
/** @brief CTC_TRIMVALUE_OFFSET 宏定义 */
#define CTC_TRIMVALUE_OFFSET ((uint32_t)8U)
/** @brief CTC_TRIM_VALUE_OFFSET 宏定义 */
#define CTC_TRIM_VALUE_OFFSET ((uint32_t)8U)
/** @brief CTC_REFCAP_OFFSET 宏定义 */
#define CTC_REFCAP_OFFSET ((uint32_t)16U)
/** @brief CTC_LIMIT_VALUE_OFFSET 宏定义 */
#define CTC_LIMIT_VALUE_OFFSET ((uint32_t)16U)
/*!
@@ -79,7 +84,7 @@ void ctc_counter_disable(void)
/*!
\brief configure the IRC48M trim value
\param[in] ctc_trim_value: 8-bit IRC48M trim value
\param[in] trim_value: 8-bit IRC48M trim value
\arg 0x00 - 0x3F
\param[out] none
\retval none
@@ -163,6 +168,11 @@ void ctc_refsource_signal_select(uint32_t refs)
\param[out] none
\retval none
*/
/**
* @brief ctc_refsource_prescaler_config 函数
* @param[in] prescaler 参数
* @return void
*/
void ctc_refsource_prescaler_config(uint32_t prescaler)
{
CTC_CTL1 &= (uint32_t)(~CTC_CTL1_REFPSC);
@@ -218,11 +228,14 @@ uint16_t ctc_counter_capture_value_read(void)
*/
FlagStatus ctc_counter_direction_read(void)
{
FlagStatus ret;
if(RESET != (CTC_STAT & CTC_STAT_REFDIR)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -297,9 +310,15 @@ void ctc_interrupt_disable(uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief ctc_interrupt_flag_get 函数
* @param[in] interrupt 参数
* @return FlagStatus 返回值
*/
FlagStatus ctc_interrupt_flag_get(uint32_t interrupt)
{
uint32_t intflag = 0U, intenable = 0U;
FlagStatus ret;
/* check whether the interrupt is enabled */
if(RESET != (interrupt & CTC_FLAG_MASK)){
@@ -312,10 +331,11 @@ FlagStatus ctc_interrupt_flag_get(uint32_t interrupt)
intflag = CTC_STAT & interrupt;
if(intflag && intenable){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -332,6 +352,11 @@ FlagStatus ctc_interrupt_flag_get(uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief ctc_interrupt_flag_clear 函数
* @param[in] interrupt 参数
* @return void
*/
void ctc_interrupt_flag_clear(uint32_t interrupt)
{
if(RESET != (interrupt & CTC_FLAG_MASK)){
@@ -355,13 +380,21 @@ void ctc_interrupt_flag_clear(uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief ctc_flag_get 函数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus ctc_flag_get(uint32_t flag)
{
FlagStatus ret;
if(RESET != (CTC_STAT & flag)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -378,6 +411,11 @@ FlagStatus ctc_flag_get(uint32_t flag)
\param[out] none
\retval none
*/
/**
* @brief ctc_flag_clear 函数
* @param[in] flag 参数
* @return void
*/
void ctc_flag_clear(uint32_t flag)
{
if(RESET != (flag & CTC_FLAG_MASK)){
+43 -2
View File
@@ -2,11 +2,11 @@
\file gd32c10x_dac.c
\brief DAC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,8 +35,11 @@ OF SUCH DAMAGE.
#include "gd32c10x_dac.h"
/* DAC register bit offset */
/** @brief OUT1_REG_OFFSET 宏定义 */
#define OUT1_REG_OFFSET ((uint32_t)0x00000010U)
/** @brief DH_12BIT_OFFSET 宏定义 */
#define DH_12BIT_OFFSET ((uint32_t)0x00000010U)
/** @brief DH_8BIT_OFFSET 宏定义 */
#define DH_8BIT_OFFSET ((uint32_t)0x00000008U)
/*!
@@ -203,6 +206,14 @@ uint16_t dac_output_value_get(uint32_t dac_periph, uint8_t dac_out)
\param[out] none
\retval none
*/
/**
* @brief dac_data_set 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] dac_align 参数
* @param[in] data 参数
* @return void
*/
void dac_data_set(uint32_t dac_periph, uint8_t dac_out, uint32_t dac_align, uint16_t data)
{
/* DAC_OUT0 data alignment */
@@ -299,6 +310,13 @@ void dac_trigger_disable(uint32_t dac_periph, uint8_t dac_out)
\param[out] none
\retval none
*/
/**
* @brief dac_trigger_source_config 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] triggersource 参数
* @return void
*/
void dac_trigger_source_config(uint32_t dac_periph, uint8_t dac_out, uint32_t triggersource)
{
if(DAC_OUT0 == dac_out){
@@ -318,6 +336,7 @@ void dac_trigger_source_config(uint32_t dac_periph, uint8_t dac_out, uint32_t tr
\brief enable DAC software trigger
\param[in] dac_periph: DACx(x=0)
\param[in] dac_out: DAC_OUTx(x=0,1)
\param[out] none
\retval none
*/
void dac_software_trigger_enable(uint32_t dac_periph, uint8_t dac_out)
@@ -379,6 +398,13 @@ void dac_wave_mode_config(uint32_t dac_periph, uint8_t dac_out, uint32_t wave_mo
\param[out] none
\retval none
*/
/**
* @brief dac_lfsr_noise_config 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] unmask_bits 参数
* @return void
*/
void dac_lfsr_noise_config(uint32_t dac_periph, uint8_t dac_out, uint32_t unmask_bits)
{
if(DAC_OUT0 == dac_out){
@@ -415,6 +441,13 @@ void dac_lfsr_noise_config(uint32_t dac_periph, uint8_t dac_out, uint32_t unmask
\param[out] none
\retval none
*/
/**
* @brief dac_triangle_noise_config 函数
* @param[in] dac_periph 参数
* @param[in] dac_out 参数
* @param[in] amplitude 参数
* @return void
*/
void dac_triangle_noise_config(uint32_t dac_periph, uint8_t dac_out, uint32_t amplitude)
{
if(DAC_OUT0 == dac_out){
@@ -513,6 +546,14 @@ void dac_concurrent_output_buffer_disable(uint32_t dac_periph)
\param[out] none
\retval none
*/
/**
* @brief dac_concurrent_data_set 函数
* @param[in] dac_periph 参数
* @param[in] dac_align 参数
* @param[in] data0 参数
* @param[in] data1 参数
* @return void
*/
void dac_concurrent_data_set(uint32_t dac_periph, uint32_t dac_align, uint16_t data0, uint16_t data1)
{
uint32_t data = 0U;
@@ -2,11 +2,11 @@
\file gd32c10x_dbg.c
\brief DBG driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
+90 -4
View File
@@ -2,11 +2,11 @@
\file gd32c10x_dma.c
\brief DMA driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,9 +35,16 @@ OF SUCH DAMAGE.
#include "gd32c10x_dma.h"
#include <stdio.h>
/** @brief DMA_WRONG_HANDLE 宏定义 */
#define DMA_WRONG_HANDLE while(1){}
/* check whether peripheral matches channels or not */
/**
* @brief dma_periph_and_channel_check 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @return ErrStatus 返回值
*/
static ErrStatus dma_periph_and_channel_check(uint32_t dma_periph, dma_channel_enum channelx);
/*!
@@ -110,6 +117,13 @@ void dma_struct_para_init(dma_parameter_struct* init_struct)
\param[out] none
\retval none
*/
/**
* @brief dma_init 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] init_struct 参数
* @return void
*/
void dma_init(uint32_t dma_periph, dma_channel_enum channelx, dma_parameter_struct* init_struct)
{
uint32_t ctl;
@@ -365,6 +379,13 @@ uint32_t dma_transfer_number_get(uint32_t dma_periph, dma_channel_enum channelx)
\param[out] none
\retval none
*/
/**
* @brief dma_priority_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] priority 参数
* @return void
*/
void dma_priority_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t priority)
{
uint32_t ctl;
@@ -396,6 +417,13 @@ void dma_priority_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_
\param[out] none
\retval none
*/
/**
* @brief dma_memory_width_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] mwidth 参数
* @return void
*/
void dma_memory_width_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t mwidth)
{
uint32_t ctl;
@@ -427,6 +455,13 @@ void dma_memory_width_config(uint32_t dma_periph, dma_channel_enum channelx, uin
\param[out] none
\retval none
*/
/**
* @brief dma_periph_width_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] pwidth 参数
* @return void
*/
void dma_periph_width_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t pwidth)
{
uint32_t ctl;
@@ -533,6 +568,13 @@ void dma_periph_increase_disable(uint32_t dma_periph, dma_channel_enum channelx)
\param[out] none
\retval none
*/
/**
* @brief dma_transfer_direction_config 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] direction 参数
* @return void
*/
void dma_transfer_direction_config(uint32_t dma_periph, dma_channel_enum channelx, uint32_t direction)
{
if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){
@@ -561,6 +603,13 @@ void dma_transfer_direction_config(uint32_t dma_periph, dma_channel_enum channel
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief dma_flag_get 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus dma_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
FlagStatus reval;
@@ -591,6 +640,13 @@ FlagStatus dma_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t
\param[out] none
\retval none
*/
/**
* @brief dma_flag_clear 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return void
*/
void dma_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx);
@@ -611,9 +667,17 @@ void dma_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t fla
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief dma_interrupt_flag_get 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
uint32_t interrupt_enable = 0U, interrupt_flag = 0U;
FlagStatus status = RESET;
switch(flag){
case DMA_INT_FLAG_FTF:
@@ -637,10 +701,11 @@ FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx
/* when the interrupt flag is set and enabled, return SET */
if((0U != interrupt_flag) && (0U != interrupt_enable)){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
/*!
@@ -659,6 +724,13 @@ FlagStatus dma_interrupt_flag_get(uint32_t dma_periph, dma_channel_enum channelx
\param[out] none
\retval none
*/
/**
* @brief dma_interrupt_flag_clear 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] flag 参数
* @return void
*/
void dma_interrupt_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, uint32_t flag)
{
DMA_INTC(dma_periph) |= DMA_FLAG_ADD(flag, channelx);
@@ -679,6 +751,13 @@ void dma_interrupt_flag_clear(uint32_t dma_periph, dma_channel_enum channelx, ui
\param[out] none
\retval none
*/
/**
* @brief dma_interrupt_enable 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] source 参数
* @return void
*/
void dma_interrupt_enable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source)
{
if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){
@@ -703,6 +782,13 @@ void dma_interrupt_enable(uint32_t dma_periph, dma_channel_enum channelx, uint32
\param[out] none
\retval none
*/
/**
* @brief dma_interrupt_disable 函数
* @param[in] dma_periph 参数
* @param[in] channelx 参数
* @param[in] source 参数
* @return void
*/
void dma_interrupt_disable(uint32_t dma_periph, dma_channel_enum channelx, uint32_t source)
{
if(ERROR == dma_periph_and_channel_check(dma_periph, channelx)){
+23 -2
View File
@@ -2,11 +2,11 @@
\file gd32c10x_exmc.c
\brief EXMC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,25 +35,41 @@ OF SUCH DAMAGE.
#include "gd32c10x_exmc.h"
/* EXMC bank0 register reset value */
/** @brief BANK0_SNCTL_RESET 宏定义 */
#define BANK0_SNCTL_RESET ((uint32_t)0x000030DBU)
/** @brief BANK0_SNTCFG_RESET 宏定义 */
#define BANK0_SNTCFG_RESET ((uint32_t)0x0FFFFFFFU)
/** @brief BANK0_SNWTCFG_RESET 宏定义 */
#define BANK0_SNWTCFG_RESET ((uint32_t)0x0FFFFFFFU)
/* EXMC register bit offset */
/** @brief SNCTL_NRMUX_OFFSET 宏定义 */
#define SNCTL_NRMUX_OFFSET ((uint32_t)1U)
/** @brief SNCTL_SBRSTEN_OFFSET 宏定义 */
#define SNCTL_SBRSTEN_OFFSET ((uint32_t)8U)
/** @brief SNCTL_WRAPEN_OFFSET 宏定义 */
#define SNCTL_WRAPEN_OFFSET ((uint32_t)10U)
/** @brief SNCTL_WREN_OFFSET 宏定义 */
#define SNCTL_WREN_OFFSET ((uint32_t)12U)
/** @brief SNCTL_NRWTEN_OFFSET 宏定义 */
#define SNCTL_NRWTEN_OFFSET ((uint32_t)13U)
/** @brief SNCTL_EXMODEN_OFFSET 宏定义 */
#define SNCTL_EXMODEN_OFFSET ((uint32_t)14U)
/** @brief SNCTL_ASYNCWAIT_OFFSET 宏定义 */
#define SNCTL_ASYNCWAIT_OFFSET ((uint32_t)15U)
/** @brief SNTCFG_AHLD_OFFSET 宏定义 */
#define SNTCFG_AHLD_OFFSET ((uint32_t)4U)
/** @brief SNTCFG_DSET_OFFSET 宏定义 */
#define SNTCFG_DSET_OFFSET ((uint32_t)8U)
/** @brief SNTCFG_BUSLAT_OFFSET 宏定义 */
#define SNTCFG_BUSLAT_OFFSET ((uint32_t)16U)
/** @brief SNWTCFG_WAHLD_OFFSET 宏定义 */
#define SNWTCFG_WAHLD_OFFSET ((uint32_t)4U)
/** @brief SNWTCFG_WDSET_OFFSET 宏定义 */
#define SNWTCFG_WDSET_OFFSET ((uint32_t)8U)
/** @brief SNWTCFG_WBUSLAT_OFFSET 宏定义 */
#define SNWTCFG_WBUSLAT_OFFSET ((uint32_t)16U)
/*!
@@ -130,6 +146,11 @@ void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_i
\param[out] none
\retval none
*/
/**
* @brief exmc_norsram_init 函数
* @param[in] exmc_norsram_init_struct 参数
* @return void
*/
void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct)
{
uint32_t snctl = 0x00000000U, sntcfg = 0x00000000U, snwtcfg = 0x00000000U;
+20 -6
View File
@@ -2,11 +2,11 @@
\file gd32c10x_exti.c
\brief EXTI driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -34,6 +34,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_exti.h"
/** @brief EXTI_REG_RESET_VALUE 宏定义 */
#define EXTI_REG_RESET_VALUE ((uint32_t)0x00000000U)
/*!
@@ -69,6 +70,13 @@ void exti_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief exti_init 函数
* @param[in] linex 参数
* @param[in] mode 参数
* @param[in] trig_type 参数
* @return void
*/
void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type)
{
/* reset the EXTI line x */
@@ -196,11 +204,14 @@ void exti_software_interrupt_disable(exti_line_enum linex)
*/
FlagStatus exti_flag_get(exti_line_enum linex)
{
FlagStatus ret;
if(RESET != (EXTI_PD & (uint32_t)linex)) {
return SET;
ret = SET;
} else {
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -226,11 +237,14 @@ void exti_flag_clear(exti_line_enum linex)
*/
FlagStatus exti_interrupt_flag_get(exti_line_enum linex)
{
FlagStatus ret;
if(RESET != (EXTI_PD & (uint32_t)linex)) {
return SET;
ret = SET;
} else {
return RESET;
ret = RESET;
}
return ret;
}
/*!
+59 -9
View File
@@ -2,11 +2,11 @@
\file gd32c10x_fmc.c
\brief FMC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -33,28 +33,47 @@ OF SUCH DAMAGE.
*/
#include "gd32c10x_fmc.h"
#include "stdio.h"
/* FMC mask */
/** @brief LOW_8BITS_MASK 宏定义 */
#define LOW_8BITS_MASK ((uint32_t)0x000000FFU)
/** @brief HIGH_8BITS_MASK 宏定义 */
#define HIGH_8BITS_MASK ((uint32_t)0x0000FF00U)
/** @brief LOW_8BITS_MASK1 宏定义 */
#define LOW_8BITS_MASK1 ((uint32_t)0x00FF0000U)
/** @brief HIGH_8BITS_MASK1 宏定义 */
#define HIGH_8BITS_MASK1 ((uint32_t)0xFF000000U)
/** @brief LOW_16BITS_MASK 宏定义 */
#define LOW_16BITS_MASK ((uint32_t)0x0000FFFFU)
/** @brief HIGH_16BITS_MASK 宏定义 */
#define HIGH_16BITS_MASK ((uint32_t)0xFFFF0000U)
/* USER of option bytes mask */
/** @brief OB_USER_MASK 宏定义 */
#define OB_USER_MASK ((uint8_t)0xF8U)
/* OB_WP mask */
/** @brief OB_WP0_MASK 宏定义 */
#define OB_WP0_MASK ((uint32_t)0x000000FFU)
/** @brief OB_WP1_MASK 宏定义 */
#define OB_WP1_MASK ((uint32_t)0x0000FF00U)
/** @brief OB_WP2_MASK 宏定义 */
#define OB_WP2_MASK ((uint32_t)0x00FF0000U)
/** @brief OB_WP3_MASK 宏定义 */
#define OB_WP3_MASK ((uint32_t)0xFF000000U)
/* return the FMC state */
/**
* @brief fmc_state_get 函数
* @return fmc_state_enum 返回值
*/
static fmc_state_enum fmc_state_get(void);
/* check FMC ready or not */
/**
* @brief fmc_ready_wait 函数
* @param[in] timeout 参数
* @return fmc_state_enum 返回值
*/
static fmc_state_enum fmc_ready_wait(uint32_t timeout);
/* FMC main memory programming functions */
@@ -447,6 +466,11 @@ fmc_state_enum ob_erase(void)
\arg FMC_PGAERR: program alignment error
\arg FMC_TOERR: timeout error
*/
/**
* @brief ob_write_protection_enable 函数
* @param[in] ob_wp 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum ob_write_protection_enable(uint32_t ob_wp)
{
uint32_t i;
@@ -506,9 +530,14 @@ fmc_state_enum ob_write_protection_enable(uint32_t ob_wp)
\arg FMC_PGAERR: program alignment error
\arg FMC_TOERR: timeout error
*/
/**
* @brief ob_security_protection_config 函数
* @param[in] ob_spc 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum ob_security_protection_config(uint8_t ob_spc)
{
uint8_t i;
uint32_t i;
uint32_t op_byte[4];
fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT);
for(i = 0U; i < 4U; i++){
@@ -572,9 +601,16 @@ fmc_state_enum ob_security_protection_config(uint8_t ob_spc)
\arg FMC_PGAERR: program alignment error
\arg FMC_TOERR: timeout error
*/
/**
* @brief ob_user_write 函数
* @param[in] ob_fwdgt 参数
* @param[in] ob_deepsleep 参数
* @param[in] ob_stdby 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum ob_user_write(uint8_t ob_fwdgt, uint8_t ob_deepsleep, uint8_t ob_stdby)
{
uint8_t i;
uint32_t i;
uint32_t temp;
uint32_t op_byte[4];
fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT);
@@ -582,7 +618,7 @@ fmc_state_enum ob_user_write(uint8_t ob_fwdgt, uint8_t ob_deepsleep, uint8_t ob_
for(i = 0U; i < 4U; i++){
op_byte[i] = OP_BYTE(i);
}
temp = ((uint8_t)((uint8_t)((uint8_t)(ob_fwdgt) | ob_deepsleep) | ob_stdby) | (OB_USER_MASK));
temp = (uint32_t)(((uint32_t)((uint32_t)(ob_fwdgt) | ob_deepsleep) | ob_stdby) | OB_USER_MASK);
op_byte[0] = ((uint32_t)(temp) << 16U) | ((op_byte[0] & LOW_16BITS_MASK));
if(FMC_READY == fmc_state){
@@ -632,7 +668,7 @@ fmc_state_enum ob_user_write(uint8_t ob_fwdgt, uint8_t ob_deepsleep, uint8_t ob_
fmc_state_enum ob_data_program(uint16_t ob_data)
{
uint8_t i;
uint32_t i;
uint32_t op_byte[4];
fmc_state_enum fmc_state = fmc_ready_wait(FMC_TIMEOUT_COUNT);
for(i = 0U; i < 4U; i++){
@@ -701,6 +737,11 @@ uint16_t fmc_ecc_address_get(void)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief fmc_ecc_flag_get 函数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus fmc_ecc_flag_get(uint32_t flag)
{
FlagStatus status = RESET;
@@ -726,6 +767,11 @@ FlagStatus fmc_ecc_flag_get(uint32_t flag)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief fmc_ecc_flag_clear 函数
* @param[in] flag 参数
* @return void
*/
void fmc_ecc_flag_clear(uint32_t flag)
{
FMC_ECCCS |= flag;
@@ -774,17 +820,21 @@ void fmc_ecc_interrupt_disable(uint32_t interrupt)
FlagStatus fmc_ecc_interrupt_flag_get(uint32_t int_flag)
{
uint32_t intenable = 0U, flagstatus = 0U;
FlagStatus status = RESET;
/* get the interrupt enable bit status */
intenable = (FMC_REG_VAL(int_flag) & BIT(FMC_BIT_POS(int_flag)));
/* get the corresponding flag bit status */
flagstatus = (FMC_REG_VAL2(int_flag) & BIT(FMC_BIT_POS2(int_flag)));
if(flagstatus && intenable) {
return SET;
status = SET;
} else {
return RESET;
status = RESET;
}
/* return the state of corresponding FMC interrupt flag */
return status;
}
/*!
+65 -41
View File
@@ -2,11 +2,11 @@
\file gd32c10x_fwdgt.c
\brief FWDGT driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -81,27 +81,33 @@ void fwdgt_enable(void)
\param[out] none
\retval ErrStatus: ERROR or SUCCESS
*/
/**
* @brief fwdgt_prescaler_value_config 函数
* @param[in] prescaler_value 参数
* @return ErrStatus 返回值
*/
ErrStatus fwdgt_prescaler_value_config(uint16_t prescaler_value)
{
uint32_t timeout = FWDGT_PSC_TIMEOUT;
uint32_t flag_status = RESET;
uint32_t flag_status;
ErrStatus status = SUCCESS;
/* enable write access to FWDGT_PSC */
FWDGT_CTL = FWDGT_WRITEACCESS_ENABLE;
/* configure FWDGT_PSC */
FWDGT_PSC = (uint32_t)prescaler_value;
/* wait until the PUD flag to be reset */
do {
flag_status = FWDGT_STAT & FWDGT_STAT_PUD;
} while((--timeout > 0U) && ((uint32_t)RESET != flag_status));
} while((--timeout > 0U) && (0U != flag_status));
if((uint32_t)RESET != flag_status) {
return ERROR;
if(0U != flag_status) {
status = ERROR;
}
/* configure FWDGT */
FWDGT_PSC = (uint32_t)prescaler_value;
return SUCCESS;
return status;
}
/*!
@@ -113,23 +119,25 @@ ErrStatus fwdgt_prescaler_value_config(uint16_t prescaler_value)
ErrStatus fwdgt_reload_value_config(uint16_t reload_value)
{
uint32_t timeout = FWDGT_RLD_TIMEOUT;
uint32_t flag_status = RESET;
uint32_t flag_status;
ErrStatus status = SUCCESS;
/* enable write access to FWDGT_RLD */
FWDGT_CTL = FWDGT_WRITEACCESS_ENABLE;
/* configure FWDGT_RLD */
FWDGT_RLD = RLD_RLD(reload_value);
/* wait until the RUD flag to be reset */
do {
flag_status = FWDGT_STAT & FWDGT_STAT_RUD;
} while((--timeout > 0U) && ((uint32_t)RESET != flag_status));
} while((--timeout > 0U) && (0U != flag_status));
if((uint32_t)RESET != flag_status) {
return ERROR;
if(0U != flag_status) {
status = ERROR;
}
FWDGT_RLD = RLD_RLD(reload_value);
return SUCCESS;
return status;
}
/*!
@@ -146,7 +154,7 @@ void fwdgt_counter_reload(void)
/*!
\brief configure counter reload value, and prescaler divider value
\param[in] reload_value: specify reload value(0x0000 - 0x0FFF)
\param[in] prescaler_div: FWDGT prescaler value
\param[in] prescaler_value: FWDGT prescaler value
only one parameter can be selected which is shown as below:
\arg FWDGT_PSC_DIV4: FWDGT prescaler set to 4
\arg FWDGT_PSC_DIV8: FWDGT prescaler set to 8
@@ -158,42 +166,57 @@ void fwdgt_counter_reload(void)
\param[out] none
\retval ErrStatus: ERROR or SUCCESS
*/
ErrStatus fwdgt_config(uint16_t reload_value, uint8_t prescaler_div)
/**
* @brief fwdgt_config 函数
* @param[in] reload_value 参数
* @param[in] prescaler_value 参数
* @return ErrStatus 返回值
*/
ErrStatus fwdgt_config(uint16_t reload_value, uint8_t prescaler_value)
{
uint32_t timeout = FWDGT_PSC_TIMEOUT;
uint32_t flag_status = RESET;
uint32_t flag_status;
ErrStatus status = SUCCESS;
/* start the free watchdog timer counter */
FWDGT_CTL = FWDGT_KEY_ENABLE;
/* enable write access to FWDGT_PSC,and FWDGT_RLD */
FWDGT_CTL = FWDGT_WRITEACCESS_ENABLE;
/* configure FWDGT_PSC */
FWDGT_PSC = (uint32_t)prescaler_value;
/* wait until the PUD flag to be reset */
do {
flag_status = FWDGT_STAT & FWDGT_STAT_PUD;
} while((--timeout > 0U) && ((uint32_t)RESET != flag_status));
} while((--timeout > 0U) && (0U != flag_status));
if((uint32_t)RESET != flag_status) {
return ERROR;
}
/* configure FWDGT */
FWDGT_PSC = (uint32_t)prescaler_div;
timeout = FWDGT_RLD_TIMEOUT;
/* wait until the RUD flag to be reset */
do {
flag_status = FWDGT_STAT & FWDGT_STAT_RUD;
} while((--timeout > 0U) && ((uint32_t)RESET != flag_status));
if((uint32_t)RESET != flag_status) {
return ERROR;
if(0U != flag_status) {
status = ERROR;
}
if(SUCCESS == status) {
/* configure FWDGT_RLD */
FWDGT_RLD = RLD_RLD(reload_value);
/* wait until the RUD flag to be reset */
timeout = FWDGT_RLD_TIMEOUT;
do {
flag_status = FWDGT_STAT & FWDGT_STAT_RUD;
} while((--timeout > 0U) && (0U != flag_status));
if(0U != flag_status) {
status = ERROR;
}
}
if(SUCCESS == status) {
/* reload the counter */
FWDGT_CTL = FWDGT_KEY_RELOAD;
}
return SUCCESS;
return status;
}
/*!
@@ -207,9 +230,10 @@ ErrStatus fwdgt_config(uint16_t reload_value, uint8_t prescaler_div)
*/
FlagStatus fwdgt_flag_get(uint16_t flag)
{
if(FWDGT_STAT & flag) {
return SET;
}
FlagStatus flag_status = RESET;
return RESET;
if(RESET != (FWDGT_STAT & flag)) {
flag_status = SET;
}
return flag_status;
}
+41 -8
View File
@@ -2,11 +2,11 @@
\file gd32c10x_gpio.c
\brief GPIO driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -34,6 +34,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_gpio.h"
/** @brief AFIO_EXTI_SOURCE_MASK 宏定义 */
#define AFIO_EXTI_SOURCE_MASK ((uint8_t)0x03U) /*!< AFIO exti source selection mask*/
#define AFIO_EXTI_SOURCE_FIELDS ((uint8_t)0x04U) /*!< select AFIO exti source registers */
#define LSB_16BIT_MASK ((uint16_t)0xFFFFU) /*!< LSB 16-bit mask */
@@ -120,6 +121,14 @@ void gpio_afio_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief gpio_init 函数
* @param[in] gpio_periph 参数
* @param[in] mode 参数
* @param[in] speed 参数
* @param[in] pin 参数
* @return void
*/
void gpio_init(uint32_t gpio_periph, uint32_t mode, uint32_t speed, uint32_t pin)
{
uint16_t i;
@@ -264,11 +273,13 @@ void gpio_port_write(uint32_t gpio_periph,uint16_t data)
*/
FlagStatus gpio_input_bit_get(uint32_t gpio_periph,uint32_t pin)
{
FlagStatus status = RESET;
if((uint32_t)RESET != (GPIO_ISTAT(gpio_periph)&(pin))){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
/*!
@@ -293,11 +304,13 @@ uint16_t gpio_input_port_get(uint32_t gpio_periph)
*/
FlagStatus gpio_output_bit_get(uint32_t gpio_periph, uint32_t pin)
{
FlagStatus status = RESET;
if((uint32_t)RESET !=(GPIO_OCTL(gpio_periph)&(pin))){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
/*!
@@ -348,6 +361,12 @@ uint16_t gpio_output_port_get(uint32_t gpio_periph)
\param[out] none
\retval none
*/
/**
* @brief gpio_pin_remap_config 函数
* @param[in] remap 参数
* @param[in] newvalue 参数
* @return void
*/
void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue)
{
uint32_t remap1 = 0U, remap2 = 0U, temp_reg = 0U, temp_mask = 0U;
@@ -405,6 +424,12 @@ void gpio_pin_remap_config(uint32_t remap, ControlStatus newvalue)
\param[out] none
\retval none
*/
/**
* @brief gpio_exti_source_select 函数
* @param[in] output_port 参数
* @param[in] output_pin 参数
* @return void
*/
void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin)
{
uint32_t source = 0U;
@@ -445,6 +470,12 @@ void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin)
\param[out] none
\retval none
*/
/**
* @brief gpio_event_output_config 函数
* @param[in] output_port 参数
* @param[in] output_pin 参数
* @return void
*/
void gpio_event_output_config(uint8_t output_port, uint8_t output_pin)
{
uint32_t reg = 0U;
@@ -530,9 +561,11 @@ void gpio_compensation_config(uint32_t compensation)
*/
FlagStatus gpio_compensation_flag_get(void)
{
FlagStatus status = RESET;
if(((uint32_t)RESET) != (AFIO_CPSCTL & AFIO_CPSCTL_CPS_RDY)){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
+56 -6
View File
@@ -2,11 +2,11 @@
\file gd32c10x_i2c.c
\brief I2C driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_i2c.h"
/* I2C register bit mask */
/** @brief I2CCLK_MAX 宏定义 */
#define I2CCLK_MAX ((uint32_t)0x0000003FU) /*!< i2cclk maximum value */
#define I2CCLK_MIN ((uint32_t)0x00000002U) /*!< i2cclk minimum value */
#define I2C_FLAG_MASK ((uint32_t)0x0000FFFFU) /*!< i2c flag mask */
@@ -42,6 +43,7 @@ OF SUCH DAMAGE.
#define I2C_ADDRESS2_MASK ((uint32_t)0x000000FEU) /*!< the second i2c address mask */
/* I2C register bit offset */
/** @brief STAT1_PECV_OFFSET 宏定义 */
#define STAT1_PECV_OFFSET ((uint32_t)0x00000008U) /*!< bit offset of PECV in I2C_STAT1 */
/*!
@@ -167,6 +169,14 @@ void i2c_clock_config(uint32_t i2c_periph, uint32_t clkspeed, uint32_t dutycyc)
\param[out] none
\retval none
*/
/**
* @brief i2c_mode_addr_config 函数
* @param[in] i2c_periph 参数
* @param[in] mode 参数
* @param[in] addformat 参数
* @param[in] addr 参数
* @return void
*/
void i2c_mode_addr_config(uint32_t i2c_periph, uint32_t mode, uint32_t addformat, uint32_t addr)
{
/* SMBus/I2C mode selected */
@@ -633,13 +643,21 @@ void i2c_sam_timeout_disable(uint32_t i2c_periph)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief i2c_flag_get 函数
* @param[in] i2c_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus i2c_flag_get(uint32_t i2c_periph, i2c_flag_enum flag)
{
FlagStatus status = RESET;
if(RESET != (I2C_REG_VAL(i2c_periph, flag) & BIT(I2C_BIT_POS(flag)))){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
/*!
@@ -662,6 +680,12 @@ FlagStatus i2c_flag_get(uint32_t i2c_periph, i2c_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief i2c_flag_clear 函数
* @param[in] i2c_periph 参数
* @param[in] flag 参数
* @return void
*/
void i2c_flag_clear(uint32_t i2c_periph, i2c_flag_enum flag)
{
if(I2C_FLAG_ADDSEND == flag){
@@ -688,6 +712,12 @@ void i2c_flag_clear(uint32_t i2c_periph, i2c_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief i2c_interrupt_enable 函数
* @param[in] i2c_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void i2c_interrupt_enable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
{
I2C_REG_VAL(i2c_periph, interrupt) |= BIT(I2C_BIT_POS(interrupt));
@@ -708,6 +738,12 @@ void i2c_interrupt_enable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
\param[out] none
\retval none
*/
/**
* @brief i2c_interrupt_disable 函数
* @param[in] i2c_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void i2c_interrupt_disable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
{
I2C_REG_VAL(i2c_periph, interrupt) &= ~BIT(I2C_BIT_POS(interrupt));
@@ -739,9 +775,16 @@ void i2c_interrupt_disable(uint32_t i2c_periph, i2c_interrupt_enum interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief i2c_interrupt_flag_get 函数
* @param[in] i2c_periph 参数
* @param[in] int_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus i2c_interrupt_flag_get(uint32_t i2c_periph, i2c_interrupt_flag_enum int_flag)
{
uint32_t intenable = 0U, flagstatus = 0U, bufie;
FlagStatus status = RESET;
/* check BUFIE */
bufie = I2C_CTL1(i2c_periph)&I2C_CTL1_BUFIE;
@@ -759,10 +802,11 @@ FlagStatus i2c_interrupt_flag_get(uint32_t i2c_periph, i2c_interrupt_flag_enum i
}
}
if((0U != flagstatus) && (0U != intenable)){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
/*!
@@ -785,6 +829,12 @@ FlagStatus i2c_interrupt_flag_get(uint32_t i2c_periph, i2c_interrupt_flag_enum i
\param[out] none
\retval none
*/
/**
* @brief i2c_interrupt_flag_clear 函数
* @param[in] i2c_periph 参数
* @param[in] int_flag 参数
* @return void
*/
void i2c_interrupt_flag_clear(uint32_t i2c_periph, i2c_interrupt_flag_enum int_flag)
{
if(I2C_INT_FLAG_ADDSEND == int_flag){
+14 -28
View File
@@ -2,11 +2,11 @@
\file gd32c10x_misc.c
\brief MISC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -63,45 +63,31 @@ void nvic_irq_enable(IRQn_Type nvic_irq,
uint8_t nvic_irq_pre_priority,
uint8_t nvic_irq_sub_priority)
{
uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U;
uint32_t nvic_prigroup, nvic_priority;
/* use the priority group value to get the temp_pre and the temp_sub */
switch((SCB->AIRCR) & (uint32_t)0x700U) {
/* check current priority group */
switch(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) {
case NVIC_PRIGROUP_PRE0_SUB4:
temp_pre = 0U;
temp_sub = 0x4U;
break;
case NVIC_PRIGROUP_PRE1_SUB3:
temp_pre = 1U;
temp_sub = 0x3U;
break;
case NVIC_PRIGROUP_PRE2_SUB2:
temp_pre = 2U;
temp_sub = 0x2U;
break;
case NVIC_PRIGROUP_PRE3_SUB1:
temp_pre = 3U;
temp_sub = 0x1U;
break;
case NVIC_PRIGROUP_PRE4_SUB0:
temp_pre = 4U;
temp_sub = 0x0U;
break;
default:
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
temp_pre = 2U;
temp_sub = 0x2U;
break;
}
/* get the temp_priority to fill the NVIC->IP register */
temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre);
temp_priority |= nvic_irq_sub_priority & (0x0FU >> (0x4U - temp_sub));
temp_priority = temp_priority << 0x04U;
NVIC->IP[nvic_irq] = (uint8_t)temp_priority;
/* get the priority group value */
nvic_prigroup = NVIC_GetPriorityGrouping();
/* encoding the pre-emption, subpriority priority */
nvic_priority = NVIC_EncodePriority(nvic_prigroup, (uint32_t)nvic_irq_pre_priority, (uint32_t)nvic_irq_sub_priority);
/* set priority */
NVIC_SetPriority(nvic_irq, nvic_priority);
/* enable the selected IRQ */
NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU);
NVIC_EnableIRQ(nvic_irq);
}
/*!
@@ -113,7 +99,7 @@ void nvic_irq_enable(IRQn_Type nvic_irq,
void nvic_irq_disable(IRQn_Type nvic_irq)
{
/* disable the selected IRQ.*/
NVIC->ICER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU);
NVIC->ICER[(uint32_t)nvic_irq >> 0x05U] = (uint32_t)0x01U << ((uint32_t)nvic_irq & (uint8_t)0x1FU);
}
/*!
+18 -4
View File
@@ -2,11 +2,11 @@
\file gd32c10x_pmu.c
\brief PMU driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -62,6 +62,11 @@ void pmu_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief pmu_lvd_select 函数
* @param[in] lvdt_n 参数
* @return void
*/
void pmu_lvd_select(uint32_t lvdt_n)
{
/* disable LVD */
@@ -139,6 +144,12 @@ void pmu_to_sleepmode(uint8_t sleepmodecmd)
\param[out] none
\retval none
*/
/**
* @brief pmu_to_deepsleepmode 函数
* @param[in] ldo 参数
* @param[in] deepsleepmodecmd 参数
* @return void
*/
void pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd)
{
static uint32_t reg_snap[4];
@@ -261,11 +272,14 @@ void pmu_backup_write_disable(void)
*/
FlagStatus pmu_flag_get(uint32_t flag)
{
FlagStatus ret;
if(RESET != (PMU_CS & flag)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
+100 -7
View File
@@ -2,11 +2,11 @@
\file gd32c10x_rcu.c
\brief RCU driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,24 +35,34 @@ OF SUCH DAMAGE.
#include "gd32c10x_rcu.h"
/* define clock source */
/** @brief SEL_IRC8M 宏定义 */
#define SEL_IRC8M ((uint16_t)0U) /* IRC8M is selected as CK_SYS */
/** @brief SEL_HXTAL 宏定义 */
#define SEL_HXTAL ((uint16_t)1U) /* HXTAL is selected as CK_SYS */
/** @brief SEL_PLL 宏定义 */
#define SEL_PLL ((uint16_t)2U) /* PLL is selected as CK_SYS */
/* define startup timeout count */
/** @brief OSC_STARTUP_TIMEOUT 宏定义 */
#define OSC_STARTUP_TIMEOUT ((uint32_t)0x000FFFFFU)
/** @brief LXTAL_STARTUP_TIMEOUT 宏定义 */
#define LXTAL_STARTUP_TIMEOUT ((uint32_t)0x03FFFFFFU)
/* ADC clock prescaler offset */
/** @brief RCU_ADC_PSC_OFFSET 宏定义 */
#define RCU_ADC_PSC_OFFSET ((uint32_t)14U)
/* RCU IRC8M adjust value mask and offset*/
/** @brief RCU_IRC8M_ADJUST_MASK 宏定义 */
#define RCU_IRC8M_ADJUST_MASK ((uint8_t)0x1FU)
/** @brief RCU_IRC8M_ADJUST_OFFSET 宏定义 */
#define RCU_IRC8M_ADJUST_OFFSET ((uint32_t)3U)
/* RCU PLL1 clock multiplication factor offset */
/** @brief RCU_CFG1_PLL1MF_OFFSET 宏定义 */
#define RCU_CFG1_PLL1MF_OFFSET ((uint32_t)8U)
/* RCU PREDV1 division factor offset*/
/** @brief RCU_CFG1_PREDV1_OFFSET 宏定义 */
#define RCU_CFG1_PREDV1_OFFSET ((uint32_t)4U)
/*!
@@ -115,6 +125,11 @@ void rcu_deinit(void)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_clock_enable 函数
* @param[in] periph 参数
* @return void
*/
void rcu_periph_clock_enable(rcu_periph_enum periph)
{
RCU_REG_VAL(periph) |= BIT(RCU_BIT_POS(periph));
@@ -146,6 +161,11 @@ void rcu_periph_clock_enable(rcu_periph_enum periph)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_clock_disable 函数
* @param[in] periph 参数
* @return void
*/
void rcu_periph_clock_disable(rcu_periph_enum periph)
{
RCU_REG_VAL(periph) &= ~BIT(RCU_BIT_POS(periph));
@@ -201,6 +221,11 @@ void rcu_periph_clock_sleep_disable(rcu_periph_sleep_enum periph)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_reset_enable 函数
* @param[in] periph_reset 参数
* @return void
*/
void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset)
{
RCU_REG_VAL(periph_reset) |= BIT(RCU_BIT_POS(periph_reset));
@@ -228,6 +253,11 @@ void rcu_periph_reset_enable(rcu_periph_reset_enum periph_reset)
\param[out] none
\retval none
*/
/**
* @brief rcu_periph_reset_disable 函数
* @param[in] periph_reset 参数
* @return void
*/
void rcu_periph_reset_disable(rcu_periph_reset_enum periph_reset)
{
RCU_REG_VAL(periph_reset) &= ~BIT(RCU_BIT_POS(periph_reset));
@@ -371,6 +401,11 @@ void rcu_apb2_clock_config(uint32_t ck_apb2)
\param[out] none
\retval none
*/
/**
* @brief rcu_ckout0_config 函数
* @param[in] ckout0_src 参数
* @return void
*/
void rcu_ckout0_config(uint32_t ckout0_src)
{
uint32_t reg;
@@ -520,6 +555,11 @@ void rcu_pll2_config(uint32_t pll_mul)
\param[out] none
\retval none
*/
/**
* @brief rcu_adc_clock_config 函数
* @param[in] adc_psc 参数
* @return void
*/
void rcu_adc_clock_config(uint32_t adc_psc)
{
uint32_t reg0,reg1;
@@ -565,7 +605,7 @@ void rcu_adc_clock_config(uint32_t adc_psc)
/*!
\brief configure the USBFS prescaler factor
\param[in] adc_div: USB prescaler factor
\param[in] usb_psc: USB prescaler factor
only one parameter can be selected which is shown as below:
\arg RCU_CKUSB_CKPLL_DIV1_5: USBFS prescaler select CK_PLL/1.5
\arg RCU_CKUSB_CKPLL_DIV1: USBFS prescaler select CK_PLL/1
@@ -577,6 +617,11 @@ void rcu_adc_clock_config(uint32_t adc_psc)
\param[out] none
\retval none
*/
/**
* @brief rcu_usb_clock_config 函数
* @param[in] usb_psc 参数
* @return void
*/
void rcu_usb_clock_config(uint32_t usb_psc)
{
uint32_t reg;
@@ -688,14 +733,23 @@ void rcu_ck48m_clock_config(uint32_t ck48m_clock_source)
\param[out] none
\retval none
*/
/**
* @brief rcu_flag_get 函数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus rcu_flag_get(rcu_flag_enum flag)
{
FlagStatus rcu_flag = RESET;
/* get the rcu flag */
if(RESET != (RCU_REG_VAL(flag) & BIT(RCU_BIT_POS(flag)))){
return SET;
rcu_flag = SET;
}else{
return RESET;
rcu_flag = RESET;
}
return rcu_flag;
}
/*!
@@ -725,14 +779,23 @@ void rcu_all_reset_flag_clear(void)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief rcu_interrupt_flag_get 函数
* @param[in] int_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag)
{
FlagStatus rcu_int_flag = RESET;
/* get the rcu interrupt flag */
if(RESET != (RCU_REG_VAL(int_flag) & BIT(RCU_BIT_POS(int_flag)))){
return SET;
rcu_int_flag = SET;
}else{
return RESET;
rcu_int_flag = RESET;
}
return rcu_int_flag;
}
/*!
@@ -751,6 +814,11 @@ FlagStatus rcu_interrupt_flag_get(rcu_int_flag_enum int_flag)
\param[out] none
\retval none
*/
/**
* @brief rcu_interrupt_flag_clear 函数
* @param[in] int_flag_clear 参数
* @return void
*/
void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag_clear)
{
RCU_REG_VAL(int_flag_clear) |= BIT(RCU_BIT_POS(int_flag_clear));
@@ -771,6 +839,11 @@ void rcu_interrupt_flag_clear(rcu_int_flag_clear_enum int_flag_clear)
\param[out] none
\retval none
*/
/**
* @brief rcu_interrupt_enable 函数
* @param[in] stab_int 参数
* @return void
*/
void rcu_interrupt_enable(rcu_int_enum stab_int)
{
RCU_REG_VAL(stab_int) |= BIT(RCU_BIT_POS(stab_int));
@@ -791,6 +864,11 @@ void rcu_interrupt_enable(rcu_int_enum stab_int)
\param[out] none
\retval none
*/
/**
* @brief rcu_interrupt_disable 函数
* @param[in] stab_int 参数
* @return void
*/
void rcu_interrupt_disable(rcu_int_enum stab_int)
{
RCU_REG_VAL(stab_int) &= ~BIT(RCU_BIT_POS(stab_int));
@@ -833,6 +911,11 @@ void rcu_lxtal_drive_capability_config(uint32_t lxtal_dricap)
\param[out] none
\retval ErrStatus: SUCCESS or ERROR
*/
/**
* @brief rcu_osci_stab_wait 函数
* @param[in] osci 参数
* @return ErrStatus 返回值
*/
ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci)
{
uint32_t stb_cnt = 0U;
@@ -966,6 +1049,11 @@ ErrStatus rcu_osci_stab_wait(rcu_osci_type_enum osci)
\param[out] none
\retval none
*/
/**
* @brief rcu_osci_on 函数
* @param[in] osci 参数
* @return void
*/
void rcu_osci_on(rcu_osci_type_enum osci)
{
RCU_REG_VAL(osci) |= BIT(RCU_BIT_POS(osci));
@@ -986,6 +1074,11 @@ void rcu_osci_on(rcu_osci_type_enum osci)
\param[out] none
\retval none
*/
/**
* @brief rcu_osci_off 函数
* @param[in] osci 参数
* @return void
*/
void rcu_osci_off(rcu_osci_type_enum osci)
{
RCU_REG_VAL(osci) &= ~BIT(RCU_BIT_POS(osci));
+15 -6
View File
@@ -2,11 +2,11 @@
\file gd32c10x_rtc.c
\brief RTC driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,10 +35,13 @@ OF SUCH DAMAGE.
#include "gd32c10x_rtc.h"
/* RTC register high / low bits mask */
/** @brief RTC_HIGH_BITS_MASK 宏定义 */
#define RTC_HIGH_BITS_MASK ((uint32_t)0x000F0000U) /* RTC high bits mask */
/** @brief RTC_LOW_BITS_MASK 宏定义 */
#define RTC_LOW_BITS_MASK ((uint32_t)0x0000FFFFU) /* RTC low bits mask */
/* RTC register high bits offset */
/** @brief RTC_HIGH_BITS_OFFSET 宏定义 */
#define RTC_HIGH_BITS_OFFSET ((uint32_t)16U)
/*!
@@ -191,11 +194,14 @@ uint32_t rtc_divider_get(void)
*/
FlagStatus rtc_flag_get(uint32_t flag)
{
FlagStatus ret;
if(RESET != (RTC_CTL & flag)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -227,11 +233,14 @@ void rtc_flag_clear(uint32_t flag)
*/
FlagStatus rtc_interrupt_flag_get(uint32_t flag)
{
FlagStatus ret;
if(RESET != (RTC_CTL & flag)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
+61 -30
View File
@@ -2,11 +2,11 @@
\file gd32c10x_spi.c
\brief SPI driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -34,21 +34,31 @@ OF SUCH DAMAGE.
#include "gd32c10x_spi.h"
/** @brief SPI_ERROR_HANDLE 宏定义 */
#define SPI_ERROR_HANDLE(s) do{}while(1)
/* SPI/I2S parameter initialization mask */
/** @brief SPI_INIT_MASK 宏定义 */
#define SPI_INIT_MASK ((uint32_t)0x00003040U) /* SPI parameter initialization mask */
/** @brief I2S_INIT_MASK 宏定义 */
#define I2S_INIT_MASK ((uint32_t)0x0000F047U) /* I2S parameter initialization mask */
/* I2S clock source selection, multiplication and division mask */
/** @brief I2S1_CLOCK_SEL 宏定义 */
#define I2S1_CLOCK_SEL ((uint32_t)0x00020000U) /* I2S1 clock source selection */
/** @brief I2S2_CLOCK_SEL 宏定义 */
#define I2S2_CLOCK_SEL ((uint32_t)0x00040000U) /* I2S2 clock source selection */
/** @brief I2S_CLOCK_MUL_MASK 宏定义 */
#define I2S_CLOCK_MUL_MASK ((uint32_t)0x0000F000U) /* I2S clock multiplication mask */
/** @brief I2S_CLOCK_DIV_MASK 宏定义 */
#define I2S_CLOCK_DIV_MASK ((uint32_t)0x000000F0U) /* I2S clock division mask */
/* default value and offset */
/** @brief SPI_I2SPSC_DEFAULT_VALUE 宏定义 */
#define SPI_I2SPSC_DEFAULT_VALUE ((uint32_t)0x00000002U) /* default value of SPI_I2SPSC register */
/** @brief RCU_CFG1_PREDV1_OFFSET 宏定义 */
#define RCU_CFG1_PREDV1_OFFSET 4U /* PREDV1 offset in RCU_CFG1 */
/** @brief RCU_CFG1_PLL2MF_OFFSET 宏定义 */
#define RCU_CFG1_PLL2MF_OFFSET 12U /* PLL2MF offset in RCU_CFG1 */
/*!
@@ -115,6 +125,12 @@ void spi_struct_para_init(spi_parameter_struct* spi_struct)
\param[out] none
\retval none
*/
/**
* @brief spi_init 函数
* @param[in] spi_periph 参数
* @param[in] spi_struct 参数
* @return void
*/
void spi_init(uint32_t spi_periph, spi_parameter_struct* spi_struct)
{
uint32_t reg = 0U;
@@ -187,6 +203,14 @@ void spi_disable(uint32_t spi_periph)
\param[out] none
\retval none
*/
/**
* @brief i2s_init 函数
* @param[in] spi_periph 参数
* @param[in] mode 参数
* @param[in] standard 参数
* @param[in] ckpl 参数
* @return void
*/
void i2s_init(uint32_t spi_periph, uint32_t mode, uint32_t standard, uint32_t ckpl)
{
uint32_t reg = 0U;
@@ -233,6 +257,14 @@ void i2s_init(uint32_t spi_periph, uint32_t mode, uint32_t standard, uint32_t ck
\param[out] none
\retval none
*/
/**
* @brief i2s_psc_config 函数
* @param[in] spi_periph 参数
* @param[in] audiosample 参数
* @param[in] frameformat 参数
* @param[in] mckout 参数
* @return void
*/
void i2s_psc_config(uint32_t spi_periph, uint32_t audiosample, uint32_t frameformat, uint32_t mckout)
{
uint32_t i2sdiv = 2U, i2sof = 0U;
@@ -564,11 +596,14 @@ void spi_crc_next(uint32_t spi_periph)
*/
uint16_t spi_crc_get(uint32_t spi_periph,uint8_t crc)
{
uint16_t crc_value;
if(SPI_CRC_TX == crc){
return ((uint16_t)(SPI_TCRC(spi_periph)));
crc_value = ((uint16_t)(SPI_TCRC(spi_periph)));
}else{
return ((uint16_t)(SPI_RCRC(spi_periph)));
crc_value = ((uint16_t)(SPI_RCRC(spi_periph)));
}
return crc_value;
}
/*!
@@ -670,28 +705,6 @@ void spi_quad_read_enable(uint32_t spi_periph)
SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_QRD;
}
/*!
\brief enable SPI quad wire mode SPI_IO2 and SPI_IO3 pin output
\param[in] spi_periph: SPIx(only x=0)
\param[out] none
\retval none
*/
void spi_quad_io23_output_enable(uint32_t spi_periph)
{
SPI_QCTL(spi_periph) |= (uint32_t)SPI_QCTL_IO23_DRV;
}
/*!
\brief disable SPI quad wire mode SPI_IO2 and SPI_IO3 pin output
\param[in] spi_periph: SPIx(only x=0)
\param[out] none
\retval none
*/
void spi_quad_io23_output_disable(uint32_t spi_periph)
{
SPI_QCTL(spi_periph) &= (uint32_t)(~SPI_QCTL_IO23_DRV);
}
/*!
\brief enable SPI and I2S interrupt
\param[in] spi_periph: SPIx(x=0,1,2)
@@ -771,8 +784,15 @@ void spi_i2s_interrupt_disable(uint32_t spi_periph, uint8_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief spi_i2s_interrupt_flag_get 函数
* @param[in] spi_periph 参数
* @param[in] interrupt 参数
* @return FlagStatus 返回值
*/
FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt)
{
FlagStatus status = RESET;
uint32_t reg1 = SPI_STAT(spi_periph);
uint32_t reg2 = SPI_CTL1(spi_periph);
@@ -817,10 +837,12 @@ FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt)
}
/* get SPI/I2S interrupt flag status */
if((0U != reg1) && (0U != reg2)){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
/*!
@@ -845,11 +867,20 @@ FlagStatus spi_i2s_interrupt_flag_get(uint32_t spi_periph, uint8_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief spi_i2s_flag_get 函数
* @param[in] spi_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus spi_i2s_flag_get(uint32_t spi_periph, uint32_t flag)
{
FlagStatus status = RESET;
if(RESET != (SPI_STAT(spi_periph) & flag)){
return SET;
status = SET;
}else{
return RESET;
status = RESET;
}
return status;
}
+233 -6
View File
@@ -2,11 +2,11 @@
\file gd32c10x_timer.c
\brief TIMER driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,6 +35,7 @@ OF SUCH DAMAGE.
#include "gd32c10x_timer.h"
/* TIMER init parameter mask */
/** @brief ALIGNEDMODE_MASK 宏定义 */
#define ALIGNEDMODE_MASK ((uint32_t)0x00000060U) /*!< TIMER init parameter aligne dmode mask */
#define COUNTERDIRECTION_MASK ((uint32_t)0x00000010U) /*!< TIMER init parameter counter direction mask */
#define CLOCKDIVISION_MASK ((uint32_t)0x00000300U) /*!< TIMER init parameter clock division value mask */
@@ -154,6 +155,12 @@ void timer_struct_para_init(timer_parameter_struct* initpara)
\param[out] none
\retval none
*/
/**
* @brief timer_init 函数
* @param[in] timer_periph 参数
* @param[in] initpara 参数
* @return void
*/
void timer_init(uint32_t timer_periph, timer_parameter_struct* initpara)
{
/* configure the counter prescaler value */
@@ -431,6 +438,12 @@ void timer_update_source_config(uint32_t timer_periph, uint32_t update)
\param[out] none
\retval none
*/
/**
* @brief timer_dma_enable 函数
* @param[in] timer_periph 参数
* @param[in] dma 参数
* @return void
*/
void timer_dma_enable(uint32_t timer_periph, uint16_t dma)
{
TIMER_DMAINTEN(timer_periph) |= (uint32_t) dma;
@@ -451,6 +464,12 @@ void timer_dma_enable(uint32_t timer_periph, uint16_t dma)
\param[out] none
\retval none
*/
/**
* @brief timer_dma_disable 函数
* @param[in] timer_periph 参数
* @param[in] dma 参数
* @return void
*/
void timer_dma_disable(uint32_t timer_periph, uint16_t dma)
{
TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)(dma));
@@ -507,6 +526,13 @@ void timer_channel_dma_request_source_select(uint32_t timer_periph, uint32_t dma
\param[out] none
\retval none
*/
/**
* @brief timer_dma_transfer_config 函数
* @param[in] timer_periph 参数
* @param[in] dma_baseaddr 参数
* @param[in] dma_lenth 参数
* @return void
*/
void timer_dma_transfer_config(uint32_t timer_periph, uint32_t dma_baseaddr, uint32_t dma_lenth)
{
TIMER_DMACFG(timer_periph) &= (~(uint32_t)(TIMER_DMACFG_DMATA | TIMER_DMACFG_DMATC));
@@ -529,6 +555,12 @@ void timer_dma_transfer_config(uint32_t timer_periph, uint32_t dma_baseaddr, uin
\param[out] none
\retval none
*/
/**
* @brief timer_event_software_generate 函数
* @param[in] timer_periph 参数
* @param[in] event 参数
* @return void
*/
void timer_event_software_generate(uint32_t timer_periph, uint16_t event)
{
TIMER_SWEVG(timer_periph) |= (uint32_t)event;
@@ -566,6 +598,12 @@ void timer_break_struct_para_init(timer_break_parameter_struct* breakpara)
\param[out] none
\retval none
*/
/**
* @brief timer_break_config 函数
* @param[in] timer_periph 参数
* @param[in] breakpara 参数
* @return void
*/
void timer_break_config(uint32_t timer_periph, timer_break_parameter_struct* breakpara)
{
TIMER_CCHP(timer_periph) = (uint32_t)(((uint32_t)(breakpara->runoffstate)) |
@@ -710,6 +748,13 @@ void timer_channel_output_struct_para_init(timer_oc_parameter_struct* ocpara)
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocpara 参数
* @return void
*/
void timer_channel_output_config(uint32_t timer_periph, uint16_t channel, timer_oc_parameter_struct* ocpara)
{
switch(channel){
@@ -852,6 +897,13 @@ void timer_channel_output_config(uint32_t timer_periph, uint16_t channel, timer_
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_mode_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocmode 参数
* @return void
*/
void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel, uint16_t ocmode)
{
switch(channel){
@@ -893,6 +945,13 @@ void timer_channel_output_mode_config(uint32_t timer_periph, uint16_t channel, u
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_pulse_value_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] pulse 参数
* @return void
*/
void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t channel, uint32_t pulse)
{
switch(channel){
@@ -933,6 +992,13 @@ void timer_channel_output_pulse_value_config(uint32_t timer_periph, uint16_t cha
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_shadow_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocshadow 参数
* @return void
*/
void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel, uint16_t ocshadow)
{
switch(channel){
@@ -977,6 +1043,13 @@ void timer_channel_output_shadow_config(uint32_t timer_periph, uint16_t channel,
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_fast_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocfast 参数
* @return void
*/
void timer_channel_output_fast_config(uint32_t timer_periph, uint16_t channel, uint16_t ocfast)
{
switch(channel){
@@ -1021,6 +1094,13 @@ void timer_channel_output_fast_config(uint32_t timer_periph, uint16_t channel, u
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_clear_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] occlear 参数
* @return void
*/
void timer_channel_output_clear_config(uint32_t timer_periph, uint16_t channel, uint16_t occlear)
{
switch(channel){
@@ -1065,6 +1145,13 @@ void timer_channel_output_clear_config(uint32_t timer_periph, uint16_t channel,
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_polarity_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocpolarity 参数
* @return void
*/
void timer_channel_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocpolarity)
{
switch(channel){
@@ -1108,6 +1195,13 @@ void timer_channel_output_polarity_config(uint32_t timer_periph, uint16_t channe
\param[out] none
\retval none
*/
/**
* @brief timer_channel_complementary_output_polarity_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocnpolarity 参数
* @return void
*/
void timer_channel_complementary_output_polarity_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnpolarity)
{
switch(channel){
@@ -1147,6 +1241,13 @@ void timer_channel_complementary_output_polarity_config(uint32_t timer_periph, u
\param[out] none
\retval none
*/
/**
* @brief timer_channel_output_state_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] state 参数
* @return void
*/
void timer_channel_output_state_config(uint32_t timer_periph, uint16_t channel, uint32_t state)
{
switch(channel){
@@ -1190,6 +1291,13 @@ void timer_channel_output_state_config(uint32_t timer_periph, uint16_t channel,
\param[out] none
\retval none
*/
/**
* @brief timer_channel_complementary_output_state_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] ocnstate 参数
* @return void
*/
void timer_channel_complementary_output_state_config(uint32_t timer_periph, uint16_t channel, uint16_t ocnstate)
{
switch(channel){
@@ -1248,6 +1356,13 @@ void timer_channel_input_struct_para_init(timer_ic_parameter_struct* icpara)
\param[out] none
\retval none
*/
/**
* @brief timer_input_capture_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] icpara 参数
* @return void
*/
void timer_input_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpara)
{
switch(channel){
@@ -1353,6 +1468,13 @@ void timer_input_capture_config(uint32_t timer_periph, uint16_t channel, timer_i
\param[out] none
\retval none
*/
/**
* @brief timer_channel_input_capture_prescaler_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] prescaler 参数
* @return void
*/
void timer_channel_input_capture_prescaler_config(uint32_t timer_periph, uint16_t channel, uint16_t prescaler)
{
switch(channel){
@@ -1436,6 +1558,13 @@ uint32_t timer_channel_capture_value_register_read(uint32_t timer_periph, uint16
\param[out] none
\retval none
*/
/**
* @brief timer_input_pwm_capture_config 函数
* @param[in] timer_periph 参数
* @param[in] channel 参数
* @param[in] icpwm 参数
* @return void
*/
void timer_input_pwm_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpwm)
{
uint16_t icpolarity = 0x0U;
@@ -1570,6 +1699,12 @@ void timer_hall_mode_config(uint32_t timer_periph, uint32_t hallmode)
\param[out] none
\retval none
*/
/**
* @brief timer_input_trigger_source_select 函数
* @param[in] timer_periph 参数
* @param[in] intrigger 参数
* @return void
*/
void timer_input_trigger_source_select(uint32_t timer_periph, uint32_t intrigger)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_TRGS);
@@ -1592,6 +1727,12 @@ void timer_input_trigger_source_select(uint32_t timer_periph, uint32_t intrigger
\param[out] none
\retval none
*/
/**
* @brief timer_master_output_trigger_source_select 函数
* @param[in] timer_periph 参数
* @param[in] outrigger 参数
* @return void
*/
void timer_master_output_trigger_source_select(uint32_t timer_periph, uint32_t outrigger)
{
TIMER_CTL1(timer_periph) &= (~(uint32_t)TIMER_CTL1_MMC);
@@ -1615,6 +1756,12 @@ void timer_master_output_trigger_source_select(uint32_t timer_periph, uint32_t o
\retval none
*/
/**
* @brief timer_slave_mode_select 函数
* @param[in] timer_periph 参数
* @param[in] slavemode 参数
* @return void
*/
void timer_slave_mode_select(uint32_t timer_periph, uint32_t slavemode)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)TIMER_SMCFG_SMC);
@@ -1659,6 +1806,14 @@ void timer_master_slave_mode_config(uint32_t timer_periph, uint32_t masterslave)
\param[out] none
\retval none
*/
/**
* @brief timer_external_trigger_config 函数
* @param[in] timer_periph 参数
* @param[in] extprescaler 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_trigger_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter)
{
TIMER_SMCFG(timer_periph) &= (~(uint32_t)(TIMER_SMCFG_ETP | TIMER_SMCFG_ETPSC | TIMER_SMCFG_ETFC));
@@ -1685,6 +1840,14 @@ void timer_external_trigger_config(uint32_t timer_periph, uint32_t extprescaler,
\param[out] none
\retval none
*/
/**
* @brief timer_quadrature_decoder_mode_config 函数
* @param[in] timer_periph 参数
* @param[in] decomode 参数
* @param[in] ic0polarity 参数
* @param[in] ic1polarity 参数
* @return void
*/
void timer_quadrature_decoder_mode_config(uint32_t timer_periph, uint32_t decomode, uint16_t ic0polarity, uint16_t ic1polarity)
{
/* configure the quadrature decoder mode */
@@ -1745,6 +1908,14 @@ void timer_internal_trigger_as_external_clock_config(uint32_t timer_periph, uint
\param[out] none
\retval none
*/
/**
* @brief timer_external_trigger_as_external_clock_config 函数
* @param[in] timer_periph 参数
* @param[in] extrigger 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_trigger_as_external_clock_config(uint32_t timer_periph, uint32_t extrigger, uint16_t extpolarity, uint32_t extfilter)
{
if(TIMER_SMCFG_TRGSEL_CI1FE1 == extrigger){
@@ -1807,6 +1978,14 @@ void timer_external_trigger_as_external_clock_config(uint32_t timer_periph, uint
\param[out] none
\retval none
*/
/**
* @brief timer_external_clock_mode0_config 函数
* @param[in] timer_periph 参数
* @param[in] extprescaler 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_clock_mode0_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter)
{
/* configure TIMER external trigger input */
@@ -1834,6 +2013,14 @@ void timer_external_clock_mode0_config(uint32_t timer_periph, uint32_t extpresca
\param[out] none
\retval none
*/
/**
* @brief timer_external_clock_mode1_config 函数
* @param[in] timer_periph 参数
* @param[in] extprescaler 参数
* @param[in] extpolarity 参数
* @param[in] extfilter 参数
* @return void
*/
void timer_external_clock_mode1_config(uint32_t timer_periph, uint32_t extprescaler, uint32_t extpolarity, uint32_t extfilter)
{
/* configure TIMER external trigger input */
@@ -1910,6 +2097,12 @@ void timer_output_value_selection_config(uint32_t timer_periph, uint16_t outsel)
\param[out] none
\retval none
*/
/**
* @brief timer_interrupt_enable 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt)
{
TIMER_DMAINTEN(timer_periph) |= (uint32_t) interrupt;
@@ -1931,6 +2124,12 @@ void timer_interrupt_enable(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief timer_interrupt_disable 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void timer_interrupt_disable(uint32_t timer_periph, uint32_t interrupt)
{
TIMER_DMAINTEN(timer_periph) &= (~(uint32_t)interrupt);
@@ -1952,15 +2151,23 @@ void timer_interrupt_disable(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief timer_interrupt_flag_get 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return FlagStatus 返回值
*/
FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t interrupt)
{
uint32_t val;
FlagStatus ret;
val = (TIMER_DMAINTEN(timer_periph) & interrupt);
if((RESET != (TIMER_INTF(timer_periph) & interrupt)) && (RESET != val)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -1979,6 +2186,12 @@ FlagStatus timer_interrupt_flag_get(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval none
*/
/**
* @brief timer_interrupt_flag_clear 函数
* @param[in] timer_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void timer_interrupt_flag_clear(uint32_t timer_periph, uint32_t interrupt)
{
TIMER_INTF(timer_periph) = (~(uint32_t)interrupt);
@@ -2004,13 +2217,21 @@ void timer_interrupt_flag_clear(uint32_t timer_periph, uint32_t interrupt)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief timer_flag_get 函数
* @param[in] timer_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus timer_flag_get(uint32_t timer_periph, uint32_t flag)
{
FlagStatus ret;
if(RESET != (TIMER_INTF(timer_periph) & flag)){
return SET;
ret = SET;
}else{
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -2033,6 +2254,12 @@ FlagStatus timer_flag_get(uint32_t timer_periph, uint32_t flag)
\param[out] none
\retval none
*/
/**
* @brief timer_flag_clear 函数
* @param[in] timer_periph 参数
* @param[in] flag 参数
* @return void
*/
void timer_flag_clear(uint32_t timer_periph, uint32_t flag)
{
TIMER_INTF(timer_periph) = (~(uint32_t)flag);
@@ -2,11 +2,11 @@
\file gd32c10x_usart.c
\brief USART driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -35,8 +35,11 @@ OF SUCH DAMAGE.
#include "gd32c10x_usart.h"
/* USART register bit offset */
/** @brief GP_GUAT_OFFSET 宏定义 */
#define GP_GUAT_OFFSET ((uint32_t)8U) /* bit offset of GUAT in USART_GP */
/** @brief CTL3_SCRTNUM_OFFSET 宏定义 */
#define CTL3_SCRTNUM_OFFSET ((uint32_t)1U) /* bit offset of SCRTNUM in USART_CTL3 */
/** @brief RT_BL_OFFSET 宏定义 */
#define RT_BL_OFFSET ((uint32_t)24U) /* bit offset of BL in USART_RT */
/*!
@@ -261,6 +264,12 @@ void usart_data_first_config(uint32_t usart_periph, uint32_t msbf)
\param[out] none
\retval none
*/
/**
* @brief usart_invert_config 函数
* @param[in] usart_periph 参数
* @param[in] invertpara 参数
* @return void
*/
void usart_invert_config(uint32_t usart_periph, usart_invert_enum invertpara)
{
/* inverted or not the specified signal */
@@ -514,6 +523,14 @@ void usart_synchronous_clock_disable(uint32_t usart_periph)
\param[out] none
\retval none
*/
/**
* @brief usart_synchronous_clock_config 函数
* @param[in] usart_periph 参数
* @param[in] clen 参数
* @param[in] cph 参数
* @param[in] cpl 参数
* @return void
*/
void usart_synchronous_clock_config(uint32_t usart_periph, uint32_t clen, uint32_t cph, uint32_t cpl)
{
USART_CTL1(usart_periph) &= ~(USART_CTL1_CLEN | USART_CTL1_CPH | USART_CTL1_CPL);
@@ -755,13 +772,22 @@ void usart_hardware_flow_coherence_config(uint32_t usart_periph, uint32_t hcm)
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief usart_flag_get 函数
* @param[in] usart_periph 参数
* @param[in] flag 参数
* @return FlagStatus 返回值
*/
FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag)
{
FlagStatus ret;
if(RESET != (USART_REG_VAL(usart_periph, flag) & BIT(USART_BIT_POS(flag)))) {
return SET;
ret = SET;
} else {
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -779,6 +805,12 @@ FlagStatus usart_flag_get(uint32_t usart_periph, usart_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief usart_flag_clear 函数
* @param[in] usart_periph 参数
* @param[in] flag 参数
* @return void
*/
void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag)
{
if(USART_FLAG_EPERR == flag) {
@@ -806,6 +838,12 @@ void usart_flag_clear(uint32_t usart_periph, usart_flag_enum flag)
\param[out] none
\retval none
*/
/**
* @brief usart_interrupt_enable 函数
* @param[in] usart_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrupt)
{
USART_REG_VAL(usart_periph, interrupt) |= BIT(USART_BIT_POS(interrupt));
@@ -829,6 +867,12 @@ void usart_interrupt_enable(uint32_t usart_periph, usart_interrupt_enum interrup
\param[out] none
\retval none
*/
/**
* @brief usart_interrupt_disable 函数
* @param[in] usart_periph 参数
* @param[in] interrupt 参数
* @return void
*/
void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interrupt)
{
USART_REG_VAL(usart_periph, interrupt) &= ~BIT(USART_BIT_POS(interrupt));
@@ -855,19 +899,27 @@ void usart_interrupt_disable(uint32_t usart_periph, usart_interrupt_enum interru
\param[out] none
\retval FlagStatus: SET or RESET
*/
/**
* @brief usart_interrupt_flag_get 函数
* @param[in] usart_periph 参数
* @param[in] int_flag 参数
* @return FlagStatus 返回值
*/
FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_enum int_flag)
{
uint32_t intenable = 0U, flagstatus = 0U;
FlagStatus ret;
/* get the interrupt enable bit status */
intenable = (USART_REG_VAL(usart_periph, int_flag) & BIT(USART_BIT_POS(int_flag)));
/* get the corresponding flag bit status */
flagstatus = (USART_REG_VAL2(usart_periph, int_flag) & BIT(USART_BIT_POS2(int_flag)));
if((0U != flagstatus) && (0U != intenable)) {
return SET;
ret = SET;
} else {
return RESET;
ret = RESET;
}
return ret;
}
/*!
@@ -884,6 +936,12 @@ FlagStatus usart_interrupt_flag_get(uint32_t usart_periph, usart_interrupt_flag_
\param[out] none
\retval none
*/
/**
* @brief usart_interrupt_flag_clear 函数
* @param[in] usart_periph 参数
* @param[in] int_flag 参数
* @return void
*/
void usart_interrupt_flag_clear(uint32_t usart_periph, usart_interrupt_flag_enum int_flag)
{
USART_REG_VAL2(usart_periph, int_flag) = ~BIT(USART_BIT_POS2(int_flag));
@@ -2,11 +2,11 @@
\file gd32c10x_wwdgt.c
\brief WWDGT driver
\version 2025-08-07, V1.6.0, firmware for GD32C10x
\version 2026-01-30, V1.7.0, firmware for GD32C10x
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Copyright (c) 2026, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -81,6 +81,13 @@ void wwdgt_counter_update(uint16_t counter_value)
\param[out] none
\retval none
*/
/**
* @brief wwdgt_config 函数
* @param[in] counter 参数
* @param[in] window 参数
* @param[in] prescaler 参数
* @return void
*/
void wwdgt_config(uint16_t counter, uint16_t window, uint32_t prescaler)
{
WWDGT_CTL = (uint32_t)(CTL_CNT(counter));
@@ -95,11 +102,13 @@ void wwdgt_config(uint16_t counter, uint16_t window, uint32_t prescaler)
*/
FlagStatus wwdgt_flag_get(void)
{
FlagStatus flag_status = RESET;
if(RESET != (WWDGT_STAT & WWDGT_STAT_EWIF)) {
return SET;
flag_status = SET;
}
return RESET;
return flag_status;
}
/*!
@@ -1,3 +1,9 @@
/**
* @file RTE/Device/GD32C103CB/regions_GD32C103CB.h
* @brief GD32 RTE 外设库或运行时配置源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef REGIONS_GD32C103CB_H
#define REGIONS_GD32C103CB_H
@@ -42,6 +42,7 @@
#define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */
/* Vector Table base offset */
/** @brief APP_BASE 宏定义 */
#define APP_BASE ((uint32_t)0x08012000U) /*!< main FLASH base address */
#define VECT_TAB_OFFSET (uint32_t)APP_BASE-FLASH_BASE /* This value must be a multiple of 0x200. */
@@ -62,6 +63,7 @@
/* The following is to prevent Vcore fluctuations caused by frequency switching.
It is strongly recommended to include it to avoid issues caused by self-removal. */
/** @brief RCU_MODIFY_4 宏定义 */
#define RCU_MODIFY_4(__delay) do{ \
volatile uint32_t i, reg; \
if(0 != __delay){ \
@@ -103,49 +105,101 @@
} \
}while(0)
/** @brief SEL_IRC8M 宏定义 */
#define SEL_IRC8M 0x00U
/** @brief SEL_HXTAL 宏定义 */
#define SEL_HXTAL 0x01U
/** @brief SEL_PLL 宏定义 */
#define SEL_PLL 0x02U
/* set the system clock frequency and declare the system clock configuration function */
#ifdef __SYSTEM_CLOCK_IRC8M
uint32_t SystemCoreClock = __SYSTEM_CLOCK_IRC8M;
/**
* @brief system_clock_8m_irc8m 函数
* @return void
*/
static void system_clock_8m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_IRC8M;
/**
* @brief system_clock_48m_irc8m 函数
* @return void
*/
static void system_clock_48m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M;
/**
* @brief system_clock_72m_irc8m 函数
* @return void
*/
static void system_clock_72m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_IRC8M;
/**
* @brief system_clock_108m_irc8m 函数
* @return void
*/
static void system_clock_108m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC8M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_IRC8M;
/**
* @brief system_clock_120m_irc8m 函数
* @return void
*/
static void system_clock_120m_irc8m(void);
#elif defined (__SYSTEM_CLOCK_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL;
/**
* @brief system_clock_hxtal 函数
* @return void
*/
static void system_clock_hxtal(void);
#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_48M_PLL_HXTAL;
/**
* @brief system_clock_48m_hxtal 函数
* @return void
*/
static void system_clock_48m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL;
/**
* @brief system_clock_72m_hxtal 函数
* @return void
*/
static void system_clock_72m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_108M_PLL_HXTAL;
/**
* @brief system_clock_108m_hxtal 函数
* @return void
*/
static void system_clock_108m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_HXTAL;
/**
* @brief system_clock_120m_hxtal 函数
* @return void
*/
static void system_clock_120m_hxtal(void);
#endif /* __SYSTEM_CLOCK_IRC8M */
/* configure the system clock */
/**
* @brief system_clock_config 函数
* @return void
*/
static void system_clock_config(void);
/* software delay to prevent the impact of Vcore fluctuations.
It is strongly recommended to include it to avoid issues caused by self-removal. */
/**
* @brief _soft_delay_ 函数
* @param[in] time 参数
* @return void
*/
static void _soft_delay_(uint32_t time)
{
__IO uint32_t i;
+19 -19
View File
@@ -1,6 +1,6 @@
/*
* CSOLUTION generated file: DO NOT EDIT!
* Generated by: csolution version 2.12.0+p41-gfeadfc5e
* Generated by: csolution version 2.12.0
*
* Project: 'Robot_V2.Debug+GD32C103CB'
* Target: 'Debug+GD32C103CB'
@@ -15,41 +15,41 @@
*/
#define CMSIS_device_header "gd32c10x.h"
/* GigaDevice::Device:GD32C10x_StdPeripherals:ADC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:ADC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_ADC
/* GigaDevice::Device:GD32C10x_StdPeripherals:BKP@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:BKP@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_BKP
/* GigaDevice::Device:GD32C10x_StdPeripherals:CAN@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:CAN@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_CAN
/* GigaDevice::Device:GD32C10x_StdPeripherals:CRC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:CRC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_CRC
/* GigaDevice::Device:GD32C10x_StdPeripherals:DBG@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:DBG@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_DBG
/* GigaDevice::Device:GD32C10x_StdPeripherals:DMA@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:DMA@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_DMA
/* GigaDevice::Device:GD32C10x_StdPeripherals:EXTI@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:EXTI@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_EXTI
/* GigaDevice::Device:GD32C10x_StdPeripherals:FMC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:FMC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_FMC
/* GigaDevice::Device:GD32C10x_StdPeripherals:FWDGT@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:FWDGT@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_FWDGT
/* GigaDevice::Device:GD32C10x_StdPeripherals:GPIO@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:GPIO@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_GPIO
/* GigaDevice::Device:GD32C10x_StdPeripherals:MISC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:MISC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_MISC
/* GigaDevice::Device:GD32C10x_StdPeripherals:PMU@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:PMU@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_PMU
/* GigaDevice::Device:GD32C10x_StdPeripherals:RCU@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:RCU@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_RCU
/* GigaDevice::Device:GD32C10x_StdPeripherals:RTC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:RTC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_RTC
/* GigaDevice::Device:GD32C10x_StdPeripherals:SPI_I2S@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:SPI_I2S@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_SPI
/* GigaDevice::Device:GD32C10x_StdPeripherals:TIMER@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:TIMER@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_TIMER
/* GigaDevice::Device:GD32C10x_StdPeripherals:USART@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:USART@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_USART
/* GigaDevice::Device:GD32C10x_StdPeripherals:WWDGT@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:WWDGT@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_WWDGT
@@ -1,6 +1,6 @@
/*
* CSOLUTION generated file: DO NOT EDIT!
* Generated by: csolution version 2.12.0+p41-gfeadfc5e
* Generated by: csolution version 2.13.0+p3-g40b6564e
*
* Project: 'Robot_V2.Release+GD32C103CB'
* Target: 'Release+GD32C103CB'
@@ -15,41 +15,41 @@
*/
#define CMSIS_device_header "gd32c10x.h"
/* GigaDevice::Device:GD32C10x_StdPeripherals:ADC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:ADC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_ADC
/* GigaDevice::Device:GD32C10x_StdPeripherals:BKP@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:BKP@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_BKP
/* GigaDevice::Device:GD32C10x_StdPeripherals:CAN@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:CAN@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_CAN
/* GigaDevice::Device:GD32C10x_StdPeripherals:CRC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:CRC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_CRC
/* GigaDevice::Device:GD32C10x_StdPeripherals:DBG@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:DBG@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_DBG
/* GigaDevice::Device:GD32C10x_StdPeripherals:DMA@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:DMA@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_DMA
/* GigaDevice::Device:GD32C10x_StdPeripherals:EXTI@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:EXTI@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_EXTI
/* GigaDevice::Device:GD32C10x_StdPeripherals:FMC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:FMC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_FMC
/* GigaDevice::Device:GD32C10x_StdPeripherals:FWDGT@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:FWDGT@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_FWDGT
/* GigaDevice::Device:GD32C10x_StdPeripherals:GPIO@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:GPIO@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_GPIO
/* GigaDevice::Device:GD32C10x_StdPeripherals:MISC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:MISC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_MISC
/* GigaDevice::Device:GD32C10x_StdPeripherals:PMU@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:PMU@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_PMU
/* GigaDevice::Device:GD32C10x_StdPeripherals:RCU@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:RCU@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_RCU
/* GigaDevice::Device:GD32C10x_StdPeripherals:RTC@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:RTC@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_RTC
/* GigaDevice::Device:GD32C10x_StdPeripherals:SPI_I2S@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:SPI_I2S@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_SPI
/* GigaDevice::Device:GD32C10x_StdPeripherals:TIMER@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:TIMER@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_TIMER
/* GigaDevice::Device:GD32C10x_StdPeripherals:USART@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:USART@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_USART
/* GigaDevice::Device:GD32C10x_StdPeripherals:WWDGT@1.0.0 */
/* GigaDevice::Device:GD32C10x_StdPeripherals:WWDGT@1.3.0 */
#define RTE_DEVICE_STDPERIPHERALS_WWDGT
+6 -2
View File
@@ -4,6 +4,8 @@ project:
files:
- file: main.h
- file: main.cpp
- file: tasks.h
- file: tasks.cpp
- group: sys
files:
- file: sys/systick.cpp
@@ -12,6 +14,8 @@ project:
- file: sys/initial.h
- file: sys/health.h
- file: sys/health.cpp
- file: sys/run_inhibit.h
- file: sys/run_inhibit.cpp
- file: sys/flash.h
- file: sys/flash.cpp
- file: sys/config.h
@@ -25,7 +29,6 @@ project:
- file: device/adc.h
- file: device/Timer.cpp
- file: device/Timer.h
- file: device/test.cpp
- file: device/motor.cpp
- file: device/motor.h
- group: Algorithm
@@ -59,7 +62,7 @@ project:
- component: Device:GD32C10x_StdPeripherals:BKP
- component: Device:GD32C10x_StdPeripherals:CRC
- component: Device:GD32C10x_StdPeripherals:RTC
- component: CMSIS:DSP
- component: CMSIS:DSP&Source
output:
type:
- elf
@@ -69,6 +72,7 @@ project:
packs:
- pack: ARM::CMSIS
- pack: GigaDevice::GD32C10x_DFP
- pack: ARM::CMSIS-DSP
misc:
- for-compiler: AC6
C-CPP:
+368 -113
View File
@@ -1,146 +1,364 @@
/**
* @file device/ADC.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "adc.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
#include "config.h"
#include "initial.h"
#include "math.h"
#include "systick.h"
#include <cstring>
#include <gd32c10x_dma.h>
#include <cstdint>
#include <cstdio>
#include <cstring>
// ADC 换算常量均为编译期常量,非可变全局状态。
// 用于 ADC 原始值到电压、温度、电流的固定比例换算。
/** @brief ADC_SCALE 常量定义 */
constexpr float ADC_SCALE = 3.3f / 4096.0f;
/** @brief TEMP_FACTOR 常量定义 */
constexpr float TEMP_FACTOR = 1000.0f / 4.3f;
/** @brief VCC_FACTOR 常量定义 */
constexpr float VCC_FACTOR = 11.0f;
#if GATE_DRIVER_DRV8323
/** @brief CURRENT_FACTOR 常量定义(新板:DRV8323 片上 CSA)
* SOx = VREF/2 ± I×R×G(SPI 已配置 VREF_DIV=1、GAIN 见 config.h)。
* ADC_SCALE 输出为伏特,故系数单位为 mA/V = 1000/(G×R):
* G=10V/V、R=10mΩ → 10000 mA/V,量程约 ±16.5A。
* 修改 config.h 的 DRV8323_CSA_GAIN_VV 后此系数自动跟随。 */
constexpr float CURRENT_FACTOR =
1000.0f / (DRV8323_CSA_GAIN_VV * DRV8323_SHUNT_OHM);
#elif CURRENT_SENSE_INA240
/** @brief CURRENT_FACTOR 常量定义(低边采样板:下桥臂 10mΩ + INA240A2)
* 电流链路单位为安培:get_current() 返回 A,cal_Iq_Id/Can 上报处 ×1000 转 mA。
* OUT = 1.65V ± I×R×G(REF1=AGND、REF2=A3V3,芯片内部分压取中点 1.65V),
* G=50V/V、R=10mΩ → 0.5V/A → 2.0 A/V,ADC 3.3V 满量程对应约 ±3.3A。
* 与老板外置放大链(50倍×10mΩ)标定数值恰好一致;换增益/阻值时此系数
* 随 config.h 的 INA240_CSA_GAIN_VV / INA240_SHUNT_OHM 自动跟随。 */
constexpr float CURRENT_FACTOR =
1.0f / (INA240_CSA_GAIN_VV * INA240_SHUNT_OHM);
#else
/** @brief CURRENT_FACTOR 常量定义(老板:外置放大链,保持原标定) */
constexpr float CURRENT_FACTOR =
1.0f / 50.0f * 100.0f; // 50倍放大,100倍电流采样电阻, 单位mA
#endif
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controlroller FOC;
extern FOC_Controller FOC;
// 全局采样器单例:系统只有一组 ADC 采样缓存,由 hardware_init() 调用 sampler.init() 初始化。
// ADC0 只跑 injected 组(PWM 同步):中断写 adc_current_value[](U/V/W)与
// adc_mcu_temp_raw(温度,挂注入序列尾部同步转换)并置位 adc_current_frame_ready。
// VBUS / DRV温度 由 ADC1 在慢速任务中软件触发轮询,与电流采样彻底隔离。
// 背景:此前温度/VBUS/DRV 放在 ADC0 regular 组连续扫描,与 injected 抢占同一 ADC。
// 温度通道提到 239.5 周期后,稳态下注入触发几乎必然打断温度采样中段:
// regular 被打断后从 rank0 重启而 DMA 计数不复位 → adc_value[] 数据错位;
// 采样电容长期挂在高内阻温度源电压上,干扰注入组首通道 → 相电流检测故障。
sampler sampler; // 采样器初始化
u16 adc_value[6] = {0}; // 显式初始化数组元素为0
volatile u16 adc_mcu_temp_raw = 0; // ADC0 injected rank3: MCU温度原始值
static volatile u16 adc_current_value[3] = {0}; // injected 同步采样: U / V / W
static volatile u8 adc_current_frame_ready = 0;
// ---- 三相电流零点配平结果(上电一次,hardware_init 在 gate_driver_bringup
// 之后调用 zero_calibrate 采集)。含义:各通道实测零点相对 CURRENT_OFFSET
// 基准的偏差(V),采样换算时逐通道扣除,吸收 REF 中点偏差、AGND/PGND 地差
// 与 INA 失调。初始 0 = 未配平(按标称零点换算)。
static float adc_zero_offset_v[3] = {0.0f, 0.0f, 0.0f};
/**
* @brief 将电流 ADC 原始值换算为电流值
* @param[in] raw_adc 注入组转换原始值
* @param[in] ch 通道号 0/1/2 = U/V/W(逐通道扣除上电配平零点偏差)
* @return float 返回值
* @note 函数名沿用历史(_to_ma),实际返回单位是安培:MIN/MAX_CURRENT(±40)
* 为安培阈值,cal_Iq_Id / CAN 上报处再 ×1000 转 mA。
*/
static inline float current_adc_to_ma(u16 raw_adc, u8 ch) {
return (raw_adc * ADC_SCALE + CURRENT_OFFSET - adc_zero_offset_v[ch]) *
CURRENT_FACTOR;
}
// ADC1 连续转换运行标志:adc1_config() 启动后置位,
// adc1_read_raw() 据此判断是否需要暂停/恢复连续模式。
static bool adc1_continuous_active = false;
/*!
\brief configure the DMA peripheral
\brief configure the ADC1 peripheral(VBUS 连续转换 / DRV温度 单次轮询)
\param[in] none
\param[out] none
\retval none
\note ADC1 独占慢速量:ch6(VBUS) 连续转换,读取零等待;ch9(DRV NTC)
由 adc1_read_raw() 按需暂停连续模式单次轮询。无 DMA、无中断,
与 ADC0 的 PWM 同步注入组互不干扰(独立外设)。
原实现每次读取都要"软件触发+死等EOC":fast_voltage_check
10kHz 调用,每次阻塞 ~1.5μs,纯浪费在等待上。
*/
void dma_config(void) {
/* enable DMA0 clock */
rcu_periph_clock_enable(RCU_DMA0);
/* ADC_DMA_channel configuration */
dma_parameter_struct dma_data_parameter;
void adc1_config(void) {
/* enable ADC1 clock(ADC 时钟分频与 ADC0 共用 RCU 配置) */
rcu_periph_clock_enable(RCU_ADC1);
/* ADC DMA_channel configuration */
dma_deinit(DMA0, DMA_CH0);
adc_deinit(ADC1);
adc_mode_config(ADC_MODE_FREE);
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
/* initialize DMA single data mode */
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_data_parameter.memory_addr = (uint32_t)(adc_value);
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_data_parameter.number = 6;
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
/* VBUS 单通道连续转换:软件触发一次后自持续 */
adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 1);
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5);
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL,
ADC0_1_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);
dma_circulation_enable(DMA0, DMA_CH0);
adc_enable(ADC1);
/* ADC校准(校准完成后再进入连续模式,避免校准期间 CONT 位参与) */
adc_calibration_enable(ADC1);
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE);
/* enable DMA channel */
dma_channel_enable(DMA0, DMA_CH0);
/* 启动连续转换并等待首个结果,保证 DR 立即有效 */
adc_flag_clear(ADC1, ADC_FLAG_EOC);
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
}
adc_flag_clear(ADC1, ADC_FLAG_EOC);
adc1_continuous_active = true;
}
/*!
\brief configure the ADC peripheral
\brief 零等待读取最新 VBUS 转换结果
\retval u16 最近一次完成转换的原始值
\note 仅在 VBUS 连续模式运行时调用(主循环任务上下文)。
读 DR 硬件清 EOC,下一次完成转换会重新刷新 DR;两次读取之间
完成的转换被静默丢弃(F1 型 ADC 无溢出标志问题),
读到的值陈旧度 ≤1 个转换周期(41 周期 @30MHz ≈ 1.4μs)。
*/
u16 adc1_read_vbus_latest(void) {
return (u16)adc_regular_data_read(ADC1);
}
/*!
\brief ADC1 单次软件触发转换并等待完成
\param[in] channel ADC 通道号(如 ADC_CHANNEL_9)
\retval u16 转换原始值(超时返回上次 RDATA 残留值)
\note 仅在主循环任务上下文调用(所有任务由调度器串行派发,无并发,
与 fast_voltage_check / update_vbus_voltage 不会交错执行)。
单次转换约 41 周期 ≈ 1.4μs @30MHz ADC 时钟。
连续模式运行中调用时:暂停→单次转换→恢复,VBUS 流中断约 3~5μs。
*/
u16 adc1_read_raw(u8 channel) {
const bool was_continuous = adc1_continuous_active;
u16 timeout = 0;
if (was_continuous) {
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, DISABLE);
/* 先清 EOC 再等置位:连续模式下 EOC 常态置位(DR 久未读,
* 后续转换溢出丢失),直接等会观察到陈旧标志而另有转换在飞行中。
* 清除后观察到的置位必然来自"暂停瞬间在飞行中"的最后一次转换
* 完成后才置位——此时 ADC 真正静止,后续配置/触发不会再被
* 飞行中的转换干扰(SWSTART 被忽略、读到上一通道的 DR,
* 曾表现为 DRV 温度偶发出错)。超时兜底覆盖"暂停恰好落在
* 两次转换间隙"的无在飞行转换情形。 */
adc_flag_clear(ADC1, ADC_FLAG_EOC);
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
/* 超时只需覆盖一个转换周期(41 ADC 周期 ≈ 20 次循环),
* 仅"暂停恰好落在转换间隙"时才会走到超时 */
if (++timeout > 100U) break;
}
adc_flag_clear(ADC1, ADC_FLAG_EOC);
}
adc_regular_channel_config(ADC1, 0, channel, ADC_SAMPLETIME_28POINT5);
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
timeout = 0;
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
if (++timeout > 2000U) { // 硬件异常保护,避免死等
break;
}
}
u16 raw = (u16)adc_regular_data_read(ADC1);
adc_flag_clear(ADC1, ADC_FLAG_EOC);
if (was_continuous) {
/* 恢复 VBUS 连续转换 */
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_6,
ADC_SAMPLETIME_28POINT5);
adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE);
adc_software_trigger_enable(ADC1, ADC_REGULAR_CHANNEL);
/* 等恢复后的首个 ch6 转换完成并读走(清 EOC),
* 避免 vbus 零等待读取在接下来的 ~1.4μs 窗口内
* 读到 ch9 的残留值 */
timeout = 0;
while (adc_flag_get(ADC1, ADC_FLAG_EOC) == RESET) {
if (++timeout > 2000U) break;
}
(void)adc_regular_data_read(ADC1);
}
return raw;
}
/*!
\brief configure the ADC0 peripheral(PWM 同步电流采样专用)
\param[in] none
\param[out] none
\retval none
\note ADC0 只保留 injected 组,regular 组整体移除:
此前 regular 连续扫描与 injected 触发抢占同一 ADC,注入打断后
regular 从头重启而 DMA 不复位导致数据错位,且温度长采样被打断后
采样电容残余污染注入组首通道。移除 regular 后注入组再无打断源。
*/
void adc_config(void) {
/* 电流采样引脚配模拟输入:U/V/W = PA3/PA4/PA5(ADC0_IN3/4/5)。
* 手册要求 ADC 引脚必须配 AIN——复位默认浮空输入态下数字输入缓冲器
* 挂在引脚上,静态 1.65V 中点正处于线性区,开关噪声环境下会向引脚
* 注入电荷、污染 13.5 周期短采样窗口(v1 外置放大链下凑合未暴露)。
* 顺手关掉该三脚的数字输入缓冲(低功耗&抗扰)。 */
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_2MHZ,
GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);
/* enable ADC clock */
rcu_periph_clock_enable(RCU_ADC0);
/* config ADC clock */
/* config ADC clock(ADC0/ADC1 共用分频:120MHz/4 = 30MHz) */
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4);
/* 复位ADC */
adc_deinit(ADC0);
/* ADC模式配置 */
adc_mode_config(ADC_MODE_FREE);
/* 使能ADC连续转换功能 */
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
/* 使能ADC扫描模式 */
/* 扫描模式:注入序列内 4 通道触发后自动连续转换 */
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
/* ADC数据右对齐配置 */
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
/* 使能ADC温度传感器和内部参考电压 */
/* 使能ADC温度传感器(内部通道仅连接 ADC0 的 ch16/ch17) */
adc_tempsensor_vrefint_enable();
/* 配置ADC通道长度 */
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 6);
/* 配置ADC规则通道 */
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_16,
/* injected 组:rank0=U, rank1=V, rank2=W, rank3=MCU温度,TIMER1 TRGO 触发。
* 电流通道用最短 13.5 周期采样(INA240 低阻输出足以驱动采样电容):
* 每通道 26 周期,U/V/W 的采样保持窗末端仅滞后触发点 0.45/1.32/2.18µs
* (54/158/262 个计数),三相样本整体压进下管导通窗头部,最大占空比
* ~90% 时仍全部有效;温度通道 ch16 高内阻按手册仍需 239.5 周期,挂尾部。
* 整段 26×3+252 = 330 周期 = 11µs。
* 触发点紧贴计数峰顶(见 config.h):中心对齐下 CH3 每周期匹配两次
* (上升沿 2989 / 下降沿 3011,仅差 22 计数),无论第二次触发是被 ADC
* 忽略(注入忙)还是重启序列,帧的等效采样位置都贴着峰顶——三相下管
* 同时导通窗的正中,帧率 20kHz 与控制环同频。
* 序列空闲期采样电容挂在温度通道(~1.3V),U 相(ch3 运放低阻输出)的
* 13.5 周期保持窗足以完成重建。 */
adc_channel_length_config(ADC0, ADC_INSERTED_CHANNEL, 4);
adc_inserted_channel_config(ADC0, 0, ADC_CHANNEL_3, ADC_SAMPLETIME_13POINT5);
adc_inserted_channel_config(ADC0, 1, ADC_CHANNEL_4, ADC_SAMPLETIME_13POINT5);
adc_inserted_channel_config(ADC0, 2, ADC_CHANNEL_5, ADC_SAMPLETIME_13POINT5);
adc_inserted_channel_config(ADC0, 3, ADC_CHANNEL_16,
ADC_SAMPLETIME_239POINT5);
adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_9, ADC_SAMPLETIME_28POINT5);
adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_3, ADC_SAMPLETIME_28POINT5);
adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_4, ADC_SAMPLETIME_28POINT5);
adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_5, ADC_SAMPLETIME_28POINT5);
adc_regular_channel_config(ADC0, 5, ADC_CHANNEL_6, ADC_SAMPLETIME_28POINT5);
/* ADC触发配置 */
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL,
ADC0_1_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_external_trigger_source_config(ADC0, ADC_INSERTED_CHANNEL,
ADC0_1_EXTTRIG_INSERTED_T1_TRGO);
adc_external_trigger_config(ADC0, ADC_INSERTED_CHANNEL, ENABLE);
/* 使能ADC接口 */
adc_enable(ADC0);
/* ADC校准 */
adc_calibration_enable(ADC0);
/* 使能ADC DMA功能 */
adc_dma_mode_enable(ADC0);
/* 使能ADC软件触发 */
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
/* injected 完成中断负责搬运完整三相样本与温度原始值 */
adc_interrupt_enable(ADC0, ADC_INT_EOIC);
}
/**
* @brief 采样类初始化
*/
u8 sampler::init() {
dma_config(); // DMA采样ADC初始化
adc_config(); // ADC初始化
adc_config(); // ADC0:PWM 同步注入组(U/V/W + MCU温度)
adc1_config(); // ADC1:VBUS / DRV温度 慢速轮询
current_sample_head = 0;
adc_current_frame_ready = 0;
delay_1ms(1);
const float current_init = current_adc_to_ma(
static_cast<u16>((-CURRENT_OFFSET / ADC_SCALE) + 0.5f), 0); // 配平前按标称零点
// 初始采样
for (int i = 0; i < ADC_FILTER_SIZE + 1; i++) {
for (int i = 0; i < ADC_FILTER_SIZE; i++) {
mcu_temp_samples[i] =
(1.43f - adc_value[0] * ADC_SCALE) * TEMP_FACTOR + 35.0f;
(1.43f - adc_mcu_temp_raw * ADC_SCALE) * TEMP_FACTOR + 35.0f;
currU_samples[i] =
(adc_value[2] * ADC_SCALE + CURRENT_OFFSET) * CURRENT_FACTOR;
currV_samples[i] =
(adc_value[3] * ADC_SCALE + CURRENT_OFFSET) * CURRENT_FACTOR;
currW_samples[i] =
(adc_value[4] * ADC_SCALE + CURRENT_OFFSET) * CURRENT_FACTOR;
currU_samples[i] = current_init;
currV_samples[i] = current_init;
currW_samples[i] = current_init;
vbus_samples[i] = adc_value[5] * ADC_SCALE * VCC_FACTOR;
vbus_samples[i] = adc1_read_vbus_latest() * ADC_SCALE * VCC_FACTOR;
float R_now = (float)adc_value[1] / (4096 - (float)adc_value[1]) * 4700;
const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9);
float R_now = drv_raw / (4096 - drv_raw) * 4700;
drv_temp_samples[i] =
1.0f / (0.00335f + 0.00025f * log(R_now / 10000.0f)) - 273.15f;
delay_1ms(1);
}
return 0;
}
// ---- 三相电流零点配平(上电一次,hardware_init 在 gate_driver_bringup 之后调用) ----
// 必须在门驱动唤醒后进行:DRV8323 板唤醒后 INLx=DRVSLP 常高,0% 占空比下
// 三相下管导通、采样电阻经下管接地,INA240 输入有确定共模(0V),输出即
// 真实零点;若在芯片睡眠 Hi-Z 时采样,下管源极悬空,INA240 IN+ 浮空会把
// 输出拉到轨,配平值无效(老板下管常通,两种时序均有效,但统一放后面)。
// 采集 N 帧均值作为每通道零点偏差(V,相对 CURRENT_OFFSET 基准),折算
// 电流超过 CURRENT_ZERO_CAL_LIMIT 视为电流检测初始化错误。
/**
* @brief 三相电流零点配平并校验
* @return u8 0=成功;1=无采样帧(ADC/触发链路异常);
* 2=配平值超过 CURRENT_ZERO_CAL_LIMIT(电流检测初始化错误)
* @note 阻塞约 15ms(跳过 5ms 上电瞬态 + 采集 200 帧 @20kHz),仅限
* hardware_init 上下文调用(看门狗在其后才初始化,无喂狗需求)。
* main 循环尚未启动,期间 EOIC 中断只刷新 adc_current_value,
* 不会与 update_current 并发写环形缓冲。
*/
u8 sampler::zero_calibrate(void) {
delay_1ms(5); // 跳过上电瞬态(INA 建立、驱动使能稳定),≈100 帧
u32 zero_acc[3] = {0, 0, 0};
const u16 ZERO_CAL_FRAMES = 200;
for (u16 i = 0; i < ZERO_CAL_FRAMES; i++) {
u32 timeout = 0;
while (adc_current_frame_ready == 0U) {
/* 帧周期 50µs,超时覆盖 ~30ms 无帧:ADC/触发链路异常 */
if (++timeout > 600000U) {
printf("[ADC] zero cal fail: no sample frame\r\n");
return 1;
}
}
zero_acc[0] += adc_current_value[0];
zero_acc[1] += adc_current_value[1];
zero_acc[2] += adc_current_value[2];
adc_current_frame_ready = 0U;
}
u8 result = 0;
printf("[ADC] zero cal:");
for (u8 ch = 0; ch < 3U; ch++) {
/* 实测零点偏差(V)= 样本均值电压 + CURRENT_OFFSET 基准 */
adc_zero_offset_v[ch] =
((float)zero_acc[ch] / (float)ZERO_CAL_FRAMES) * ADC_SCALE +
CURRENT_OFFSET;
/* 配平值折算电流(A)超限 → 电流检测初始化错误 */
float offset_a = adc_zero_offset_v[ch] * CURRENT_FACTOR;
printf(" %c=%dmA", "UVW"[ch], (int)(offset_a * 1000.0f));
if (offset_a > CURRENT_ZERO_CAL_LIMIT ||
offset_a < -CURRENT_ZERO_CAL_LIMIT) {
result = 2;
}
}
if (result == 2) {
printf(" -> over limit, current sense init error\r\n");
} else {
printf(" ok\r\n");
}
return result;
}
/**
* @brief 获取MCU温度
* @return float
* 温度值,单位为摄氏度,范围应在-40到145之间,如果读取失败则返回0.0f
*/
float sampler::get_mcu_temp() {
tempora_sample = medium_filter(mcu_temp_samples, ADC_FILTER_SIZE + 1);
// 检查温度读数是否在有效范围内
if (tempora_sample < MIN_TEMP or tempora_sample > MAX_TEMP) {
return ERROR_ADC_VAL;
}
return tempora_sample;
return cached_mcu_temp;
}
/**
@@ -151,12 +369,7 @@ float sampler::get_mcu_temp() {
* 如果计算结果超出有效范围(-40到255摄氏度),则重新采样计算。
*/
float sampler::get_drv_temp() {
tempora_sample = medium_filter(drv_temp_samples, ADC_FILTER_SIZE + 1);
// 检查温度读数是否在有效范围内
if (tempora_sample < MIN_TEMP || tempora_sample > MAX_TEMP) {
return ERROR_ADC_VAL;
}
return tempora_sample;
return cached_drv_temp;
}
/**
@@ -167,20 +380,19 @@ float sampler::get_drv_temp() {
* 通过ADC采样计算各相电流值
*/
float sampler::get_current(char type) {
u8 idx = (current_sample_head == 0U) ? (ADC_FILTER_SIZE - 1U)
: (current_sample_head - 1U);
switch (type) {
case 'U': {
// return currU_samples[current_sample_index];
tempora_sample = medium_filter(currU_samples, ADC_FILTER_SIZE + 1);
tempora_sample = currU_samples[idx];
break;
}
case 'V': {
// return currV_samples[current_sample_index];
tempora_sample = medium_filter(currV_samples, ADC_FILTER_SIZE + 1);
tempora_sample = currV_samples[idx];
break;
}
case 'W': {
// return currW_samples[current_sample_index];
tempora_sample = medium_filter(currW_samples, ADC_FILTER_SIZE + 1);
tempora_sample = currW_samples[idx];
break;
}
default: {
@@ -202,13 +414,7 @@ float sampler::get_current(char type) {
* 如果计算结果超出有效范围(-40到255伏特),则重新采样计算。
*/
float sampler::get_vbus_voltage() {
tempora_sample = medium_filter(vbus_samples, ADC_FILTER_SIZE + 1);
// 检查电压读数是否在有效范围内
if (vbus_samples[ADC_FILTER_SIZE / 2] > MAX_VCC or
vbus_samples[ADC_FILTER_SIZE / 2] < MIN_VCC) {
return ERROR_ADC_VAL;
}
return tempora_sample;
return cached_vbus;
}
/**
@@ -218,50 +424,99 @@ float sampler::get_vbus_voltage() {
* 如果计算结果超出有效范围(-40到255摄氏度),则重新采样计算。
*/
void sampler::update_mcu_temp() {
memmove(mcu_temp_samples, mcu_temp_samples + 1,
ADC_FILTER_SIZE * sizeof(float));
mcu_temp_samples[ADC_FILTER_SIZE] =
(1.43f - adc_value[0] * ADC_SCALE) * TEMP_FACTOR + 35.0f;
// 环形缓冲区写入,替代 memmove
mcu_temp_samples[mcu_temp_head] =
(1.43f - adc_mcu_temp_raw * ADC_SCALE) * TEMP_FACTOR + 35.0f;
mcu_temp_head++;
if (mcu_temp_head >= ADC_FILTER_SIZE) mcu_temp_head = 0;
// 更新缓存:中值滤波
tempora_sample = medium_filter(mcu_temp_samples, ADC_FILTER_SIZE);
if (tempora_sample < MIN_TEMP || tempora_sample > MAX_TEMP) {
cached_mcu_temp = ERROR_ADC_VAL;
} else {
cached_mcu_temp = tempora_sample;
}
}
/**
* @brief 更新MCU温度
*/
void sampler::update_drv_temp() {
memmove(drv_temp_samples, drv_temp_samples + 1,
ADC_FILTER_SIZE * sizeof(float));
float R_now = (float)adc_value[1] / (4096 - (float)adc_value[1]) * 4700;
drv_temp_samples[ADC_FILTER_SIZE] =
// ADC1 单次轮询转换 ch9(NTC 分压,低阻源)
const float drv_raw = (float)adc1_read_raw(ADC_CHANNEL_9);
// 环形缓冲区写入,替代 memmove
float R_now = drv_raw / (4096 - drv_raw) * 4700;
drv_temp_samples[drv_temp_head] =
1.0f / (0.00335f + 0.00025f * log(R_now / 10000.0f)) - 273.15f;
drv_temp_head++;
if (drv_temp_head >= ADC_FILTER_SIZE) drv_temp_head = 0;
// 更新缓存:中值滤波
tempora_sample = medium_filter(drv_temp_samples, ADC_FILTER_SIZE);
if (tempora_sample < MIN_TEMP || tempora_sample > MAX_TEMP) {
cached_drv_temp = ERROR_ADC_VAL;
} else {
cached_drv_temp = tempora_sample;
}
}
/**
* @brief 更新相电流
*/
void sampler::update_current() {
u8 idx = current_sample_index;
extern FOC_Controlroller FOC;
// 优化计算
const float scale = ADC_SCALE * CURRENT_FACTOR;
const float offset = CURRENT_OFFSET * CURRENT_FACTOR;
currU_samples[idx] = adc_value[2] * scale + offset;
currV_samples[idx] = adc_value[3] * scale + offset;
currW_samples[idx] = adc_value[4] * scale + offset;
// 更新索引
current_sample_index++;
if (current_sample_index >= ADC_FILTER_SIZE - 1) {
current_sample_index = 0;
if (adc_current_frame_ready == 0U) {
return;
}
// 卡尔曼滤波更新
float voltage = FOC.voltage_power_supply;
const u8 idx = current_sample_head;
currU_samples[idx] = current_adc_to_ma(adc_current_value[0], 0);
currV_samples[idx] = current_adc_to_ma(adc_current_value[1], 1);
currW_samples[idx] = current_adc_to_ma(adc_current_value[2], 2);
current_sample_head++;
if (current_sample_head >= ADC_FILTER_SIZE) {
current_sample_head = 0;
}
adc_current_frame_ready = 0U;
}
/**
* @brief 更新供电电压
*/
void sampler::update_vbus_voltage() {
memmove(vbus_samples, vbus_samples + 1, ADC_FILTER_SIZE * sizeof(float));
vbus_samples[ADC_FILTER_SIZE] = adc_value[5] * ADC_SCALE * VCC_FACTOR;
// ADC1 连续模式零等待读最新 VBUS(ch6,电阻分压,低阻源)
// 环形缓冲区写入,替代 memmove
vbus_samples[temp_vbus_head] =
adc1_read_vbus_latest() * ADC_SCALE * VCC_FACTOR;
temp_vbus_head++;
if (temp_vbus_head >= ADC_FILTER_SIZE) temp_vbus_head = 0;
// 更新缓存:中值滤波 + 范围检查
tempora_sample = medium_filter(vbus_samples, ADC_FILTER_SIZE);
const u8 mid = ADC_FILTER_SIZE / 2;
if (vbus_samples[mid] > MAX_VCC || vbus_samples[mid] < MIN_VCC) {
cached_vbus = ERROR_ADC_VAL;
} else {
cached_vbus = tempora_sample;
}
}
/**
* @brief ADC0_1_IRQHandler 中断处理函数
* @return void
*/
extern "C" void ADC0_1_IRQHandler(void) {
if (adc_interrupt_flag_get(ADC0, ADC_INT_FLAG_EOIC) != RESET) {
/* rank0/1/2 = U/V/W 同一 PWM 时刻样本,rank3 = MCU温度(序列尾部) */
adc_current_value[0] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_0);
adc_current_value[1] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_1);
adc_current_value[2] = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_2);
adc_mcu_temp_raw = adc_inserted_data_read(ADC0, ADC_INSERTED_CHANNEL_3);
adc_current_frame_ready = 1U;
adc_interrupt_flag_clear(ADC0, ADC_INT_FLAG_EOIC);
extern TaskScheduler scheduler;
scheduler.taskFlags |= (1u << TASK_CURRENT_UPDATE);
}
}
+1066 -540
View File
File diff suppressed because it is too large Load Diff
+391 -41
View File
@@ -1,3 +1,9 @@
/**
* @file device/Can.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _CAN_H
#define _CAN_H
@@ -6,21 +12,75 @@
extern "C" {
#endif
#include <cstdint>
#include "gd32c10x_can.h"
#include "config.h"
// 定义一个联合体类型用于float和byte数组之间的转换
// 定义一个联合体类型用于 float 和 byte 数组之间的转换
/**
* @brief 匿名联合体 联合体定义
*/
typedef union {
float f;
u8 b[4];
} float_union_t;
// 添加辅助函数用于将u16类型数据打包到u8数组中
// 添加辅助函数用于将 u16 类型数据打包到 u8 数组中
/**
* @brief pack_u16_to_bytes 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] value 参数
* @return void
*/
static inline void pack_u16_to_bytes(u8 *buffer, u16 offset, u16 value) {
buffer[offset] = (u8)(value & 0xFF);
buffer[offset + 1] = (u8)((value >> 8) & 0xFF);
}
// 添加辅助函数用于将float类型数据打包到u8数组中
/**
* @brief pack_u32_to_bytes 函数(小端)
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] value 参数
* @return void
*/
static inline void pack_u32_to_bytes(u8 *buffer, u16 offset, u32 value) {
pack_u16_to_bytes(buffer, offset, (u16)(value & 0xFFFF));
pack_u16_to_bytes(buffer, offset + 2, (u16)((value >> 16) & 0xFFFF));
}
// 将 mA 电流值饱和转换为 int16 后按小端打包。
// DBC 中 U/V/W current 定义为 signed int16([-32768,32767] mA);
// 相电流有正有负,此前用 u16() 打包负值被削成 0,上位机波形下半周丢失。
/**
* @brief pack_current_s16_to_bytes 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] value_mA 参数
* @return void
*/
static inline void pack_current_s16_to_bytes(u8 *buffer, u16 offset,
float value_mA) {
short value;
if (value_mA > 32767.0f) {
value = 32767; // 饱和,避免 float→int 超范围截断环绕
} else if (value_mA < -32768.0f) {
value = -32768;
} else {
value = (short)value_mA;
}
pack_u16_to_bytes(buffer, offset, (u16)value);
}
// 添加辅助函数用于将 float 类型数据打包到 u8 数组中
/**
* @brief pack_float_to_bytes 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @param[in] float_union 参数
* @return void
*/
static inline void pack_float_to_bytes(u8 *buffer, u16 offset,
float_union_t &float_union) {
buffer[offset] = float_union.b[0];
@@ -29,19 +89,37 @@ static inline void pack_float_to_bytes(u8 *buffer, u16 offset,
buffer[offset + 3] = float_union.b[3];
}
// 添加辅助函数用于从u8数组中解包short类型数据
// 添加辅助函数用于从 u8 数组中解包 short 类型数据
/**
* @brief unpack_bytes_to_short 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @return short 返回值
*/
static inline short unpack_bytes_to_short(u8 *buffer, u16 offset) {
short value = (short)(buffer[offset] | (buffer[offset + 1] << 8));
return value;
}
// 添加辅助函数用于从u8数组中解包u16类型数据
// 添加辅助函数用于从 u8 数组中解包 u16 类型数据
/**
* @brief unpack_bytes_to_u16 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @return u16 返回值
*/
static inline u16 unpack_bytes_to_u16(u8 *buffer, u16 offset) {
u16 value = (u16)(buffer[offset] | (buffer[offset + 1] << 8));
return value;
}
// 添加辅助函数用于从u8数组中解包float类型数据
// 添加辅助函数用于从 u8 数组中解包 float 类型数据
/**
* @brief unpack_bytes_to_float 函数
* @param[in] buffer 参数
* @param[in] offset 参数
* @return float_union_t 返回值
*/
static inline float_union_t unpack_bytes_to_float(u8 *buffer, u16 offset) {
float_union_t float_union;
float_union.b[0] = buffer[offset];
@@ -51,71 +129,343 @@ static inline float_union_t unpack_bytes_to_float(u8 *buffer, u16 offset) {
return float_union;
}
u8 can_gpio_init(void); // CAN引脚初始化
void can_config(void); // CAN初始化
void canfd_config(void); // CANFD初始化
void can_dma_init(void); // CAN DMA初始化
void CAN0_RX0_IRQHandler(void); // CAN接收中断处理函数
void DMA0_Channel4_IRQHandler(void); // DMA通道4中断处理函数(CAN接收)
void DMA0_Channel5_IRQHandler(void); // DMA通道5中断处理函数(CAN发送)
/**
* @brief 初始化 CAN 引脚
* @return u8 返回值
*/
u8 can_gpio_init(void); // CAN 引脚初始化
/**
* @brief 配置 CAN FD 外设
* @return void
*/
void canfd_config(void); // CANFD 初始化
/**
* @brief 初始化 CAN DMA 通道
* @return void
*/
void can_dma_init(void); // CAN DMA 初始化
/**
* @brief CAN0 发送中断处理函数
* @return void
*/
void CAN0_TX_IRQHandler(void); // CAN 发送中断处理函数
/**
* @brief CAN0 接收中断处理函数
* @return void
*/
void CAN0_RX0_IRQHandler(void); // CAN 接收中断处理函数
/**
* @brief DMA0 通道 4 中断处理函数
* @return void
*/
void DMA0_Channel4_IRQHandler(void); // DMA 通道 4 中断处理函数 (CAN 接收)
/**
* @brief DMA0 通道 5 中断处理函数
* @return void
*/
void DMA0_Channel5_IRQHandler(void); // DMA 通道 5 中断处理函数 (CAN 发送)
/**
* @brief jumpToBOOT 函数
* @return void
*/
void jumpToBOOT();
void can_loopback_init(void);
void can_loopback_test(void);
/*!
\brief CAN通信类
\note 封装CAN发送和接收功能
\brief CAN 通信类
\note 封装 CAN 发送和接收功能,支持多电机节点 ID 隔离
*/
class CAN_transmiter {
private:
bool is_sleep = false; // 是否休眠
static const uint8_t CAN_RX_BUFFER_SIZE = 8; // CAN 接收缓冲区大小
static const u32 CAN_SILENT_DURATION_MS = 10000; // CAN 静默时长 10 秒
u8 controller_Mode = 0; // 电机模式
float_union_t limit_velocity_union = {0}; // 限制速度
u16 limit_voltage = 0; // 限制电压
short mcu_temp = 0; // MCU温度
bool is_sleep = false; // 是否休眠
bool is_silent = false; // 是否静默(收到 base_id=0x0000 后进入静默)
u32 silent_start_time = 0; // 静默开始时间(ms)
u16 node_id; // 本节点 ID(0-254),使用 CAN ID 高 8 位存储 (在 init() 中初始化为 DEFAULT_NODE_ID)
u8 Target_Control_Mode = 0; // 目标控制模式
u8 controller_Mode = 0; // 当前模式
short mcu_temp = 0; // MCU 温度
short motor_temp = 0; // 电机温度
u16 Power_Voltage = 0; // 电源电压
u16 limit_voltage = 0; // 限制电压
u8 cpu_usage = 0; // CPU 使用率(%)
float_union_t rt_angle_union = {0}; // 实时角度
float_union_t rt_velocity_union = {0}; // 实时速度
bool MotorStatus = 0; // 电机状态码
float_union_t rt_current_union = {0}; // 实时电流
float_union_t target_angle_union = {0}; // 目标角度
u8 Target_Control_Mode = 0; // 目标控制模式
u8 EEPROMStatus = 0; // EEPROM状态码
float_union_t target_velocity_union = {0}; // 目标速度
float_union_t target_current_union = {0};
float_union_t u_current_union = {0}; // U相电流
float_union_t w_current_union = {0}; // W相电流
float_union_t v_current_union = {0}; // V相电流
float_union_t target_current_union = {0}; // 目标电流
float_union_t u_current_union = {0}; // U 相电流
float_union_t w_current_union = {0}; // W 相电流
float_union_t v_current_union = {0}; // V 相电流
u8 EEPROMStatus = 0; // EEPROM 状态码
// MotorStatus 不在此处:判据与上报都收在 health 的 1ms nFAULT 轮询里,
// 声明见 public 段(与 MagStatus/ThermalStatus 同处),避免出现第二个写手。
can_trasnmit_message_struct transmit_message; // 发送消息结构体
u8 transmit_mailbox = CAN_NOMAILBOX; // 初始化为无mailbox状态
u8 transmit_data[64]; // CANFD最大64字节
u8 transmit_mailbox = CAN_NOMAILBOX; // 初始化为无 mailbox 状态
u8 transmit_data[64]; // CANFD 最大 64 字节
// 周期反馈帧独立刷新率(ms,0=停发)与 1ms 节拍分频计数器。
// init() 从 flashEEP 的 RAM 副本加载,SET 命令(0x0400 扩展格式)运行时修改
u16 status_period_ms = CANFD_TX_PERIOD_STATUS_DEFAULT_MS;
u16 status_period_counter = 0;
u16 config_period_ms = CANFD_TX_PERIOD_CONFIG_DEFAULT_MS;
u16 config_period_counter = 0;
// CAN 接收消息队列
can_receive_message_struct rx_message_buffer[CAN_RX_BUFFER_SIZE];
volatile uint8_t rx_buffer_head = 0;
volatile uint8_t rx_buffer_tail = 0;
/**
* @brief 更新对象内部状态或采样数据
* @return void
*/
void update(); // 更新数据
u8 CAN_Receive(); // CAN接收
u8 CANFD_Receive(); // CANFD接收
/**
* @brief 打包并发送 CONFIG 反馈帧(0x0101,控制模式/目标设定回显)
* @return void
*/
void send_config_frame(); // CONFIG 反馈帧发送
/**
* @brief CAN_Receive 函数
* @return u8 返回值
*/
u8 CAN_Receive(); // CAN 接收
/**
* @brief CANFD_Receive 函数
* @return u8 返回值
*/
u8 CANFD_Receive(); // CANFD 接收
/**
* @brief is_rx_buffer_full 函数
* @return bool 返回值
*/
bool is_rx_buffer_full();
/**
* @brief is_rx_buffer_empty 函数
* @return bool 返回值
*/
bool is_rx_buffer_empty();
/**
* @brief dequeue_rx_message 函数
* @param[in] message 参数
* @return bool 返回值
*/
bool dequeue_rx_message(can_receive_message_struct& message);
/**
* @brief check_silent_mode 函数
* @return void
*/
void check_silent_mode(); // 检查静默模式是否结束
/**
* @brief 处理周期性发送请求,静默模式下只检查是否退出静默
* @return void
*/
void handle_periodic_send_tick();
/**
* @brief 将底层接收队列项转换为 CAN 消息并按协议分发
* @param[in] id 接收到的扩展 CAN ID
* @param[in] length 接收数据长度
* @param[in] data 接收数据指针
* @return void
*/
void handle_received_queue_item(u32 id, u8 length, const u8 *data);
/**
* @brief 处理不受本节点过滤限制的特殊消息
* @param[in,out] current_message 当前 CAN 消息
* @param[in] base_id 低 16 位基础 ID
* @param[in] sender_node_id 发送方节点 ID
* @return bool true 表示消息已处理,false 表示继续按普通节点消息处理
*/
bool handle_special_message(can_receive_message_struct &current_message,
u32 base_id, u16 sender_node_id);
/**
* @brief 处理 BOOT 命令或其他节点 BOOT 广播导致的静默逻辑
* @param[in] sender_node_id 发送方节点 ID
* @return void
*/
void handle_boot_message(u16 sender_node_id);
/**
* @brief 处理节点 ID 设置命令,发送应答并保存新 ID 到 Flash
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_set_node_id_message(can_receive_message_struct &current_message);
/**
* @brief 处理 0x0400 扩展格式参数设置(DLC=4:[选择器 u16][值 u16]),
* 选择器 0x0000=节点 ID、反馈帧基础 ID=对应帧刷新周期(ms)
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_set_param_message(can_receive_message_struct &current_message);
/**
* @brief 校验并保存新节点 ID 到 Flash(不修改应答帧)
* @param[in] new_id 新节点 ID
* @return u8 1=成功,0=非法 ID 或 Flash 写入失败
*/
u8 apply_new_node_id(u16 new_id);
/**
* @brief 设置反馈帧刷新周期并持久化到 Flash
* @param[in] base_id 反馈帧基础 ID(CANFD_STATUS_ID/CANFD_CONFIG_ID)
* @param[in] period_ms 刷新周期 ms(0=停发)
* @return u8 1=成功,0=未知帧或 Flash 写入失败
*/
u8 set_tx_period(u16 base_id, u16 period_ms);
/**
* @brief 处理已经确认发给本节点的普通协议消息
* @param[in,out] current_message 当前 CAN 消息
* @param[in] base_id 低 16 位基础 ID
* @return void
*/
void handle_node_message(can_receive_message_struct &current_message,
u32 base_id);
/**
* @brief 解析控制命令并更新 FOC 目标值和 PID 参数
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_control_command(can_receive_message_struct &current_message);
/**
* @brief 解析复位命令各 bit 位并执行对应动作
* @param[in,out] current_message 当前 CAN 消息
* @return void
*/
void handle_reset_command(can_receive_message_struct &current_message);
/**
* @brief 处理 OTA ACK 查询,APP 有效时回发确认帧
* @return void
*/
void handle_ota_ack_message();
public:
// 节点 ID 相关辅助函数 (使用高 16 位存储节点 ID)
/**
* @brief build_can_id 函数
* @param[in] base_id 参数
* @return u32 返回值
*/
u32 build_can_id(u32 base_id); // 根据节点 ID 构建 CAN ID,格式:[节点 ID(16 位)][基础 ID(16 位)]
/**
* @brief is_message_for_me 函数
* @param[in] received_id 参数
* @return bool 返回值
*/
bool is_message_for_me(u32 received_id); // 判断消息是否发给本节点
public:
// 添加构造函数声明
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init(void);
can_receive_message_struct receive_message; // 接收消息结构体
FlagStatus can0_receive_flag; // CAN0接收标志
FlagStatus can0_send_flag; // CAN0发送标志
FlagStatus can0_send_flag; // CAN0 发送标志
float_union_t testfloat_union = {0}; // 测试用
u16 test_u16 = 0;
bool MagStatus = 0; // 磁状态码
bool ThermalStatus = 0; // 热能状态码
// nFAULT 状态位(STATUS 帧 motStatus)。唯一写手 = health.cpp 的 1ms nFAULT
// 轮询,值与"是否允许运行"的判据同源;CAN update() 不再自行读引脚。
bool MotorStatus = 0; // 门驱动故障状态码
/**
* @brief communication_check 函数
* @return void
*/
void communication_check(); // 通信检查
u8 CAN_Sleep(); // CAN进入睡眠
u8 CAN_Wakeup(); // CAN进入睡眠
u8 CAN_AsyncSend(u32 ID, u8 length, u8 *data); // CAN异步发送(使用DMA)
u8 CANFD_AsyncSend(u32 ID, u8 length, u8 *data); // CANFD异步发送(使用DMA)
u8 CAN_all_Send(); // CAN发送
u8 CANFD_all_Send(); // CANFD发送
/**
* @brief 打包并发送 STATUS 反馈帧(0x0100,实时状态与错误位图)。
* 不做分频与静默检查,初始化失败上报路径直接调用
* @return u8 1=已提交发送,0=发送失败
*/
u8 send_status_frame(); // STATUS 反馈帧发送
/**
* @brief CAN_Sleep 函数
* @return u8 返回值
*/
u8 CAN_Sleep(); // CAN 进入睡眠
/**
* @brief CAN_Wakeup 函数
* @return u8 返回值
*/
u8 CAN_Wakeup(); // CAN 唤醒
/**
* @brief CAN_AsyncSend 函数
* @param[in] ID 参数
* @param[in] length 参数
* @param[in] data 参数
* @return u8 返回值
*/
u8 CAN_AsyncSend(u32 ID, u8 length, u8 *data); // CAN 异步发送 (使用 DMA)
/**
* @brief CANFD_AsyncSend 函数
* @param[in] ID 参数
* @param[in] length 参数
* @param[in] data 参数
* @return u8 返回值
*/
u8 CANFD_AsyncSend(u32 ID, u8 length, u8 *data); // CANFD 异步发送 (使用 DMA)
/**
* @brief enter_silent_mode 函数
* @return void
*/
void enter_silent_mode(); // 进入静默模式(停发 10 秒)
/**
* @brief is_in_silent_mode 函数
* @return bool 返回值
*/
bool is_in_silent_mode(); // 检查是否在静默模式中
// 节点 ID 管理接口
/**
* @brief 获取 get_node_id 对应的数据或状态
* @return u16 返回值
*/
u16 get_node_id(); // 获取当前节点 ID
/**
* @brief 设置 set_node_id 对应的参数或状态
* @param[in] new_id 参数
* @return void
*/
void set_node_id(u16 new_id); // 设置节点 ID
/**
* @brief load_node_id_from_flash 函数
* @return u8 返回值
*/
u8 load_node_id_from_flash(); // 从 Flash 加载节点 ID
/**
* @brief save_node_id_to_flash 函数
* @param[in] new_id 参数
* @return u8 返回值
*/
u8 save_node_id_to_flash(u16 new_id); // 保存节点 ID 到 Flash
// 中断处理函数需要访问的公共接口
/**
* @brief enqueue_rx_message_from_isr 函数
* @param[in] message 参数
* @return void
*/
void enqueue_rx_message_from_isr(const can_receive_message_struct& message) {
if (!is_rx_buffer_full()) {
rx_message_buffer[rx_buffer_head] = message;
rx_buffer_head = (rx_buffer_head + 1) % CAN_RX_BUFFER_SIZE;
}
// 如果缓冲区满,丢弃新消息
}
};
#ifdef __cplusplus
+141 -106
View File
@@ -1,7 +1,14 @@
/**
* @file device/Timer.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "Timer.h"
#include "Can.h"
#include "FOC.h"
#include "Timer.h"
#include "config.h"
#include "gd32c10x.h"
#include "gd32c10x_gpio.h"
#include "gd32c10x_misc.h"
@@ -9,33 +16,61 @@
#include "gd32c10x_timer.h"
#include "initial.h"
volatile u32 total_us100_counter = 0; // 总计数器,每100微秒增加1
extern u16 adc_value[7];
extern FOC_Controlroller FOC;
volatile u32 total_us100_counter = 0; // TIMER2 ISR 内部 100us 调度索引
extern FOC_Controller FOC;
extern TaskScheduler scheduler;
extern CAN_transmiter CAN0_transmiter;
// 添加系统运行时间计数器
extern u32 cnt_test;
// 任务调度查找表 - 每 100us 执行的任务 (1ms 周期内)
// MCU/DRV 温度保持 1kHz:热模型兼作过流保护,温度积累按 1ms 步进,
// 每毫秒都要拿到新温度采样。
static const u32 task_schedule_100us[10] = {
(1u << TASK_MCU_TEMP_UPDATE), // 0: MCU 温度更新
(1u << TASK_DRV_TEMP_UPDATE), // 1: 驱动温度更新
(1u << TASK_VBUS_UPDATE), // 2: 电压更新
0, 0, 0, 0, 0, 0, // 3~8: 空闲
(1u << TASK_CAN_TRANSMIT) // 9: CAN 发送
};
// 任务调度查找表 - 每 1ms 执行的任务 (10ms 周期内)
static const u32 task_schedule_1ms[10] = {
(1u << TASK_THERMAL_CONTROL), // 0: 热量控制
(1u << TASK_MAGNETIC_CHECK), // 1: 磁感应强度检测
0, 0, 0, 0, 0, 0, 0, 0 // 空闲
};
// 任务调度查找表 - 每 10ms 执行的任务 (100ms 周期内)
static const u32 task_schedule_10ms[10] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 当前无任务,预留扩展
};
// 任务调度查找表 - 每 100ms 执行的任务 (1s 周期内)
static const u32 task_schedule_100ms[10] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 当前无任务,预留扩展
};
/**
* @brief 设置引脚中断优先级
* @return void
*
*设置引脚中断优先级
* 设置引脚中断优先级
*/
void nvic_config(void) {
/* 设置NVIC优先级分组,1位抢占优先级,3位响应优先级 */
/* 设置 NVIC 优先级分组,1 位抢占优先级,3 位响应优先级 */
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
/* 使能TIMER2中断,设置抢占优先级为1,子优先级为1 */
/* 使能 TIMER2 中断,设置抢占优先级为 1,子优先级为 1 */
nvic_irq_enable(TIMER2_IRQn, 0, 1);
nvic_irq_enable(ADC0_1_IRQn, 0, 0);
nvic_irq_enable(TIMER1_IRQn, 0, 2);
}
/**
* @brief 配置定时器2-任务管理
* @brief 配置定时器 2-任务管理
* @return void
*
*配置定时器2,用于定时执行FOC控制、采样、CAN发送等任务
* 配置定时器 2,用于定时执行 FOC 控制、采样、CAN 发送等任务
*/
void TIMER2_config(void) {
/* ----------------------------------------------------------------------------
@@ -46,21 +81,21 @@ void TIMER2_config(void) {
*/
timer_parameter_struct timer_struct;
/* 使能TIMER2时钟 */
/* 使能 TIMER2 时钟 */
rcu_periph_clock_enable(RCU_TIMER2);
/* 复位TIMER2外设到默认状态 */
/* 复位 TIMER2 外设到默认状态 */
timer_deinit(TIMER2);
/* 初始化TIMER初始化参数结构体 */
/* 初始化 TIMER 初始化参数结构体 */
timer_struct_para_init(&timer_struct);
/* TIMER2配置参数设置 */
/* TIMER2 配置参数设置 */
timer_struct.prescaler =
TIMER2_PRESCALER; /* 预分频值:120分频(从0开始计数) */
TIMER2_PRESCALER; /* 预分频值:120 分频 (从 0 开始计数) */
timer_struct.alignedmode = TIMER_COUNTER_EDGE; /* 边沿对齐模式 */
timer_struct.counterdirection = TIMER_COUNTER_UP; /* 向上计数模式 */
timer_struct.period =
TIMER2_PERIOD; /* 自动重装载值:100次计数(从0开始计数) */
timer_struct.clockdivision = TIMER_CKDIV_DIV1; /* 时钟分频值:1分频 */
TIMER2_PERIOD; /* 自动重装载值:100 次计数 (从 0 开始计数) */
timer_struct.clockdivision = TIMER_CKDIV_DIV1; /* 时钟分频值:1 分频 */
timer_init(TIMER2, &timer_struct);
/* 清除更新中断标志位 */
@@ -68,108 +103,85 @@ void TIMER2_config(void) {
/* 使能更新中断 */
timer_interrupt_enable(TIMER2, TIMER_INT_UP);
/* 使能TIMER2 */
/* 使能 TIMER2 */
timer_enable(TIMER2);
}
/**
* @brief 配置TIMER2-任务管理中断
* @brief 配置 TIMER2-任务管理中断
* @return void
*/
void User_timer_init(void) {
/* 配置TIMER2 */
/* 配置 TIMER2 */
TIMER2_config();
/* 配置TIMER2中断 */
/* 配置 TIMER2 中断 */
nvic_config();
}
/**
* @brief TIMER2-任务管理中断处理函数
* @brief TIMER2-任务管理中断处理函数 (优化版)
* @return void
* @note 使用查找表替代多层 switch-case,减少代码复杂度和执行时间
* 中断周期:50us
* - 每 50us: 电机控制任务 (角度、速度更新)
* - 每 100us: 传感器采样、CAN 通信
* - 每 1ms: 热量控制、磁检
* - 每 10ms/100ms/1s: 预留扩展任务
*/
void TIMER2_IRQHandler(void) {
// 检查更新中断标志并处理
if (timer_interrupt_flag_get(TIMER2, TIMER_INT_UP) != RESET) {
// 清除更新中断标志
// 快速路径:检查并清除中断标志
if (timer_interrupt_flag_get(TIMER2, TIMER_INT_UP) == RESET) {
return;
}
timer_interrupt_flag_clear(TIMER2, TIMER_INT_UP);
// 每50us执行一次的操作 - 电机控制
scheduler.taskFlags |= (1 << TASK_MOTOR_CONTROL);
scheduler.taskFlags |=
(1 << TASK_ANGLE_UPDATE) | (1 << TASK_VELOCITY_UPDATE);
// 固定任务:每 50us 执行电机控制
scheduler.taskFlags |= (1u << TASK_ANGLE_UPDATE) |
(1u << TASK_VELOCITY_UPDATE) |
(1u << TASK_MOTOR_CONTROL);
// 使用标志位控制,每100us执行一次其他任务
static bool isEvenCall = false;
isEvenCall = !isEvenCall;
// 每 100us 执行一次的任务(使用位运算替代布尔变量)
static u32 call_counter = 0;
call_counter++;
if (isEvenCall) {
// 每100us执行一次的操作 - 保持原有频率
switch (total_us100_counter) { // 每1ms执行的代码,负载均衡
case 0: {
scheduler.taskFlags |= (1 << TASK_MCU_TEMP_UPDATE);
break;
}
case 1: {
scheduler.taskFlags |= (1 << TASK_DRV_TEMP_UPDATE);
break;
}
case 2: {
scheduler.taskFlags |= (1 << TASK_VBUS_UPDATE);
break;
}
case 3: {
break;
}
case 9: {
scheduler.taskFlags |= (1 << TASK_CAN_TRANSMIT);
break;
}
}
if ((call_counter & 1u) == 0u) { // 偶数次调用(每 100us)
// 使用查找表设置 1ms 周期内的任务
scheduler.taskFlags |= task_schedule_100us[total_us100_counter];
total_us100_counter++;
if (total_us100_counter > 9) {
if (total_us100_counter >= 10u) {
total_us100_counter = 0;
// 可以添加每1ms执行的代码,无负载均衡
static u8 ms1_counter = 0;
switch (ms1_counter) { // 每10ms执行的代码,负载均衡
case 0: {
scheduler.taskFlags |= (1 << TASK_THERMAL_CONTROL);
break;
}
case 1: {
scheduler.taskFlags |= (1 << TASK_MAGNETIC_CHECK);
break;
}
case 2: {
break;
}
}
// 每 1ms 执行的任务
static u32 ms1_counter = 0;
ms1_counter++;
if (ms1_counter > 9) {
if (ms1_counter >= 10u) {
ms1_counter = 0;
// 可以添加每10ms执行的代码,无负载均衡
static u8 ms10_counter = 0;
// 每 10ms 执行的任务
static u32 ms10_counter = 0;
ms10_counter++;
if (ms10_counter > 9) {
if (ms10_counter >= 10u) {
ms10_counter = 0;
// 可以添加每100ms执行的代码
static u8 sec_counter = 0;
sec_counter++;
if (sec_counter > 9) {
sec_counter = 0;
// 可以添加每1s执行的代码
}
// 每 100ms 执行的任务
static u32 ms100_counter = 0;
ms100_counter++;
if (ms100_counter >= 10u) {
ms100_counter = 0;
// 每 1s 任务预留位置
}
scheduler.taskFlags |= task_schedule_100ms[ms100_counter];
}
scheduler.taskFlags |= task_schedule_10ms[ms10_counter];
}
scheduler.taskFlags |= task_schedule_1ms[ms1_counter];
}
}
}
/**
* @brief TIMER1-PWM GPIO配置函数
* @brief TIMER1-PWM GPIO 配置函数
*/
void timer1_gpio_config(void) {
// 时钟使能
@@ -178,17 +190,25 @@ void timer1_gpio_config(void) {
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_TIMER1);
// TIMER1 GPIO初始化
// TIMER1 GPIO 初始化
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
#if GATE_DRIVER_DRV8323
/* 新板:PB12(DRVSLP=ENABLE+INLx) 由 gate_driver 统一管理——
* gate_driver_preinit() 已置低保持芯片睡眠,gate_driver_bringup()
* 在 SPI 配置完成后才拉高。此处严禁提前置高(SPI 未配置前芯片
* 带默认 6x PWM 模式醒来,INLx=1 时上管永远无法导通)。
* PB0 板上接地未用,不配置。 */
#else
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_0 | GPIO_PIN_12);
gpio_bit_set(GPIOB, GPIO_PIN_0);
gpio_bit_set(GPIOB, GPIO_PIN_12);
#endif
/* 配置TIMER1完全重映射*/
/* 配置 TIMER1 完全重映射*/
gpio_pin_remap_config(GPIO_TIMER1_FULL_REMAP, DISABLE);
}
@@ -199,16 +219,17 @@ void timer1_pwm_config(void) {
timer_parameter_struct timer_initpara;
timer_oc_parameter_struct timer_ocinitpara;
// 使能TIMER1时钟
// 使能 TIMER1 时钟
rcu_periph_clock_enable(RCU_TIMER1);
// 复位TIMER1
// 复位 TIMER1
timer_deinit(TIMER1);
// 配置TIMER1基本参数
// 配置 TIMER1 基本参数
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = TIMER1_PRESCALER; // 预分频器
timer_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
timer_initpara.alignedmode = TIMER_COUNTER_CENTER_BOTH; // 中心对齐,给采样留对称平台窗
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = TIMER1_PERIOD; // 计数周期
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER1, &timer_initpara);
@@ -218,23 +239,26 @@ void timer1_pwm_config(void) {
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
// 配置三个通道为PWM模式
// 配置三个通道为 PWM 模式
timer_channel_output_config(TIMER1, TIMER_CH_0, &timer_ocinitpara);
timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocinitpara);
timer_channel_output_config(TIMER1, TIMER_CH_2, &timer_ocinitpara);
// 配置第四个通道(CH3)用于在PWM周期x%位置触发中断
// 配置第四个通道 (CH3) 为固定采样点,只用于导出 ADC 触发
timer_ocinitpara.outputstate =
TIMER_CCX_DISABLE; // 不输出PWM波形,仅用于触发中断
TIMER_CCX_DISABLE; // 不输出 PWM 波形,仅用于比较事件
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_channel_output_config(TIMER1, TIMER_CH_3, &timer_ocinitpara);
// 设置PWM模式和占空比
// 设置 PWM 模式和占空比
timer_channel_output_mode_config(TIMER1, TIMER_CH_0, TIMER_OC_MODE_PWM0);
timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM0);
timer_channel_output_mode_config(TIMER1, TIMER_CH_2, TIMER_OC_MODE_PWM0);
timer_channel_output_mode_config(TIMER1, TIMER_CH_3,
TIMER_OC_MODE_PWM0); // CH3也设为PWM模式
TIMER_OC_MODE_PWM0); // CH3 也设为 PWM 模式
/* 将 CH3 预装载比较点作为 TRGO 导出,供 ADC injected 组同步采样 */
timer_master_output_trigger_source_select(TIMER1, TIMER_TRI_OUT_SRC_O3CPRE);
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_0,
TIMER1_DEFAULT_PULSE);
@@ -242,7 +266,8 @@ void timer1_pwm_config(void) {
TIMER1_DEFAULT_PULSE);
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_2,
TIMER1_DEFAULT_PULSE);
// 设置CH3比较值为PWM周期x%位置
// 设置 CH3 为固定 ADC 采样点(低边采样:必须落在三相下管同时导通窗,
// 即中心对齐计数的峰顶附近,见 config.h TIMER1_CH3_COMPARE_VALUE 说明)
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_3,
TIMER1_CH3_COMPARE_VALUE);
@@ -250,26 +275,18 @@ void timer1_pwm_config(void) {
timer_primary_output_config(TIMER1, ENABLE);
timer_auto_reload_shadow_enable(TIMER1);
// 使能CH3比较中断
timer_interrupt_enable(TIMER1, TIMER_INT_CH3);
// 启动TIMER1
// 启动 TIMER1
timer_enable(TIMER1);
}
// 添加TIMER1-PWM中断处理函数
// CH3 现在仅作为 TRGO 采样点保留;采样完成由 ADC injected 中断接管
/**
* @brief TIMER1中断处理函数
* @brief TIMER1 中断处理函数
* @return void
*/
void TIMER1_IRQHandler(void) {
// 检查CH3比较中断标志并处理
if (timer_interrupt_flag_get(TIMER1, TIMER_INT_FLAG_CH3) != RESET) {
// 清除CH3比较中断标志
timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_CH3);
scheduler.taskFlags |= (1 << TASK_CURRENT_UPDATE);
// 例如触发ADC采样或其他操作
}
}
@@ -285,6 +302,15 @@ void TIMER1_IRQHandler(void) {
void Set_Pwm(u16 channel, float duty_cycle) {
float pulse = duty_cycle * TIMER1_PERIOD;
#if GATE_DRIVER_DRV8323
/* 新板通道重映射:逻辑相 A→CH2、B→CH1、C→CH0(硬件 INHA/B/C
* 分别接 PWM1/2/3 = PA2/PA1/PA0),与 Set_3Pwm 保持一致 */
if (channel == 0) {
channel = 2;
} else if (channel == 2) {
channel = 0;
}
#endif
if (channel <= 3) {
timer_channel_output_pulse_value_config(TIMER1, channel, pulse);
} else {
@@ -301,8 +327,17 @@ void Set_Pwm(u16 channel, float duty_cycle) {
\note The channel parameter must be between 0 and 3 inclusive.
*/
void Set_3Pwm(float duty_cycle0, float duty_cycle1, float duty_cycle2) {
// 直接写寄存器
#if GATE_DRIVER_DRV8323
/* 新板硬件映射:CH0(PA0)→PWM3→INHC,CH1(PA1)→PWM2→INHB,
* CH2(PA2)→PWM1→INHA。入参按 (A,B,C) 相序书写,此处把 A/C 换到
* 物理通道,修正新板与老板相反的 INHx 接线。 */
TIMER_CH0CV(TIMER1) = (uint32_t)(duty_cycle2 * TIMER1_PERIOD);
TIMER_CH1CV(TIMER1) = (uint32_t)(duty_cycle1 * TIMER1_PERIOD);
TIMER_CH2CV(TIMER1) = (uint32_t)(duty_cycle0 * TIMER1_PERIOD);
#else
// 直接写寄存器(老板:CH0/1/2 = IN1/2/3 = A/B/C 相)
TIMER_CH0CV(TIMER1) = (uint32_t)(duty_cycle0 * TIMER1_PERIOD);
TIMER_CH1CV(TIMER1) = (uint32_t)(duty_cycle1 * TIMER1_PERIOD);
TIMER_CH2CV(TIMER1) = (uint32_t)(duty_cycle2 * TIMER1_PERIOD);
#endif
}
+41
View File
@@ -1,19 +1,60 @@
/**
* @file device/Timer.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _TIMER_H
#define _TIMER_H
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <cstdint>
/**
* @brief User_timer_init 函数
* @return void
*/
void User_timer_init(void);
/**
* @brief Set_Pwm 函数
* @param[in] channel 参数
* @param[in] duty_cycle 参数
* @return void
*/
void Set_Pwm(u16 channel, float duty_cycle);
/**
* @brief Set_3Pwm 函数
* @param[in] duty_cycle0 参数
* @param[in] duty_cycle1 参数
* @param[in] duty_cycle2 参数
* @return void
*/
void Set_3Pwm(float duty_cycle0, float duty_cycle1,float duty_cycle2);
/**
* @brief TIMER2 中断处理函数
* @return void
*/
void TIMER2_IRQHandler(void);
/**
* @brief timer1_pwm_config 函数
* @return void
*/
void timer1_pwm_config(void);
/**
* @brief timer1_gpio_config 函数
* @return void
*/
void timer1_gpio_config(void);
/**
* @brief TIMER1 中断处理函数
* @return void
*/
void TIMER1_IRQHandler(void); // 添加TIMER1中断处理函数声明
#ifdef __cplusplus
+97 -7
View File
@@ -1,3 +1,9 @@
/**
* @file device/adc.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _ADC_H
#define _ADC_H
@@ -7,31 +13,115 @@ extern "C" {
#include "config.h"
/**
* @brief sampler 类或结构体
*/
class sampler {
private:
float vbus_samples[ADC_FILTER_SIZE + 1];
float currU_samples[ADC_FILTER_SIZE + 1];
float currV_samples[ADC_FILTER_SIZE + 1];
float currW_samples[ADC_FILTER_SIZE + 1];
float mcu_temp_samples[ADC_FILTER_SIZE + 1];
float drv_temp_samples[ADC_FILTER_SIZE + 1];
u8 current_sample_index;
float vbus_samples[ADC_FILTER_SIZE];
float currU_samples[ADC_FILTER_SIZE];
float currV_samples[ADC_FILTER_SIZE];
float currW_samples[ADC_FILTER_SIZE];
float mcu_temp_samples[ADC_FILTER_SIZE];
float drv_temp_samples[ADC_FILTER_SIZE];
u8 current_sample_head;
u8 temp_vbus_head; // 环形缓冲区 head
u8 mcu_temp_head;
u8 drv_temp_head;
float tempora_sample;
float cached_mcu_temp; // getter 缓存
float cached_drv_temp;
float cached_vbus;
public:
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init(void);
/**
* @brief 三相电流零点配平并校验(hardware_init 在 FOC.init 之后调用)
* @return u8 0=成功;1=无采样帧(ADC/触发链路异常);
* 2=配平值超过 CURRENT_ZERO_CAL_LIMIT(电流检测初始化错误)
*/
u8 zero_calibrate(void);
/**
* @brief 更新 mcu_temp 相关状态
* @return void
*/
void update_mcu_temp(void);
/**
* @brief 更新 drv_temp 相关状态
* @return void
*/
void update_drv_temp(void);
/**
* @brief 更新 vbus_voltage 相关状态
* @return void
*/
void update_vbus_voltage(void);
/**
* @brief 更新 current 相关状态
* @return void
*/
void update_current(void);
/**
* @brief 获取 get_mcu_temp 对应的数据或状态
* @return float 返回值
*/
float get_mcu_temp(void);
/**
* @brief 获取 get_drv_temp 对应的数据或状态
* @return float 返回值
*/
float get_drv_temp(void);
/**
* @brief 获取 get_vbus_voltage 对应的数据或状态
* @return float 返回值
*/
float get_vbus_voltage(void);
/**
* @brief 获取 get_current 对应的数据或状态
* @param[in] char 参数
* @return float 返回值
*/
float get_current(char);
};
/**
* @brief ADC0_1_IRQHandler 中断处理函数
* @return void
*/
void ADC0_1_IRQHandler(void);
/**
* @brief ADC1 初始化:VBUS 连续转换模式(零等待读取)
* @return void
* @note ADC1 上电后即对 ch6(VBUS) 连续转换,软件触发一次后自持续,
* 之后读取 DR 寄存器即可拿到最新完成的转换值(无需阻塞等待)。
*/
void adc1_config(void);
/**
* @brief 零等待读取最新 VBUS 原始值(连续模式下任意时刻可调)
* @return u16 最近一次完成转换的原始值(陈旧度约 1 个转换周期 ≈1.4μs)
* @note 仅主循环任务上下文调用(与其他 ADC1 访问串行,无并发)。
* 原理:读 DR 硬件清 EOC,随后下一次完成转换会刷新 DR,
* 其间完成的转换被丢弃——每次读到的都是刚完成的样本。
*/
u16 adc1_read_vbus_latest(void);
/**
* @brief ADC1 单次软件触发转换并等待完成(仅主循环任务上下文调用)
* @param[in] channel ADC 通道号(如 ADC_CHANNEL_9)
* @return u16 转换原始值
* @note 会临时暂停 VBUS 连续模式、切到目标通道单次转换、再恢复连续,
* 全程约 3~5μs,期间 VBUS 读数最多滞后该时长。drv 温度 1kHz 用。
*/
u16 adc1_read_raw(u8 channel);
#ifdef __cplusplus
}
#endif
+74 -127
View File
@@ -1,3 +1,9 @@
/**
* @file device/ang_sensor.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "ang_sensor.h"
#include "Can.h"
#include "Filter.h"
@@ -9,138 +15,70 @@
#include <cstdio>
#include <cstring>
#include <stdint.h>
#define ANG_SENS_DEBUG 1
// ANG_SENS 调试宏定义
#if ANG_SENS_DEBUG
/** @brief ANG_SENS_DEBUG_PRINT 宏定义 */
#define ANG_SENS_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief ANG_SENS_DEBUG_PRINT 宏定义 */
#define ANG_SENS_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
/** @brief ANG_SPI_CS_ENABLE 宏定义 */
#define ANG_SPI_CS_ENABLE() gpio_bit_reset(GPIOA, GPIO_PIN_15)
/** @brief ANG_SPI_CS_DISABLE 宏定义 */
#define ANG_SPI_CS_DISABLE() gpio_bit_set(GPIOA, GPIO_PIN_15)
/** @brief DRV_SPI_CS_ENABLE 宏定义 */
#define DRV_SPI_CS_ENABLE() gpio_bit_reset(GPIOB, GPIO_PIN_14)
/** @brief DRV_SPI_CS_DISABLE 宏定义 */
#define DRV_SPI_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
// DMA传输完成标志
volatile bool spi_dma_tx_complete = false;
volatile bool spi_dma_rx_complete = false;
// 全局角度传感器单例:系统只有一个 KTH7823 角度传感器。
// 初始化顺序由 main::hardware_init() 调用 KTH7823.init() 保证。
angle_sensor KTH7823; // 角度传感器初始化
extern FlashEEPROM flashEEP;
extern EncoderAngleKalman Angle_kalman;
extern EncoderVelocityKalman Vel_kalman;
extern CAN_transmiter CAN0_transmiter; // CAN初始化
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
/**
* @brief SPI DMA initialization
*/
void spi_dma_init(void) {
dma_parameter_struct dma_init_struct;
// 使能DMA时钟
rcu_periph_clock_enable(RCU_DMA0);
// 初始化发送DMA通道
dma_deinit(DMA0, DMA_CH2);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)0; // 实际地址将在传输时设置
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.number = 1;
dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI0));
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH2, &dma_init_struct);
// 初始化接收DMA通道
dma_deinit(DMA0, DMA_CH1);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)0; // 实际地址将在传输时设置
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.number = 1;
dma_init_struct.periph_addr = (uint32_t)(&SPI_DATA(SPI0));
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA0, DMA_CH1, &dma_init_struct);
// 使能SPI的DMA发送和接收功能
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
// 配置DMA中断
nvic_irq_enable(DMA0_Channel2_IRQn, 0, 1);
nvic_irq_enable(DMA0_Channel1_IRQn, 0, 1);
// 使能DMA传输完成中断
dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF);
dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF);
}
/**
* @brief Send or read half word through SPI using DMA
* @brief Send or read half word through SPI by register polling
*
* @param[in] spi_periph SPI peripheral
* @param[in] tx_data Data to transmit
* @param[out] rx_data Pointer to store received data
* @return 0 if success, -1 if error
* @note 单半字轮询收发。DMA 版本(已移除)每次传输需要:重配 2 个 DMA
* 通道(约 10 次寄存器写) + 2 次 FTF 中断 + 忙等完成标志,而 16bit
* @5MHz 帧本身只有 3.2μs——DMA 的准备与中断开销和传输时间同量级。
* 轮询同样忙等帧时间,但砍掉了全部 DMA 开销与 40k 次/秒的中断。
*/
int spi_rw_half_word_dma(uint32_t spi_periph, uint16_t tx_data,
int spi_rw_half_word_polling(uint32_t spi_periph, uint16_t tx_data,
uint16_t *rx_data) {
// 重置完成标志
spi_dma_tx_complete = false;
spi_dma_rx_complete = false;
u32 timeout = 1000;
// 配置发送DMA
dma_channel_disable(DMA0, DMA_CH2);
dma_memory_address_config(DMA0, DMA_CH2, (uint32_t)&tx_data);
dma_transfer_number_config(DMA0, DMA_CH2, 1);
dma_channel_enable(DMA0, DMA_CH2);
// 配置接收DMA
dma_channel_disable(DMA0, DMA_CH1);
dma_memory_address_config(DMA0, DMA_CH1, (uint32_t)rx_data);
dma_transfer_number_config(DMA0, DMA_CH1, 1);
dma_channel_enable(DMA0, DMA_CH1);
// 等待传输完成,超时时间为100个循环
uint32_t timeout = 100;
while ((!spi_dma_tx_complete || !spi_dma_rx_complete) && --timeout) {
// 空循环等待DMA传输完成
}
// 检查是否超时
if (timeout == 0) {
// 禁用DMA通道
dma_channel_disable(DMA0, DMA_CH2);
dma_channel_disable(DMA0, DMA_CH1);
printf("SPI DMA transfer timeout\r\n");
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE) == RESET) {
if (--timeout == 0U) {
return -1;
}
}
spi_i2s_data_transmit(spi_periph, tx_data);
timeout = 1000;
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE) == RESET) {
if (--timeout == 0U) {
return -1;
}
}
*rx_data = spi_i2s_data_receive(spi_periph);
// 等待移位完成再返回:调用方返回后立即拉高 CS,
// 若最后一位仍在移位会截断帧
while (spi_i2s_flag_get(spi_periph, SPI_FLAG_TRANS) != RESET) {
}
return 0;
}
/**
* @brief DMA0_Channel2中断处理函数
* @param 无
*/
extern "C" void DMA0_Channel2_IRQHandler(void) {
if (dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INT_FLAG_FTF);
spi_dma_tx_complete = true;
}
}
/**
* @brief DMA0_Channel1中断处理函数
* @param 无
*/
extern "C" void DMA0_Channel1_IRQHandler(void) {
if (dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF)) {
dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF);
spi_dma_rx_complete = true;
}
}
/**
* @brief 角度传感器初始化
* @param 无
@@ -174,7 +112,7 @@ void KTH7823_spi_init(void) {
spi_parameter_struct spi_init_str;
spi_struct_para_init(&spi_init_str);
/* SPI参数:16bit, 主机, 全双工, CPOL=1 CPHA=1, MSB first */
/* SPI 参数:16bit, 主机,全双工,CPOL=1 CPHA=1, MSB first */
spi_init_str.device_mode = SPI_MASTER;
spi_init_str.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_str.frame_size = SPI_FRAMESIZE_16BIT;
@@ -194,18 +132,18 @@ void KTH7823_spi_init(void) {
* @return 0:初始化成功 1:初始化失败
*/
u8 angle_sensor::init(void) {
KTH7823_gpio_init(); // GPIO初始化
KTH7823_spi_init(); // SPI初始化
spi_dma_init(); // DMA初始化
KTH7823_gpio_init(); // GPIO 初始化
KTH7823_spi_init(); // SPI 初始化
return 0;
}
/**
* @ 角度传感器类 设置当前角度
* @param angle 输出级角度值
* @return 0成功/1失败
* @return 0 成功/1 失败
*/
u8 angle_sensor::angle_set(float angle) {
m_cumulativeAngle = angle * REDUCTION_RATIO; // 从Flash读取上次保存的角度
m_cumulativeAngle = angle * REDUCTION_RATIO;
m_firstUpdate = true;
return 0;
}
@@ -218,7 +156,7 @@ inline float angle_sensor::read_sensorAngle(void) {
uint16_t raw_data;
ANG_SPI_CS_ENABLE();
u8 result = spi_rw_half_word_dma(SPI0, DEFAULT_COMMAND, &raw_data);
u8 result = spi_rw_half_word_polling(SPI0, DEFAULT_COMMAND, &raw_data);
ANG_SPI_CS_DISABLE();
if (result == 0) {
@@ -229,14 +167,14 @@ inline float angle_sensor::read_sensorAngle(void) {
return angle_deg;
}
// 仅在调试模式下输出错误,避免发布版本中的I/O开销
// 仅在调试模式下输出错误,避免发布版本中的 I/O 开销
if (result != 0) {
// 使用更高效的串口输出替代printf(如果可用)
printf("Error reading angle from sensor.\n");
ANG_SENS_DEBUG_PRINT("Error reading angle from sensor.\n");
}
return (float)FAULT_RECEIVE;
}
/**
* @brief 更新传感器角度(输出级)
* @return 累积角度值
@@ -253,24 +191,23 @@ float angle_sensor::update(void) {
// 计算角度变化
float delta = current_angle_deg - m_lastAngle;
// 处理角度绕回情况(0度和360度之间的跳变)
// 如果变化超过300度,说明发生了绕回
// 处理角度绕回情况(0 度和 360 度之间的跳变)
// 如果变化超过 300 度,说明发生了绕回
if (delta > ANGLE_WRAP_THRESHOLD) {
// 从接近360度绕回到接近0度
// 从接近 360 度绕回到接近 0 度
delta -= ANGLE_WRAP_VALUE;
} else if (delta < -ANGLE_WRAP_THRESHOLD) {
// 从接近0度绕到接近360度
// 从接近 0 度绕到接近 360 度
delta += ANGLE_WRAP_VALUE;
}
m_cumulativeAngle += delta;
m_lastAngle = current_angle_deg;
return m_cumulativeAngle;
}
/**
* @brief 获取累积角度值(输出级)
* @brief 获取累积角度值 (输出级)
* @return 累积角度值
*/
float angle_sensor::getAngle(void) const {
@@ -278,15 +215,25 @@ float angle_sensor::getAngle(void) const {
}
/**
* @brief 获取累积角度值(原始级)
* @brief 获取累积角度值 (原始级)
* @return 累积角度值
*/
float angle_sensor::getRawAngle(void) const { return m_cumulativeAngle; }
/**
* @brief 获取角度值(原始级)
* @return 角度值(原始级)
* @brief 获取角度值 (原始级)
* @return 角度值 (原始级)
*/
float angle_sensor::getcurrent_sensorangle(void) const {
float angle_sensor::getcurrent_sensorangle(void) const{
return current_angle_deg;
}
/**
* @brief 设置电角度零位校准角
* @param[in] angle 校准角(度)
* @return 0 成功
*/
u8 angle_sensor::set_calibration(float angle) {
calibration_angle = normalizeAngle360(angle);
return 0;
}
+80 -4
View File
@@ -1,3 +1,9 @@
/**
* @file device/ang_sensor.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _angsensor_H
#define _angsensor_H
#ifdef __cplusplus
@@ -7,21 +13,80 @@ extern "C" {
#include <cstring>
#include "config.h"
/**
* @brief spi_rw_half_word 函数
* @param[in] spi_periph 参数
* @param[in] i_HalfWord 参数
* @return u16 返回值
*/
u16 spi_rw_half_word(u32 spi_periph, const u16 i_HalfWord);
int spi_rw_half_word_dma(uint32_t spi_periph, uint16_t tx_data,
/**
* @brief spi_rw_half_word_polling 函数
* @param[in] spi_periph SPI 外设
* @param[in] tx_data 发送的半字
* @param[out] rx_data 接收数据输出指针
* @return int 0 成功,-1 超时
* @note 单个半字收发:直接寄存器轮询。原 DMA 实现每次传输要重配两个
* DMA 通道并触发两次 FTF 中断,之后 CPU 仍忙等完成——对单半字
* 传输 DMA 只有开销没有收益(20kHz 调用下约 40k 次/秒额外中断)。
*/
int spi_rw_half_word_polling(uint32_t spi_periph, uint16_t tx_data,
uint16_t *rx_data);
/**
* @brief angle_sensor 类或结构体
*/
class angle_sensor {
public:
float calibration_angle = ANGLESENS_CALIBRATION_ANGLE;
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init(void);
/**
* @brief 读取 read_sensorAngle 相关数据
* @return float 返回值
*/
float read_sensorAngle(void);
/**
* @brief 更新对象内部状态或采样数据
* @return float 返回值
*/
float update(void);
/**
* @brief 复位对象内部状态
* @return void
*/
void reset(void);
/**
* @brief 获取 getAngle 对应的数据或状态
* @return float 返回值
*/
float getAngle(void) const;
/**
* @brief 获取 getRawAngle 对应的数据或状态
* @return float 返回值
*/
float getRawAngle(void) const;
/**
* @brief 获取 getcurrent_sensorangle 对应的数据或状态
* @return float 返回值
*/
float getcurrent_sensorangle(void) const;
/**
* @brief angle_set 函数
* @param[in] angle 参数
* @return u8 返回值
*/
u8 angle_set(float angle);
/**
* @brief 设置 set_calibration 对应的参数或状态
* @param[in] angle 参数
* @return u8 返回值
*/
u8 set_calibration(float angle);
protected:
float m_cumulativeAngle;
@@ -29,10 +94,21 @@ class angle_sensor {
float current_angle_rad;
bool m_firstUpdate;
float m_lastAngle;
float m_lastcumulativeAngle;
float velocity[VEL_FILTER_SIZE] = {0.0f};
};
/**
* @brief 快速角度归一化到 [0, 360)
* 小角度保留 while 快路径;大角度先一次性扣掉整圈数,避免累计角很大时 while 循环过多。
* 不依赖 fmodf,减少嵌入式数学库开销和链接风险。
*/
inline float normalizeAngle360(float angle) {
if (angle >= 720.0f || angle <= -360.0f) {
int turns = (int)(angle / 360.0f);
angle -= (float)turns * 360.0f;
}
while (angle >= 360.0f) angle -= 360.0f;
while (angle < 0.0f) angle += 360.0f;
return angle;
}
#ifdef __cplusplus
}
#endif
+221 -10
View File
@@ -1,9 +1,218 @@
/**
* @file device/motor.cpp
* @brief 底层设备驱动与外设封装源文件
* @note 功率级门驱动芯片抽象层:
* - 老板 DRV8313(集成 FET 三半桥,无 SPI):保持原使能时序
* - 新板 DRV8323RS(外置 MOS):使能前经 SPI 配置 3x PWM 模式、
* CSA 增益、VDS 过流门限、死区与 IDRIVE,并逐条回读校验
*/
#include "motor.h"
#include "Timer.h"
#include "config.h"
#include "ang_sensor.h"
#include "gd32c10x_eval.h"
#include "gd32c10x_gpio.h"
#include "gd32c10x_spi.h"
#include "systick.h"
#include <cstdio>
/* 角度传感器 SPI 初始化(ang_sensor.cpp):DRV 配置完成后恢复其 SPI 模式 */
extern void KTH7823_spi_init(void);
/**
* @brief 门驱动芯片引脚预初始化
* @note 新板:DRVSLP(PB12) 置低保持 DRV8323 睡眠、片选 PB14 释放、
* PB2(nFAULT) 内部上拉输入。必须在任何可能拉高使能脚的代码之前调用;
* 老板:无操作(原时序在 timer1_gpio_config 中保持不变)。
*/
void gate_driver_preinit(void) {
#if GATE_DRIVER_DRV8323
rcu_periph_clock_enable(RCU_GPIOB);
/* 使能脚推挽输出并保持低:芯片睡眠,栅极全 Hi-Z,寄存器默认值不起作用 */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
gpio_bit_reset(GPIOB, GPIO_PIN_12);
/* SPI 片选默认释放(SCLK 空闲低时才允许 CS 翻转) */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
gpio_bit_set(GPIOB, GPIO_PIN_14);
/* nFAULT 低有效,开漏输出 → 内部上拉,避免浮空误读成故障 */
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
#endif
}
#if GATE_DRIVER_DRV8323
/* ---------------- DRV8323 SPI ----------------
* 帧格式:[15:11] 寄存器地址 [10] 1读0写 [9:0] 数据。
* 时序:SCLK 空闲低、SDI 下降沿采样(SPI 模式1),与角度传感器
* (模式3)不同:配置前重初始化 SPI0,配置完调 KTH7823_spi_init() 恢复。
* 寄存器/位定义来源:TI SLVSDJ3D(DRV832x datasheet)Register Maps。 */
#define DRV_CS_ENABLE() gpio_bit_reset(GPIOB, GPIO_PIN_14)
#define DRV_CS_DISABLE() gpio_bit_set(GPIOB, GPIO_PIN_14)
#define DRV8323_REG_FAULT1 0x00u /* FAULT/VDS_OCP/GDF/UVLO/OTSD/VDS_Hx,VDS_Lx */
#define DRV8323_REG_VGS2 0x01u /* SA/SB/SC_OC、OTW、CPUV、VGS_Hx,VGS_Lx */
#define DRV8323_REG_DRVCTRL 0x02u /* [6:5]PWM_MODE [2]COAST [1]BRAKE [0]CLR_FLT */
#define DRV8323_REG_GDRV_HS 0x03u /* [10]LOCK [9:6]IDRIVEP_HS [5:2]IDRIVEN_HS */
#define DRV8323_REG_GDRV_LS 0x04u /* [10]CBC [9:8]TDRIVE [7:4]IDRIVEP_LS [3:0]IDRIVEN_LS */
#define DRV8323_REG_OCPCTRL 0x05u /* [10]TRETRY [9:8]DEAD_TIME [7:6]OCP_MODE [5:4]OCP_DEG [3:0]VDS_LVL */
#define DRV8323_REG_CSACTRL 0x06u /* [10]CSA_FET [9]VREF_DIV [8]LS_REF [7:6]CSA_GAIN [1:0]SEN_LVL */
/* PWM_MODE=01b:3x PWM。INHx 低→下管开、高→上管开,死区芯片内部插入。
芯片复位默认 00b(6x PWM),6x 下 INLx 语义不同,本板 INLx=DRVSLP 常高,
必须切到 3x,否则上管永远开不了(电流全走体二极管)。 */
#define DRV8323_PWM_MODE_3X (1u << 5)
/* 栅驱动电流:源 170mA/灌 190mA(WSD6035DN33 Qg≈12.6nC,边沿约 75ns,
* 无栅电阻布局下避免过快边沿加重振铃) */
#define DRV8323_IDRIVE_HS ((0x6u << 6) | (0x7u << 2))
#define DRV8323_IDRIVE_HS_MASK ((0xFu << 6) | (0xFu << 2))
#define DRV8323_IDRIVE_LS ((0x6u << 4) | (0x7u << 0))
#define DRV8323_IDRIVE_LS_MASK ((0xFu << 4) | (0xFu << 0))
/* 死区 200ns(20kHz 大 Qg 管的安全值);OCP_MODE=10b 仅上报不动作:
* VDS 门限受 RDSon 温漂影响大(热态 20→30mΩ),先靠软件限流+热模型,
* 避免误跳闸;VDS_LVL=0.75V(默认),故障位可经 drv_fault_status() 读取 */
#define DRV8323_OCP_CFG ((0x2u << 8) | (0x2u << 6) | (0x9u << 0))
#define DRV8323_OCP_CFG_MASK ((0x3u << 8) | (0x3u << 6) | (0xFu << 0))
/** @brief CSA 增益值(V/V) → 0x06[7:6] 编码(5:00b 10:01b 20:10b 40:11b) */
constexpr u16 drv8323_gain_code(float g) {
return (g <= 5.0f) ? 0x0u : (g <= 10.0f) ? 0x1u : (g <= 20.0f) ? 0x2u : 0x3u;
}
#define DRV8323_CSA_GAIN_BITS (drv8323_gain_code(DRV8323_CSA_GAIN_VV) << 6)
/**
* @brief DRV8323 单帧 SPI 收发(片选内 16bit,帧间隔由调用节奏保证)
* @param[in] frame 完整 16bit 帧
* @return u16 从机返回的 10bit 数据
*/
static u16 drv8323_xfer(u16 frame) {
u16 rx = 0;
DRV_CS_ENABLE();
spi_rw_half_word_polling(SPI0, frame, &rx);
DRV_CS_DISABLE();
return rx & 0x03FFu;
}
/**
* @brief 读改写一个寄存器并回读校验
* @param[in] addr 寄存器地址
* @param[in] clrmask 要清零的位
* @param[in] setmask 要置一的位
* @return u8 0=回读一致 1=校验失败
*/
static u8 drv8323_write_verify(u8 addr, u16 clrmask, u16 setmask) {
u16 cur = drv8323_xfer((u16)(((u16)addr << 11) | (1u << 10)));
u16 nv = (u16)((cur & ~clrmask) | setmask);
drv8323_xfer((u16)(((u16)addr << 11) | nv));
u16 rb = drv8323_xfer((u16)(((u16)addr << 11) | (1u << 10)));
return (rb == nv) ? 0u : 1u;
}
#endif /* GATE_DRIVER_DRV8323 */
/**
* @brief 门驱动芯片上电配置
* @return u8 0=成功;非 0 按位指示回读校验失败的寄存器
* (bit0:DRVCTRL bit1:GDRV_HS bit2:GDRV_LS bit3:OCPCTRL bit4:CSACTRL)
* @note 调用时机:TIMER1/PWM 已配置且占空比为 0 之后。拉高 DRVSLP 唤醒
* 芯片的瞬间 INHx=0、INLx=1,默认 6x 模式下为"三相下管导通",
* 静止电机零电流,与 3x 模式 0% 占空比稳态一致,随后写模式无扰动。
*/
u8 gate_driver_bringup(void) {
#if GATE_DRIVER_DRV8323
/* 1. 唤醒芯片:DRVSLP 网络 200nF + 芯片内部复位,留足建立时间 */
drv_enable(1);
delay_1ms(10);
/* 2. SPI0 切到 DRV8323 时序(模式1:CPOL=0/下降沿采样,2MHz) */
spi_parameter_struct spi_init_str;
spi_struct_para_init(&spi_init_str);
spi_init_str.device_mode = SPI_MASTER;
spi_init_str.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_str.frame_size = SPI_FRAMESIZE_16BIT;
spi_init_str.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
spi_init_str.nss = SPI_NSS_SOFT;
spi_init_str.endian = SPI_ENDIAN_MSB;
spi_init_str.prescale = 60; /* 120MHz/60 = 2MHz */
spi_init(SPI0, &spi_init_str);
spi_nss_output_enable(SPI0);
spi_nssp_mode_enable(SPI0);
spi_enable(SPI0);
/* 3. 配置并逐条回读校验(读改写,保留未触碰位的出厂默认:
* VREF_DIV=1→SOx 中点 VREF/2,与 CURRENT_OFFSET 的 1.655V 一致;
* CSA_FET=0→正端 SPx,与 shunt 的 SP/SN 接法一致) */
u8 err = 0;
err |= (u8)(drv8323_write_verify(DRV8323_REG_DRVCTRL, 0x0000u,
DRV8323_PWM_MODE_3X)
<< 0);
err |= (u8)(drv8323_write_verify(DRV8323_REG_GDRV_HS,
DRV8323_IDRIVE_HS_MASK, DRV8323_IDRIVE_HS)
<< 1);
err |= (u8)(drv8323_write_verify(DRV8323_REG_GDRV_LS,
DRV8323_IDRIVE_LS_MASK, DRV8323_IDRIVE_LS)
<< 2);
err |= (u8)(drv8323_write_verify(DRV8323_REG_OCPCTRL, DRV8323_OCP_CFG_MASK,
DRV8323_OCP_CFG)
<< 3);
err |= (u8)(drv8323_write_verify(DRV8323_REG_CSACTRL, 0x3u << 6,
DRV8323_CSA_GAIN_BITS)
<< 4);
/* 4. CLR_FLT 脉冲清一次上电故障锁存,再恢复正常值 */
drv8323_xfer((u16)((DRV8323_REG_DRVCTRL << 11) | DRV8323_PWM_MODE_3X | 0x1u));
drv8323_xfer((u16)((DRV8323_REG_DRVCTRL << 11) | DRV8323_PWM_MODE_3X));
/* 5. 恢复角度传感器的 SPI 模式(模式3) */
KTH7823_spi_init();
return err;
#else
return 0; /* 老板 DRV8313 无 SPI,原使能时序保持不变 */
#endif
}
/**
* @brief 门驱动运行时使能/睡眠
* @note 新板:PB12 同时是 DRV8323 的 ENABLE 与 INLA/B/C 电平——
* 高=正常运转,低=芯片睡眠三相 Hi-Z(惯性滑行,兼作 MCU 复位
* 时的天然 fail-safe);老板:PB12+PC13 双脚,保持原行为。
*/
void drv_enable(u8 en) {
if (en) {
gpio_bit_set(GPIOB, GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
gpio_bit_set(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
#endif
} else {
gpio_bit_reset(GPIOB, GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
gpio_bit_reset(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
#endif
}
}
/**
* @brief 读取 nFAULT 引脚状态(PB2,低有效)
* @return u8 1=故障有效 0=正常
*/
u8 drv_nfault_active(void) {
return (gpio_input_bit_get(GPIOB, GPIO_PIN_2) == RESET) ? 1u : 0u;
}
/**
* @brief 读取门驱动故障状态寄存器
* @return u32 [9:0]=Fault Status1(0x00) [19:10]=VGS Status2(0x01);老板返回 0
*/
u32 drv_fault_status(void) {
#if GATE_DRIVER_DRV8323
u32 f1 = drv8323_xfer((u16)((DRV8323_REG_FAULT1 << 11) | (1u << 10)));
u32 f2 = drv8323_xfer((u16)((DRV8323_REG_VGS2 << 11) | (1u << 10)));
return f1 | (f2 << 10);
#else
return 0;
#endif
}
/**
* @brief driver输出初始化
*/
@@ -12,16 +221,20 @@ void motor_init(void) {
timer1_pwm_config();
// 使能GPIOB时钟
rcu_periph_clock_enable(RCU_GPIOB);
// 配置PB2为输入引脚,用于读取电机状态
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
// 配置PB2为输入引脚,用于读取电机状态(nFAULT)。
// 用内部上拉(GPIO_MODE_IPU)而不是浮空:nFAULT 是开漏输出,无故障时靠
// 上拉保持高。现在 PB2 电平直接决定"允不允许运行",浮空输入在接线未确认
// 或无上拉时会随机读低,把整机锁死;内部上拉(~40kΩ)与外部上拉并联无害。
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
/* 配置GPIOB的PB14为推挽输出 SPICS */
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,
GPIO_PIN_14 | GPIO_PIN_12);
#if !GATE_DRIVER_DRV8323
// 使能GPIOC时钟,用于电机驱动使能引脚
rcu_periph_clock_enable(RCU_MOTOR_EN_GPIO);
// 配置PC13为推挽输出,用于控制电机驱动器使能
// 配置PC13为推挽输出,用于控制电机驱动器使能(老板专用)
gpio_init(MOTOR_EN_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, MOTOR_EN_GPIO_PIN);
#endif
}
/**
@@ -29,7 +242,9 @@ void motor_init(void) {
*/
void motor_reset(void) {
motor_sleep();
#if FOC_DEBUG
printf("motor reseting...\r\n");
#endif
motor_resume();
}
@@ -37,16 +252,12 @@ void motor_reset(void) {
* @brief 电机休眠
*/
void motor_sleep(void) {
gpio_bit_reset(GPIOB, GPIO_PIN_12);
// 同时禁用电机驱动器
gpio_bit_reset(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
drv_enable(0);
}
/**
* @brief 电机启动
*/
void motor_resume(void) {
gpio_bit_set(GPIOB, GPIO_PIN_12);
// 同时启用电机驱动器
gpio_bit_set(MOTOR_EN_GPIO_PORT, MOTOR_EN_GPIO_PIN);
drv_enable(1);
}
+62
View File
@@ -1,14 +1,76 @@
/**
* @file device/motor.h
* @brief 底层设备驱动与外设封装源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _MOTOR_H
#define _MOTOR_H
#include "config.h"
#include "gd32c10x_eval.h"
// 定义电机驱动使能引脚
/** @brief MOTOR_EN_GPIO_PORT 宏定义 */
#define MOTOR_EN_GPIO_PORT GPIOC
/** @brief MOTOR_EN_GPIO_PIN 宏定义 */
#define MOTOR_EN_GPIO_PIN GPIO_PIN_13
/** @brief RCU_MOTOR_EN_GPIO 宏定义 */
#define RCU_MOTOR_EN_GPIO RCU_GPIOC
/**
* @brief 初始化电机驱动输出
* @return void
*/
void motor_init(void);
/**
* @brief 复位电机驱动
* @return void
*/
void motor_reset(void);
/**
* @brief 唤醒电机驱动
* @return void
*/
void motor_resume(void);
/**
* @brief 使电机驱动进入休眠
* @return void
*/
void motor_sleep(void);
/**
* @brief 门驱动芯片引脚预初始化(全部置安全态:使能拉低、CS 释放)
* @note 必须在任何可能拉高使能脚的代码之前调用
* @return void
*/
void gate_driver_preinit(void);
/**
* @brief 门驱动芯片上电配置
* 新板(DRV8323RS):拉高使能 → SPI 写 3x PWM 模式/CSA 增益/VDS
* 保护/死区/IDRIVE 并逐条回读校验,最后恢复角度传感器 SPI 模式;
* 老板(DRV8313):无 SPI,保持原上电序列(无操作)。
* @return u8 0 成功;非 0 为配置/回读校验失败的寄存器位图
* @note 调用时机:TIMER1/PWM 已配置且占空比为 0 之后(此时使能芯片,
* 半桥只会进入"下管常开"的静止安全态)
*/
u8 gate_driver_bringup(void);
/**
* @brief 门驱动运行时使能/睡眠(分板封装 PB12,老板同时控制 PC13)
* @param[in] en 1=使能 0=睡眠
* @return void
*/
void drv_enable(u8 en);
/**
* @brief 读取 nFAULT 引脚状态
* @return u8 1=故障有效(低电平) 0=正常
*/
u8 drv_nfault_active(void);
/**
* @brief 读取门驱动故障状态寄存器(仅 DRV8323 有效)
* @return u16 [9:0]=Fault Status1(0x00) [19:10]=VGS Status2(0x01);老板返回 0
*/
u32 drv_fault_status(void);
#endif
-25
View File
@@ -1,25 +0,0 @@
#include "adc.h"
#include <cstdio>
constexpr int status_print = 0;
extern sampler sampler;
void test_1111() {
// 初始化CAN回环测试
// can_loopback_init();
// can_config();
if (status_print) {
/* value print */
printf(" the temperature data is %2.2f degrees Celsius\r\n",
sampler.get_mcu_temp());
printf(" the drv_temperature data is %2.2f degrees Celsius\r\n",
sampler.get_drv_temp());
printf(" the U current data is %2.2f mA\r\n", sampler.get_current('U'));
printf(" the V current data is %2.2f mA\r\n", sampler.get_current('V'));
printf(" the W current data is %2.2f mA\r\n", sampler.get_current('W'));
printf(" the VBUS is %2.2f V\r\n", sampler.get_vbus_voltage());
// printf(" the angle is %2.2f\r\n", tle5012b_read());
printf("\n----------------------------------------------------\n");
}
// can_loopback_test();
}
+104 -76
View File
@@ -1,4 +1,11 @@
/**
* @file main.cpp
* @brief 应用程序主流程源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "main.h"
#include "tasks.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
@@ -11,27 +18,48 @@
#include "motor.h"
#include "systick.h"
extern CAN_transmiter CAN0_transmiter; // CAN初始化
// MAIN 调试宏定义
#if MAIN_DEBUG
/** @brief MAIN_DEBUG_PRINT 宏定义 */
#define MAIN_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief MAIN_DEBUG_PRINT 宏定义 */
#define MAIN_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
extern sampler sampler; // 采样器
extern angle_sensor KTH7823; // 角度传感器
extern EncoderVelocityKalman Vel_kalman; // 速度滤波
extern EncoderAngleKalman Angle_kalman; // 角度滤波
extern ThermalController thermalController; // 热量控制器
extern FOC_Controlroller FOC; // FOC控制
extern FlashEEPROM flashEEP; // EEPROM类
extern VersionManager Version;
extern FOC_Controller FOC; // FOC 控制
extern FlashEEPROM flashEEP; // EEPROM 类
// 全局版本管理器实例
// 主任务调度共享位图:TIMER2 ISR 置位任务,main 循环短关中断后取快照并清位。
TaskScheduler scheduler = {0};
volatile u32 motor_runtime_50us = 0; // 电机驱动计算轮次
volatile u32 cnt_test = 0; // 负载检测
// 电机 50us 控制周期计数:主循环电机任务递增,观测器/计时逻辑读取。
u32 motor_runtime_50us = 0;
// 负载检测/通信心跳计数:主循环递增,CAN 周期发送任务清零。
u32 cnt_test = 0;
// 初始化错误位图(INIT_ERR_*,config.h):hardware_init 按模块置位,
// CAN STATUS 帧 InitStatus 反馈给上位机,0 表示全部初始化正常
volatile u32 init_error_flags = 0;
/**
* @brief 应用程序入口函数
* @return int 返回值
*/
int main(void) {
u8 init_status = hardware_init();
while (init_status) { // 初始化检测
std::printf("初始化失败\r\n");
// 初始化失败:持续通过 CAN STATUS 帧上报 InitStatus 错误码
// (含电流检测配平超限/无采样帧),上位机无需调试串口即可识别故障模块
printf("初始化失败 InitStatus=0x%08X\r\n", init_error_flags);
CAN0_transmiter.send_status_frame(); // STATUS 帧字节 32~35 携带错误位图
delay_1ms(100);
}
OTA_initial(); // OTA初始化,A/B分区处理
OTA_initial(); // OTA 初始化,A/B 分区处理
angsens_MG_check(); // 角度传感器磁感应强度测试
watchdog_init(50); // 看门狗初始化
@@ -43,83 +71,83 @@ int main(void) {
scheduler.taskFlags &= ~currentTasks; // 一次性清除所有已处理的任务标志
__enable_irq();
if (currentTasks == 0U) {
idle_task_count();
continue;
}
do_task(currentTasks);
}
return 0;
}
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
*/
u8 hardware_init(void) {
__enable_irq();
systick_config(); // 系统时钟初始化
unused_pin_init(); // 未使用引脚初始化
gate_driver_preinit(); // 门驱动使能脚预置安全态(新板:DRVSLP 低=芯片睡眠)
USART0_init(); // 调试串口初始化
User_timer_init(); // 任务Timer初始化
User_timer_init(); // 任务 Timer 初始化
u8 init_status = 0;
init_status += KTH7823.init(); // 角度传感器初始化
init_status += sampler.init(); // 采样器初始化
flashEEP.init(); // EEPROM初始化,必须放在角度卡尔曼之前,角度传感器之后
// 初始化错误码按模块置位(INIT_ERR_*,位或组合;0=全部正常),
// 经 CAN STATUS 帧 InitStatus 反馈
init_error_flags = 0;
if (KTH7823.init()) {
init_error_flags |= INIT_ERR_ANGLE_SENSOR; // 角度传感器初始化
}
if (sampler.init()) {
init_error_flags |= INIT_ERR_ADC; // 采样器/ADC 初始化
}
init_status += Angle_kalman.init(KTH7823.getAngle()); // 角度滤波初始化
init_status += Vel_kalman.init(0.0f); // 速度滤波初始化
init_status += CAN0_transmiter.init(); // CAN初始化
init_status += FOC.init(); // FOC初始化
return init_status;
// EEPROM 初始化 (必须在 CAN 初始化之前,因为 CAN 会从 Flash 读取节点 ID)
flashEEP.init(); // EEPROM 初始化
// CAN 初始化 (会先设置默认节点 ID,然后从 Flash 加载已保存的节点 ID)
if (CAN0_transmiter.init()) {
init_error_flags |= INIT_ERR_CAN; // CAN 初始化
}
if (Angle_kalman.init(KTH7823.getAngle())) {
init_error_flags |= INIT_ERR_ANG_KALMAN; // 角度滤波器初始化
}
if (Vel_kalman.init(0.0f)) {
init_error_flags |= INIT_ERR_VEL_KALMAN; // 速度滤波器初始化
}
if (FOC.init()) {
init_error_flags |= INIT_ERR_FOC; // FOC 初始化
}
// 门驱动上电配置放在 FOC 校验之后:此时 TIMER1/PWM 已启动且占空比为 0,
// 新板(DRV8323)在此拉高 DRVSLP 唤醒芯片并经 SPI 配置 3x PWM/增益/保护,
// 随后即使任务立即输出 0 占空比,桥臂也处于"下管常开"的静止安全态
if (gate_driver_bringup()) {
init_error_flags |= INIT_ERR_GATE_DRIVER; // 门驱动 bringup
}
// 三相电流零点配平:采样前唤醒门驱动,让 0% 占空比下三相下管导通,
// PGNDx 与功率地等电位、共模与运行时一致,配平结果才可用。
// 老板 bringup 是空操作、上电默认睡眠:此处临时唤醒(此时占空比 0=
// 三相下管导通,静止安全态),配平完恢复睡眠,保持"上电未使能"的
// 原始安全态(模式 0 的 drv_hold 恒占空比不会真实输出);新板
// bringup 已唤醒并保持常醒,无需额外操作。配平值超限/无采样帧
// 分别置独立错误位。
#if !GATE_DRIVER_DRV8323
motor_resume();
#endif
u8 current_cal = sampler.zero_calibrate();
#if !GATE_DRIVER_DRV8323
motor_sleep();
#endif
if (current_cal == 1) {
init_error_flags |= INIT_ERR_CURRENT_NOFRAME; // 无采样帧
} else if (current_cal == 2) {
init_error_flags |= INIT_ERR_CURRENT_BAL; // 配平值超限
}
return (init_error_flags != 0U) ? 1U : 0U;
}
void do_task(u32 currentTasks) {
cnt_test++;
// 按照优先级和重要性处理任务
if (currentTasks & (1 << TASK_CURRENT_UPDATE)) { // 更新电流采样值
sampler.update_current();
}
if (currentTasks & (1 << TASK_ANGLE_UPDATE)) { // 更新角度传感器值
KTH7823.update();
FOC.rt_angle = Angle_kalman.update(KTH7823.getAngle());
}
if (currentTasks & (1 << TASK_VELOCITY_UPDATE)) { // 更新速度传感器值
FOC.rt_velocity = Vel_kalman.update(Angle_kalman.getVelocity());
}
if (currentTasks & (1 << TASK_MOTOR_CONTROL)) { // 电机控制任务(最高优先级)
motor_runtime_50us++;
// 函数指针数组,包含各种控制模式对应的函数
static FOCFunctionPtr control_functions[] = {
&FOC_Controlroller::drv_hold, // 0: 占空比置
&FOC_Controlroller::
angle_control_with_velocity_limit, // 1:
// 角度闭环控制,带速度限制
&FOC_Controlroller::velocity_control, // 2: 速度闭环控制
&FOC_Controlroller::current_control, // 3: 电流闭环控制
&FOC_Controlroller::velocity_open_loop, // 4: 速度开环控制
};
// 边界检查,防止数组越界
if (FOC.control_mode >= 0 && FOC.control_mode < 5) { // 更新为7个控制模式
// 调用对应的控制函数
(FOC.*control_functions[FOC.control_mode])();
}
}
if (currentTasks & (1 << TASK_MCU_TEMP_UPDATE)) { // 更新mcu温度传感器值
sampler.update_mcu_temp();
}
if (currentTasks & (1 << TASK_DRV_TEMP_UPDATE)) { // 更新电机温度传感器值
sampler.update_drv_temp();
}
if (currentTasks & (1 << TASK_THERMAL_CONTROL)) { // 热量控制
thermalController.update();
}
if (currentTasks & (1 << TASK_MAGNETIC_CHECK)) { // 磁感应强度检测
angsens_MG_check();
}
if (currentTasks & (1 << TASK_VBUS_UPDATE)) { // 更新vbus电压值
power_manage();
}
if (currentTasks & (1 << TASK_CAN_TRANSMIT)) { // CAN通信
// CAN0_transmiter.testfloat_union.f = cnt_test; // 负载检测
cnt_test = 0;
CAN0_transmiter.can0_send_flag = SET;
CAN0_transmiter.communication_check();
fwdgt_counter_reload(); // 看门狗复位
}
}
+6 -1
View File
@@ -34,9 +34,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef MAIN_H
#define MAIN_H
#include "config.h"
#include <cstdint>
void do_task(u32 currentTasks);
/**
* @brief 初始化应用层使用的硬件与软件模块
* @return u8 返回值
*/
u8 hardware_init(void);
#endif /* MAIN_H */
+358 -66
View File
@@ -1,108 +1,361 @@
/**
* @file sys/config.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef __CONFIG_H
#define __CONFIG_H
#include "gd32c10x_eval.h"
#include <cstdint>
// 如果使用CMSIS DSP库,需要定义ARM_MATH_CM4
// 项目级定宽类型别名(u8/u16/u32 等):整个工程统一从这里取,
// 重复 typedef 同一类型在 C++ 中合法,包含多次无副作用
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef volatile uint8_t vu8;
typedef volatile uint16_t vu16;
typedef volatile uint32_t vu32;
// 如果使用 CMSIS DSP 库,需要定义 ARM_MATH_CM4
#define ARM_MATH_CM4
#define EEP_DEBUG 1
#define OTA_DEBUG 1
#define SLEEP_DEBUG 1
#define EEP_DEBUG 0
#define OTA_DEBUG 0
#define SLEEP_DEBUG 0
#define ANG_SENS_DEBUG 0
#define CAN_DEBUG 0
#define MAIN_DEBUG 0
#define FOC_DEBUG 0
// 版本号定义 - 可根据实际项目进行调整
constexpr u16 SOFTWARE_VERSION_MAJOR = 26;
constexpr u16 SOFTWARE_VERSION_MINOR = 1;
constexpr u16 SOFTWARE_VERSION_PATCH = 24;
// ===================== 版本号定义 =====================
// 软件版本 = 编译日期自动生成(年.月.日)+ 当天构建小版本号(手动递增)
// __DATE__ 形如 "Mmm dd yyyy"(如 "Aug 29 2026"):2026-08-29 当天第一次
// 构建得到 26.8.29.1;同一天多次出固件时把 SOFTWARE_VERSION_BUILD +1
#define CFG_VER_YEAR \
((__DATE__[7] - '0') * 1000 + (__DATE__[8] - '0') * 100 + \
(__DATE__[9] - '0') * 10 + (__DATE__[10] - '0'))
#define CFG_VER_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n')
#define CFG_VER_IS_FEB (__DATE__[0] == 'F' && __DATE__[1] == 'e' && __DATE__[2] == 'b')
#define CFG_VER_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r')
#define CFG_VER_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p' && __DATE__[2] == 'r')
#define CFG_VER_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y')
#define CFG_VER_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n')
#define CFG_VER_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l')
#define CFG_VER_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u' && __DATE__[2] == 'g')
#define CFG_VER_IS_SEP (__DATE__[0] == 'S' && __DATE__[1] == 'e' && __DATE__[2] == 'p')
#define CFG_VER_IS_OCT (__DATE__[0] == 'O' && __DATE__[1] == 'c' && __DATE__[2] == 't')
#define CFG_VER_IS_NOV (__DATE__[0] == 'N' && __DATE__[1] == 'o' && __DATE__[2] == 'v')
#define CFG_VER_IS_DEC (__DATE__[0] == 'D' && __DATE__[1] == 'e' && __DATE__[2] == 'c')
#define CFG_VER_MONTH \
(CFG_VER_IS_JAN ? 1 \
: CFG_VER_IS_FEB ? 2 \
: CFG_VER_IS_MAR ? 3 \
: CFG_VER_IS_APR ? 4 \
: CFG_VER_IS_MAY ? 5 \
: CFG_VER_IS_JUN ? 6 \
: CFG_VER_IS_JUL ? 7 \
: CFG_VER_IS_AUG ? 8 \
: CFG_VER_IS_SEP ? 9 \
: CFG_VER_IS_OCT ? 10 \
: CFG_VER_IS_NOV ? 11 \
: CFG_VER_IS_DEC ? 12 \
: 0)
/* 日字段两位,个位数时首字符为空格(如 "Aug 5 2026") */
#define CFG_VER_DAY \
((__DATE__[4] == ' ') ? (__DATE__[5] - '0') \
: ((__DATE__[4] - '0') * 10 + (__DATE__[5] - '0')))
constexpr u16 SOFTWARE_VERSION_MAJOR = CFG_VER_YEAR % 100; // 年后两位,如 26
constexpr u16 SOFTWARE_VERSION_MINOR = CFG_VER_MONTH; // 月
constexpr u16 SOFTWARE_VERSION_PATCH = CFG_VER_DAY; // 日
// 当天第几次构建(日期由 __DATE__ 自动带出):
// 26.10.10.2 = nFAULT(MotorStatus) 由"仅上报"改为运行门
// 26.10.11.1 = 停机/解除统一收进 RunInhibit 闸门,删掉 FOC 里 5 个互引的开关机函数
constexpr u16 SOFTWARE_VERSION_BUILD = 1;
// 硬件版本:全手写,仅在 PCB 改版时修改(编译 APP 不影响)
constexpr u16 HARDWARE_VERSION_MAJOR = 25;
constexpr u16 HARDWARE_VERSION_MINOR = 12;
constexpr u16 HARDWARE_VERSION_PATCH = 01;
constexpr u16 HARDWARE_VERSION_PATCH = 1;
constexpr u16 HARDWARE_VERSION_BUILD = 0;
// BOOT 版本:全手写,仅在 bootloader 更新时修改(APP 编译不影响)
constexpr u16 BOOT_VERSION_MAJOR = 26;
constexpr u16 BOOT_VERSION_MINOR = 1;
constexpr u16 BOOT_VERSION_PATCH = 12;
constexpr u16 BOOT_VERSION_BUILD = 0;
// Flash分区配置
constexpr u32 BOOTLOADER_SIZE = 0x6000; // 24KB
constexpr u32 BOOTLOADER_START_ADDR = 0x08000000; // BOOT起始地址
constexpr u32 APPLATEST_START_ADDR = 0x08012000; // APP起始地址
constexpr u32 APPBACK_START_ADDR = 0x08006000; // APPBACK起始地址
// ===================== 功率级门驱动芯片选择 =====================
// 1 = 新板 DRV8323RS(外置 WSD6035DN33):使能前经 SPI 配置 3x PWM 模式、
// CSA 增益、VDS 过流门限;3 路 PWM 接 INHA/B/C(CH2/CH1/CH0,
// 固件内已做通道重映射);PB12(DRVSLP) 同时是使能脚与 INLA/B/C 电平
// (高=正常运转,低=三相 Hi-Z 睡眠滑行)。
// 0 = 老板 DRV8313(集成 FET 三半桥):无 SPI,保持原使能时序与相序。
#define GATE_DRIVER_DRV8323 0
#if GATE_DRIVER_DRV8323
// DRV8323 片上 CSA 增益(V/V),经 SPI 写入 0x06[7:6],仅 5/10/20/40 有效。
// 双极性量程 = ±1.655V/(G×Rshunt):G=10、R=10mΩ 时约 ±16.5A。
// 修改后电流换算系数 CURRENT_FACTOR(ADC.cpp)自动跟随,但软件限流标定需复核。
constexpr float DRV8323_CSA_GAIN_VV = 10.0f;
constexpr float DRV8323_SHUNT_OHM = 0.010f;
#endif
// ===================== 相电流采样前端选择(独立于门驱动芯片) =====================
// 1 = 低边采样板(DRV8313 + 下桥臂 10mΩ + INA240A2):采样电阻串在各相下管
// 源极(PGNDx)与功率地之间,INA240A2(50V/V)放大后接 ADC0 注入组。
// OUT 静态中点 = (V_REF1+V_REF2)/2 = 1.65V(REF1 接 AGND、REF2 接 A3V3,
// 由芯片内部分压取中,数据手册支持的接法)。
// 换算 0.5V/A → 2.0 A/V,与老板外置放大链(50倍×10mΩ)数值一致,
// 电流链路单位为安培(get_current 返回 A,上报处 ×1000 转 mA)。
// 注意:下桥臂采样电阻只有对应下管导通(INx=0)时才有相电流流过,
// ADC 采样点必须在三相下管同时导通的窗口内(见 TIMER1_CH3_COMPARE_VALUE)。
// 0 = 老板(DRV8313 + 外置放大链):保持原标定系数与采样点。
#define CURRENT_SENSE_INA240 1
#if CURRENT_SENSE_INA240
constexpr float INA240_CSA_GAIN_VV = 50.0f; // INA240A2 = 50V/V(A1=20/A3=100/A4=200)
constexpr float INA240_SHUNT_OHM = 0.010f; // 低边采样电阻 10mΩ
#endif
// 编译时间(AC6 编译器自动生成)
#define APP_BUILD_DATE __DATE__
#define APP_BUILD_TIME __TIME__
// Flash 分区配置
constexpr u32 BOOTLOADER_START_ADDR = 0x08000000; // BOOT 起始地址
constexpr u32 APPLATEST_START_ADDR = 0x08012000; // APP 起始地址
constexpr u32 APPBACK_START_ADDR = 0x08006000; // APPBACK 起始地址
constexpr u32 APP_MAX_SIZE = 0xC000; // 48KB
constexpr u32 FMC_PAGE_SIZE = 0x400; // 1KB
constexpr u32 SLEEP_CONFIG_START = 0x0801E400; // 睡眠配置起始地址
constexpr u32 SLEEP_CONFIG_END = 0x0801E800; // 睡眠配置结束地址
constexpr u32 FLASH_EMPTY = 0xFFFFFFFF; // 空数据
constexpr u32 SLEEP_ENABLE = 0x11111111; // 睡眠使能
constexpr u32 SLEEP_DISABLE = 0x00000000; // 睡眠禁止
// 节点 ID 配置 (用于多电机 CAN 总线隔离)
// 使用 CAN ID 高 16 位 (bits 31-16) 存储节点 ID,低 16 位 (bits 15-0) 存储基础 ID
// 格式:[节点 ID(16 位)][基础 ID(16 位)],例如:node_id=1, base_id=0x0200 → 0x00010200
//
// 节点 ID 存储方案:
// - 使用独立 Flash 页存储节点 ID,与EEPROM完全隔离
// - 采用双重备份机制:主备份与备用备份分属两个独立 1KB 页,
// 擦写其一不影响另一份,掉电时至少保留一份有效数据
// - 写入采用乒乓方式:总是写入"非最新"的那一页,另一页全程保有旧值
// - 使用魔数 (NODE_ID_MAGIC) 验证数据有效性,防止位翻转导致错读
// - 使用写入计数器区分最新数据(计数器跳过 0,比较用有符号差值容忍回绕)
constexpr u32 NODE_ID_PAGE_ADDR = 0x0801E800; // 节点 ID 主存储页 (1KB,独立区域)
constexpr u32 NODE_ID_PRIMARY_ADDR = 0x0801E800; // 主备份地址
constexpr u32 NODE_ID_BACKUP_ADDR = 0x0801EC00; // 备用备份地址(独立 1KB 页)
constexpr u32 NODE_ID_MAGIC = 0x4E4F4445; // 魔数 'NODE' 用于验证数据有效性
constexpr u16 DEFAULT_NODE_ID = 1; // 默认节点 ID
constexpr u16 BROADCAST_NODE_ID = 0x1FFF; // 广播 ID (所有节点响应)
constexpr u16 MAX_NODE_ID = 0x1FFE; // 最大有效节点 ID (0~0x1FFE)
constexpr u16 SETTABLE_NODE_ID_MAX = 0x00FF; // SET_NODE_ID 命令允许设置的上限 (1~0xFF)
// 节点 ID 存储结构 (主备份/备用备份各一份,每份 16 字节,位于各自页首)
// 地址偏移 0x00: 魔数 (0x4E4F4445 = 'NODE')
// 地址偏移 0x04: 节点 ID (u16)
// 地址偏移 0x06: 保留 (u16,填充为0xFFFF)
// 地址偏移 0x08: 写入计数器 (u32,用于区分最新数据)
// 地址偏移 0x0C: CRC32 校验 (u32)
constexpr u8 NODE_ID_MAGIC_OFFSET = 0x00; // 魔数偏移
constexpr u8 NODE_ID_VALUE_OFFSET = 0x04; // 节点ID值偏移
constexpr u8 NODE_ID_COUNTER_OFFSET = 0x08; // 写入计数器偏移
constexpr u8 NODE_ID_CRC_OFFSET = 0x0C; // CRC偏移
// BOOT 相关
// 版图/页大小核验:以下 6 个 OTA 标志位共处 0x0801E000 所在的 1KB Flash 页。
// OTA_SET 会整页擦除该页,故必须保证 FMC 页大小为 1KB(0x400),否则会连带擦除
// 相邻的 SLEEP_CONFIG(0x0801E400)/NODE_ID(0x0801E800)。GD32C103CB 实测页大小为
// 1KB(睡眠/节点 ID/EEPROM 分居相邻 1KB 页且互不影响即为佐证),与 FMC_PAGE_SIZE 一致。
constexpr u32 OTA_CONFIG_NUM = 6;
constexpr u32 n_APP_LATEST_AVAL_ADDR = 0x0801E000; // 最新APP可用地址
constexpr u32 n_APP_BACKUP_AVAL_ADDR = 0x0801E004; // 备份APP可用地址
constexpr u32 n_APP_LATEST_EN_ADDR = 0x0801E008; // 最新APP激活
constexpr u32 n_APP_BACKUP_EN_ADDR = 0x0801E00C; // 备份APP激活
constexpr u32 n_APP_LATEST_CHECK_ADDR = 0x0801E010; // 最新APP校验
constexpr u32 n_APP_BACKUP_CHECK_ADDR = 0x0801E014; // 备份APP校验
constexpr u32 n_APP_LATEST_AVAL_ADDR = 0x0801E000; // 最新 APP 可用地址
constexpr u32 n_APP_BACKUP_AVAL_ADDR = 0x0801E004; // 备份 APP 可用地址
constexpr u32 n_APP_LATEST_EN_ADDR = 0x0801E008; // 最新 APP 激活
constexpr u32 n_APP_BACKUP_EN_ADDR = 0x0801E00C; // 备份 APP 激活
constexpr u32 n_APP_LATEST_CHECK_ADDR = 0x0801E010; // 最新 APP 校验
constexpr u32 n_APP_BACKUP_CHECK_ADDR = 0x0801E014; // 备份 APP 校验
constexpr u32 OTA_NOT_AVAL = 0xFFFFFFFF;
constexpr u32 OTA_AVAL = 0x00000000; // 全零表示可用
// 参数配置
constexpr u8 PP = 11;
constexpr float DIR = -1.0f;
constexpr float DIR = 1.0f; // 默认方向:-1.0f 反转,1.0f 正转
constexpr u16 REDUCTION_RATIO = 25;
constexpr float CURRENT_OFFSET = -1.62f; // 当前电流传感器的零点偏移
constexpr float HOLD_PWM_DUTY = 0.95f;
constexpr u16 ANGLESENS_OFFSET = +0;
constexpr float KE_FACTOR = 14.0f;
// PID配置
constexpr float current_loop_p = 6e-2f / 100 * 12; // 电流P
constexpr float vel_loop_p = 0.01f / 100 * 12; // 速度P
constexpr float angle_loop_p = 20.0f / 100 * 12; // 角度P
constexpr float vel_angloop_p = 0.12f / 100 * 12; // 速度 速度-角度P
constexpr float ang_angloop_p = 100.0f / 100 * 12; // 角度 速度-角度P
constexpr float CURRENT_OFFSET = -3.31f / 2.0f; // 电流通道零点偏移(V),
// 与 INA240 中点 1.65V / DRV8323 SO 中点 VREF/2 一致
// 三相电流零点配平:配平值(各通道实测零偏折算电流)超过该限值视为
// 电流检测初始化错误,随 init_status 阻止启动
constexpr float CURRENT_ZERO_CAL_LIMIT = 0.1f; // 100mA
// ===================== 初始化错误码(CAN STATUS 帧 InitStatus 反馈) =====================
// hardware_init 按模块置位(可位或组合),InitStatus=0 表示全部初始化正常;
// 信号定义见 DBC v0.74 的 STATUS 帧字节 32~35
constexpr u32 INIT_ERR_ANGLE_SENSOR = 1u << 0; // 角度传感器 KTH7823 初始化错误
constexpr u32 INIT_ERR_ADC = 1u << 1; // 采样器/ADC 初始化错误
constexpr u32 INIT_ERR_CAN = 1u << 2; // CAN 初始化错误
constexpr u32 INIT_ERR_ANG_KALMAN = 1u << 3; // 角度滤波器初始化错误
constexpr u32 INIT_ERR_VEL_KALMAN = 1u << 4; // 速度滤波器初始化错误
constexpr u32 INIT_ERR_FOC = 1u << 5; // FOC 初始化错误
constexpr u32 INIT_ERR_CURRENT_BAL = 1u << 6; // 电流检测:零点配平值超限(>100mA)
constexpr u32 INIT_ERR_CURRENT_NOFRAME = 1u << 7; // 电流检测:无采样帧(触发链路异常)
constexpr u32 INIT_ERR_GATE_DRIVER = 1u << 8; // 门驱动 bringup 错误
extern volatile u32 init_error_flags; // 初始化错误位图,定义在 main.cpp
constexpr float HOLD_PWM_DUTY = 0.1f;
constexpr u16 ANGLESENS_OFFSET = 0;
constexpr float ANGLESENS_CALIBRATION_ANGLE = 83.0f;
// 电角度零位校准:施加的对齐占空比与稳定等待时间
constexpr float ALIGN_PWM_DUTY = 0.25f;
constexpr u32 ALIGN_SETTLE_MS = 1000;
constexpr float CALIB_MAX_SPEED = 5.0f; // 输出级速度阈值(deg/s),超过则拒绝校准
constexpr float KE_FACTOR = 0.0f;
// 角度/速度 α-β 观测器参数
constexpr float ANGLE_KALMAN_INITIAL_STATE = 0.0f;
constexpr float ANGLE_KALMAN_INITIAL_COVARIANCE = 0.0f;
constexpr float ANGLE_KALMAN_PROCESS_NOISE_Q = 0.0f;
constexpr float ANGLE_KALMAN_MEASUREMENT_NOISE_R = 0.0f;
constexpr float ANGLE_OBSERVER_ALPHA = 0.75f; // 角度校正增益,越大越跟测量
constexpr float ANGLE_OBSERVER_BETA = 0.002f; // 速度校正增益,原0.001微调至0.002
// 旧速度 Kalman 参数保留用于接口兼容;主速度输出现在直接来自 α-β 观测器
constexpr float VELOCITY_KALMAN_INITIAL_STATE = 0.0f;
constexpr float VELOCITY_KALMAN_INITIAL_COVARIANCE = 1e-3f;
constexpr float VELOCITY_KALMAN_PROCESS_NOISE_Q =
5e-4f; // 略增大,减少拖尾
constexpr float VELOCITY_KALMAN_MEASUREMENT_NOISE_R =
0.05f; // 明显小于旧值,避免压得太死
// 电流卡尔曼滤波器参数
constexpr float CURRENT_KALMAN_INITIAL_STATE = 0.0f;
constexpr float CURRENT_KALMAN_INITIAL_COVARIANCE = 100.0f; // 增大初始协方差
constexpr float CURRENT_KALMAN_PROCESS_NOISE_Q = 10.0f; // 降低过程噪声
constexpr float CURRENT_KALMAN_MEASUREMENT_NOISE_R = 5e-5f; // 增大测量噪声
// PID 配置
constexpr float DEFAULT_current_loop_p = 0.1f / 100 * 12; // 电流 P
constexpr float current_loop_Ki = 0.55f; // 电流 P
// 电流反馈噪声大(CurrentKalman 稳态增益≈1,几乎无滤波),D 项只放大噪声,置零
constexpr float current_loop_Kd = 0.0f;
constexpr float DEFAULT_vel_loop_p = 0.005f / 100 * 12; // 速度 P
constexpr float vel_loop_Ku = 3.600000e-03 * 0.7 * 100 / 12;
constexpr float vel_loop_Tu = 17.0f * 1e-3f;
constexpr float vel_loop_Ki = 1.2 * vel_loop_Ku / vel_loop_Tu;
// 速度 D 恢复(提供加速度阻尼);现走"测量微分+1ms低通"路径,无目标阶跃踢、
// 噪声被低通抑制,比旧的裸误差微分安全
constexpr float vel_loop_Kd = 0.075 * vel_loop_Ku * vel_loop_Tu * 2;
// PID 抗饱和与微分滤波(负载稳定性优化,2026-08)
// 抗饱和主体为条件积分(饱和方向冻结,见 pid.cpp);此增益仅用于
// bleed_integral() 外部饱和泄放(前馈叠加超限时),且带同号保护不过零
constexpr float ANTI_WINDUP_GAIN = 0.3f; // 外部饱和泄放比例
constexpr float DERIV_LPF_TAU = 0.001f; // 微分项一阶低通时间常数 (s)
constexpr float MIN_LIMIT_VOLTAGE = 0.1f; // P 增益换算分母下限,防 limit_voltage=0 除零
// mode2 角度同步 Uq 前馈补偿:固定 Uq 下的真实速度波动不应从反馈中滤掉,
// 而应按物理 360° 提前补偿负载/阻力差异,降低速度 PID 的周期性纠偏量。
constexpr bool MODE2_UQ_FF_ENABLE = true;
constexpr bool MODE2_UQ_FF_LEARN_ENABLE = true;
constexpr u16 MODE2_UQ_FF_TABLE_SIZE = 180;
constexpr float MODE2_UQ_FF_LEARN_ALPHA = 0.01f; // 越小越慢,避免误学瞬态
constexpr float MODE2_UQ_FF_MIN_TARGET = 1.5f; // 目标速度过低时不学习
constexpr float MODE2_UQ_FF_GAIN = 0.003f; // 速度误差(deg/s)到前馈电压(V)的学习比例
constexpr float MODE2_UQ_FF_MAX = 3.5f; // 前馈补偿电压限幅(V)
constexpr float MODE2_UQ_FF_STOP_SPEED = 2.0f; // 目标/实际速度低于该值时不输出前馈
constexpr float MODE2_UQ_FF_BRAKE_ERR = 3.0f; // 明显超速/刹车时不输出前馈,单位同 velocity_error
constexpr float MODE2_UQ_FF_LEARN_ERR_MAX = 375.0f; // 速度误差带内才学习(≈15°/s×25),防启停瞬态污染表
// mode3 角度同步 Uq 前馈补偿:单独建表,避免和 mode2 互相污染。
constexpr bool MODE3_UQ_FF_ENABLE = true;
constexpr bool MODE3_UQ_FF_LEARN_ENABLE = true;
constexpr u16 MODE3_UQ_FF_TABLE_SIZE = 180;
constexpr float MODE3_UQ_FF_LEARN_ALPHA = 0.01f;
constexpr float MODE3_UQ_FF_MIN_TARGET = 2.5f; // mode3 内部目标速度过低时不学习
constexpr float MODE3_UQ_FF_GAIN = 0.3f; // mode3 速度误差未乘减速比,故增益独立于 mode2
constexpr float MODE3_UQ_FF_MAX = 3.5f;
constexpr float MODE3_UQ_FF_STOP_SPEED = 2.0f; // mode3 内部目标/实际速度低于该值时不输出前馈
constexpr float MODE3_UQ_FF_BRAKE_ERR = 3.0f; // 明显超速/刹车时不输出前馈,单位同 velocity_error
constexpr float MODE3_UQ_FF_LEARN_ERR_MAX = 15.0f; // 速度误差带内(°/s)才学习,防启停瞬态污染表
constexpr float DEFAULT_vel_angloop_p = 0.4f / 100; // 速度 速度 - 角度 P
constexpr float vel_angloop_Ki = 5.0f; // 速度 速度 - 角度 I
constexpr float vel_angloop_Kd = 5e-05f; // 速度 速度 - 角度 D(恢复,走测量微分+低通)
// mode3 低速/静止附近速度内环增益调度(D 保持满值):
// P/I 各自独立地板——I 地板更低,停止瞬间积分充电速率仅为全速的 15%,
// 与静摩擦相互作用(到位 hunting:停下瞬间小幅来回抖动)的能量被大幅压缩;
// P 地板保持足够刚度,减小突破静摩擦所需的误差。
constexpr bool MODE3_LOW_SPEED_P_SCALE_ENABLE = true;
constexpr float MODE3_LOW_SPEED_P_MIN_SCALE = 0.5f;
constexpr float MODE3_LOW_SPEED_I_MIN_SCALE = 0.15f;
constexpr float MODE3_LOW_SPEED_P_LOW = 1.0f;
constexpr float MODE3_LOW_SPEED_P_FULL = 8.0f;
constexpr float DEFAULT_angle_angloop_p =
150.0f / 100 * 12; // 角度 速度 - 角度 P
constexpr float ang_angloop_Ki = 0.00f; // 角度 速度 - 角度 I
constexpr float ang_angloop_Kd = 8e-04f; // 角度 速度 - 角度 D
constexpr float DEFAULT_angle_loop_p = 60.0f / 100 * 12; // 角度 P
constexpr float DEFAULT_angle_MIT_p = 100.0f / 100 * 12; // 角度 MIT P
constexpr float angle_MIT_Ki = 0.0f;
constexpr float angle_MIT_Kd = 8e-04f;
constexpr float DEFAULT_vel_MIT_p = 5.0f / 100; // 速度 MIT P
constexpr float vel_MIT_Ki = 15.0f;
constexpr float vel_MIT_Kd = 1e-04f; // 速度 MIT D(恢复,走测量微分+低通)
constexpr float DEFAULT_current_MIT_p = 0.2f / 100 * 12; // 电流 MIT P
constexpr float current_MIT_Ki = 0.1f;
constexpr float current_MIT_Kd = 0.0f; // 电流 MIT D(置零,同 current_loop_Kd)
// 常量定义
constexpr u8 SIZE_FLOAT = sizeof(float);
constexpr u8 SIZE_U32 = sizeof(uint32_t);
constexpr float ADC_MAX = 65536.0f;
constexpr float PI = 3.14159265358979323846f;
constexpr float ANGLE_FACTOR = 25.0f / 180.0f * PI;
constexpr float DEG2RAD = 1.0f / 180.0f * PI;
constexpr float RAD2DEG = 180.0f / PI; // 修正RAD2DEG定义,应该是180/π
constexpr float _1_SQRT3 = 0.57735026919f; // 1/sqrt(3)
constexpr float sqrt3dv2 = 0.86602540378f; // sqrt(3)/2的预计算值
constexpr float SQRT3DV2 = 0.8660254037844386f; // sqrt(3)/2
constexpr float DEG2RAD = 1.0f / 180.0f * PI;
constexpr u8 DEFAULT_VALUE = 0;
constexpr u8 CONFIG_SAVED_NUM = 6;
// 三角函数快速计算LUT
constexpr u16 LUT_SIZE = 1024;
constexpr u16 LUT_MASK = (LUT_SIZE - 1);
constexpr u16 LUT_SCALE = (LUT_SIZE / (2.0f * PI));
// CAN 相关
// 注意:以下为 CAN 基础 ID(只保留低 16 位),实际发送/接收时会自动叠加节点 ID
// 到高 16 位 格式:[节点 ID(16 位)][基础 ID(16 位)] 例如:节点 ID=1,发送命令
// ID = (1 << 16) | CANFD_CMD_ID = 0x00010200
// 反馈基础 ID
constexpr u16 CANFD_STATUS_ID = 0x0100; // 状态反馈基础 ID
constexpr u16 CANFD_CONFIG_ID = 0x0101; // 配置信息基础 ID
constexpr u16 CANFD_VERSION_ID = 0x00FF; // 版本信息基础 ID
// 周期反馈帧刷新周期(单位 ms,基于 1ms CAN 任务节拍分频;0=停发),
// 可通过 SET_NODE_ID(0x0400) 扩展格式运行时修改并持久化,
// 未配置时使用以下默认值(CONFIG 帧内容为控制模式/目标设定回显,
// 不实时依赖,默认 250Hz 降低总线占用)
constexpr u16 CANFD_TX_PERIOD_STATUS_DEFAULT_MS = 1; // STATUS 帧默认 1kHz
constexpr u16 CANFD_TX_PERIOD_CONFIG_DEFAULT_MS = 4; // CONFIG 帧默认 250Hz
// 0x0400/0x0401 扩展格式(DLC>=2/4)的参数选择器:0x0000=节点 ID,
// 其余选择器直接使用目标反馈帧的基础 ID(如 0x0100/0x0101)
constexpr u16 CANFD_PARAM_NODE_ID = 0x0000;
// 命令基础 ID
constexpr u16 CANFD_CMD_ID = 0x0200; // 控制命令基础 ID
constexpr u16 CANFD_RESET_ID = 0x0301; // 重置命令基础 ID
// 节点 ID 管理基础 ID
constexpr u16 CANFD_SET_NODE_ID = 0x0400; // 设置节点 ID 基础 ID
constexpr u16 CANFD_GET_NODE_ID = 0x0401; // 查询节点 ID 基础 ID
// CAN相关
constexpr u16 DEV_CAN0_ID = 0x0000;
constexpr u16 DEV_CAN0_MASK = 0x0000;
constexpr u16 DEV_CAN_LOOPBACK_ID = 0x0000;
// 反馈ID
constexpr u32 CANFD_STATUS_ID = 0x00010100;
constexpr u32 CANFD_CONFIG_ID = 0x00010101;
constexpr u32 CANFD_VERSION_ID = 0x00010000;
// 命令ID
constexpr u32 CANFD_CMD_ID = 0x00010200;
constexpr u32 CANFD_RESET_ID = 0x00010301;
// OTA相关ID
constexpr u32 CANFD_BOOT_ID = 0x00000000;
constexpr u32 CANFD_OTA_REQ_ID = 0x00000100;
constexpr u32 CANFD_OTA_DATA_ID = 0x00000101;
constexpr u32 CANFD_OTA_ACK_ID = 0x00000102;
constexpr u32 CANFD_OTA_WAIT_ID = 0x00000103;
// OTA 相关基础 ID 不受到node ID影响
constexpr u16 CANFD_BOOT_ID = 0x0000; // BOOT 模式基础 ID (进入 BOOT 使用此 ID)
constexpr u16 CANFD_OTA_ACK_ID = 0x0102; // OTA 确认基础 ID
// ADC
constexpr u8 ADC_FILTER_SIZE = 6;
constexpr u8 BLOCK_SIZE = ADC_FILTER_SIZE * SIZE_FLOAT;
constexpr float ERROR_ADC_VAL = 255.0f;
constexpr float MAX_TEMP = 150.0f;
constexpr float MIN_TEMP = -40.0f;
@@ -112,24 +365,63 @@ constexpr float MAX_VCC = 65.0f;
constexpr float MIN_VCC = -0.1f;
// ang_sensor
constexpr u16 VEL_FILTER_SIZE = 4;
constexpr u16 DEFAULT_COMMAND = 0x0000;
constexpr u16 FAULT_RECEIVE = 0xFFFF;
constexpr float ANGLE_WRAP_THRESHOLD = 300.0f;
constexpr float ANGLE_WRAP_VALUE = 360.0f;
constexpr float RAD_FACTOR = 1.0f / ADC_MAX * 2.0f * PI;
constexpr float ANGLESENS_FACTOR = 1.0f / ADC_MAX * 360.0f;
// TIMER2-任务调度
constexpr int TIMER2_PRESCALER = 119; // 120MHz / 120 = 1MHz
constexpr int TIMER2_PERIOD = 49; // 1MHz / 50 = 50us
constexpr float TIMER2_FREQUENCY = 1 / 5e-5;
constexpr int TIMER2_PERIOD = 49; // 1 / 1MHz * 50 = 50us
constexpr int MOTOR_PERIOD = 1; // 控制每50us执行一次,无分频
constexpr float TIME_SLICE =
(TIMER2_PERIOD + 1) * (TIMER2_PRESCALER + 1) / 120000000.0f; // 周期
constexpr float MOTOR_FREQUENCY = 1 / TIME_SLICE / MOTOR_PERIOD; // 频率
// TIMER1-PWM
constexpr int TIMER1_PRESCALER = 0; // 不分频,计数器时钟为120MHz
constexpr int TIMER1_PERIOD = 5999; // 120MHz / (0+1) / (5999+1) = 20kHz
constexpr int TIMER1_PRESCALER = 0; // 不分频,计数器时钟为 120MHz
constexpr int TIMER1_PERIOD = 2999; // 120MHz / (0+1) / (2999+1) = 40kHz计数, 中心对齐实际PWM 20kHz
constexpr int TIMER1_DEFAULT_PULSE = 0;
constexpr int TIMER1_CH3_COMPARE_VALUE =
(TIMER1_PERIOD + 1) * 0.5 - 1; // PWM周期50%位置触发中断
// CH3 为固定 ADC 采样触发点(TRGO=O3CPRE)。低边采样要求触发落在"三相下管
// 同时导通"的窗口内:中心对齐 PWM0 下 INx 高(上管导通)区间以计数谷底
// CNT=0 为中心,INx 低(下管导通)区间以计数峰顶 CNT=PERIOD 为中心,故采样
// 点必须贴近峰顶(在线采样时代取 41% 是任意有效点,直接沿用会采到上管
// 导通期、三相电流恒读零)。
// 取 PERIOD-10:紧贴峰顶。中心对齐下每周期匹配两次(上升沿 2989 / 下降沿
// 3011,仅差 22 计数),无论第二次触发被 ADC 忽略(注入转换忙)还是重启
// 序列,帧的等效采样位置都在峰顶附近——行为确定,不依赖芯片的触发丢弃
// 策略;配合 ADC.cpp 电流通道 13.5 周期短采样,最大占空比 ~90% 时三相样本
// 仍全部落在下管导通窗内。占空比更高时序列末位的 W 相先落出窗口(读数
// 趋零、U+V+W≠0),软件侧需对占空比限幅。
constexpr int TIMER1_CH3_COMPARE_VALUE = TIMER1_PERIOD - 10;
// 电压检测参数(基于跌幅检测,不依赖固定额定电压)
// 快速检测层(fast_voltage_check,100us周期):
// - 使用峰值检测算法跟踪当前额定电压(vbus_reference)
// - 当电压相对于基准电压下跌超过 VBUS_DROP_THRESHOLD → 关机
// - 当电压低于绝对下限 VBUS_ABSOLUTE_MIN → 关机
// 慢速管理层(power_manage,~10ms周期):
// - 执行Flash保存
// - 状态恢复判断(电压恢复到基准电压的90%以上)
constexpr float VBUS_DROP_THRESHOLD = 5.0f; // 电压跌幅阈值 (V)
constexpr float VBUS_ABSOLUTE_MIN = 12.0f; // 绝对最低电压 (V)
constexpr u32 VBUS_DEBOUNCE_COUNT = 5; // 快速检测:防抖计数 (5次×100us = 500us)
constexpr float VBUS_RECOVERY_RATIO = 0.9f; // 恢复比例(基准电压的90%)
// ===================== 门驱动 nFAULT(STATUS 帧 motStatus / MotorStatus)=====================
// PB2 读门驱动 nFAULT(开漏低有效,固件已配内部上拉)。DRV8313/DRV8323 的
// UVLO、热关断、过流都会把它拉低,因此它就是"电机侧出错"的硬件凭据。
// 轮询点在 thermalController.update()(TASK_THERMAL_CONTROL,1ms)。
constexpr u8 DRV_FAULT_DEBOUNCE_MS = 3u; // 连续 N 次 1ms 采样为低才判故障
// 解除禁止后驱动需要重新建立:DRV8313 手册 7.4.1 给 ≈1ms,按 50us 控制节拍取 20 拍。
// 建立期内输出保持 0、不派发控制函数(见 sys/run_inhibit.cpp)。
constexpr u8 DRV_WAKE_SETTLE_50US = 20u;
#endif
+523 -147
View File
File diff suppressed because it is too large Load Diff
+122 -3
View File
@@ -1,13 +1,25 @@
/**
* @file sys/flash.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef __FLASH_H
#define __FLASH_H
#include "config.h"
#include "gd32c10x.h" // 添加GD32主头文件以确保外设函数声明可用
#include <cstdint>
// 修改后 - 强制4字节对齐
/** @brief ALIGN_4BYTE 宏定义 */
#define ALIGN_4BYTE(addr) ((addr) & 0xFFFFFFFC) // 4字节对齐
// EEPROM操作状态
/**
* @brief 匿名枚举 枚举定义
*/
typedef enum {
FLASH_EEPROM_OK = 0, // 操作成功
FLASH_EEPROM_WRITE_ERROR, // 写入错误
@@ -20,11 +32,14 @@ typedef enum {
} FlashEepromStatus;
// Flash EEPROM类
/**
* @brief FlashEEPROM 类或结构体
*/
class FlashEEPROM {
public:
// EEPROM emulation constants
static constexpr u32 EEPROM_START_ADDRESS = 0x0801F800; // 第一页(1KB)
static constexpr u32 EEPROM_DATA_PAGE2_ADDRESS = 0x801FC00; // 第二页(1KB)
static constexpr u32 EEPROM_DATA_PAGE2_ADDRESS = 0x0801FC00; // 第二页(1KB)
static constexpr u32 EEPROM_END_ADDRESS = 0x08020000; // Flash结束地址
static constexpr u32 EEPROM_MAX_SIZE = 0x800; // EEPROM最大容量
static constexpr u32 EEPROM_DATA_START_ADDRESS =
@@ -40,39 +55,143 @@ class FlashEEPROM {
static constexpr u8 ADDR_TARGET_VEL = 0x03; // 速度命令数据存储地址
static constexpr u8 ADDR_VOLTAGE_LIMIT = 0x4; // 电压限制
static constexpr u8 ADDR_VEL_LIMIT = 0x05; // 角度数据存储地址
static constexpr u8 ADDR_NODE_ID = 0x06; // 节点 ID 存储地址
static constexpr u8 ADDR_CALIBRATION_ANGLE = 0x07; // 电角度零位校准角存储地址
static constexpr u8 ADDR_TX_PERIOD_STATUS = 0x08; // STATUS 帧刷新周期(ms)存储地址
static constexpr u8 ADDR_TX_PERIOD_CONFIG = 0x09; // CONFIG 帧刷新周期(ms)存储地址
bool angle_saved = false;
bool system_config_saved = false;
u8 EEPROMStatus;
// 周期反馈帧刷新周期 RAM 副本(ms,0=停发)。init() 从 Flash 加载,
// 无记录时回退 config.h 默认值;CAN_transmiter::init() 读取应用,
// SET 命令修改后经 write_tx_periods() 持久化
u16 tx_period_status_ms = CANFD_TX_PERIOD_STATUS_DEFAULT_MS;
u16 tx_period_config_ms = CANFD_TX_PERIOD_CONFIG_DEFAULT_MS;
// 构造函数
FlashEEPROM();
// 初始化Flash EEPROM模拟
/**
* @brief 初始化对象或模块
* @return u8 返回值
*/
u8 init();
// 从Flash EEPROM中读取数据
/**
* @brief 读取数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus read(u8 virtualAddr, u16 *data) const;
// 向Flash EEPROM写入数据
/**
* @brief 写入数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus write(u8 virtualAddr, u16 data);
// Float数据读相关函数
/**
* @brief 从 Flash EEPROM 读取浮点数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus readFloat(u8 virtualAddr, float *data) const;
// Float数据存储相关函数
/**
* @brief 向 Flash EEPROM 写入浮点数据
* @param[in] virtualAddr 参数
* @param[in] data 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus writeFloat(u8 virtualAddr, float data);
// 格式化Flash EEPROM页面
/**
* @brief 格式化存储区域
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus format();
/**
* @brief 保存系统配置参数
* @param[in] saved 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus save_system_config(bool saved);
/**
* @brief 读取已保存的系统配置参数
* @return u8 返回值
*/
u8 read_saved_system_config(void);
float read_angle();
/**
* @brief 保存电角度零位校准角到 Flash
* @param[in] angle 校准角(度)
* @return FlashEepromStatus 操作状态
*/
FlashEepromStatus save_calibration_angle(float angle);
// 周期反馈帧刷新率持久化(独立 u16 记录,不属于 CRC 参数组,
// 读取失败/无记录由调用方回退默认值,避免老设备升级后 CRC 全组失效)
/**
* @brief 将 RAM 副本中的两帧刷新周期写入 Flash(值不变自动跳过)
* @return FlashEepromStatus 操作状态
*/
FlashEepromStatus write_tx_periods();
// 节点 ID 独立读写函数(主/备双备份分属独立 Flash 页 + 魔数 + 计数器 + CRC)
// 从 Flash 读取节点 ID(双备份均有效取计数器最新,单份有效则回退)
/**
* @brief 读取节点 ID 配置
* @param[in] node_id 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus readNodeId(u16 *node_id) const;
// 保存节点 ID 到 Flash(乒乓写入非最新页,另一页保有旧值,掉电安全)
/**
* @brief 写入节点 ID 配置
* @param[in] node_id 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus writeNodeId(u16 node_id);
// 擦除节点 ID 双备份存储页(恢复默认值)
/**
* @brief 擦除节点 ID 配置
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus eraseNodeId();
private:
/**
* @brief 写入 writeCRC 相关数据
* @param[in] CRC_NUM 参数
* @param[in] saved 参数
* @return FlashEepromStatus 返回值
*/
FlashEepromStatus writeCRC(u32 CRC_NUM, bool saved);
};
/**
* @brief flash_eeprom_example_main 函数
* @return void
*/
void flash_eeprom_example_main(void);
u8 OTA_SET(u32 addr, u32 data);
/**
* @brief move_app_data 函数
* @return fmc_state_enum 返回值
*/
fmc_state_enum move_app_data(void);
/**
* @brief 写入 write_ADDRESS 相关数据
* @param[in] address 参数
* @param[in] data 参数
* @return fmc_state_enum 返回值
*/
fmc_state_enum write_ADDRESS(u32 address, u32 data);
#endif /* __FLASH_H */
+147 -30
View File
@@ -1,26 +1,67 @@
/**
* @file sys/health.cpp
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "health.h"
#include "config.h"
#include "Can.h"
#include "FOC.h"
#include "adc.h"
#include "flash.h"
#include "motor.h"
#include "run_inhibit.h"
#include <cmath>
extern sampler sampler;
extern CAN_transmiter CAN0_transmiter;
extern FOC_Controlroller FOC;
extern FOC_Controller FOC;
extern FlashEEPROM flashEEP;
// 全局热管理控制器单例:由 main 任务周期调用 update(),用于维护过热保护状态。
ThermalController thermalController; // 热量控制器初始化
#define max(a, b, c) \
((a) > (b) ? ((a) > (c) ? (a) : (c)) : ((b) > (c) ? (b) : (c)))
/** @brief PROTECT_TIME 常量定义 */
constexpr u16 PROTECT_TIME = 6000; // 过热保护时间,单位*10ms
// 三相电流连续全部失效帧数(×10ms)阈值,达到后按过热关机处理
constexpr u16 CURRENT_ERROR_SHUTDOWN_COUNT = 100;
// 电压管理状态机:仅 power_manage() 慢速任务访问,文件内封装。
static VoltageState voltage_state = VoltageState::NORMAL;
static float last_vbus_slow = 0.0f; // 上次电压值(慢速任务用)
/**
** @brief 热能计算
** @param None
** @retval 热量值
*/
u32 ThermalController::update() {
// nFAULT 检测(STATUS 帧 motStatus / MotorStatus):本函数 1ms 执行一次。
// 旧实现整段被 #if GATE_DRIVER_DRV8323 圈住,而当前固件按 DRV8313 编译
// (config.h 该宏 = 0)→ 驱动报故障(UVLO / 热关断 / OCP 锁存,三者都会
// 拉低 nFAULT)只上报不动作,控制环照常输出。现在两板都判:
// 连续 DRV_FAULT_DEBOUNCE_MS 次采到低电平 → 申请禁止位,
// 停机由 run_inhibit 在 50us 控制任务里执行;解除只认上位机 motReset。
// DRV8323 板的 VDS 过流配置为"仅上报不动作",不会拉低 nFAULT,不在此触发。
static u8 nfault_busy_ms = 0;
const bool nfault = (drv_nfault_active() != 0U);
if (nfault) {
if (++nfault_busy_ms >= DRV_FAULT_DEBOUNCE_MS) {
nfault_busy_ms = 0;
run_inhibit.request(INHIBIT_DRV_NFAULT);
}
} else {
nfault_busy_ms = 0;
}
// MotorStatus 唯一写手:闸门里还有没有 nFAULT 这一位,或此刻引脚确实在低
CAN0_transmiter.MotorStatus =
(run_inhibit.sources() & INHIBIT_DRV_NFAULT) != 0U || nfault;
// 上报位与闸门同源:每拍从禁止位图派生,不要再在各处置 0/1
// (历史上"停机了但状态位说正常"就是多处写造成的)
CAN0_transmiter.ThermalStatus =
(run_inhibit.sources() & INHIBIT_THERMAL) != 0U;
thermal_value = Q16_16_MUL(thermal_value, thermalRate);
// 添加温度积累
int drv_temp = (int)sampler.get_drv_temp();
@@ -31,6 +72,22 @@ u32 ThermalController::update() {
mcu_temp = 0;
thermal_value += drv_temp * drv_temp + mcu_temp * mcu_temp;
// 三相电流采样有效性先算好:热积累与"冷却到点该不该撤销采样失效禁止位"都要用
const float raw_current[3] = {sampler.get_current('U'),
sampler.get_current('V'),
sampler.get_current('W')};
u32 currents[3] = {0, 0, 0};
bool valid[3] = {false, false, false};
for (u8 i = 0; i < 3; i++) {
if (raw_current[i] == ERROR_ADC_VAL) {
continue;
}
// get_current 已保证有效值 |I| ≤ MAX_CURRENT(40A),转 mA ≤ 40000
currents[i] = (u32)(fabsf(raw_current[i]) * 1000.0f);
valid[i] = true;
}
const bool any_valid = valid[0] || valid[1] || valid[2];
if (remainingTime10Ms) {
// 过热停机状态
// 检查是否可以恢复
@@ -39,43 +96,70 @@ u32 ThermalController::update() {
if (thermal_value < recovery_threshold) {
remainingTime10Ms = 1; // 可以恢复工作
}
if (remainingTime10Ms == 1) { // 过热恢复
FOC.resume();
CAN0_transmiter.ThermalStatus = 0;
if (remainingTime10Ms == 1) { // 冷却到点:各自撤销自己申请的禁止位
run_inhibit.release(INHIBIT_THERMAL);
if (any_valid) {
run_inhibit.release(INHIBIT_ISENSE_FAIL);
}
return --remainingTime10Ms;
}
return --remainingTime10Ms;
} else {
// 当前没有过热停机
// 添加电流热积累
u32 currents[3] = {(u32)(sampler.get_current('U') * 1000),
(u32)(sampler.get_current('V') * 1000),
(u32)(sampler.get_current('W') * 1000)};
// 添加电流热积累:取三相有效相的最大绝对值(热量与电流方向无关,
// 负半周同样发热)。采样失效帧(ERROR_ADC_VAL)不参与积累:
// 失效值 255 按 mA 参与平方运算会超出 int/u32 范围,热积累不可控;
// 三相持续全部失效说明采样链异常,按独立原因 fail-safe 停机
// (不再并入"过热"上报,上位机看到的是 INHIBIT_ISENSE_FAIL)。
static u16 current_error_frames = 0;
// 找最大电流(原始ADC值)
current_max = max(currents[0], currents[1], currents[2]);
if (!any_valid) {
if (++current_error_frames >= CURRENT_ERROR_SHUTDOWN_COUNT) {
current_error_frames = 0;
run_inhibit.request(INHIBIT_ISENSE_FAIL);
remainingTime10Ms = PROTECT_TIME;
return thermal_value;
}
return thermal_value;
}
current_error_frames = 0;
// 找最大电流(mA,绝对值);个别相失效时用剩余有效相
u32 max_current = 0;
for (u8 i = 0; i < 3; i++) {
if (valid[i] && currents[i] > max_current) {
max_current = currents[i];
}
}
current_max = (u16)max_current;
// 平方运算先转 float:40A 档 40000²×10 超 int 范围,整数运算会溢出
const float imax = (float)current_max;
// 电流系数按"绝对值全周期"口径标定:取 fabs 后正负半周都积累,
// 交流负载下积累速度约为旧版(仅正半周)的 2 倍,故系数统一取旧值一半,
// 保持原实测触发时机不变;温度积累与恢复阈值不受此补偿影响。
if (current_max < 2000) {
thermal_value += current_max * current_max * gapTimeMs *
0.00007f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.000035f); // 计算当前电流热积累值
} else if (current_max > 3800) {
thermal_value += current_max * current_max * gapTimeMs *
0.01f; // 添加当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.005f); // 添加当前电流热积累值
} else if (current_max > 3400) {
thermal_value += current_max * current_max * gapTimeMs *
0.005f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.0025f); // 计算当前电流热积累值
} else if (current_max > 3000) {
thermal_value += current_max * current_max * gapTimeMs *
0.0002f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.0001f); // 计算当前电流热积累值
} else if (current_max > 2000) {
thermal_value += current_max * current_max * gapTimeMs *
0.0001f; // 计算当前电流热积累值
thermal_value += (u32)(imax * imax * gapTimeMs *
0.00005f); // 计算当前电流热积累值
}
}
if (thermal_value > thermal_capacity) {
FOC.shut_down();
// 过热:只申请本模块的禁止位,停机由 run_inhibit 执行;
// ThermalStatus 由位图派生(见本函数开头),此处不再单独写
run_inhibit.request(INHIBIT_THERMAL);
remainingTime10Ms = PROTECT_TIME; // 过热停机
CAN0_transmiter.ThermalStatus = 1;
return remainingTime10Ms;
}
return thermal_value;
@@ -90,15 +174,48 @@ void angsens_MG_check(void) {
CAN0_transmiter.MagStatus = MGH or MGL;
}
/**
* @brief 电压管理(低频任务,约10ms调用一次)
*
* 负责:
* 1. 执行Flash保存(阻塞操作,不能在高频循环中执行)
* 2. 状态恢复判断(基于基准电压的比例恢复)
* 3. 更新FOC的电源电压参数
*/
void power_manage(void) {
float last_vbus = sampler.get_vbus_voltage();
sampler.update_vbus_voltage();
FOC.update_power();
float vbus = sampler.get_vbus_voltage();
if ((vbus < 22.0f) and (last_vbus > vbus) and vbus > 8.0f) {
FOC.shut_down();
// 更新FOC的电源电压参数:使用本周期刚采到的 vbus,避免上一拍电压滞后放大速度/电流耦合。
FOC.update_power(vbus);
last_vbus_slow = vbus;
// 如果快速检测已触发紧急关机,执行保存
if (FOC.emergency_shutdown) {
flashEEP.save_system_config(true);
} else {
FOC.resume();
FOC.emergency_shutdown = false; // 清除紧急关机标志
voltage_state = VoltageState::SHUTDOWN_SAVE;
return;
}
// 状态机处理
switch (voltage_state) {
case VoltageState::NORMAL:
// 正常状态:无需操作
break;
case VoltageState::SHUTDOWN_SAVE:
// 关机保存状态:检查是否可以恢复
// 恢复条件:电压恢复到基准电压的90%以上。
// 只撤销欠压这一位;是否真的能运行由闸门看位图是否清空决定,
// 这里不查其它故障源(过热/nFAULT 各自有各自的解除方)。
if (FOC.vbus_reference > 0 && vbus > FOC.vbus_reference * VBUS_RECOVERY_RATIO) {
run_inhibit.release(INHIBIT_UNDERVOLTAGE);
voltage_state = VoltageState::NORMAL;
}
break;
default:
break;
}
}
+42 -1
View File
@@ -1,18 +1,43 @@
/**
* @file sys/health.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _HEALTH_H
#define _HEALTH_H
#include "config.h"
#include <cstdint>
typedef int32_t q16_16_t; // Q16.16格式
// 定点数运算宏
/** @brief Q16_16_ONE 宏定义 */
#define Q16_16_ONE 65536 // 1.0 in Q16.16
/** @brief FLOAT_TO_Q16_16 宏定义 */
#define FLOAT_TO_Q16_16(x) ((q16_16_t)((x) * Q16_16_ONE))
/** @brief Q16_16_TO_FLOAT 宏定义 */
#define Q16_16_TO_FLOAT(x) ((float)(x) / Q16_16_ONE)
/** @brief Q16_16_MUL 宏定义 */
#define Q16_16_MUL(a, b) ((q16_16_t)(((int64_t)(a) * (b)) >> 16))
/** @brief Q16_16_SQUARE 宏定义 */
#define Q16_16_SQUARE(x) Q16_16_MUL((x), (x))
// 电压监测状态机状态
/**
* @brief VoltageState 枚举定义
*/
enum class VoltageState : uint8_t {
NORMAL, // 正常运行
VOLTAGE_DROP, // 电压跌落检测中(防抖等待)
SHUTDOWN_SAVE // 已关机,等待保存
};
/**
* @brief ThermalController 类或结构体
*/
class ThermalController {
private:
u8 temperature;
u16 current_max;
q16_16_t thermalRate; // 热耗散系数(每单位时间散热比例)
@@ -27,14 +52,30 @@ class ThermalController {
ThermalController()
: thermalRate(FLOAT_TO_Q16_16(0.995f)),
recoveryRate(FLOAT_TO_Q16_16(0.33f)) {}
/**
* @brief 更新对象内部状态或采样数据
* @return u32 返回值
*/
u32 update();
/**
* @brief 复位对象内部状态
* @return void
*/
void reset() {
thermal_value = 0;
remainingTime10Ms = 0;
}
};
/**
* @brief angsens_MG_check 函数
* @return void
*/
void angsens_MG_check(void);
/**
* @brief power_manage 函数
* @return void
*/
void power_manage(void);
#endif
+148 -75
View File
@@ -1,3 +1,9 @@
/**
* @file sys/initial.cpp
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#include "initial.h"
#include "Can.h"
#include "config.h"
@@ -11,7 +17,26 @@
#include <cstdio>
#include <cstring>
extern CAN_transmiter CAN0_transmiter; // CAN初始化
// OTA 调试宏定义
#if OTA_DEBUG
/** @brief OTA_DEBUG_PRINT 宏定义 */
#define OTA_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief OTA_DEBUG_PRINT 宏定义 */
#define OTA_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// EEP 调试宏定义
#if EEP_DEBUG
/** @brief EEP_DEBUG_PRINT 宏定义 */
#define EEP_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief EEP_DEBUG_PRINT 宏定义 */
#define EEP_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
extern CAN_transmiter CAN0_transmiter; // CAN 初始化
// 全局版本管理器单例:构造函数只复制 constexpr 版本号常量,不依赖外设初始化。
VersionManager Version;
VersionManager::VersionManager() {
@@ -24,18 +49,38 @@ VersionManager::VersionManager() {
BOOT_VERSION_BUILD);
}
/**
* @brief 获取 getSoftwareVersion 对应的数据或状态
* @return VersionManager::VersionInfo 返回值
*/
VersionManager::VersionInfo VersionManager::getSoftwareVersion() const {
return softwareVersion;
}
/**
* @brief 获取 getHardwareVersion 对应的数据或状态
* @return VersionManager::VersionInfo 返回值
*/
VersionManager::VersionInfo VersionManager::getHardwareVersion() const {
return hardwareVersion;
}
/**
* @brief 获取 getBootVersion 对应的数据或状态
* @return VersionManager::VersionInfo 返回值
*/
VersionManager::VersionInfo VersionManager::getBootVersion() const {
return bootVersion;
}
/**
* @brief 设置 setSoftwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void VersionManager::setSoftwareVersion(u16 major, u16 minor, u16 patch,
u16 build) {
softwareVersion.major = major;
@@ -44,6 +89,14 @@ void VersionManager::setSoftwareVersion(u16 major, u16 minor, u16 patch,
softwareVersion.build = build;
}
/**
* @brief 设置 setHardwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void VersionManager::setHardwareVersion(u16 major, u16 minor, u16 patch,
u16 build) {
hardwareVersion.major = major;
@@ -52,6 +105,14 @@ void VersionManager::setHardwareVersion(u16 major, u16 minor, u16 patch,
hardwareVersion.build = build;
}
/**
* @brief 设置 setBootVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void VersionManager::setBootVersion(u16 major, u16 minor, u16 patch,
u16 build) {
bootVersion.major = major;
@@ -60,25 +121,63 @@ void VersionManager::setBootVersion(u16 major, u16 minor, u16 patch,
bootVersion.build = build;
}
static void appendVersionChar(char *&out, size_t &remaining, char ch) {
if (remaining > 1U) {
*out++ = ch;
--remaining;
*out = '\0';
}
}
static void appendVersionU16(char *&out, size_t &remaining, u16 value) {
char digits[5]; // u16 最大值 65535,最多 5 位十进制数字
u8 count = 0;
do {
digits[count++] = static_cast<char>('0' + (value % 10U));
value = static_cast<u16>(value / 10U);
} while (value > 0U && count < sizeof(digits));
while (count > 0U) {
appendVersionChar(out, remaining, digits[--count]);
}
}
char *VersionManager::versionToString(const VersionInfo &version, char *buffer,
size_t bufSize) const {
if (buffer == nullptr) {
return nullptr;
}
if (bufSize == 0U) {
return buffer;
}
snprintf(buffer, bufSize, "%d.%d.%d.%d", version.major, version.minor,
version.patch, version.build);
char *out = buffer;
size_t remaining = bufSize;
*out = '\0';
appendVersionU16(out, remaining, version.major);
appendVersionChar(out, remaining, '.');
appendVersionU16(out, remaining, version.minor);
appendVersionChar(out, remaining, '.');
appendVersionU16(out, remaining, version.patch);
appendVersionChar(out, remaining, '.');
appendVersionU16(out, remaining, version.build);
return buffer;
}
/**
* @brief sendVersionInfoViaCan 函数
* @return void
*/
void VersionManager::sendVersionInfoViaCan() const {
// 准备发送版本信息 (ID: CANFD_VERSION_ID)
// 只使用必要的字节数(24字节)来传输三个版本的信息(每个版本6字节)
// 只使用必要的字节数(24 字节)来传输三个版本的信息(每个版本 6 字节)
u8 versionData[24] = {0};
// 将版本信息打包到24字节数组中
// 前8字节:软件版本信息 (4个uint16_t,每个占2字节)
// 将版本信息打包到 24 字节数组中
// 前 8 字节:软件版本信息 (4 个 uint16_t,每个占 2 字节)
versionData[0] = softwareVersion.major & 0xFF; // 低字节
versionData[1] = (softwareVersion.major >> 8) & 0xFF; // 高字节
versionData[2] = softwareVersion.minor & 0xFF; // 低字节
@@ -88,7 +187,7 @@ void VersionManager::sendVersionInfoViaCan() const {
versionData[6] = softwareVersion.build & 0xFF; // 低字节
versionData[7] = (softwareVersion.build >> 8) & 0xFF; // 高字节
// 8-15字节:硬件版本信息 (4个uint16_t,每个占2字节)
// 8-15 字节:硬件版本信息 (4 个 uint16_t,每个占 2 字节)
versionData[8] = hardwareVersion.major & 0xFF; // 低字节
versionData[9] = (hardwareVersion.major >> 8) & 0xFF; // 高字节
versionData[10] = hardwareVersion.minor & 0xFF; // 低字节
@@ -98,7 +197,7 @@ void VersionManager::sendVersionInfoViaCan() const {
versionData[14] = hardwareVersion.build & 0xFF; // 低字节
versionData[15] = (hardwareVersion.build >> 8) & 0xFF; // 高字节
// 16-23字节:BOOT版本信息 (4个uint16_t,每个占2字节)
// 16-23 字节:BOOT 版本信息 (4 个 uint16_t,每个占 2 字节)
versionData[16] = bootVersion.major & 0xFF; // 低字节
versionData[17] = (bootVersion.major >> 8) & 0xFF; // 高字节
versionData[18] = bootVersion.minor & 0xFF; // 低字节
@@ -108,24 +207,36 @@ void VersionManager::sendVersionInfoViaCan() const {
versionData[22] = bootVersion.build & 0xFF; // 低字节
versionData[23] = (bootVersion.build >> 8) & 0xFF; // 高字节
// 使用CANFD发送函数发送版本信息
CAN0_transmiter.CANFD_AsyncSend(CANFD_VERSION_ID, 24, versionData);
// 使用 CANFD 发送函数发送版本信息,使用 build_can_id 构建带 node_id 的 CAN
// ID
CAN0_transmiter.CANFD_AsyncSend(
CAN0_transmiter.build_can_id(CANFD_VERSION_ID), 24, versionData);
}
/**
* @brief printVersions 函数
* @return void
*/
void VersionManager::printVersions() const {
char versionStr[32];
printf(
OTA_DEBUG_PRINT(
"Software Version: %s\r\n",
versionToString(getSoftwareVersion(), versionStr, sizeof(versionStr)));
printf(
OTA_DEBUG_PRINT(
"Hardware Version: %s\r\n",
versionToString(getHardwareVersion(), versionStr, sizeof(versionStr)));
printf("BOOT Version: %s\r\n",
OTA_DEBUG_PRINT(
"BOOT Version: %s\r\n",
versionToString(getBootVersion(), versionStr, sizeof(versionStr)));
}
/* retarget the C library printf function to the USART */
/**
* @brief 初始化 USART0 调试串口
* @return u8 返回值
*/
u8 USART0_init() {
gd_eval_com_init(EVAL_COM0);
return 1;
@@ -153,10 +264,14 @@ int fputc(int ch, FILE *f) throw() {
return ch;
}
/**
* @brief 初始化未使用引脚状态
* @return void
*/
void unused_pin_init() {
rcu_periph_clock_enable(RCU_GPIOA); // 使能GPIOA时钟
rcu_periph_clock_enable(RCU_GPIOB); // 使能GPIOB时钟
rcu_periph_clock_enable(RCU_GPIOC); // 使能GPIOC时钟
rcu_periph_clock_enable(RCU_GPIOA); // 使能 GPIOA 时钟
rcu_periph_clock_enable(RCU_GPIOB); // 使能 GPIOB 时钟
rcu_periph_clock_enable(RCU_GPIOC); // 使能 GPIOC 时钟
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_2MHZ,
GPIO_PIN_8 | GPIO_PIN_11 | GPIO_PIN_12);
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_2MHZ,
@@ -168,52 +283,15 @@ void unused_pin_init() {
gpio_bit_reset(GPIOC, GPIO_PIN_14 | GPIO_PIN_15);
}
/*!
\brief 检测并打印复位源信息
\param[in] none
\param[out] none
\retval none
*/
u16 get_reset_cause(void) {
u16 cause = 0;
if (RESET != rcu_flag_get(RCU_FLAG_FWDGTRST)) {
// printf("Free Watchdog Timer Reset\r\n");
cause = RCU_FLAG_FWDGTRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_EPRST)) {
// printf("External Pin Reset\r\n");
cause = RCU_FLAG_EPRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_PORRST)) {
// printf("Power Reset\r\n");
cause = RCU_FLAG_PORRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_WWDGTRST)) {
// printf("Window Watchdog Timer Reset\r\n");
cause = RCU_FLAG_WWDGTRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_LPRST)) {
// printf("Low Power Reset\r\n");
cause = RCU_FLAG_LPRST;
}
if (RESET != rcu_flag_get(RCU_FLAG_SWRST)) {
// printf("Software Reset\r\n");
cause = RCU_FLAG_SWRST;
}
// 清除所有复位标志
rcu_all_reset_flag_clear();
// printf("cause:%d\r\n", cause);
return cause;
}
/**
* @brief 初始化 OTA 与 A/B 分区状态
* @return void
*/
void OTA_initial(void) {
#if OTA_DEBUG
u32 temp_val[6];
// 3. 对于需要将0改回1的操作,必须先擦除整个Flash页
// 3. 对于需要将 0 改回 1 的操作,必须先擦除整个 Flash 页
volatile u32 *flash_ptr1 = (volatile u32 *)n_APP_LATEST_AVAL_ADDR;
temp_val[0] = *flash_ptr1;
flash_ptr1 = (volatile u32 *)n_APP_BACKUP_AVAL_ADDR;
@@ -226,20 +304,19 @@ void OTA_initial(void) {
temp_val[4] = *flash_ptr1;
flash_ptr1 = (volatile u32 *)n_APP_BACKUP_CHECK_ADDR;
temp_val[5] = *flash_ptr1;
// 修复:完整输出所有6个配置项的值,便于调试
printf("APP:CHECK LatestAval:0x%x, BackupAval:0x%x, LatestEn:0x%x, "
// 修复:完整输出所有 6 个配置项的值,便于调试
OTA_DEBUG_PRINT(
"APP:CHECK LatestAval:0x%x, BackupAval:0x%x, LatestEn:0x%x, "
"BackupEn:0x%x, LatestCheck:0x%x, BackupCheck:0x%x\r\n",
temp_val[0], temp_val[1], temp_val[2], temp_val[3], temp_val[4],
temp_val[5]);
#endif
// 确认当前APP可用
// 确认当前 APP 可用
volatile u32 *flash_ptr = (volatile u32 *)n_APP_LATEST_CHECK_ADDR;
u32 app_check = *flash_ptr;
if (app_check == OTA_NOT_AVAL) { // 新的APP刷入后第一次启动
#if OTA_DEBUG
std::printf("APP enabled\r\n");
#endif
if (app_check == OTA_NOT_AVAL) { // 新的 APP 刷入后第一次启动
OTA_DEBUG_PRINT("APP enabled\r\n");
move_app_data();
}
@@ -248,7 +325,7 @@ void OTA_initial(void) {
fmc_lock();
u8 transmit_data[2] = {0x0A, 0xAC};
// 确认APP可用
// 确认 APP 可用
CAN0_transmiter.CANFD_AsyncSend(CANFD_OTA_ACK_ID, 2, transmit_data);
flash_ptr = (volatile u32 *)n_APP_BACKUP_AVAL_ADDR;
@@ -256,18 +333,14 @@ void OTA_initial(void) {
flash_ptr = (volatile u32 *)n_APP_BACKUP_CHECK_ADDR;
u32 back_check = *flash_ptr;
if (back_aval == OTA_NOT_AVAL) { // 如果BACK不可用
#if OTA_DEBUG
std::printf("notaval\r\n");
#endif
if (back_aval == OTA_NOT_AVAL) { // 如果 BACK 不可用
OTA_DEBUG_PRINT("notaval\r\n");
move_app_data();
} else if (back_aval == OTA_AVAL &&
back_check == OTA_NOT_AVAL) { // BACK已刷写,但未检查
#if OTA_DEBUG
std::printf("aval\r\n");
#endif
back_check == OTA_NOT_AVAL) { // BACK 已刷写,但未检查
OTA_DEBUG_PRINT("aval\r\n");
move_app_data();
} else {
printf("check\r\n");
EEP_DEBUG_PRINT("check\r\n");
}
}
+84 -1
View File
@@ -1,3 +1,9 @@
/**
* @file sys/initial.h
* @brief 系统服务与平台支持源文件
* @note 本文件仅补充 Doxygen 文档注释,不改变代码逻辑。
*/
#ifndef _INITIAL_H
#define _INITIAL_H
@@ -11,6 +17,9 @@ extern "C" {
#include <cstdio>
// 任务标志定义
/**
* @brief 匿名枚举 枚举定义
*/
typedef enum {
TASK_MOTOR_CONTROL = 0,
TASK_CURRENT_UPDATE,
@@ -25,14 +34,40 @@ typedef enum {
TASK_COUNT
} SystemTask;
/**
* @brief rcu_config 函数
* @return void
*/
void rcu_config(void);
static volatile uint32_t timer_delay_count = 0; // volatile 很重要!
/**
* @brief fputc 函数
* @param[in] ch 参数
* @param[in] f 参数
* @return int 返回值
*/
int fputc(int ch, FILE *f); // 声明fputc函数以重载标准库实现
/**
* @brief 初始化 USART0 调试串口
* @return u8 返回值
*/
u8 USART0_init(void);
/**
* @brief 初始化独立看门狗
* @param[in] time 参数
* @return u8 返回值
*/
u8 watchdog_init(u32 time);
u16 get_reset_cause(void);
/**
* @brief 初始化未使用引脚状态
* @return void
*/
void unused_pin_init();
/**
* @brief 初始化 OTA 与 A/B 分区状态
* @return void
*/
void OTA_initial(void);
// 任务管理结构
@@ -41,6 +76,9 @@ typedef struct {
} TaskScheduler;
// 版本管理类
/**
* @brief VersionManager 类或结构体
*/
class VersionManager {
public:
// 版本信息结构体
@@ -55,31 +93,76 @@ class VersionManager {
VersionManager();
// 获取软件版本
/**
* @brief 获取 getSoftwareVersion 对应的数据或状态
* @return VersionInfo 返回值
*/
VersionInfo getSoftwareVersion() const;
// 获取硬件版本
/**
* @brief 获取 getHardwareVersion 对应的数据或状态
* @return VersionInfo 返回值
*/
VersionInfo getHardwareVersion() const;
// 获取BOOT版本
/**
* @brief 获取 getBootVersion 对应的数据或状态
* @return VersionInfo 返回值
*/
VersionInfo getBootVersion() const;
// 设置软件版本
/**
* @brief 设置 setSoftwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void setSoftwareVersion(u16 major, u16 minor, u16 patch, u16 build);
// 设置硬件版本
/**
* @brief 设置 setHardwareVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void setHardwareVersion(u16 major, u16 minor, u16 patch, u16 build);
// 设置BOOT版本
/**
* @brief 设置 setBootVersion 对应的参数或状态
* @param[in] major 参数
* @param[in] minor 参数
* @param[in] patch 参数
* @param[in] build 参数
* @return void
*/
void setBootVersion(u16 major, u16 minor, u16 patch, u16 build);
// 将版本信息转换为字符串
char *versionToString(const VersionInfo &version, char *buffer,
size_t bufSize) const;
// 打印所有版本信息
/**
* @brief printVersions 函数
* @return void
*/
void printVersions() const;
// 通过CAN发送版本信息
/**
* @brief sendVersionInfoViaCan 函数
* @return void
*/
void sendVersionInfoViaCan() const;
private:
+50
View File
@@ -0,0 +1,50 @@
/**
* @file sys/run_inhibit.cpp
* @brief 电机"禁止运行"闸门实现
*/
#include "run_inhibit.h"
#include "Timer.h"
#include "motor.h"
// 全局单例:定义放本文件,声明在 main.cpp 之外由各调用方 extern(见 run_inhibit.h)
RunInhibit run_inhibit;
/**
* @brief 申请禁止
*/
void RunInhibit::request(u32 src) { bits |= src; }
/**
* @brief 撤销本来源的禁止申请
*/
void RunInhibit::release(u32 src) { bits &= ~src; }
/**
* @brief 执行闸门:禁止/唤醒建立期封 PWM,禁止时让驱动睡眠;解除后唤醒并等建立时间
* @note 睡眠(nSLEEP 拉低)本身会复位 DRV8313 内部逻辑含故障锁存,所以
* "芯片侧锁存"由这里顺带清掉;"要不要继续禁止"完全由位图决定。
* 解除后不立即派发控制函数:DRV8313 手册 7.4.1 要求唤醒后约 1ms 才
* 完全工作,这段建立期内输出保持 0。若故障源还在,检测方会在下一轮
* 轮询里重新 request(),本模块不需要知道是谁、为什么。
*/
void RunInhibit::update(void) {
if (bits != 0U) {
if (!stopped) {
motor_sleep();
stopped = true;
}
// 建立窗口每拍重置:解除后要重新等满 DRV_WAKE_SETTLE_50US
wake_ticks = DRV_WAKE_SETTLE_50US;
Set_3Pwm(0.0f, 0.0f, 0.0f);
return;
}
if (stopped) {
motor_resume();
stopped = false;
}
if (wake_ticks != 0U) {
Set_3Pwm(0.0f, 0.0f, 0.0f);
wake_ticks--;
}
}
+77
View File
@@ -0,0 +1,77 @@
/**
* @file sys/run_inhibit.h
* @brief 电机"禁止运行"闸门:唯一有权决定桥臂能不能输出的模块。
* @note 分层约定(新增故障必须照此办,不要在检测处写停机动作):
* - 故障检测方 只调 request(来源位) —— 申请禁止,不碰 PWM/使能脚
* - 故障解除方 只调 release(来源位) —— 撤销自己的申请,不查别人的位
* - 功率级动作 只发生在本模块 update() —— 由 50us 控制任务驱动
* 因此任何一处都不需要知道其它故障源的存在;"能不能运行"= 位图是否为空。
*/
#ifndef _RUN_INHIBIT_H
#define _RUN_INHIBIT_H
#include "config.h"
/**
* @brief 禁止来源位图(每一位一个独立申请方,可并存)
*/
enum InhibitSource : u32 {
INHIBIT_DRV_NFAULT = 1u << 0, // 门驱动 nFAULT(PB2) 有效:UVLO/热关断/OCP 锁存
INHIBIT_UNDERVOLTAGE = 1u << 1, // 母线跌幅超阈值或低于绝对下限
INHIBIT_THERMAL = 1u << 2, // 热模型过热(含温度采集正常时的电流热积累)
INHIBIT_ISENSE_FAIL = 1u << 3, // 三相电流采样持续失效(原先被并入"过热"上报)
// 上位机还没下发过非零模式:保持驱动睡眠。这不是故障,而是"未被使能",
// 放在同一张位图里是为了让功率级只有一个执行者。老板 v2 的 EN 有 10k 下拉、
// DRV8313 上电默认睡眠;若上电即唤醒,模式 0(drv_hold 恒 10% 占空比锁止)
// 会立刻给 A 相通流发热,而"上电先发模式 0 当静止待机"是现有上位机行为。
INHIBIT_NOT_ENABLED = 1u << 7,
};
/**
* @brief RunInhibit 类:禁止运行闸门
*/
class RunInhibit {
// 上电即禁止:等上位机首次下发非零模式解除(见 INHIBIT_NOT_ENABLED)
u32 bits = INHIBIT_NOT_ENABLED;
bool stopped = false; // 上一拍是否已施加停机(避免每 50us 重复翻转使能脚)
u8 wake_ticks = 0; // 唤醒建立倒计时(50us 节拍)
public:
/**
* @brief 申请禁止(幂等;同一位重复申请无副作用)
* @param[in] src InhibitSource 位
* @return void
*/
void request(u32 src);
/**
* @brief 撤销本来源的禁止申请;位图清空后才允许运行
* @param[in] src InhibitSource 位
* @return void
*/
void release(u32 src);
/**
* @brief 是否允许运行(只看位图,不看唤醒状态)
* @return bool true=允许
*/
bool permitted() const { return bits == 0U; }
/**
* @brief 允许运行且驱动唤醒建立已完成,控制函数可派发
* @return bool true=可派发
*/
bool ready() const { return bits == 0U && wake_ticks == 0U; }
/**
* @brief 当前禁止来源位图(上报/诊断用)
* @return u32 位图
*/
u32 sources() const { return bits; }
/**
* @brief 执行闸门:50us 控制任务每拍调用,唯一碰 PWM 与驱动使能的地方
* @return void
*/
void update(void);
};
extern RunInhibit run_inhibit;
#endif
+128 -22
View File
@@ -35,19 +35,75 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "systick.h"
#include "Can.h"
#include "config.h"
#include "core_cmFunc.h"
#include "flash.h"
#include "gd32c10x.h"
#include "gd32c10x_exti.h"
#include "gd32c10x_fmc.h"
#include "initial.h"
#include "motor.h"
#include "tasks.h"
#include <cstdio>
// SLEEP 调试宏定义
#if SLEEP_DEBUG
/** @brief SLEEP_DEBUG_PRINT 宏定义 */
#define SLEEP_DEBUG_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
/** @brief SLEEP_DEBUG_PRINT 宏定义 */
#define SLEEP_DEBUG_PRINT(fmt, ...) ((void)0)
#endif
// SysTick 模块内部状态,仅在本文件访问:
// - delay / systick_ms 由 SysTick 中断维护,用 volatile 保证主循环可见性;
// - DWT 统计变量用于 CPU 占用率采样;
// - static 限制文件作用域,外部只能通过 get_systick_ms()/get_cpu_usage() 读取。
volatile static uint32_t delay;
volatile static uint32_t systick_ms = 0; // 系统运行时间(毫秒)
// ============================================================================
// DWT-based CPU usage measurement
// ============================================================================
// GD32C103 = Cortex-M4 @ 120MHz, DWT_CYCCNT 可用
//
// 原理:
// do_task() 入口记录 CYCCNT → task_enter_cyccnt
// do_task() 出口记录 CYCCNT → 差值 = 本次忙碌周期
// 累加到 busy_cycles_accum
// SysTick 每 1s 统计:cpu% = busy_cycles_accum * 100 / SystemCoreClock
//
// 不需要 idle_task_count(),DWT 直接数周期
// ============================================================================
volatile static uint32_t task_enter_cyccnt = 0; // do_task 入口 CYCCNT
volatile static uint32_t busy_cycles_accum = 0; // 1s 内累计忙碌周期
volatile static uint16_t sample_seconds = 0; // 统计窗口(秒)
volatile static uint8_t cpu_usage = 0; // CPU 使用率(0~100%)
volatile static uint8_t dwt_initialized = 0; // DWT 初始化标志
extern CAN_transmiter CAN0_transmiter;
extern FlashEEPROM flashEEP;
/*!
\brief 初始化 DWT 周期计数器
\note 使能 DWT 的 CYCCNT,用于精确测量 CPU 周期
*/
static void dwt_init(void) {
/* 使能 DWT 和 ITM(TRCENA 位在 DEMCR 中) */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* 复位 CYCCNT */
DWT->CYCCNT = 0;
/* 使能 CYCCNT */
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
dwt_initialized = 1;
}
/*!
\brief get system tick time in milliseconds
\param[in] none
\param[out] none
\retval system time in ms
*/
uint32_t get_systick_ms(void) {
return systick_ms;
}
/*!
\brief configure systick
\param[in] none
@@ -55,6 +111,9 @@ extern FlashEEPROM flashEEP;
\retval none
*/
void systick_config(void) {
/* 初始化 DWT 周期计数器 */
dwt_init();
/* setup systick timer for 1000Hz interrupts */
if (SysTick_Config(SystemCoreClock / 1000U)) {
/* capture error */
@@ -64,16 +123,35 @@ void systick_config(void) {
/* configure the systick handler priority */
NVIC_SetPriority(SysTick_IRQn, 0x00U);
}
/*!
\brief delay a time in seconds
\param[in] count: count in seconds
\brief SysTick handler - increment millisecond counter
\param[in] none
\param[out] none
\retval none
*/
void delay_1s(uint32_t count) {
uint32_t i;
for (i = 0; i < count; i++) {
delay_1ms(1000);
extern "C" void SysTick_Handler(void) {
systick_ms++; // 递增系统时间
delay_decrement(); // 递减延时计数器
/* 每 1 秒统计一次 CPU 占用率 */
sample_seconds++;
if (sample_seconds >= 1000U) {
if (dwt_initialized) {
/* 1 秒总周期 = SystemCoreClock (120,000,000) */
/* CPU% = busy_cycles_accum * 100 / SystemCoreClock */
uint64_t busy_64 = (uint64_t)busy_cycles_accum * 100U;
uint32_t busy_percent = (uint32_t)(busy_64 / SystemCoreClock);
if (busy_percent > 100U) {
busy_percent = 100U;
}
cpu_usage = (uint8_t)busy_percent;
}
busy_cycles_accum = 0;
sample_seconds = 0;
// 按任务负载统计与全局口径同步:同一秒窗口
task_busy_cycles_reset();
}
}
@@ -102,6 +180,37 @@ void delay_decrement(void) {
}
}
/*!
\brief 空闲任务计数(兼容接口,DWT 版本无需操作)
*/
void idle_task_count(void) {
/* DWT 版本:空闲周期 = 总周期 - 忙碌周期,无需手动计数 */
}
/*!
\brief do_task 入口调用,记录忙碌起始周期
*/
void busy_task_enter(void) {
if (dwt_initialized) {
task_enter_cyccnt = DWT->CYCCNT;
}
}
/*!
\brief do_task 出口调用,累加忙碌周期
*/
void busy_task_exit(void) {
if (dwt_initialized) {
busy_cycles_accum += (DWT->CYCCNT - task_enter_cyccnt);
}
}
/**
* @brief 获取 CPU 使用率
* @return uint8_t 返回值
*/
uint8_t get_cpu_usage(void) { return cpu_usage; }
/*!
\brief system soft reset
\param[in] none
@@ -131,7 +240,7 @@ void app_prepare_deep_sleep(bool save_data) {
if (save_data) {
flashEEP.save_system_config(true);
}
// 将SIT1044设置为低功耗待机模式
// 将 SIT1044 设置为低功耗待机模式
// CAN0_transmiter.CAN_Sleep();
motor_sleep();
// 在睡眠前调整看门狗超时时间为最大值,使其在唤醒前不会触发
@@ -139,8 +248,9 @@ void app_prepare_deep_sleep(bool save_data) {
fwdgt_config(0xFFF, FWDGT_PSC_DIV256); // 设置最大超时时间
fwdgt_write_disable();
uint32_t primask = __get_PRIMASK();
__disable_irq();
// 遍历最后一个数字,后一个地址写+1
// 遍历最后一个数字,后一个地址写 +1
for (u32 address = SLEEP_CONFIG_END - SIZE_U32;
address >= SLEEP_CONFIG_START; address -= SIZE_U32) { // 遍历睡眠存储区
volatile u32 *flash_ptr = (volatile u32 *)address;
@@ -149,9 +259,7 @@ void app_prepare_deep_sleep(bool save_data) {
address > SLEEP_CONFIG_START) { // 空数据且未到头跳过
continue;
} else { // 有数据的话读取
#if SLEEP_DEBUG
printf("app sleep found %08x at %08x\r\n", sleep_enable, address);
#endif
SLEEP_DEBUG_PRINT("app sleep found %08x at %08x\r\n", sleep_enable, address);
if (address == SLEEP_CONFIG_END - SIZE_U32) {
fmc_unlock();
fmc_state_enum fmc_status =
@@ -159,32 +267,30 @@ void app_prepare_deep_sleep(bool save_data) {
if (fmc_status != FMC_READY) { // 擦除失败报错
fmc_lock();
flashEEP.EEPROMStatus = FLASH_EEPROM_ERASE_ERROR;
__set_PRIMASK(primask); // 失败返回前恢复进入前的中断状态
return;
}
address = SLEEP_CONFIG_START - SIZE_U32; // 重新开始写入
}
address += SIZE_U32;
#if SLEEP_DEBUG
printf("app sleep prepare write %08x at %08x\r\n", SLEEP_ENABLE,
SLEEP_DEBUG_PRINT("app sleep prepare write %08x at %08x\r\n", SLEEP_ENABLE,
address);
#endif
fmc_state_enum fmc_status =
write_ADDRESS(address, SLEEP_ENABLE); // 写入数据
#if SLEEP_DEBUG
volatile u32 *flash_ptr = (volatile u32 *)address;
u32 sleep_check = *flash_ptr;
printf("app sleep wrote %08x at %08x\r\n", sleep_check, address);
SLEEP_DEBUG_PRINT("app sleep wrote %08x at %08x\r\n", sleep_check, address);
#endif
if (fmc_status != FMC_READY) { // 写入失败报错
flashEEP.EEPROMStatus = FLASH_EEPROM_WRITE_ERROR;
#if SLEEP_DEBUG
printf("app wrote %08x at %08x ERROR\r\n", SLEEP_ENABLE,
SLEEP_DEBUG_PRINT("app wrote %08x at %08x ERROR\r\n", SLEEP_ENABLE,
address);
#endif
__set_PRIMASK(primask); // 失败返回前恢复进入前的中断状态
return;
}
break;
}
}
jumpToBOOT(); // 跳转到bootloader正式休眠,进入低功耗待机模式
jumpToBOOT(); // 跳转到 bootloader 正式休眠,进入低功耗待机模式
}
+54 -4
View File
@@ -18,7 +18,7 @@ this list of conditions and the following disclaimer.
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.
software without 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
@@ -41,22 +41,72 @@ extern "C" {
#include <cstdint>
/* configure systick */
/**
* @brief 配置 SysTick 定时器
* @return void
*/
void systick_config(void);
/* get system tick time in milliseconds */
/**
* @brief 获取系统毫秒计数
* @return uint32_t 返回值
*/
uint32_t get_systick_ms(void);
/* delay a time in milliseconds */
/**
* @brief 毫秒级阻塞延时
* @param[in] count 参数
* @return void
*/
void delay_1ms(uint32_t count);
/* delay a time in seconds */
void delay_1s(uint32_t count);
/* delay decrement */
/**
* @brief 递减延时计数器
* @return void
*/
void delay_decrement(void);
/* 空闲任务计数 */
/* 空闲任务计数(兼容接口,DWT 版本无需操作) */
/**
* @brief 记录空闲任务计数
* @return void
*/
void idle_task_count(void);
/* do_task 入口调用,记录忙碌起始周期 */
/**
* @brief 记录忙碌任务开始周期
* @return void
*/
void busy_task_enter(void);
/* do_task 出口调用,累加忙碌周期 */
/**
* @brief 记录忙碌任务结束并累加周期
* @return void
*/
void busy_task_exit(void);
/* 获取CPU使用率 */
/**
* @brief 获取 CPU 使用率
* @return uint8_t 返回值
*/
uint8_t get_cpu_usage(void);
/* 系统软复位 */
/**
* @brief 执行系统软复位
* @return void
*/
void system_soft_reset(void);
/* 准备进入低功耗模式 */
/**
* @brief 准备进入应用低功耗模式
* @param[in] save_data 参数
* @return void
*/
void app_prepare_deep_sleep(bool save_data);
/* 中断唤醒 */
/**
* @brief EXTI5_9 中断处理函数
* @return void
*/
void EXTI5_9_IRQHandler(void);
#ifdef __cplusplus
+199
View File
@@ -0,0 +1,199 @@
/**
* @file tasks.cpp
* @brief 周期任务调度与任务执行实现
*/
#include "tasks.h"
#include "Can.h"
#include "FOC.h"
#include "Filter.h"
#include "adc.h"
#include "ang_sensor.h"
#include "health.h"
#include "run_inhibit.h"
#include "systick.h"
#include "gd32c10x.h"
extern CAN_transmiter CAN0_transmiter;
extern sampler sampler;
extern angle_sensor KTH7823;
extern EncoderAngleKalman Angle_kalman;
extern ThermalController thermalController;
extern FOC_Controller FOC;
extern u32 motor_runtime_50us;
extern u32 cnt_test;
// 函数指针数组,包含各种控制模式对应的函数
static FOCFunctionPtr control_functions[] = {
&FOC_Controller::drv_hold, // 0: 占空比置
&FOC_Controller::angle_control, // 1: 位置闭环控制
&FOC_Controller::velocity_control, // 2: 速度闭环控制
&FOC_Controller::
angle_control_with_velocity_limit, // 3:
// 角度闭环控制,带速度限制
&FOC_Controller::MIT_Mode, // 4: MIT 模式
&FOC_Controller::current_control, // 5: 电流闭环控制
&FOC_Controller::velocity_open_loop, // 6: 速度开环控制
&FOC_Controller::PWM_out, // 7: 占空比输出
};
/**
* @brief 判断任务标志中是否包含指定任务
* @param[in] currentTasks 当前任务位图快照
* @param[in] task 待检查任务枚举
* @return bool true 表示该任务需要执行
*/
static bool has_task(u32 currentTasks, SystemTask task) {
return (currentTasks & (1u << task)) != 0U;
}
// 按任务 DWT 负载计量:定位 CPU 占用分布(与全局 cpu_usage 同 1s 口径,
// 由 SysTick 统计块调用 task_busy_cycles_reset() 同步清零)。
// 120MHz 下 120000 周期 ≈ 1% CPU。仅统计任务本体,不含中断抢占时间。
static volatile u32 task_busy_cycles[TASK_COUNT] = {0};
u32 get_task_busy_cycles(u8 task) {
return (task < TASK_COUNT) ? task_busy_cycles[task] : 0U;
}
void task_busy_cycles_reset(void) {
for (u8 i = 0; i < TASK_COUNT; i++) {
task_busy_cycles[i] = 0;
}
}
// 执行任务并累计其忙碌周期
#define RUN_TIMED(task_enum, call) \
do { \
u32 t0_ = DWT->CYCCNT; \
call; \
task_busy_cycles[task_enum] += DWT->CYCCNT - t0_; \
} while (0)
/**
* @brief 控制模式变化时复位所有控制环,避免积分项跨模式继承
* @return void
*/
static void reset_control_loops_on_mode_change(void) {
static int last_control_mode = -1;
if (FOC.control_mode == last_control_mode) return;
FOC.vel_loop.reset();
FOC.angle_loop.reset();
FOC.current_loop.reset();
FOC.vel_inVelAngloop.reset();
FOC.ang_inVelAngloop.reset();
FOC.ang_inMITloop.reset();
FOC.vel_inMITloop.reset();
FOC.current_inMITloop.reset();
last_control_mode = FOC.control_mode;
}
/**
* @brief 执行 50us 电机控制任务,包含快速电压检测和控制函数派发
* @return void
*/
static void run_motor_control_task(void) {
motor_runtime_50us++;
static u8 vcheck_div = 0;
// 快速电压检测每 100us 执行一次,避免每 50us 都读取处理。
if (++vcheck_div >= 2u) {
vcheck_div = 0;
FOC.fast_voltage_check();
}
// 运行闸门:全工程唯一执行停机/唤醒功率级的地方。放在模式判断之前,
// 这样即使 control_mode 非法(>=8)也照样封住输出。
run_inhibit.update();
if (!run_inhibit.ready()) {
return; // 被禁止运行,或刚解除还在驱动唤醒建立期(输出已由闸门保持 0)
}
if (FOC.control_mode >= 8) return;
// 模式合法时再派发控制函数,防止数组越界。
reset_control_loops_on_mode_change();
(FOC.*control_functions[FOC.control_mode])();
}
/**
* @brief 执行高频采样和电机控制相关任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
static void run_fast_sample_tasks(u32 currentTasks) {
if (has_task(currentTasks, TASK_CURRENT_UPDATE)) {
RUN_TIMED(TASK_CURRENT_UPDATE, sampler.update_current());
}
if (has_task(currentTasks, TASK_ANGLE_UPDATE)) {
RUN_TIMED(TASK_ANGLE_UPDATE, KTH7823.update());
RUN_TIMED(TASK_ANGLE_UPDATE,
FOC.rt_angle = Angle_kalman.update(KTH7823.getAngle()));
}
if (has_task(currentTasks, TASK_VELOCITY_UPDATE)) {
RUN_TIMED(TASK_VELOCITY_UPDATE,
FOC.rt_velocity = Angle_kalman.getVelocity());
}
if (has_task(currentTasks, TASK_MOTOR_CONTROL)) {
RUN_TIMED(TASK_MOTOR_CONTROL, run_motor_control_task());
}
}
/**
* @brief 执行温度、热管理、磁检测和母线电压等低频维护任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
static void run_housekeeping_tasks(u32 currentTasks) {
if (has_task(currentTasks, TASK_MCU_TEMP_UPDATE)) {
RUN_TIMED(TASK_MCU_TEMP_UPDATE, sampler.update_mcu_temp());
}
if (has_task(currentTasks, TASK_DRV_TEMP_UPDATE)) {
RUN_TIMED(TASK_DRV_TEMP_UPDATE, sampler.update_drv_temp());
}
if (has_task(currentTasks, TASK_THERMAL_CONTROL)) {
RUN_TIMED(TASK_THERMAL_CONTROL, thermalController.update());
}
if (has_task(currentTasks, TASK_MAGNETIC_CHECK)) {
RUN_TIMED(TASK_MAGNETIC_CHECK, angsens_MG_check());
}
if (has_task(currentTasks, TASK_VBUS_UPDATE)) {
RUN_TIMED(TASK_VBUS_UPDATE, power_manage());
}
}
/**
* @brief 执行 CAN 周期发送任务并喂狗
* @return void
*/
static void run_can_task(void) {
u32 t0 = DWT->CYCCNT;
CAN0_transmiter.test_u16 = CAN0_transmiter.get_node_id();
cnt_test = 0;
CAN0_transmiter.can0_send_flag = SET;
CAN0_transmiter.communication_check();
fwdgt_counter_reload();
task_busy_cycles[TASK_CAN_TRANSMIT] += DWT->CYCCNT - t0;
}
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
void do_task(u32 currentTasks) {
busy_task_enter(); // DWT: 记录忙碌起始周期
cnt_test++;
// 高频任务先执行,保持电机控制优先级。
run_fast_sample_tasks(currentTasks);
run_housekeeping_tasks(currentTasks);
// CAN 发送放在最后,避免通信处理影响前面的实时控制任务。
if (has_task(currentTasks, TASK_CAN_TRANSMIT)) {
run_can_task();
}
busy_task_exit(); // DWT: 累加忙碌周期
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef TASKS_H
#define TASKS_H
#include "config.h"
#include "initial.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 根据任务标志调度周期性业务任务
* @param[in] currentTasks 当前任务位图快照
* @return void
*/
void do_task(u32 currentTasks);
/**
* @brief 读取上一统计周期(1s)内指定任务累计消耗的 CPU 周期数
* @param[in] task SystemTask 枚举值
* @return u32 周期数(120MHz 下 120000 = 1% CPU)
*/
u32 get_task_busy_cycles(u8 task);
/**
* @brief 清零按任务负载统计(SysTick 每秒统计 cpu_usage 时同步调用)
* @return void
*/
void task_busy_cycles_reset(void);
#ifdef __cplusplus
}
#endif
#endif /* TASKS_H */
+161
View File
@@ -0,0 +1,161 @@
# 静态检查告警审查说明
本文件用于记录人工确认后的静态检查告警处理结论。原则:
- 不为消除低风险 warning 引入会改变实时行为的代码。
- 裸机系统级单例、编译期常量、ISR/主循环共享状态分别分类说明。
- 对涉及实时路径的并发项,优先记录访问边界和偏离理由;只有经过实机验证后再做逻辑修复。
## SUP-GLB-001:文件作用域对象
### 编译期常量
以下 `constexpr` 对象为编译期常量,非可变全局状态,无运行期写入、并发访问或动态初始化顺序风险:
- `device/ADC.cpp`: `ADC_SCALE`, `TEMP_FACTOR`, `VCC_FACTOR`, `CURRENT_FACTOR`
- `sys/health.cpp`: `PROTECT_TIME`
- `sys/config.h`: 版本号、Flash 地址、协议 ID、滤波/PID 参数、数学常量、Timer 参数等 `constexpr` 配置项
处理结论:按静态检查误报/偏离处理。
### 系统级硬件/模块单例
以下对象对应裸机固件中的唯一硬件资源或唯一管理器:
- `Algorithm/FOC.cpp`: `FOC_Controller FOC`
- `device/ADC.cpp`: `sampler sampler`
- `device/Can.cpp`: `CAN_transmiter CAN0_transmiter`
- `device/ang_sensor.cpp`: `angle_sensor KTH7823`
- `sys/health.cpp`: `ThermalController thermalController`
- `sys/initial.cpp`: `VersionManager Version`
访问边界:
- 初始化顺序由 `main::hardware_init()` 显式控制,例如 `KTH7823.init()`、`sampler.init()`、`CAN0_transmiter.init()`、`FOC.init()`。
- `VersionManager Version` 构造仅复制 `constexpr` 版本号常量,不依赖外设初始化。
- 运行期主要由主循环任务访问;CAN 命令路径会更新 FOC 目标值和参数。
处理结论:接受系统级单例设计,保留文件作用域对象,并通过注释说明初始化顺序和访问边界。
### ISR/主循环共享状态
#### `main.cpp`: `TaskScheduler scheduler`
- `TIMER2_IRQHandler()` 置位任务标志。
- main 循环短关中断后读取快照并清除已处理任务位。
处理结论:已有短临界区保护,接受。
#### `device/Can.cpp`: CAN RX/TX 队列索引
涉及:
- `rx_queue_head`, `rx_queue_tail`, `rx_queue_count`
- `tx_queue_head`, `tx_queue_tail`, `tx_queue_count`
访问边界:
- RX/TX 队列被主循环、CAN 中断、DMA 完成中断共同访问。
- 队列 push/pop/peek/disable 通过 `CAN_EnterCritical()` / `CAN_ExitCritical()` 保护。
- `CAN_ExitCritical()` 使用 `__set_PRIMASK(primask)` 恢复进入前中断状态,而不是直接 `__enable_irq()`,避免误打开外层已关闭的中断。
处理结论:已有临界区保护,接受。
#### `device/ADC.cpp`: injected 电流采样缓存
涉及:
- `adc_current_value[3]`
- `adc_current_frame_ready`
访问边界:
- `ADC0_1_IRQHandler()` 写入 U/V/W injected 同步采样值,并置位 `adc_current_frame_ready`。
- `sampler::update_current()` 在主循环任务中看到 ready 后读取三相采样并更新滤波缓存,随后清 ready。
- 该路径属于高频电机采样/控制链路。此前尝试在读取侧加入 PRIMASK 快照后,实机出现上电连续重启;回滚后恢复正常。
处理结论:当前不再为消除 warning 增加临界区逻辑,避免改变高频实时路径时序。保留现有 `volatile` 共享标志设计,作为实机已验证的稳定实现;如后续需要修正,应单独建立实验分支并做示波/日志验证。
#### `sys/systick.cpp`: DWT CPU 占用率统计变量
涉及:
- `task_enter_cyccnt`
- `busy_cycles_accum`
- `sample_seconds`
- `cpu_usage`
- `dwt_initialized`
访问边界:
- `busy_task_enter()` / `busy_task_exit()` 在主循环任务侧记录和累加忙碌周期。
- `SysTick_Handler()` 每 1 秒读取并清零统计值,更新 `cpu_usage`。
- 此路径仅用于诊断 CPU 使用率,不参与控制闭环。
- 此前尝试在 `busy_task_exit()` 加入 PRIMASK 临界区后,与 ADC 临界区改动同轮导致实机上电连续重启;已回滚并恢复正常。
处理结论:当前不再为该诊断统计项增加临界区,避免影响高频任务时序。允许 1 秒窗口内存在极小统计误差,作为诊断值可接受。
#### `device/ang_sensor.cpp`: SPI DMA 完成标志
涉及:
- `spi_dma_tx_complete`
- `spi_dma_rx_complete`
访问边界:
- SPI 传输前清零标志。
- DMA ISR 完成后置位。
- 轮询函数等待标志或超时。
处理结论:典型 ISR/轮询共享 volatile 标志,接受。
#### `device/Timer.cpp`: `total_us100_counter`
访问边界:
- 仅在 `TIMER2_IRQHandler()` 内作为 100us 调度索引写入和使用。
处理结论:虽然为文件作用域变量,但无跨上下文并发写风险,接受。后续可考虑改为 ISR 内 `static`,但当前不为 warning 改运行逻辑。
#### `sys/health.cpp`: 电压管理状态
涉及:
- `voltage_state`
- `last_vbus_slow`
访问边界:
- 仅 `power_manage()` 慢速任务访问。
- 文件内 `static` 封装。
处理结论:模块私有状态机变量,接受。
## SUP-CMP-001:浮点比较
### `normalizeAngle360()` 边界判断
代码形态:
```cpp
while (angle >= 360.0f) angle -= 360.0f;
while (angle < 0.0f) angle += 360.0f;
```
结论:该比较用于角度归一化边界判断,目标区间严格定义为 `[0, 360)`,不是浮点相等性判断,也不是传感器阈值判定。不需要容差。
## printf / snprintf 类检查
- FOC 齿槽校准打印位于 `#if FOC_DEBUG`,正式配置 `FOC_DEBUG=0` 时不参与编译。
- `VersionManager::versionToString()` 已从 `snprintf()` 改为固定格式的轻量手写拼接,避免格式化库引入。
## AUTO-MET-001:圈复杂度
已拆分以下函数,降低单函数圈复杂度:
- `FOC_Controller::calibrate_cogging_tick()`:拆分为状态派发和各状态处理函数。
- `CAN_transmiter::communication_check()`:拆分为周期发送、接收分发、特殊消息、节点消息等处理函数。
- `do_task()`:拆分为高频采样/电机控制、低频维护、CAN 任务。
处理结论:拆分后原目标函数复杂度已显著下降;实编仍需在 Keil/CMSIS/cbuild 环境验证。
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
# STATIC_ANALYSIS_NOTES 复核修订版(2026-10-19)
> 原文件:仓库根目录 `STATIC_ANALYSIS_NOTES.md`(最后修改于 7c4aaa5,2026-09-01),本次保留未改。
> 复核基线:master `86ca766`(2026-10-13)。完整评审见 [Motor_v2_专家评审_2026-10-19.html](Motor_v2_专家评审_2026-10-19.html)。
| 条目 | 原结论 | 当前状态 | 证据 / 说明 |
|---|---|---|---|
| SUP-GLB-001 编译期常量(ADC/health/config constexpr) | 误报/偏离 | **仍成立** | ADC.cpp:22-47;health.cpp:107;config.h 全部为 constexpr |
| 硬件单例 FOC / sampler / CAN0_transmiter / KTH7823 / thermalController / Version | 接受单例 | **仍成立** | FOC.cpp:28;ADC.cpp:60;Can.cpp:86;ang_sensor.cpp:39;health.cpp:104;initial.cpp:218 |
| TaskScheduler scheduler 短临界区 | 已保护,接受 | **仍成立** | main.cpp:68-72 关中断取快照并清位;taskFlags volatile(initial.h:75);两写入方同抢占级 |
| CAN RX/TX 队列临界区与 PRIMASK 恢复 | 已保护,接受 | **基本成立,但有遗漏** | Can.cpp:644-731、480;遗漏:主循环 CANFD_AsyncSend 直接写邮箱与 TX ISR 竞争(Can.cpp:1395/744,见M21) |
| ADC injected 电流缓存不加临界区 | 保留现状 | **风险仍在** | ADC.cpp:62-63、467-481 存在三相撕裂/丢 ready(M33);建议序号双读而非 PRIMASK |
| “加 PRIMASK 后实机连续重启”归因于时序 | 归因时序 | **归因不成立** | 10 周期级临界区不足以致复位;更可能是 update_current 提前返回分支(ADC.cpp:468)未恢复中断 + 与 systick 改动捆绑未隔离 + 50 ms 看门狗(IRC40K)余量小;无复位原因记录无法定位 |
| systick DWT CPU 占用统计 | 允许小误差 | **仍成立** | systick.cpp:75-79、193-206;仅诊断值 |
| ang_sensor SPI DMA 完成标志 | 接受 volatile 标志 | **已过时** | 已改为寄存器轮询 spi_rw_half_word_polling(ang_sensor.cpp:57-82),标志不存在;新问题:TRANS 等待无超时(:78) |
| Timer total_us100_counter | 仅 ISR 使用,接受 | **仍成立** | Timer.cpp:19、149-153;可改为 ISR 内 static |
| health voltage_state / last_vbus_slow | 模块私有,接受 | **仍成立(有残留)** | health.cpp:112-113 文件内 static;last_vbus_slow 只写不读 |
| SUP-CMP-001 normalizeAngle360 浮点比较 | 无需容差 | **比较结论成立,但有新缺陷** | ang_sensor.h:103-111 输入 Inf/NaN 时死循环(M16) |
| printf:FOC 齿槽校准打印位于 FOC_DEBUG | 正式版不编译 | **已过时** | 9fbd9c3 已移除齿槽补偿;FOC_DEBUG 仅剩 motor.cpp:245 一处 |
| versionToString 去 snprintf | 已改 | **仍成立** | initial.cpp:302-346 |
| AUTO-MET-001 calibrate_cogging_tick 拆分 | 已拆分 | **已过时** | 函数随齿槽功能删除;Can.cpp:1186 bit4 改为保留位 |
| AUTO-MET-001 communication_check 拆分 | 已拆分 | **已落实** | Can.cpp:841-1115 |
| AUTO-MET-001 do_task 拆分 | 已拆分 | **已落实** | tasks.cpp:296-399 |
| “实编仍需在 Keil/CMSIS/cbuild 环境验证” | 待验证 | **仍未验证** | 本次评审为静态评审,未实编;cproject -O3 覆盖 build-types(M38)需一并处理 |
## 需要写回原记录的修订要点
1. 删除/标注过时:齿槽校准相关两条(FOC_DEBUG 打印、calibrate_cogging_tick 拆分),9fbd9c3 已移除齿槽补偿。
2. ang_sensor 已改为寄存器轮询,“SPI DMA 完成标志”条目应替换为“轮询超时”条目,并补 TRANS 等待超时。
3. ADC 电流缓存:保留“不加长临界区”的原则,但撕裂风险真实存在,建议改用序号双读;“加 PRIMASK 导致重启”的归因应改为“未定位”,并在启动时记录复位原因后在独立分支复现。
4. CAN 队列:补充“主循环 CANFD_AsyncSend 直接写邮箱与 TX ISR 竞争”的偏离说明或修复。
5. normalizeAngle360:比较结论成立,但需补充非有限输入保护。
6. health:`last_vbus_slow` 只写不读,可删除。
7. 实编验证仍未完成;同时修正 cproject 中强制 -O3 覆盖 build-types 的问题。
+2 -2
View File
@@ -9,8 +9,8 @@
"requires": {
"arm:tools/open-cmsis-pack/cmsis-toolbox": "2.12.0",
"arm:tools/kitware/cmake": "3.31.5",
"arm:tools/ninja-build/ninja": "1.12.1",
"arm:tools/arm/mdk-toolbox": "1.1.0",
"arm:compilers/arm/armclang": "6.24.0"
"arm:compilers/arm/armclang": "6.24.0",
"arm:tools/ninja-build/ninja": "1.12.1"
}
}