Merge pull request 'Feature' (#1) from feature into master

Reviewed-on: chrg/chrg#1
This commit is contained in:
hwq
2026-07-18 12:47:57 +00:00
57 changed files with 7960 additions and 836 deletions
+12
View File
@@ -0,0 +1,12 @@
---
alwaysApply: true
scene: git_message
---
在此处编写规则,自定义 AI 生成提交信息的风格。
使用中文描写本次提交内容。
格式
首行 简要描述
空一行
每行不超过80个描述一个修改/新增/删除的内容。
具体到文件级,描述文件名、修改内容。
+28
View File
@@ -0,0 +1,28 @@
# Repository Guidelines
## Project Structure & Module Organization
`apps/chrg/` is the primary STM32F405 RT-Thread application: product code lives in `applications/`, board setup and linker scripts in `board/`, and build configuration beside them. Keep device-facing code in `applications/bsp/`, protocol and control helpers in `applications/utils/`, and long-running tasks in `applications/thread/`. `apps/boot/` is the standalone bootloader. Shared CMSIS and STM32 HAL sources are under `libs/`; `rt-thread/` is vendored upstream code. Hardware specifications and protocol references belong in `docs/`. Treat `build/`, Keil `Objects/`, maps, and firmware binaries as generated output.
## Build, Test, and Development Commands
Run charger commands from `apps/chrg/`:
- `scons -j4` builds `chrg.elf` and `rtthread.bin` using the toolchain selected by `RTT_CC`/`RTT_EXEC_PATH` (GCC is the default in `rtconfig.py`).
- `scons --target=mdk5` regenerates the Keil MDK project when SCons configuration or source lists change.
- `C:\Keil_v5\UV4\UV4.exe -b chrg.uvprojx` performs a command-line Keil build. Build `apps/boot/boot.uvprojx` similarly for the bootloader.
- `keilkill.bat` removes Keil intermediates; review untracked files before running cleanup scripts.
SCons and an ARM compiler are prerequisites and are not bundled on `PATH`.
## Coding Style & Naming Conventions
Use C99 and follow the surrounding file's indentation; new blocks should use four spaces. Place braces on the next line for functions and keep paired declarations/definitions in `.h`/`.c` files. Product symbols use lower snake case with the `chrg_` prefix, structs end in `_t`, and constants/macros use uppercase snake case. Use RT-Thread types and APIs (`rt_uint16_t`, `rt_thread_mdelay`) in application code and HAL APIs at the board boundary. Preserve existing file encodings when editing Chinese comments or documentation.
## Testing Guidelines
There is no product-level automated test suite or coverage threshold. Every firmware change must compile both affected targets and be exercised on STM32F405 hardware. Record checks for boot/upgrade flow, serial console, Modbus north/south communication, relay behavior, and LCD output as applicable. Tests in `rt-thread/tools/testcases/` cover vendored build tooling only; run them when modifying that tooling with `python -m unittest discover rt-thread/tools/testcases -p "test_*.py"`.
## Commit & Pull Request Guidelines
History uses short, lowercase, imperative summaries such as `fix south ch` and `add roll thread`; keep each commit focused and avoid generated artifacts. Pull requests should describe the affected target and behavior, link the issue, list build and hardware results, and include serial logs or LCD captures for user-visible or protocol changes. Call out changes to register maps, flash layout, linker scripts, or upgrade compatibility explicitly.
+156
View File
@@ -0,0 +1,156 @@
# HCDF MID 405 充放电控制器固件
本仓库是基于 **STM32F405RG** 的四通道双向电源/电子负载控制固件。主程序运行在 **RT-Thread 5.2.2** 上,负责连接上位机、HMI、快充协议板和源载功率板,完成通道切换、源/载控制、继电器管理、状态采集、校准和固件升级。仓库同时包含独立 Bootloader,用于应用固件校验、更新和出厂固件恢复。
> 本项目直接控制电源与电子负载硬件。调试前应确认限压、限流、散热、急停和继电器默认状态;修改通道切换或输出控制逻辑后,先在断电或受限功率条件下验证。
## 系统概览
```mermaid
flowchart LR
PC["上位机 / 测试系统"] -->|"UART1 · Modbus RTU"| CTRL["STM32F405 控制板"]
HMI["HMI 屏幕"] <-->|"UART2"| CTRL
CTRL <-->|"UART3 · 多路复用"| NORTH["4 路快充协议板"]
CTRL <-->|"UART6 · 多路复用"| SOUTH["4 路源载功率板"]
CTRL --> RELAY["继电器与通道选择"]
CTRL --> FLASH["参数 / 日志 / 升级分区"]
USB["USB CDC 调试终端"] <-->|"MSH / 日志"| CTRL
```
主程序把每个物理通道的控制状态集中保存在北向对象中,并从南向功率板刷新电压、电流、功率、故障和在线状态。上位机或 HMI 写入控制参数后,轮询线程将变化转换为协议板、功率板和继电器操作;采集结果再映射回 Modbus 寄存器和屏幕数据。
## 主要功能
- 四通道 Source/Sink 控制及通信通道复用。
- 电源侧快充协议设置,代码中包含 FCP、SCP、QC2.0/QC3.0、AFC、PD、UFCS 等协议编号;实际支持范围以 `docs/` 中的协议表和目标板固件为准。
- 电子负载恒压、恒流、恒功率、恒阻和开环模式,以及短路、OCP、动态测试控制。
- 上位机 Modbus RTU 服务,地址默认为 `0x01`,实现 `0x01/0x02/0x03/0x04/0x05/0x06/0x0F/0x10`,并提供 `0x20` 升级和 `0x21` 校准扩展功能。
- FAL 片内 Flash 管理、看门狗、硬件定时器、USB CDC 虚拟串口、FinSH/MSH 和 ULog。
- 带 CRC、包头校验、AES 解密、断电恢复和出厂分区回退的 Bootloader 升级流程。
## 目录结构
| 路径 | 说明 |
| --- | --- |
| `apps/chrg/` | RT-Thread 主固件、SCons 配置和 Keil 工程 |
| `apps/chrg/applications/bsp/` | GPIO、继电器、LED、串口封装、FAL、定时器、看门狗和 USB VCOM |
| `apps/chrg/applications/thread/` | 接收、轮询、Modbus 和 LCD 六个常驻业务线程 |
| `apps/chrg/applications/utils/` | Source/Sink 控制、功率板寄存器、协议编解码和通用工具 |
| `apps/chrg/board/` | 时钟、STM32 HAL、CubeMX 配置、驱动端口和链接脚本 |
| `apps/boot/` | 裸机 HAL Bootloader、YModem、固件校验/搬运和 Keil 工程 |
| `libs/` | CMSIS、STM32F4 HAL 和共享驱动 |
| `rt-thread/` | 随仓库维护的 RT-Thread 5.2.2 源码,不应随意做产品级改动 |
| `docs/` | 原理图、引脚表、通信协议、HMI 和调试资料 |
| `bin_pack/` | 升级包及 YModem 相关工具资料 |
## 通信接口与线程
主程序中的接口配置如下:
| 接口 | 波特率 | 作用 |
| --- | ---: | --- |
| UART1 | 115200, 8N1 | `thr.comm`:上位机 Modbus RTU 服务 |
| UART2 | 921600 | `thr.lcd`HMI 收发;RT-Thread 初始控制台配置也指向 UART2 |
| UART3 | 230400 | `thr.nor`:快充协议板接收,`thr.rollnor` 负责轮询发送 |
| UART6 | 115200 | `thr.sou`:功率板接收,`thr.rollsou` 负责轮询和批量写寄存器 |
| USB CDC | USB FS | 环境初始化后将 RT-Thread 控制台切换到 `vcom` |
六个业务线程由 `chrg_thread.c` 统一创建。接收线程通过邮箱传递已解析消息,两个轮询线程通过事件触发,公共数据由互斥锁保护。修改线程优先级、栈大小或 IPC 类型时,应同时检查实时性和内存占用。
Bootloader 与主程序的串口用途不同:UART1 以 115200 接收升级数据,UART2 以 115200 输出启动和升级日志。
## Flash 布局与启动流程
STM32F405 片内 1 MiB Flash 使用三固件分区方案:
| 分区 | 地址范围 | 大小 | 用途 |
| --- | --- | ---: | --- |
| `boot` | `0x08000000` - `0x0800FFFF` | 64 KiB | Bootloader |
| `param` | `0x08010000` - `0x0801FFFF` | 64 KiB | 参数 |
| `log` | `0x08020000` - `0x0803FFFF` | 128 KiB | 日志预留区 |
| `app` | `0x08040000` - `0x0807FFFF` | 256 KiB | 当前运行固件 |
| `download` | `0x08080000` - `0x080BFFFF` | 256 KiB | 待更新固件 |
| `factory` | `0x080C0000` - `0x080FFFFF` | 256 KiB | 出厂恢复固件 |
上电后 Bootloader 等待主机约 5 秒,检查升级请求和各分区固件状态;无升级任务且 `app` 校验通过时,设置向量表并跳转到 `0x08040000`。升级过程依次完成包头检查、擦除、写入、CRC/完整性校验和状态落盘,异常时可尝试从 `download``factory` 恢复。分区地址在 `apps/boot/application/include/app_config.h``apps/chrg/board/ports/fal_cfg.h` 和主程序链接脚本中必须保持一致。
## 开发环境
推荐环境:
- Windows 10/11
- Keil MDK 5 和对应 STM32F4 Device Pack
- Python 3、SCons,以及 ARM Compiler 5 或 `arm-none-eabi-gcc`
- RT-Thread Env(需要图形化配置时使用)。
仓库不附带编译器,`apps/chrg/rtconfig.py` 中的默认 GCC 路径为 `/usr/bin`。Windows 开发机通常需要通过 `RTT_CC``RTT_EXEC_PATH` 指定实际工具链位置。
## 编译主程序
### Keil MDK
直接打开 `apps/chrg/chrg.uvprojx`,选择 `chrg` Target 后 Build。也可从仓库根目录执行:
```powershell
& 'C:\Keil_v5\UV4\UV4.exe' -b '.\apps\chrg\chrg.uvprojx'
```
源文件或 Kconfig 发生变化后,可重新生成 Keil 工程:
```powershell
Set-Location .\apps\chrg
$env:RTT_CC = 'keil'
$env:RTT_EXEC_PATH = 'C:\Keil_v5'
scons --target=mdk5
```
### SCons
```powershell
Set-Location .\apps\chrg
$env:RTT_CC = 'gcc'
$env:RTT_EXEC_PATH = 'C:\ArmGNU\bin' # 修改为本机路径
scons -j4
```
成功后生成 `chrg.elf``rtthread.bin`。常用辅助命令:
```powershell
scons --menuconfig # 修改 RT-Thread 功能配置
scons -c # 清理 SCons 输出
scons --target=mdk5 # 重新生成 Keil MDK5 工程
```
配置入口包括 `apps/chrg/.config``defconfig``rtconfig.h``Kconfig`。修改配置后应检查生成文件差异,避免 `.config``defconfig``rtconfig.h` 长期不一致。
## 编译 Bootloader
Bootloader 当前使用独立 Keil 工程:
```powershell
& 'C:\Keil_v5\UV4\UV4.exe' -b '.\apps\boot\boot.uvprojx'
```
确认链接地址为 `0x08000000`、空间不超过 64 KiB,并在烧写前核对主程序仍链接到 `0x08040000`。不要把 `Objects/``build/``.axf``.bin``.hex``.map` 文件提交到 Git。
## 下载、调试与验证
1. 首次装机先烧写 Bootloader,再写入与 `app` 分区匹配的主固件或升级包。
2. 连接 USB CDC 后打开串口终端,可通过 MSH 查看线程、设备和内存状态;主程序提示符为 `chrg `
3. 分别验证四个通道的在线状态、通道切换、源/载输出、继电器动作和测量回读。
4. 使用上位机验证 Modbus 读写、非法地址/功能码响应以及多寄存器写入的生效时序。
5. 升级相关修改至少覆盖正常升级、CRC 错误、中途断电、无有效 App 和 Factory 恢复场景。
仓库没有产品级自动化测试或覆盖率门槛。每次修改至少应完成受影响 Target 的全量编译,并在 STM32F405 实机记录固件版本、板卡版本、串口日志和测试结果。`rt-thread/tools/testcases/` 仅用于 RT-Thread 构建工具,不代表业务固件测试。
## 配置与安全注意事项
- Bootloader 的分区、包头、CRC、加密参数必须与升级包工具保持一致;任意一侧单独修改都会导致升级失败。
- 当前 Bootloader 源码包含开发阶段的静态 AES 配置。量产前应替换为受控密钥方案,禁止在公开日志、README 或提交信息中泄露生产密钥。
- 改动 Modbus 寄存器映射时同步更新 `chrg_regs.h`、上位机、HMI 和 `docs/` 协议文件,并说明兼容策略。
- `docs/` 中可能同时存在多个版本的协议资料,调试前应核对日期、板卡版本和 Source/Sink 方向。
- 仓库根目录未提供明确的开源许可证;对外分发源码、固件或第三方组件前,请先确认项目授权和各依赖许可证。
## 参与开发
编码规范、测试要求以及提交/合并请求约定见 [AGENTS.md](AGENTS.md)。提交前请检查 `git status`,只包含本次修改所需的源码和文档;协议、链接脚本、Flash 分区及升级兼容性变化必须在评审说明中单独列出。
+9
View File
@@ -102,6 +102,15 @@ static void MX_GPIO_Init (void)
HAL_GPIO_Init(LED0_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
/* Configure the MCO1 pin in alternate function mode */
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
+19 -1
View File
@@ -262,6 +262,11 @@ BSP_UART_ERR BSP_UART_Port_DisableReceive(struct UART_STRUCT *uart)
*/
BSP_UART_ERR BSP_UART_Port_Send(struct UART_STRUCT *uart, const uint8_t *data, uint16_t len, uint16_t timeout)
{
// ================================
// ?? 485 发送模式:置 1 ??
// ================================
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
_UART_Alternate(uart);
if (timeout == 0)
@@ -272,7 +277,20 @@ BSP_UART_ERR BSP_UART_Port_Send(struct UART_STRUCT *uart, const uint8_t *data,
return (BSP_UART_ERR)HAL_UART_Transmit_IT(&uart->handle, (uint8_t *)data, len);
}
else
return (BSP_UART_ERR)HAL_UART_Transmit(&uart->handle, (uint8_t *)data, len, timeout);
{
// 发送
BSP_UART_ERR err = (BSP_UART_ERR)HAL_UART_Transmit(&uart->handle, (uint8_t *)data, len, timeout);
// 等待发送完成
while(__HAL_UART_GET_FLAG(&uart->handle, UART_FLAG_TC) == RESET);
// ================================
// ?? 485 回到接收模式:置 0 ??
// ================================
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
return err;
}
}
+2 -2
View File
@@ -16,7 +16,7 @@
<TargetCommonOption>
<Device>STM32F405RGTx</Device>
<Vendor>STMicroelectronics</Vendor>
<PackID>Keil.STM32F4xx_DFP.3.1.0</PackID>
<PackID>Keil.STM32F4xx_DFP.3.1.1</PackID>
<PackURL>https://www.keil.com/pack/</PackURL>
<Cpu>IRAM(0x20000000,0x00020000) IRAM2(0x10000000,0x00010000) IROM(0x08000000,0x00100000) CPUTYPE("Cortex-M4") FPU2 CLOCK(12000000) ELITTLE</Cpu>
<FlashUtilSpec></FlashUtilSpec>
@@ -138,7 +138,7 @@
</Flash1>
<bUseTDR>1</bUseTDR>
<Flash2>BIN\UL2CM3.DLL</Flash2>
<Flash3></Flash3>
<Flash3>"" ()</Flash3>
<Flash4></Flash4>
<pFcarmOut></pFcarmOut>
<pFcarmGrp></pFcarmGrp>
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<component_viewer schemaVersion="0.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="Component_Viewer.xsd">
<component name="EventRecorderStub" version="1.0.0"/> <!--name and version of the component-->
<events>
</events>
</component_viewer>
+12 -7
View File
@@ -24,7 +24,7 @@ struct chrg_rel_t chrgrel[TOTAL_NOR_CHS] = {
/*****************************************************************
函数名称: chrg_nor_sw_rel
函数描述: 北向继电器组 组合切换。
函数描述: 北向继电器组 组合切换。P0=1 是sinkP1=1是sourceP0和P1互斥
输入参数: ch: 通道索引 id: sink/source
输出参数: -
返回说明: -
@@ -37,11 +37,11 @@ void chrg_nor_sw_rel (eIDX_NOR_CH ch, eIDX_ID id)
}
if (ID_SOURCE == id) {
rt_pin_write(chrgrel[ch].p2, PIN_HIGH);
rt_pin_write(chrgrel[ch].p0, PIN_LOW);
rt_pin_write(chrgrel[ch].p2, PIN_HIGH);//sw置1
rt_pin_write(chrgrel[ch].p1, PIN_HIGH);
rt_pin_write(chrgrel[ch].p0, PIN_LOW);
} else if (ID_SINK == id) {
rt_pin_write(chrgrel[ch].p2, PIN_HIGH);
rt_pin_write(chrgrel[ch].p2, PIN_LOW);
rt_pin_write(chrgrel[ch].p1, PIN_LOW);
rt_pin_write(chrgrel[ch].p0, PIN_HIGH);
} else {
@@ -80,22 +80,26 @@ INIT_DEVICE_EXPORT(chrg_nor_rel_init);
int rel_test (int argc, char **argv)
{
if (argc < 3) {
rt_kprintf("help : %s <get|set> <0|1|2|3> [0, 1]", argv[0]);
rt_kprintf("help : %s <get|set> <0|1|2|3> [1, 2]", argv[0]);
return -1;
}
int idx = atoi(argv[2]);
if ((idx > 3)||(idx < 0)) {
rt_kprintf("help : %s <get|set> <0|1|2|3> [0, 1]", argv[0]);
rt_kprintf("help : %s <get|set> <0|1|2|3> [1, 2]", argv[0]);
return -1;
}
if (strcmp(argv[1], "set") == 0) {
if (argc != 4) {
rt_kprintf("help : %s <get|set> <0|1|2|3> [0, 1]", argv[0]);
rt_kprintf("help : %s <get|set> <0|1|2|3> [1, 2]", argv[0]);
return -1;
}
rt_uint8_t val = (rt_uint8_t)atoi(argv[3]);
if ((idx > 3)||(idx < 0)) {
rt_kprintf("help : %s <get|set> <0|1|2|3> [1, 2]", argv[0]);
return -1;
}
chrg_nor_sw_rel((eIDX_NOR_CH)idx, (eIDX_ID)val);
} else if (strcmp(argv[1], "get") == 0) {
rt_uint8_t data[3] = {0};
@@ -106,6 +110,7 @@ int rel_test (int argc, char **argv)
rt_kprintf("rel connect [%d] : sink\r\n", idx);
} else if ((0 == data[0])&&(1 == data[1])&&(1 == data[2])) {
rt_kprintf("rel connect [%d] : source\r\n", idx);
rt_kprintf("rel connect [%d] : p0=%d, p1=%d, p2=%d\r\n",idx, data[0], data[1], data[2]);
} else {
rt_kprintf("rel connect [%d] : p0=%d, p1=%d, p2=%d\r\n",
idx, data[0], data[1], data[2]);
+2 -1
View File
@@ -32,7 +32,8 @@ extern struct chrg_rel_t chrgrel[TOTAL_NOR_CHS];
其它说明: -
*****************************************************************/
extern void chrg_nor_sw_rel (eIDX_NOR_CH ch, eIDX_ID id);
#define Main_Relay 0x01
//#define Filter_Relay
#endif
+1 -5
View File
@@ -485,7 +485,7 @@ int chrg_tty_recv (struct chrg_tty_t *pTTY, void *buf, int size)
if ((recved & TTY_EVT_RX_BREAK) != 0) {
rt_mutex_release(pTTY->lock);
rt_thread_delay(2);
rt_thread_delay(1);
return 0;
}
}
@@ -508,7 +508,6 @@ int chrg_tty_send (struct chrg_tty_t *pTTY, void *buf, int size)
{
rt_err_t ret = RT_EOK;
int send_len = 0;
if (pTTY == RT_NULL || buf == RT_NULL || size == 0) {
LOG_E("param null.");
return (-RT_ERROR);
@@ -524,13 +523,10 @@ int chrg_tty_send (struct chrg_tty_t *pTTY, void *buf, int size)
LOG_E("mutex take failed. [%d]", ret);
return (-RT_ERROR);
}
chrg_tty_mode_set(pTTY, 1);//set to send mode
send_len = rt_device_write(pTTY->dev, 0, buf, size);
chrg_tty_mode_set(pTTY, 0);//set to receive mode
rt_mutex_release(pTTY->lock);
return send_len;
}
+3 -4
View File
@@ -26,16 +26,15 @@ typedef enum {
TOTAL_TTYS
} eIDX_TTY;
//#define TTY_USING_DMA_RX //使用DMA接收
//#define TTY_USING_DMA_RX 1 //使用DMA接收
//#define TTY_USING_INT_TX //使用中断发送
//#define TTY_USING_DMA_TX //使用DMA发送
//#define TTY_USING_DMA_TX 1 //使用DMA发送
#ifndef TTY_SW_DLY_US
#define TTY_SW_DLY_US 0 // 发送引脚控制切换延时
#endif
#define SIZE_BUF_TTY 256
#define SIZE_BUF_TTY 1029
#define TTY_TX_COMP_TMO_MAX (3 * RT_TICK_PER_SECOND) // 最大DMA传输完成超时
#define TTY_BYTE_TMO_MIN 2 // 最小字节超时
+4 -3
View File
@@ -27,17 +27,18 @@ void show_app_version (void)
int main(void)
{
//rt_uint32_t tick = 0;
// rt_uint32_t tick = 0;
#if defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)
finsh_set_prompt("chrg ");
#endif
while (1) {
rt_thread_mdelay(1000);
chrg_wdt_feed();
// chrg_wdt_feed();
//rt_kprintf("%d\r\n", ++tick);
// rt_kprintf("%d\r\n", ++tick);
}
}
File diff suppressed because it is too large Load Diff
+84 -25
View File
@@ -13,23 +13,30 @@
#include <rtthread.h>
#include "chrg_tty.h"
#include "chrg_regs.h"
#define MODBUS_ADDRESS_DEFAULT 0x01
#define BITS_UCHAR 8U
#define S_DISCRETE_INPUT_START 0
#define S_DISCRETE_INPUT_NDISCRETES 16
#define S_COIL_START 0
#define S_COIL_NCOILS 64
#define S_REG_INPUT_START 0
#define S_REG_INPUT_NREGS 100
#define S_REG_HOLDING_START 0
#define S_REG_HOLDING_NREGS 100
#define MB_HANDLERS_MAX (16)
typedef struct
{
//北向协议设置
rt_uint8_t proto_changed;
rt_uint8_t group_changed;
rt_uint16_t pro_volt_changed;
rt_uint16_t pro_curr_changed;
//南向功率设置
rt_uint8_t volt_changed;
rt_uint8_t curr_changed;
rt_uint8_t ccset_changed;
rt_uint8_t cvmode_changed;
} sink_proto_diff_t;
static void chrg_sink_get_proto_diff(struct chrg_switch_t *pReal,
struct chrg_switch_t *pShadow,
sink_proto_diff_t *pDiff,
rt_uint8_t *pNoChange);
typedef enum {
MB_REG_READ, /*!< Read register values and pass to protocol stack. */
MB_REG_WRITE /*!< Update register values. */
@@ -45,20 +52,55 @@ typedef enum {
MB_EILLSTATE, /*!< protocol stack in illegal state. */
MB_ETIMEDOUT /*!< timeout error occurred. */
} eMBErrorCode;
// ========== 新寄存器地址映射 (使用0x06/0x10标准Modbus功能码) ==========
// 详细寄存器偏移定义见 chrg_regs.h
#define MB_ADDRESS_BROADCAST (0) // 广播地址
#define MB_ADDRESS_MIN (1) // 地址最小值
#define MB_ADDRESS_MAX (247) // 地址最大值
// 判断寄存器地址属于哪个区块的宏
#define REG_IS_SINK(addr) (((addr / 4) < TOTAL_SINK_CH_REGS))
#define REG_IS_SRC(addr) (((addr / 4) < TOTAL_SRC_CH_REGS))
#define REG_IS_SYS(addr) (((addr / 4) < TOTAL_SYS_CH_REGS))
#define MB_READ_COILS (0x01) // 读线圈
#define MB_READ_DISCRETE_INPUTS (0x02) // 读离散输入
#define MB_READ_HOLDING_REGISTER (0x03) // 读保持寄存器
#define MB_READ_INPUT_REGISTER (0x04) // 读输入寄存器
#define MB_WRITE_SINGLE_COIL (0x05) // 写单个线圈
#define MB_WRITE_REGISTER (0x06) // 写单个寄存器
#define MB_WRITE_MULTIPLE_COILS (0x0F) // 写多个线圈
#define MB_WRITE_MULTIPLE_REGISTERS (0x10) // 写多个寄存器
#define MB_FUNC_ERROR (0x80)
// 根据地址提取通道号和区块内偏移
#define REG_GET_SINK_CH(addr) ((addr) / TOTAL_SINK_CH_REGS)
#define REG_GET_SINK_OFFSET(addr) ((addr) % TOTAL_SINK_CH_REGS)
#define REG_GET_SRC_CH(addr) ((addr) / TOTAL_SRC_CH_REGS)
#define REG_GET_SRC_OFFSET(addr) ((addr) % TOTAL_SRC_CH_REGS)
#define REG_GET_SYS_CH(addr) ((addr) / TOTAL_SYS_CH_REGS)
#define REG_GET_SYS_OFFSET(addr) ((addr) % TOTAL_SYS_CH_REGS)
//继电器偏移量
// 继电器寄存器偏移量
#define MODBUS_COIL_PWR_SRC 0x00 // 功率板-主电源继电器(每通道第0个)
#define MODBUS_COIL_PWR_SINK 0x01 // 功率板-主负载继电器(每通道第1个)
#define MODBUS_COIL_SINK_CC 0x02 // 协议板-负载CC继电器(每通道第2个)
#define MODBUS_COIL_SRC_CC 0x03 // 协议板-电源CC继电器(每通道第3个)
// 预留偏移量(对应每通道第4,可后续扩展)
#define MODBUS_COIL_PWR_ALL 0x04 // 功率板-俩个继电器
#define MODBUS_COIL_RESERVE2 0x05 // 预留继电器2(每通道第5个)
////复位相关
//#define RESET_NORTH_ADDR 0x0F00
//#define RESET_NORTH_VALUE 0x5A5A
//#define RESET_SOUTH_ADDR 0x0F01
#define MB_ADDRESS_BROADCAST (0) // 广播地址
#define MB_ADDRESS_MIN (1) // 地址最小值
#define MB_ADDRESS_MAX (247) // 地址最大值
// Modbus 标准功能码
#define MB_READ_COILS (0x01) // 读线圈
#define MB_READ_DISCRETE_INPUTS (0x02) // 读离散输入
#define MB_READ_HOLDING_REGISTER (0x03) // 读保持寄存器
#define MB_READ_INPUT_REGISTER (0x04) // 读输入寄存器
#define MB_WRITE_SINGLE_COIL (0x05) // 写单个线圈
#define MB_WRITE_REGISTER (0x06) // 写单个寄存器
#define MB_WRITE_MULTIPLE_COILS (0x0F) // 写多个线圈
#define MB_WRITE_MULTIPLE_REGISTERS (0x10) // 写多个寄存器
// 扩展功能码 (保留,非标准Modbus)
#define MB_YMODEM_UPDATAE (0x20) // Ymodem升级指令
#define MB_TRIM_ELOAD (0x21) // 校准源载指令
#define MB_FUNC_ERROR (0x80)
typedef enum {
MB_EX_NONE = 0x00,
@@ -79,6 +121,8 @@ typedef enum {
#define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */
#define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */
typedef eMBException(*pFunctionHandler) (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
rt_uint8_t *resFrame, rt_uint16_t *resLen);
@@ -142,6 +186,18 @@ struct code_func_t {
#define MB_PDU_FUNC_WRITE_MUL_REGCNT_MAX (0x0078)
#define MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX (0x07B0)
#define MB_YMODEM_DATA_SIZE (1029)
////升级协议状态
//typedef enum {
// Ymodem_No = 0,
// Ymodem_Wait,
// Ymodem_Rec_Start,
// Ymodem_Rec_1024,
// Ymodem_Rec_End,
// Ymodem_Rec_Finish
//} Ymodem_state;
#define THR_NAME_COMM "thr.comm"
#define DEV_NAME_COMM "uart1"
@@ -170,7 +226,10 @@ extern struct chrg_comm_t chrgcomm;
*****************************************************************/
extern void chrg_comm_thread_entry (void *data);
extern rt_uint8_t chrg_com_sink_on(rt_uint8_t ch, struct chrg_switch_t *pSW, rt_uint8_t *enable);
extern rt_uint8_t chrg_com_source_on(rt_uint8_t ch, rt_uint8_t protoCmd, struct chrg_switch_t *pSW, rt_uint8_t *enable);
extern void chrg_sink_diff_send_reg(eIDX_SOU_CH ch, sink_proto_diff_t *pDiff, struct chrg_switch_t *pReal);
#endif
+321 -148
View File
@@ -11,26 +11,25 @@
#include <stdlib.h>
#include <string.h>
#include <rtthread.h>
#include "chrg_rel.h"
#include "chrg_north_pkg.h"
#include "chrg_north.h"
#include "chrg_south.h"
#include "chrg_roll_sou.h"
#include "chrg_eload.h"
#include "chrg_utils.h"
#include "chrg_lcd.h"
#include "chrg_roll_nor.h"
#define LOG_TAG "chrg.lcd"
#define DBG_LEVEL DBG_LOG
#include <rtdbg.h>
#include "chrg_eload.h"
int Flag_R1=0,Flag_R2=0,Flag_R3=0,Flag_R4=0;
struct chrg_lcd_t chrglcd = {
.devname = DEV_NAME_LCD,
.tty = RT_NULL,
};
// 55 21 00 0D 0A 负载
// 55 21 01 0D 0A 电源
// 55 31 00 0D 0A 恒流
@@ -38,6 +37,89 @@ struct chrg_lcd_t chrglcd = {
// 55 41 00 0D 0A 停止
// 55 41 01 0D 0A 运行
void chrg_sou_work(eIDX_SOU_CH ch,struct chrg_switch_t *pSW)
{
struct chrg_south_t *pSOU = &chrgsouth;
if(pSW->id == ID_SOURCE){
chrg_set_sou_reg(ch, REG_WORK, WORK_START);
// rt_thread_mdelay(50);
pSW->source.Set_CV_Flag = 1;
pSW->source.Set_Pro_Flag = 1;
pSW->source.On_Flag = 1;
pSW->sub = IDX_SET_SOURCE_PD;
}
else if(pSW->id == ID_SINK) {
pSW->sink.On_work = 1;
pSW->sub=IDX_SET_SINK_PD;
}
pSOU->online[ch] = 1;
chrg_nor_sw_rel((eIDX_NOR_CH)ch, pSW->id);
rt_thread_mdelay(50);
}
void chrg_sou_stop(eIDX_SOU_CH ch,struct chrg_switch_t *pSW,struct chrg_north_t *pNOR)
{
struct chrg_south_t *pSOU = &chrgsouth;
if(pSOU == RT_NULL){
return;
}
if(pSW->id == ID_SOURCE){
pSW->source.Stop_step_flag = 0;
pSW->source.On_Flag = 0;
pSW->sub = IDX_GET_SOURCE_VC;
}
else if(pSW->id == ID_SINK){
pSW->sink.On_work = 0;
pSW->sub = IDX_SET_SINK_WORK_STATE;
}
pSOU->online[ch] = 0;
#if LCD_OPEN
pNOR->free_flag = 1;
#endif
}
int chrg_pro_set(struct chrg_switch_t *pSW, rt_uint8_t protocol){
if (pSW->id == ID_SOURCE) {
// Source端
pSW->source.protocol = protocol;
pSW->source.LCD_Set_Pro_Flag = 1;
} else if (pSW->id == ID_SINK) {
// Sink端
pSW->sink.protocol = protocol;
if(pSW->sink.protocol==0xC1)
{
pSW->sink.protocol = 0xFE;
}
}
return 0;
}
void chrg_set_eload_sink(eIDX_SOU_CH ch,struct chrg_switch_t *pSW){
pSW->id = ID_SINK;
//pSW->sink.protocol = 0xFE;
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_LOAD);
if(pSW->continue_flag==0){
}
}
void chrg_set_eload_source(eIDX_SOU_CH ch,struct chrg_switch_t *pSW){
pSW->id = ID_SOURCE;
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_SOURCE);
}
void chrg_set_CV(rt_uint8_t ch,struct chrg_switch_t *pSW){
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_MODE, MODE_CONSTANT_VOLTAGE);
}
void chrg_set_CC(rt_uint8_t ch,struct chrg_switch_t *pSW){
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_MODE, MODE_CONSTANT_CURRENT);
}
//设置恒压恒流模式
void chrg_roll_set_cv(eIDX_NOR_CH ch,struct chrg_switch_t *pSW,rt_uint8_t CV_flag){
if(CV_flag ==1) chrg_set_CV(ch,pSW);
else chrg_set_CC(ch,pSW);
pSW->sub = IDX_SET_SOURCE_ID;
}
/*****************************************************************
函数名称: chrg_lcd_parse
函数描述: 接收显示屏的控制消息
@@ -46,117 +128,179 @@ struct chrg_lcd_t chrglcd = {
返回说明: <0: 错误
其它说明: 本部分为 HMI 文件自定义命令,需要统一规划管理
*****************************************************************/
rt_uint8_t On_Flag = 0;
rt_uint8_t ch = 0;
static int chrg_lcd_parse (rt_uint8_t *recv, rt_uint16_t len)
{
rt_uint16_t Recv_Data;
rt_uint16_t Recv_Data;
ch = 0;
if (len < 5) {
return -1;
}
if (len < 5) {
return -1;
}
if (0x55 != recv[LCD_IDX_HEAD]) {
return -2;
}
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_switch_t *pSW = RT_NULL;
struct chrg_rollsou_t *pROLL = &chrgrollsou;
switch (recv[LCD_IDX_CMD]) {
// 变电协议
case 0x11:
case 0x12:
case 0x13:
case 0x14:{
ch = recv[LCD_IDX_CMD] - 0x11;
pSW = &pNOR->sw[ch];
chrg_pro_set(pSW,recv[2]);
break;
}
// 电源/负载切换
case 0x21:
case 0x22:
case 0x23:
case 0x24: {
ch = recv[LCD_IDX_CMD] - 0x21;
if (Flag_R1 == 1 && ch == 0) return 0;
if (Flag_R2 == 1 && ch == 1) return 0;
if (Flag_R3 == 1 && ch == 2) return 0;
if (Flag_R4 == 1 && ch == 3) return 0;
pSW = &pNOR->sw[ch];
if (1 == recv[LCD_IDX_VAL]) {
// 电源模式(Source
chrg_set_eload_source(ch,pSW);
} else {
// 负载模式(Sink
chrg_set_eload_sink(ch,pSW);
}
break;
}
// 恒压/恒流模式
case 0x31:
case 0x32:
case 0x33:
case 0x34: {
ch = recv[LCD_IDX_CMD] - 0x31;
if (Flag_R1 == 1 && ch == 0) return 0;
if (Flag_R2 == 1 && ch == 1) return 0;
if (Flag_R3 == 1 && ch == 2) return 0;
if (Flag_R4 == 1 && ch == 3) return 0;
if (1 == recv[LCD_IDX_VAL]) {
chrg_set_CV((eIDX_SOU_CH)ch,pSW);
} else {
chrg_set_CC((eIDX_SOU_CH)ch,pSW);
}
break;
}
//运行停止
case 0x41:
case 0x42:
case 0x43:
case 0x44: {
ch = recv[LCD_IDX_CMD] - 0x41;
pSW = &pNOR->sw[ch];
//运行
if (1 == recv[LCD_IDX_VAL]) {
switch(ch) {
case 0: Flag_R1 = 1; break;
case 1: Flag_R2 = 1; break;
case 2: Flag_R3 = 1; break;
case 3: Flag_R4 = 1; break;
}
pNOR->enable[ch] = 1;
rt_kprintf("ch=%d\n",ch);
chrg_sou_work((eIDX_SOU_CH)ch,pSW);
pSW->continue_flag = 0;
}
//关闭
else {
switch(ch) {
case 0: Flag_R1 = 0; break;
case 1: Flag_R2 = 0; break;
case 2: Flag_R3 = 0; break;
case 3: Flag_R4 = 0; break;
}
chrg_sou_stop((eIDX_SOU_CH)ch,pSW,pNOR);
}
break;
}
// CV电压值设定
case 0x51:
case 0x52:
case 0x53:
case 0x54: {
ch = recv[LCD_IDX_CMD] - 0x51;
Recv_Data = recv[LCD_IDX_VAL+1]<<8 | recv[LCD_IDX_VAL]; // 拼接16位电压值
pSW = &pNOR->sw[ch];
// 按角色存入source/sink
pSW->source.vc.set_voltage = Recv_Data; // 电源侧电压(mV
pSW->sink.loadv = Recv_Data; // 负载侧电压(mV,)
pSW->source.Set_Pro_Flag = 1;
// 写入电压寄存器
if(pSW->id==ID_SINK) {
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, Recv_Data);
}
else chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, 5000);
break;
}
if (0x55 != recv[LCD_IDX_HEAD]) {
return -2;
}
rt_uint8_t ch = 0;
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_switch_t *pSW = RT_NULL;
switch (recv[LCD_IDX_CMD]) {
case 0x21:
case 0x22:
case 0x23:
case 0x24: { // 电源/负载
ch = recv[LCD_IDX_CMD] - 0x21;
if (Flag_R1 == 1 && ch == 0) return 0;
if (Flag_R2 == 1 && ch == 1) return 0;
if (Flag_R3 == 1 && ch == 2) return 0;
if (Flag_R4 == 1 && ch == 3) return 0;
pSW = &pNOR->sw[ch];
if (1 == recv[LCD_IDX_VAL]) {
pSW->id = ID_SOURCE;
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_SOURCE);
} else {
pSW->id = ID_SINK;
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_LOAD);
}
}
break;
case 0x31:
case 0x32:
case 0x33:
case 0x34: { // 恒压/恒流
ch = recv[LCD_IDX_CMD] - 0x31;
if (Flag_R1 == 1 && ch == 0) return 0;
if (Flag_R2 == 1 && ch == 1) return 0;
if (Flag_R3 == 1 && ch == 2) return 0;
if (Flag_R4 == 1 && ch == 3) return 0;
if (1 == recv[LCD_IDX_VAL]) {
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_MODE, MODE_CONSTANT_VOLTAGE);
} else {
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_MODE, MODE_CONSTANT_CURRENT);
}
}
break;
case 0x41:
case 0x42:
case 0x43:
case 0x44: { // 运行/停止
ch = recv[LCD_IDX_CMD] - 0x41;
if (1 == recv[LCD_IDX_VAL]) {
//pNOR->enable[ch] = 0;
//pSOU->enable[ch] = 0;
switch(ch) {
case 0: Flag_R1 = 1; break;
case 1: Flag_R2 = 1; break;
case 2: Flag_R3 = 1; break;
case 3: Flag_R4 = 1; break;
// CC电流值设定
case 0x61:
case 0x62:
case 0x63:
case 0x64: {
ch = recv[LCD_IDX_CMD] - 0x61;
Recv_Data = recv[LCD_IDX_VAL+1]<<8 | recv[LCD_IDX_VAL]; // 拼接16位电流值
pSW = &pNOR->sw[ch];
//按角色存入source/sink
pSW->source.vc.set_current = Recv_Data; // 电源侧电流(mA
pSW->sink.loadc = Recv_Data; // 负载侧电流(mA
pSW->source.Set_Pro_Flag = 1;
// rt_kprintf("cur=%d",Recv_Data);
// 写入电流寄存器
for(int i = 0;i<10;i++){
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_CURRENT_OUT+1, Recv_Data);
}
break;
}
//cc线设置
case 0x81:
case 0x82:
case 0x83:
case 0x84:{
ch = recv[LCD_IDX_CMD] - 0x81;
Recv_Data = recv[2];
pSW = &pNOR->sw[ch];
// rt_kprintf("data=%d",Recv_Data);
// rt_kprintf("ID=%d\r\n",pSW->id);
// if(pSW->id==ID_SINK){
// pSW->sub = IDX_SET_SINK_CCLINE;
// pSW->sink.cc_set = Recv_Data; // CC线开关设置
// }
switch(Recv_Data){
case 0:
chrg_set_sou_reg(ch, REG_REL_ON, ALL_REL_ON);
break;
case 1:
chrg_set_sou_reg(ch, REG_REL_ON, FU_REL_ON);
break;
case 2:
chrg_set_sou_reg(ch, REG_WORK, WORK_START);
break;
case 3:
chrg_set_sou_reg(ch, REG_WORK, WORK_START);
break;
}
break;
}
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_START);
} else {
//pNOR->enable[ch] = 1;
//pSOU->enable[ch] = 1;
switch(ch) {
case 0: Flag_R1 = 0; break;
case 1: Flag_R2 = 0; break;
case 2: Flag_R3 = 0; break;
case 3: Flag_R4 = 0; break;
}
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_STOP);
}
}
break;
case 0x51:
case 0x52:
case 0x53:
case 0x54: { //CV电压值设定
ch = recv[LCD_IDX_CMD] - 0x51;
Recv_Data = recv[LCD_IDX_VAL+1]<<8|recv[LCD_IDX_VAL];//get lower Reg_Data
//Recv_Data = swap_u16(Recv_Data);
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, Recv_Data);//plus 1set voltage lower REG
}
break;
case 0x61:
case 0x62:
case 0x63:
case 0x64:{ //CC电流值设定
ch = recv[LCD_IDX_CMD] - 0x61;
Recv_Data = recv[LCD_IDX_VAL+1]<<8|recv[LCD_IDX_VAL];//get lower Reg_Data
//Recv_Data = swap_u16(Recv_Data);
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_CURRENT_OUT+1,Recv_Data);//plus 1set current lower REG
break;
}
}
return 0;
}
return 0;
}
/*****************************************************************
函数名称: chrg_lcd_send
函数描述: 向显示屏发送控制消息
@@ -165,39 +309,69 @@ static int chrg_lcd_parse (rt_uint8_t *recv, rt_uint16_t len)
返回说明: 0:发送完成 <0: 发送失败
其它说明: 参考《淘晶驰串口屏开发手册》http://wiki.tjc1688.com/
*****************************************************************/
int chrg_lcd_send (char *cmd)
{
rt_ssize_t ret = -1;
struct chrg_tty_t *pTTY = chrglcd.tty;
if (RT_NULL == cmd) {
return -1;
}
int chrg_lcd_send (char *cmd)
{
rt_ssize_t ret = -1;
struct chrg_tty_t *pTTY = chrglcd.tty;
if (RT_NULL == cmd) {
return -1;
}
int len = rt_strlen(cmd);
rt_uint8_t *data = rt_malloc(len+3);
if (RT_NULL == data) {
LOG_E("malloc failed.");
return -1;
}
int len = rt_strlen(cmd);
rt_uint8_t *data = rt_malloc(len+3);
if (RT_NULL == data) {
LOG_E("malloc failed.");
return -1;
}
rt_memcpy(data, cmd, len);
rt_memset(&data[len], 0xFF, 3);
rt_memcpy(data, cmd, len);
rt_memset(&data[len], 0xFF, 3);
// for(int i=0;i<len+3;i++){
// rt_kprintf("%02x ",data[i]);
// }
if (RT_NULL != pTTY) {
ret = chrg_tty_send(pTTY, data, len+3);
// LOG_HEX("lcdS", 32, data, len+3);
}
if (RT_NULL != pTTY) {
ret = chrg_tty_send(pTTY, data, len+3);
// LOG_HEX("lcdS", 32, data, len+3);
}
if (RT_NULL != data) {
rt_free(data);
data = RT_NULL;
}
//rt_thread_mdelay(5);
if (RT_NULL != data) {
rt_free(data);
data = RT_NULL;
}
return ret;
}
// static rt_uint8_t lcd_send_buf[64] = {0};
rt_thread_mdelay(20);
// int chrg_lcd_send (char *cmd)
// {
// rt_ssize_t ret = -1;
// struct chrg_tty_t *pTTY = chrglcd.tty;
// // 1. 入参校验
// if (RT_NULL == cmd || RT_NULL == pTTY) {
// return -1;
// }
return ret;
}
// // 2. 计算指令长度(避免malloc,复用全局缓冲区)
// int len = rt_strlen(cmd);
// // 安全检查:避免缓冲区溢出(最长支持60字符,远大于你的36字符)
// if (len > 60) {
// LOG_E("cmd too long: %d", len);
// return -2;
// }
// // 3. 填充指令+补齐3个FF(复用全局缓冲区)
// rt_memcpy(lcd_send_buf, cmd, len);
// rt_memset(&lcd_send_buf[len], 0xFF, 3);
// // 4. 发送数据(无内存分配,直接发送)
// ret = chrg_tty_send(pTTY, lcd_send_buf, len+3);
// // 5. 无需free(全局缓冲区复用)
// return ret;
// }
/*****************************************************************
函数名称: chrg_lcd_thread_entry
@@ -207,10 +381,11 @@ int chrg_lcd_send (char *cmd)
返回说明: -
其它说明: -
*****************************************************************/
int delay_cnt = 0; // 延迟计数器
void chrg_lcd_thread_entry (void *data)
{
struct chrg_thread_t *pTHR = (struct chrg_thread_t *)data;
static uint8_t number = 0;
if (RT_NULL == pTHR) {
return ;
}
@@ -218,7 +393,7 @@ void chrg_lcd_thread_entry (void *data)
int len = 0;
struct chrg_tty_t *pTTY = RT_NULL;
struct chrg_lcd_t *pLCD = &chrglcd;
pTTY = chrg_tty_create(pLCD->devname, 115200, 0, -1, 1);
pTTY = chrg_tty_create(pLCD->devname, 921600, 0, -1, 1);
if (RT_NULL == pTTY) {
LOG_E("tty can't create.");
return ;
@@ -235,18 +410,16 @@ void chrg_lcd_thread_entry (void *data)
while (1) {
rt_memset(pLCD->rx_buf, 0, SIZE_BUF_TTY);
len = chrg_tty_recv(pTTY, pLCD->rx_buf, sizeof(pLCD->rx_buf));
if (len <= 0) {
rt_thread_mdelay(1);
continue;
}
// LOG_HEX("lcdR", 32, pLCD->rx_buf, len);
if (len >= 5) {
chrg_lcd_parse(pLCD->rx_buf, len);
}
if (len <= 0) {
rt_thread_mdelay(1);
delay_cnt++;
continue;
}
// LOG_HEX("lcdR", 32, pLCD->rx_buf, len);
if (len >= 5) {
chrg_lcd_parse(pLCD->rx_buf, len);
}
}
}
+11 -1
View File
@@ -13,7 +13,8 @@
#include <rtthread.h>
#include "chrg_tty.h"
#include "chrg_north.h"
#include "chrg_south.h"
#define THR_NAME_LCD "thr.lcd"
#define DEV_NAME_LCD "uart2"
@@ -41,7 +42,16 @@ extern int chrg_lcd_send (char *cmd);
extern void chrg_lcd_thread_entry (void *data);
extern rt_uint8_t On_Flag;
extern rt_uint8_t Flag_start;
extern void chrg_sou_work(eIDX_SOU_CH ch,struct chrg_switch_t *pSW);
extern void chrg_sou_stop(eIDX_SOU_CH ch,struct chrg_switch_t *pSW,struct chrg_north_t *pNOR);
extern void chrg_set_eload_sink(eIDX_SOU_CH ch,struct chrg_switch_t *pSW);
extern void chrg_set_eload_source(eIDX_SOU_CH ch,struct chrg_switch_t *pSW);
extern void chrg_set_CV(rt_uint8_t ch,struct chrg_switch_t *pSW);
extern void chrg_set_CC(rt_uint8_t ch,struct chrg_switch_t *pSW);
extern void chrg_roll_set_cv(eIDX_NOR_CH ch,struct chrg_switch_t *pSW,rt_uint8_t CV_flag);
#endif
+361 -39
View File
@@ -10,7 +10,7 @@
#include <string.h>
#include <stdlib.h>
#include <rtthread.h>
#include "chrg_eload.h"
#include "chrg_gpio.h"
#include "chrg_thread.h"
#include "chrg_north_pkg.h"
@@ -19,31 +19,328 @@
#include "chrg_switch.h"
#include "chrg_utils.h"
#include "ulog.h"
#include "chrg_rel.h"
//#define LOG_TAG "chrg.nor"
#define DBG_LEVEL DBG_LOG
#include <rtdbg.h>
rt_uint16_t volt;
uint16_t chrg_compare(rt_int16_t a, rt_int16_t b)
{
if(a>b) return a - b;
else return b - a;
}
/*****************************************************************
函数名称: chrg_source_stop
功能描述: 南向功率源停止流程控制函数(分步执行停止操作)
函数描述: 通过状态机分步执行南向功率停止流程:停止功率输出 -> 关闭北向继电器 -> 清空控制标志并禁用使能
输入参数:
frame_data :原始数据帧指针,用于提取当前电压值
pSW :北向通道控制结构体指针
idx :通道编号(功率通道/北向通道索引)
enable :北向通道使能,停止后关掉使能
返回说明: 无返回值
*****************************************************************/
void chrg_source_stop(rt_uint8_t *frame_data,struct chrg_switch_t *pSW,rt_uint8_t idx,rt_uint8_t *enable){
switch(pSW->source.Stop_step_flag){
case 0: // 停止工作
chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_WORK, WORK_STOP);
rt_thread_mdelay(50);
rt_uint16_t volt_mv_Now = (frame_data[4] << 8) | frame_data[5];
volt = volt_mv_Now;
if(volt_mv_Now < 1000){
pSW->source.Stop_step_flag = 1;
}
break;
case 1: // 关闭北向继电器
chrg_nor_sw_rel((eIDX_NOR_CH)idx,0);
rt_thread_mdelay(50);
pSW->source.Stop_step_flag = 2;
break;
case 2: // 状态2:已执行完停止操作,清空标志位
pSW->source.Set_CV_Flag = 0;
*enable = 0;
break;
default:
break;
}
}
/*****************************************************************
函数名称: chrg_source_standard_output
功能描述: 南向功率源标准输出控制函数
函数描述: 根据CV标志位,分别设置电源输出电压或输出电流,实现标准模式下的恒压/恒流输出
输入参数:
idx :功率通道编号
pSW :北向通道控制结构体指针,包含电压/电流设定值、CV控制标志等参数
输出参数:
pSW :更新CV控制标志位
返回说明: 无返回值
*****************************************************************/
void chrg_source_standard_output(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
if(pSW->source.Set_CV_Flag) {
chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_VOLTAGE_OUT+1, pSW->source.max_vol);
#if DEBUG_NORTH
rt_kprintf("Volt_Set%d\r\n",pSW->source.vc.set_voltage);
#endif
pSW->source.Set_CV_Flag = 1;
pSW->change_flag = 0;
}
// if(pSW->source.Set_CV_Flag==0){
// chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_CURRENT_OUT+1, pSW->source.max_vol);
//#if DEBUG_NORTH
// rt_kprintf("Curr_set%d\r\n",Curr_mA);
//#endif
// pSW->source.Set_CV_Flag = 0;
// }
}
//电源快充
void chrg_source_fast_output(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
{
struct chrg_rollsou_t *pROLL = &chrgrollsou;
rt_uint16_t volt_mv = (frame_data[8] << 8) | frame_data[9];
rt_uint16_t Curr_mA = (frame_data[10] << 8) | frame_data[11];
rt_uint16_t volt_mv_Now = (frame_data[4] << 8) | frame_data[5];
rt_uint16_t Curr_mA_Now = (frame_data[6] << 8) | frame_data[7];
rt_uint16_t volt_compare = chrg_compare(volt_mv, volt_mv_Now);
rt_uint16_t Curr_compare = chrg_compare(Curr_mA, Curr_mA_Now);
static rt_uint8_t number = 0;
#if LCD_OPEN
// rt_kprintf("volt_mv=%d,volt_mv_Now=%d,compare=%d\r\n",volt_mv,volt_mv_Now,volt_compare);
if(volt_compare<5000){ //比较设定值与实际值差距,小于2V认为达成设定,允许屏幕开始变换
number++;
number = number%10;
if(number == 9)
{
pSW->change_flag = 0;
}
}else { //没有达成设定,屏幕不允许变换
pSW->change_flag = 0x01;
}
#endif
// if(volt_mv_Now==0){
// chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_WORK, WORK_START);
// }
// else{
if (pSW->source.Set_CV_Flag == 0){
{
chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_VOLTAGE_OUT+1, volt_mv);
}
pSW->source.Set_CV_Flag = 1;
#if DEBUG_NORTH
rt_kprintf("Volt_Set%d\r\n",volt_mv);
#endif
}
else if(pSW->source.Set_CV_Flag==1){
chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_WORK, WORK_START);
pSW->source.Set_CV_Flag = 0;
}else{
pSW->source.Set_CV_Flag=0;
}
}
//电源设置
void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
{
//执行输出调整
if (pSW->source.On_Flag == 1) {
if(pSW->source.protocol==0x00){//标准下输出设定值
chrg_source_standard_output(idx, pSW);
}
else{ //快充部分按协商结果输出
chrg_source_fast_output(frame_data, idx, pSW, pNOR);
// rt_kprintf("set_end\r\n");
}
}else if(pSW->source.On_Flag == 0){
//chrg_source_stop(frame_data,pSW,idx,&pNOR->enable[idx]);
pSW->sub = IDX_SET_SOURCE_STOP_STATE;
}
}
////解析负载返回数据
//void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
// struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
//{
// rt_uint16_t volt_mv = ((frame_data[4] << 8) | frame_data[5])*10;
// static uint8_t volt_low[TOTAL_SOU_CHS] = {0};
// static uint8_t no_curr[TOTAL_SOU_CHS] = {0};
// pSW->VZ = ((frame_data[14] << 8) | frame_data[15]);
// pSW->VF = ((frame_data[16] << 8) | frame_data[17]);
//#if DEBUG_NORTH
// LOG_D("Sink[%d] real volt: %d mV", idx, volt_mv);
//#endif
//
// if(pSW->sink.On_work == 1) {
// if(pSW->sink.test.test_mode == 0){
// if (volt_mv <= WORK_MIN_VOLT) { // 电压过低,禁止进入工作状态机
// volt_low[idx]++;
// //if(volt_low[idx]>=5) pSW->sink.work_mode = SINK_WORK_LOW; // 复位工作状态机
// }
// else{ // 电压稳定时执行工作状态机(继电器/工作模式切换)
// volt_low[idx] = 0;
// pSW->change_flag = 0;
// if((pSW->sink.pro_loadv - volt_mv) <= 2000){
// pSW->sink.work_mode = SINK_WORK_RUNING;
// if(pSW->Now_current<=200){
// if(no_curr[idx]++>=5){
// pSW->sink.work_mode = SINK_WORK_WAIT;
// no_curr[idx] = 0;
// }
// }
// else no_curr[idx] = 0;
// }
// }
// }
// }
// else{
// volt_low[idx] = 0;
//// pSW->sink.work_mode = SINK_WORK_STOP;
// }
// chrg_sink_work(idx,pSW);
//}
//解析负载返回数据
void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
{
rt_uint16_t volt_mv = ((frame_data[4] << 8) | frame_data[5])*10;
static uint8_t volt_low[TOTAL_SOU_CHS] = {0};
static uint8_t no_curr[TOTAL_SOU_CHS] = {0};
pSW->VZ = ((frame_data[14] << 8) | frame_data[15]);
pSW->VF = ((frame_data[16] << 8) | frame_data[17]);
#if DEBUG_NORTH
LOG_D("Sink[%d] real volt: %d mV", idx, volt_mv);
#endif
if(pSW->sink.On_work == 1 && pSW->sink.test.test_mode == 0) {
if (volt_mv <= WORK_MIN_VOLT) { // 电压过低,禁止进入工作状态机
// volt_low[idx]++;
// if(volt_low[idx]>=5) pSW->sink.work_on_step = SINK_WORK_LOW; // 复位工作状态机
}
else{ // 电压稳定时执行工作状态机(继电器/工作模式切换)
volt_low[idx] = 0;
pSW->change_flag = 0;
if((pSW->sink.pro_loadv - volt_mv) <= 2000){
pSW->sink.work_mode = SINK_WORK_RUNING;
if(pSW->Now_current<=200){
if(no_curr[idx]++>=5){
pSW->sink.work_mode = SINK_WORK_WAIT;
no_curr[idx] = 0;
}
}
else no_curr[idx] = 0;
}
}
}
else{
volt_low[idx] = 0;
// pSW->sink.work_on_step = SINK_WORK_STOP; // 复位工作状态机
}
chrg_sink_work(idx,pSW);
}
/*****************************************************************
函数名称: chrg_north_handle_Seting_Volt
功能描述: 处理设置电压/电流命令,解析命令数据并执行相应操作
****************************************************/
rt_uint8_t change_number = 0;
static void chrg_north_handle_Seting_Volt(rt_uint8_t *frame_data, rt_uint16_t frame_len)
{
// 提取电压/电流值
struct chrg_north_t *pNOR = &chrgnorth;
rt_uint16_t idx = pNOR->idx;
struct chrg_switch_t *pSW = &chrgnorth.sw[idx];
static uint8_t number = 0;
//电源部分
if(pSW->id==ID_SOURCE){
chrg_source_setting(frame_data,(eIDX_SOU_CH)idx, pSW, pNOR);
}
else{
chrg_sink_parse(frame_data,(eIDX_SOU_CH)idx, pSW, pNOR);
}
}
struct chrg_north_t chrgnorth = {
.devname = DEV_NAME_NORTH,
.tty = RT_NULL,
.enable = {1, 1, 1, 1},
.sw[IDX_NOR_CH1] = {
.id = ID_SINK,
},
.sw[IDX_NOR_CH2] = {
.id = ID_SINK,
},
.sw[IDX_NOR_CH3] = {
.id = ID_SINK,
},
.sw[IDX_NOR_CH4] = {
.id = ID_SINK,
},
.devname = DEV_NAME_NORTH, // 设备名
.tty = RT_NULL, // 串口句柄初始为空
.enable = {0, 0, 0, 0}, // 4个通道使能初始关闭
.free_flag = 1,
// 通道1IDX_NOR_CH1)初始化
.sw[IDX_NOR_CH1] = {
.CV_mode = 2,
.change_flag = 0,
.show_step = 0,
.id = ID_SINK, // 初始为负载模式
.source = { // 电源侧参数初始化
.On_Flag = 0, // 启动标志初始关闭
.Set_CV_Flag = 1, // 启停控制初始置1
.Set_Pro_Flag = 1 // 电压/电流切换初始置1
}, // 关键:补充逗号分隔source和sink
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
},
// 通道2IDX_NOR_CH2)初始化
.sw[IDX_NOR_CH2] = {
.show_step = 0,
.id = ID_SINK,
.CV_mode = 2,
.change_flag = 0,
.source = {
.On_Flag = 0,
.Set_CV_Flag = 1,
.Set_Pro_Flag = 1
},
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
},
// 通道3IDX_NOR_CH3)初始化
.sw[IDX_NOR_CH3] = {
.show_step = 0,
.CV_mode = 2,
.id = ID_SINK,
.change_flag = 0,
.source = {
.On_Flag = 0,
.Set_CV_Flag = 1,
.Set_Pro_Flag = 1
},
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
},
// 通道4IDX_NOR_CH4)初始化
.sw[IDX_NOR_CH4] = {
.show_step = 0,
.CV_mode = 2,
.id = ID_SINK,
.change_flag = 0,
.source = {
.On_Flag = 0,
.Set_CV_Flag = 1,
.Set_Pro_Flag = 1
},
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
}
};
/*****************************************************************
函数名称: chrg_north_switch
函数描述: 北向通道选择
@@ -76,7 +373,9 @@ int chrg_north_send (rt_uint8_t *data, rt_size_t length)
struct chrg_tty_t *pTTY = chrgnorth.tty;
if (RT_NULL != pTTY) {
//LOG_HEX("sendN", 32, data, length);
#if DEBUG_NORTH //是否打印
// LOG_HEX("sendN", 32, data, length);
#endif
ret = chrg_tty_send(pTTY, data, length);
}
@@ -100,14 +399,30 @@ static int chrg_north_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eI
rt_uint8_t len_frame = 0; // 帧长度
if (RT_NULL == rb) {
rt_kprintf("len_rb=NULL%d\r\n",len_rb);
return -1;
}
len_rb = rt_ringbuffer_data_len(rb);
if (len_rb < LEN_FRAME_HEAD + 2) { // 最短5字节
return -1;
}
if (len_rb < LEN_FRAME_HEAD + 2) { // 最短5字节
// =============================
// 简单版:打印错误帧 + 清空缓冲区
// =============================
rt_kprintf("[ERROR] 帧长度不足,len=%d, data: ", len_rb);
// 把所有数据读出来、打印、同时清空
rt_uint8_t dummy;
while (rt_ringbuffer_getchar(rb, &dummy) == RT_EOK)
{
rt_kprintf("%02x ", dummy); // 打印每一个字节
}
rt_kprintf("\r\n");
return -1;
}
for (i = 0; i < len_rb - 2; i++) { // 找头三个字节 [帧长, 0x00, id]
ret = rt_ringbuffer_getchar(rb, &data);
if (0 == ret) {
@@ -119,6 +434,7 @@ static int chrg_north_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eI
head[0] = data;
len_frame = data;
if (data < MIN_FRAME_LENGTH) { // 长度不符
stage = 0;
continue;
}
@@ -140,6 +456,13 @@ static int chrg_north_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eI
if ((rt_uint8_t)id != data) {
stage = 0;
} else {
// if (id == ID_SINK) {
// rt_uint8_t ext1, ext2;
// // 主动读取并丢弃2字节扩展字段
// rt_ringbuffer_getchar(rb, &ext1);
// rt_ringbuffer_getchar(rb, &ext2);
// rt_kprintf("Sink:%02X %02X\r\n", ext1, ext2);
// }
return len_frame;
}
break;
@@ -162,28 +485,29 @@ static int chrg_north_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eI
返回说明: <0: 错误 >0:帧长度
其它说明: -
*****************************************************************/
int a_number;
static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
struct pickup_info_t *pFrame, eIDX_NOR_CH idx)
{
int cnt = 0;
rt_size_t len_rb = 0;
if ((RT_NULL == rb)||(RT_NULL == pFrame)) {
return -1;
}
struct chrg_switch_t *pSW = &chrgnorth.sw[idx];
//rt_kprintf("north_idx=%d\r\n",idx);
do {
if (0 == pFrame->findhead) {
//pFrame->len_msg = chrg_north_frame_head(pSW->id,rb, pFrame->head, pSW->id);
pFrame->len_msg = chrg_north_frame_head(rb, pFrame->head, pSW->id);
// rt_kprintf("msg=:%d",pFrame->len_msg);
if (pFrame->len_msg > 0) {
pFrame->pMB = (struct mb_msg_t *)rt_malloc(sizeof(struct mb_msg_t));
if (RT_NULL == pFrame->pMB) {
LOG_E("malloc mb failed.");
return -2;
}
pFrame->pMB->length = pFrame->len_msg;
if (pFrame->pMB->length >= SIZE_BUF_TTY) {
rt_free(pFrame->pMB);
@@ -191,23 +515,23 @@ static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
LOG_E("too long msg.");
return -3;
}
pFrame->pMB->payload = (rt_uint8_t *)rt_malloc(pFrame->len_msg);
if (RT_NULL == pFrame->pMB->payload) {
LOG_E("malloc buf failed.");
return -4;
}
rt_memcpy(pFrame->pMB->payload, pFrame->head, LEN_FRAME_HEAD);
len_rb = rt_ringbuffer_data_len(rb);
// rt_kprintf("len_rb=%d\r\n",len_rb);
// rt_kprintf("len=%d",pFrame->len_msg - LEN_FRAME_HEAD);
if (len_rb >= pFrame->len_msg - LEN_FRAME_HEAD) {
rt_ringbuffer_get(rb, &pFrame->pMB->payload[LEN_FRAME_HEAD],
pFrame->len_msg - LEN_FRAME_HEAD);
if(pFrame->pMB->length==21){
chrg_north_handle_Seting_Volt(pFrame->pMB->payload, pFrame->pMB->length);
}
chrg_thread_mb_send(IDX_THR_NORTH, pFrame->pMB);
//chrg_north_pkg_decode(pFrame->pMB->payload, pFrame->len_msg, pSW);
pFrame->length = 0;
pFrame->len_msg = 0;
cnt++;
@@ -219,7 +543,6 @@ static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
break;
}
}
} else {
len_rb = rt_ringbuffer_data_len(rb);
int len_need = pFrame->len_msg - LEN_FRAME_HEAD;
@@ -240,8 +563,8 @@ static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
break;
}
}
len_rb = rt_ringbuffer_data_len(rb);
} while (len_rb >= LEN_FRAME_HEAD);
return cnt;
@@ -266,16 +589,16 @@ void chrg_north_thread_entry (void *data)
int len = 0;
struct chrg_tty_t *pTTY = RT_NULL;
struct chrg_north_t *pNOR = &chrgnorth;
//北向协议板线程体通道初始化为CH1,
chrg_north_switch(IDX_NOR_CH1);
pTTY = chrg_tty_create(pNOR->devname, BAUD_RATE_921600, 0, -1, 1);
pTTY = chrg_tty_create(pNOR->devname, 230400, 0, -1, 1);
if (RT_NULL == pTTY) {
LOG_E("tty can't create.");
return ;
}
chrg_tty_set_recv_tmo(pTTY, 100);
chrg_tty_set_recv_tmo(pTTY, 10);
if (chrg_tty_connect(pTTY) != RT_EOK) {
chrg_tty_destory(pTTY);
return ;
@@ -298,8 +621,9 @@ void chrg_north_thread_entry (void *data)
}
rt_ringbuffer_put(pNOR->rb, pNOR->rx_buf, len);
#if DEBUG_NORTH
LOG_HEX("recvN", 32, pNOR->rx_buf, len);
#endif
if (1 == pNOR->frame.findhead) {
if (rt_tick_get() >= pNOR->frame.last_tick + 100) { // 100ms
pNOR->frame.findhead = 0; // reset
@@ -309,7 +633,6 @@ void chrg_north_thread_entry (void *data)
chrg_north_pickup_frame(pNOR->rb, &pNOR->frame, pNOR->idx);
}
}
@@ -333,4 +656,3 @@ MSH_CMD_EXPORT(north_ch, north channel select);
#endif
+69 -8
View File
@@ -11,9 +11,8 @@
#define __CHRG_NORTH_H__
#include <rtthread.h>
#include "chrg_tty.h"
#include "chrg_source.h"
#include "chrg_tty.h"
#include "chrg_sink.h"
#include "chrg_def.h"
@@ -22,15 +21,21 @@
#define LEN_FRAME_HEAD (3) // [帧长, 0x00, id]
#define MIN_FRAME_LENGTH (5) // 帧最短5字节
#define WORK_MIN_VOLT 3000 // 工作电压下限,单位mV
#define DEBUG_NORTH 0x00 // 0 = 关闭,1 = 开启
#define LCD_OPEN 0x01 // 0为关闭屏幕显示 1为打开
#define TRIM_NORTH 0x00 // 0 = 关闭, 1 = 开启
#define SINK_TEST_DEBUG 0x00 // 0 = 关闭,1 = 开启
#define PRO_TEST_DEBUG 0x00 //
#define DEBUG_SOUTH 0x00 // 0 = 关闭,1 = 开启
// 北向4通路索引 U14 ->
typedef enum {
IDX_NOR_CH1 = IDX_CH1, // J3
IDX_NOR_CH1 = IDX_CH1, // J3
IDX_NOR_CH2 = IDX_CH2, // J4
IDX_NOR_CH3 = IDX_CH3, // J9
IDX_NOR_CH4 = IDX_CH4, // J10
TOTAL_NOR_CHS
TOTAL_NOR_CHS
} eIDX_NOR_CH;
@@ -38,6 +43,60 @@ struct chrg_switch_t {
eIDX_ID id;
struct chrg_src_t source;
struct chrg_sink_t sink;
rt_uint8_t protoCmd; // 协议类型
rt_uint8_t sub; //状态机步骤
rt_uint8_t show_step; // 显示步骤标志位,0-各项参数,1-显示电压电流,
rt_uint8_t continue_flag;
rt_uint8_t CV_mode; //恒压恒流
rt_uint8_t change_flag; //正在调整功率标志位
rt_uint16_t Now_voltage; // 当前电压 mV
rt_uint16_t Now_current; // 当前电流 mA
rt_uint16_t VZ;
rt_uint16_t VF;
};
//校准结构体
//#define ModbusRTU_Trim_Start 0x01 //进入校准模式
//#defin ModbusRTU_Trim_Stop 0x02 //退出校准模式
#define ModbusRTU_Volt_CAL_K_H 0x2C //2C~2D 电压校准K值,被放大Q16倍,再移动16回去;使用两个字节
#define ModbusRTU_Volt_CAL_K_L 0x2D
#define ModbusRTU_Volt_CAL_B 0x2E //2E 电压校准B值
#define ModbusRTU_Current_CAL_K_H 0x2F //2F-30 电流校准K值
#define ModbusRTU_Current_CAL_K_L 0x30
#define ModbusRTU_Current_CAL_B 0x31 //31 电流校准B值
#define ModbusRTU_SourceCurrent_CAL_K_H 0x32 //32-33 电流校准K值
#define ModbusRTU_SourceCurrent_CAL_K_L 0x33
#define ModbusRTU_SourceCurrent_CAL_B 0x34 //34 电流校准B值
#define ModbusRTU_Trim_Zero_CAL 0x35 //35 校准零点,写入后会将电压电流都校准到0,读取时会返回当前的零点值
#define ModbusRTU_Trim_CRC_CAL 0x36 //36 校准CRC校验,写入后会将之前的校准数据生效,读取时会返回当前的CRC校验值
#define ModbusRTU_CAL_WriteEnable_ADD 0x37 //使能写入FLASH的地址
#define ModbusRTU_CAL_WriteValue 0x4361 //写入FALSH的魔数
#define ModbusRTU_CAL_WriteRecovery 0x38 //使能写入出厂数据区域;0x5763
#define ModbusRTU_CAL_WriteRecovery_Value 0x5763 //写入FALSH的魔数
#define ModbusRTU_CAL_ReadRecovery 0x39 //使能读出;将出厂数据读到当前的bank中,并更新6个参数
#define MODE_VOLT_CAL 0x01
#define MODE_CURR_CAL 0x02
#define MODE_SOURCE_CURR_CAL 0x03
#define MODE_ZERO_CAL 0x04
struct chrg_trim {
rt_uint16_t trim_volt_k_H; // 校准电压值高16位 mV
rt_uint16_t trim_volt_k_L; // 校准电压值低16位 mV
rt_int16_t trim_volt_b; // 校准电压值b mV
rt_uint16_t trim_curr_k_H; // 校准电流值高16位 mA
rt_uint16_t trim_curr_k_L; // 校准电流值低16位 mA
rt_int16_t trim_curr_b; // 校准电流值b mA
rt_uint16_t trim_source_curr_k_H; // 校准电源电流值高16位 mA
rt_uint16_t trim_source_curr_k_L; // 校准电源电流值低16位 mA
rt_int16_t trim_source_curr_b; // 校准电源电流值b mA
rt_int16_t trim_zero; // 校准飘零值 mV/mA
rt_uint8_t trim_mode; // 校准模式, 0-默认值,1-校准电压,2-校准电流,3-校准电源电流
rt_uint8_t trim_step;
rt_uint8_t trim_enable_flag; //0就是默认写用户区,1则写出场数据区
};
struct chrg_north_t {
@@ -46,13 +105,14 @@ struct chrg_north_t {
struct chrg_tty_t *tty;
struct rt_ringbuffer *rb;
struct pickup_info_t frame;
struct chrg_trim trim[TOTAL_NOR_CHS];
rt_uint8_t rx_buf[SIZE_BUF_TTY];
rt_uint8_t free_flag;
rt_uint8_t enable[TOTAL_NOR_CHS]; // 通路使能
rt_uint8_t manual; // 手动插入命令
eIDX_NOR_CH idx; // 当前通路
struct chrg_switch_t sw[TOTAL_NOR_CHS];
rt_uint8_t north_mode; // 0-正常模式, 1-校准模式
};
extern struct chrg_north_t chrgnorth;
@@ -86,7 +146,8 @@ extern int chrg_north_send (rt_uint8_t *data, rt_size_t length);
其它说明: -
*****************************************************************/
extern void chrg_north_thread_entry (void *data);
extern rt_uint8_t On_Flag2;
extern rt_bool_t Set_Pro_Flag;
#endif
+457 -125
View File
@@ -20,11 +20,10 @@
#include "chrg_north.h"
#include "chrg_switch.h"
#include "chrg_utils.h"
#include "chrg_rel.h"
#include "chrg_wdt.h"
#define LOG_TAG "chrg.rollnor"
#include <rtdbg.h>
const rt_base_t sw_uart[TOTAL_NOR_CHS] = {
IDX_GPO_UART_SW1,
IDX_GPO_UART_SW2,
@@ -33,10 +32,168 @@ const rt_base_t sw_uart[TOTAL_NOR_CHS] = {
};
struct chrg_rollnor_t chrgrollnor = {
.run = RUN_AUTO,
.run = RUN_CONSOLE, // RUN_AUTO,
.ch = IDX_NOR_CH1,
// .Set_Role = ID_SINK,
// .group = 0,
};
/*****************************************************************
函数名称: sink_fill_pd_buf
函数描述: 根据快充协议类型填充PD指令的5字节数据缓冲区
输入参数:
pd_val: 快充协议类型(如0xC1/0xF8/0xFB等)
pd_volt: 电压值(mV16位)
pd_curr: 电流值(mA16位)
buf: 输出缓冲区
输出参数: buf: 填充后的5字节协议数据
返回说明: 0:成功 -1:参数错误
*****************************************************************/
int sink_fill_pd_buf(rt_uint8_t group,rt_uint8_t pd_val, rt_uint16_t pd_volt, rt_uint16_t pd_curr,rt_uint8_t *buf)
{
// 参数合法性校验
if (buf == RT_NULL) {
rt_kprintf("sink_fill_pd_buf: buf is a null pointer!\n");
return -1;
}
// 先清空缓冲区(默认全0
rt_memset(buf, 0, 5);
// 按协议类型填充buf
switch(pd_val) {
// 标准/QC2.0/QC3.0/FCP(仅电压,第五位=电压)
case 0xFE: // 标准协议
case 0xF8: // QC2.0协议
case 0xF6: // QC3.0协议
case 0xF2: // FCP协议
buf[0] = pd_volt / 100; // 第5字节=电压(0.1V)
buf[1] = pd_curr / 100; // 第6字节=电流(0.1A)
#if PRO_TEST_DEBUG
rt_kprintf("type: %02X, Voltage(0.1V)=%d\n", pd_val, pd_volt/100);
rt_kprintf("type: %02x, Curr(0.1A)=%d\n",pd_curr,pd_curr/100);
#endif
break;
// PDO/PPS/PD3.1/UFCS/AVS(组号+电压+电流)
case 0xFC: // PPS手动/自动
case 0xE3: // AVS协议
buf[0] = group; // 第5字节=组号(默认0
buf[1] = (pd_volt >> 8) & 0xFF; // 第6字节=电压高8位(mV)
buf[2] = pd_volt & 0xFF; // 第7字节=电压低8位(mV)
buf[3] = (pd_curr >> 8) & 0xFF; // 第8字节=电流高8位(mA)
buf[4] = pd_curr & 0xFF; // 第9字节=电流低8位(mA)
#if PRO_TEST_DEBUG
rt_kprintf("type: %02X, Group=0, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
#endif
break;
case 0xE0: // UFCS协议
case 0xE1: // PD3.1协议
case 0xFB: // PDO手动
buf[0] = 0x00; // 第5字节=组号(默认0
buf[1] = (pd_volt >> 8) & 0xFF; // 第6字节=电压高8位(mV)
buf[2] = pd_volt & 0xFF; // 第7字节=电压低8位(mV)
buf[3] = (pd_curr >> 8) & 0xFF; // 第8字节=电流高8位(mA)
buf[4] = pd_curr & 0xFF; // 第9字节=电流低8位(mA)
#if PRO_TEST_DEBUG
rt_kprintf("type: %02X, Group=0, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
#endif
break;
// SCP/VIVO/传音(电压+电流)
case 0xF3: // SCP协议
case 0xFD: // VIVO协议
case 0xF7: // 传音/TFC协议
buf[0] = (pd_volt >> 8) & 0xFF; // 第6字节=电压高8位(mV)
buf[1] = pd_volt & 0xFF; // 第7字节=电压低8位(mV)
buf[2] = (pd_curr >> 8) & 0xFF; // 第8字节=电流高8位(mA)
buf[3] = pd_curr & 0xFF; // 第9字节=电流低8位(mA)
buf[4] = 0;
#if PRO_TEST_DEBUG
rt_kprintf("type: %02X, Voltage=%d mV, Current=%d mA\n", pd_val, pd_volt, pd_curr);
#endif
break;
// VOOC(无参数)
case 0xE2: // VOOC协议
#if PRO_TEST_DEBUG
rt_kprintf("type: VOOC(0xE2), No parameters\n");
#endif
break;
case 0xF9: // AFC协议
{
// 1. 电压转HEX
rt_uint8_t volt_hex = 0;
if (pd_volt >= 5000 && pd_volt <= 20000) { // 电压范围:5V(5000mV)~20V(20000mV)
rt_uint8_t volt_v = pd_volt / 1000; // 转成V5000mV→5V
volt_hex = volt_v - 5; // 5V对应020V对应150xF
} else {
#if PRO_TEST_DEBUG
rt_kprintf("AFC VOLT ERROR VOLT=:%d mV Set 5000~20000\n", pd_volt);
#endif
return -1;
}
// 2. 电流转HEX
rt_uint8_t curr_hex = 0;
if (pd_curr >= 750 && pd_curr <= 3000) { // 电流范围:0.75A(750mA)~3.0A(3000mA)
rt_uint16_t curr_ma_step = (pd_curr - 750) / 150; // 步长150mA0.75→1.5→...→3.0
curr_hex = curr_ma_step; // 0.75A对应03.0A对应150xF
rt_kprintf("curr_ma_step = %d",curr_ma_step);
} else {
#if PRO_TEST_DEBUG
rt_kprintf("AFC CURR ERROR%d mA SET 750~3000 \n", pd_curr);
#endif
return -1;
}
// 3. 合并电压Hex(高4位)和电流Hex(低4位)→ 填充第五字节
buf[0] = (volt_hex << 4) | curr_hex;
rt_kprintf("type: AFC(0xF9), Voltage=%d V, Current=%.2f A, volaue=0x%X\n",
pd_volt/1000, pd_curr/1000.0, buf[0]);
}
break;
// 未知类型
default:
// rt_kprintf("Unknown fast charge type: %02X, Parameters cleared\n", pd_val);
break;
}
return 0;
}
rt_uint16_t v1=0,v2=0;
//电源关闭
void chrg_nor_source_stop(struct chrg_switch_t *pSW,rt_uint8_t idx,rt_uint8_t *enable){
switch(pSW->source.Stop_step_flag){
case 0:
chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_WORK, WORK_STOP);
pSW->source.Stop_step_flag = 1;
break;
case 1: // 停止工作
chrg_set_sou_reg((eIDX_SOU_CH)idx, REG_WORK, WORK_STOP);
pSW->source.Stop_step_flag = 2;
break;
case 2: // 关闭北向继电器
chrg_nor_sw_rel((eIDX_NOR_CH)idx,0);
pSW->source.Stop_step_flag = 3;
break;
case 3: // 状态2:已执行完停止操作,清空标志位
pSW->source.Set_CV_Flag = 0;
if(pSW->source.change_sink_Flag == 1){
pSW->source.change_sink_Flag = 0;
// chrg_com_sink_on(idx,pSW,enable);
}else{
*enable = 0;
}
break;
default:
pSW->source.Stop_step_flag = 0;
break;
}
}
/*****************************************************************
函数名称: chrg_roll_nor_sw_uart
@@ -60,89 +217,42 @@ static void chrg_roll_nor_sw_uart (eIDX_NOR_CH ch, eIDX_ID id)
}
static int chrg_roll_nor_cmd (struct chrg_rollnor_t *pROLL)
//设置负载协议
void chrg_roll_nor_sink_pro_set(struct chrg_switch_t *pSW)
{
if (RT_NULL == pROLL) {
return -1;
}
chrg_north_switch((eIDX_NOR_CH)pROLL->ch);
if (1 == pROLL->type) {
chrg_roll_nor_sw_uart((eIDX_NOR_CH)pROLL->ch, ID_SOURCE);
switch (pROLL->sub) {
case IDX_GET_SOURCE_VC: {
source_get_vc(5000, 1000);
}
break;
case IDX_GET_SOURCE_PD: {
source_get_pd();
}
break;
case IDX_SET_SOURCE_PD: {
source_set_pd(pROLL->pd, pROLL->src, pROLL->pps,
pROLL->max_vol, pROLL->min_vol, pROLL->max_curr);
}
break;
case IDX_SET_SOURCE_CCLVL: {
source_set_cc_lvl(pROLL->cc_level);
}
break;
default:
return -1;
break;
}
} else if (0 == pROLL->type) {
chrg_roll_nor_sw_uart((eIDX_NOR_CH)pROLL->ch, ID_SINK);
switch (pROLL->sub) {
case IDX_GET_SINK_VC: {
sink_get_vc(pROLL->voltage, pROLL->current);
}
break;
case IDX_GET_SINK_PD: {
sink_get_pd();
}
break;
case IDX_SET_SINK_PD: {
rt_uint8_t buf[5] = {0};
sink_set_pd(pROLL->pd, buf);
}
break;
case IDX_SET_SINK_CCLINE: {
sink_set_cc_line(pROLL->cc_line);
}
break;
case IDX_SET_SINK_CCLVL: {
sink_set_cc_lvl(pROLL->cc_level);
}
break;
default:
return -1;
}
} else {
return -1;
}
return 0;
rt_uint8_t buf[6] = {0};
sink_fill_pd_buf(pSW->sink.Pro_Group,pSW->sink.protocol,pSW->sink.pro_loadv,pSW->sink.pro_loadc,buf);
sink_set_pd(pSW->sink.protocol,buf);
#if DEBUG_NORTH
rt_kprintf("set_pro\r\n");
#endif
}
//设置电源协议
void chrg_roll_nor_source_pro_set(struct chrg_switch_t *pSW){
if(pSW->source.Set_Pro_Flag == 1){
pSW->source.Set_Pro_Flag = 0;
if(pSW->source.LCD_Set_Pro_Flag==1){
source_set_pd(pSW->source.protocol,0,0,pSW->source.vc.set_voltage,0,pSW->source.vc.set_current);
}
else if(pSW->source.LCD_Set_Pro_Flag==2){
source_set_pd_com(pSW->source.protocol,pSW->source.pro_gear_idx);
}
else if(pSW->source.LCD_Set_Pro_Flag==3){
source_set_pd_new_com(pSW->source.protocol,pSW);
}
}else {
pSW->sub = IDX_GET_SOURCE_VC;
}
}
static int chrg_roll_nor_parse (struct chrg_rollnor_t *pROLL, rt_uint8_t *data)
{
if ((RT_NULL == pROLL)||(RT_NULL == data)) {
return -1;
}
chrg_north_switch(IDX_NOR_CH1);
// 读取北向全局结构体中当前通道的Source/Sink参数
struct chrg_switch_t *pSW = &chrgnorth.sw[pROLL->ch];
if (1 == pROLL->type) {
switch (pROLL->sub) {
case IDX_GET_SOURCE_VC: {
@@ -171,9 +281,10 @@ static int chrg_roll_nor_parse (struct chrg_rollnor_t *pROLL, rt_uint8_t *data)
}
} else if (0 == pROLL->type) {
chrg_roll_nor_sw_uart(IDX_NOR_CH1,ID_SINK);
switch (pROLL->sub) {
case IDX_GET_SINK_VC: {
sink_get_vc(pSW->sink.pro_loadv,pSW->sink.pro_loadc);
}
break;
@@ -206,7 +317,79 @@ static int chrg_roll_nor_parse (struct chrg_rollnor_t *pROLL, rt_uint8_t *data)
return 0;
}
static int chrg_roll_nor_cmd (struct chrg_rollnor_t *pROLL)
{
if (RT_NULL == pROLL) {
return -1;
}
return 0;
}
void chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,rt_uint8_t *enable)
{
if(pSW->id==ID_SOURCE){ //电源
chrg_roll_nor_sw_uart((eIDX_NOR_CH)ch, ID_SOURCE);
switch(pSW->sub){
case IDX_SET_SOURCE_ID: //角色
chrg_set_eload_source(ch,pSW);
if(pSW->continue_flag)
pSW->sub = IDX_SET_SOURCE_CC_SET;
// rt_kprintf("set_start\r\n");
break;
case IDX_SET_SOURCE_CC_SET:
source_set_cc_choose(pSW->source.cc_set);
pSW->sub = IDX_SET_SOURCE_CV;
break;
case IDX_SET_SOURCE_CV: //恒压恒流
pSW->sub = IDX_SER_SOURCE_VOLT;
break;
case IDX_SER_SOURCE_VOLT://电压
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_VOLTAGE_OUT+1, 0);
pSW->sub = IDX_SER_SOURCE_CURR;
break;
case IDX_SER_SOURCE_CURR://电流
chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_CURRENT_OUT+1, pSW->source.protective_curr);
pSW->sub = IDX_SET_SOURCE_WORK;
break;
case IDX_SET_SOURCE_WORK:
chrg_sou_work(ch,pSW);
break;
case IDX_SET_SOURCE_PD:
chrg_roll_nor_source_pro_set(pSW);
pSW->source.On_Flag = 1;
break;
case IDX_GET_SOURCE_VC:
source_get_vc(pSW->source.vc.set_voltage, pSW->source.vc.set_current);
break;
case IDX_SET_SOURCE_CCLVL:
source_set_cc_lvl(pSW->source.vol_lvl);
break;
case IDX_SET_SOURCE_STOP_STATE:
chrg_nor_source_stop(pSW,ch,enable);
break;
default:
break;
}
}else{
chrg_roll_nor_sw_uart((eIDX_NOR_CH)ch, ID_SINK);
switch(pSW->sub){
case IDX_GET_SINK_VC: //查询
sink_get_vc(pSW->sink.pro_loadv,pSW->sink.pro_loadc);
break;
case IDX_SET_SINK_CCLINE: //CC线选择
sink_set_cc_line(pSW->sink.cc_set);
pSW->sub = IDX_SET_SINK_PD;
break;
case IDX_SET_SINK_CCLVL: //CC电平
sink_set_cc_lvl(pSW->sink.cc_lvl);
break;
case IDX_SET_SINK_PD:
chrg_roll_nor_sink_pro_set(pSW);
pSW->sub = IDX_GET_SINK_VC;
break;
}
}
}
/*****************************************************************
函数名称: chrg_roll_nor_thread_entry
@@ -232,41 +415,36 @@ void chrg_roll_nor_thread_entry (void *data)
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_switch_t *pSW = RT_NULL;
struct mb_msg_t *pMB_R = RT_NULL;
for (i = 0; i < TOTAL_NOR_CHS; i++) {
chrg_roll_nor_sw_uart((eIDX_NOR_CH)i, ID_SINK);
}
while (1) {
chrg_wdt_feed();
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
run = pROLL->run;
rt_mutex_release(pTHR->mutex);
rt_mutex_release(pTHR->mutex);
switch (run) {
case RUN_AUTO: {
idx = index%TOTAL_NOR_CHS;
if (0 == pNOR->enable[idx]) {
index++;
rt_thread_mdelay(10);
rt_thread_mdelay(2);
continue;
} else {
chrg_led_flashing(IDX_LED2, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
chrg_north_switch((eIDX_NOR_CH)idx);
chrgnorth.idx = idx;
pROLL->idx = idx;
pSW = &pNOR->sw[idx];
if (ID_SOURCE == pSW->id) {
chrg_roll_nor_sw_uart((eIDX_NOR_CH)idx, ID_SOURCE);
source_get_vc(pSW->source.voltage, pSW->source.current);
} else if (ID_SINK == pSW->id) {
chrg_roll_nor_sw_uart((eIDX_NOR_CH)idx, ID_SINK);
sink_get_vc(pSW->sink.loadv, pSW->sink.loadc);
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
if(pNOR->north_mode == 0){
chrg_roll_nor_set(pROLL->idx,pSW,&pNOR->enable[idx]);
}
//rt_kprintf("Nor[%d].%d\r\n", idx, pSW->id);
if(pNOR->north_mode == 1){
chrg_roll_trim_set(pROLL->idx,pNOR);
}
rt_mutex_release(pTHR->mutex);
}
index++;
}
break;
@@ -275,15 +453,18 @@ void chrg_roll_nor_thread_entry (void *data)
chrg_roll_nor_cmd(pROLL);
}
break;
case RUN_CONSOLE: {
chrg_roll_nor_cmd(pROLL);
}
break;
case RUN_COM:
// {
// chrg_roll_nor_cmd(pROLL);
// }
break;
case RUN_NONE:
default:
rt_thread_mdelay(100);
rt_thread_mdelay(5);
continue;
break;
}
@@ -313,10 +494,11 @@ void chrg_roll_nor_thread_entry (void *data)
pMB_R = RT_NULL;
}
rt_thread_mdelay(200); // 间隔 200ms
rt_thread_mdelay(20); // 间隔 200ms
} else {
if (RUN_AUTO != run) { // 非自动轮巡
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
//todo
pROLL->run = RUN_AUTO; // 切换会自动轮巡
rt_mutex_release(pTHR->mutex);
}
@@ -357,7 +539,7 @@ static int sink (int argc, char **argv)
if (strcmp(argv[3], "vc") == 0) {
if (0 == gset) {
pROLL->sub = IDX_GET_SINK_VC;
pROLL->sub = IDX_SET_SINK_WORK_STATE;
} else {
ret = -1;
rt_kprintf("sink vc can't set.\r\n");
@@ -367,6 +549,7 @@ static int sink (int argc, char **argv)
if (0 == gset) {
pROLL->sub = IDX_GET_SINK_PD;
} else {
//长度
if (argc != 5) {
rt_kprintf("help : %s set 0-3 ccline 0-3.\r\n", argv[0]);
return -1;
@@ -422,6 +605,7 @@ static int sink (int argc, char **argv)
if (0 == ret) {
pROLL->ch = ch;
pROLL->type = 0; // sink
pROLL->run = RUN_CONSOLE;
} else {
rt_kprintf("help : %s get|set [0-3] vc|pd|ccline|cclvl\r\n", argv[0]);
}
@@ -470,12 +654,15 @@ static int source (int argc, char **argv)
pROLL->sub = IDX_GET_SOURCE_PD;
} else {
if (argc != 5) {
//if (argc != 5) {
rt_kprintf("help : %s set 0-3 pd 0-.\r\n", argv[0]);
return -1;
// sink_set_pd
} else { // sink set 0 pd 0
int type = atoi(argv[4]);
//协议
pROLL->pd = (rt_uint8_t)type;
//子命令
pROLL->sub = IDX_SET_SOURCE_PD;
}
}
@@ -505,6 +692,7 @@ static int source (int argc, char **argv)
if (0 == ret) {
pROLL->ch = ch;
pROLL->type = 1; // source
pROLL->run = RUN_CONSOLE;
} else {
rt_kprintf("help : %s get|set [0-3] vc|pd|cclvl\r\n", argv[0]);
}
@@ -514,29 +702,173 @@ static int source (int argc, char **argv)
MSH_CMD_EXPORT(source, north source test);
static int north_run (int argc, char **argv)
{
if (2 != argc) {
rt_kprintf("help : %s start|stop\r\n", argv[0]);
return -1;
}
struct chrg_rollnor_t *pROLL = &chrgrollnor;
if (rt_strcmp(argv[1], "start") == 0) {
pROLL->run = RUN_AUTO;
} else if (rt_strcmp(argv[1], "stop") == 0) {
pROLL->run = RUN_NONE;
} else {
rt_kprintf("help : %s start|stop\r\n", argv[0]);
return -1;
}
return 0;
}
MSH_CMD_EXPORT(north_run, north auto run);
#endif
//校准CRC计算
rt_uint16_t chrg_trim_crc(rt_uint8_t ch, struct chrg_north_t *pNOR)
{
struct chrg_trim *pTrim = &pNOR->trim[ch];
rt_uint8_t buf[22];
int idx = 0;
buf[idx++] = (pTrim->trim_volt_k_H >> 8) & 0xFF;
buf[idx++] = pTrim->trim_volt_k_H & 0xFF;
buf[idx++] = (pTrim->trim_volt_k_L >> 8) & 0xFF;
buf[idx++] = pTrim->trim_volt_k_L & 0xFF;
buf[idx++] = (pTrim->trim_volt_b >> 8) & 0xFF;
buf[idx++] = pTrim->trim_volt_b & 0xFF;
buf[idx++] = (pTrim->trim_curr_k_H >> 8) & 0xFF;
buf[idx++] = pTrim->trim_curr_k_H & 0xFF;
buf[idx++] = (pTrim->trim_curr_k_L >> 8) & 0xFF;
buf[idx++] = pTrim->trim_curr_k_L & 0xFF;
buf[idx++] = (pTrim->trim_curr_b >> 8) & 0xFF;
buf[idx++] = pTrim->trim_curr_b & 0xFF;
buf[idx++] = (pTrim->trim_source_curr_k_H >> 8) & 0xFF;
buf[idx++] = pTrim->trim_source_curr_k_H & 0xFF;
buf[idx++] = (pTrim->trim_source_curr_k_L >> 8) & 0xFF;
buf[idx++] = pTrim->trim_source_curr_k_L & 0xFF;
buf[idx++] = (pTrim->trim_source_curr_b >> 8) & 0xFF;
buf[idx++] = pTrim->trim_source_curr_b & 0xFF;
buf[idx++] = (pTrim->trim_zero >> 8) & 0xFF;
buf[idx++] = pTrim->trim_zero & 0xFF;
return mb_crc16(buf, 20);
}
//电源校准设置
void chrg_trim_set_volt(rt_uint8_t ch,struct chrg_north_t *pNOR){
struct chrg_trim *pTRIM = &pNOR->trim[ch];
switch(pTRIM->trim_step){
case 0:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Volt_CAL_K_H, pTRIM->trim_volt_k_H);
break;
case 1:
break;
case 2:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Volt_CAL_K_L, pTRIM->trim_volt_k_L);
break;
case 3:
break;
case 4:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Volt_CAL_B, pTRIM->trim_volt_b);
break;
pNOR->north_mode = 0;
break;
}
pTRIM->trim_step++;
}
//电流校准设置
void chrg_trim_set_curr(rt_uint8_t ch,struct chrg_north_t *pNOR){
struct chrg_trim *pTRIM = &pNOR->trim[ch];
switch(pTRIM->trim_step){
case 0:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Current_CAL_K_H, pTRIM->trim_curr_k_H);
break;
case 1:
break;
case 2:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Current_CAL_K_L, pTRIM->trim_curr_k_L);
break;
case 3:
break;
case 4:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Current_CAL_B, pTRIM->trim_curr_b);
pNOR->north_mode = 0;
break;
}
pTRIM->trim_step++;
}
//电源校准设置
void chrg_trim_set_source_curr(rt_uint8_t ch,struct chrg_north_t *pNOR){
struct chrg_trim *pTRIM = &pNOR->trim[ch];
rt_uint16_t crc = 0;
switch(pTRIM->trim_step){
case 0:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_SourceCurrent_CAL_K_H, pTRIM->trim_source_curr_k_H);
break;
case 1:
break;
case 2:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_SourceCurrent_CAL_K_L, pTRIM->trim_source_curr_k_L);
break;
case 3:
break;
case 4:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_SourceCurrent_CAL_B, pTRIM->trim_source_curr_b);
break;
case 5:
break;
case 6:
crc = chrg_trim_crc(ch, pNOR);
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Trim_CRC_CAL, crc);
rt_kprintf("crc=%04X\n",crc);
break;
case 7:
break;
case 8:
if(pTRIM->trim_enable_flag == 0){
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_CAL_WriteEnable_ADD,ModbusRTU_CAL_WriteValue);
}else{
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_CAL_WriteRecovery,ModbusRTU_CAL_WriteRecovery_Value);
}
pNOR->north_mode = 0;
break;
}
pTRIM->trim_step++;
}
//校准飘零设置
void chrg_trim_set_zero(rt_uint8_t ch,struct chrg_north_t *pNOR){
struct chrg_trim *pTRIM = &pNOR->trim[ch];
rt_uint16_t crc = 0;
switch(pTRIM->trim_step){
case 0:
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Trim_Zero_CAL, pTRIM->trim_zero);
break;
case 1:
break;
case 2:
crc = chrg_trim_crc(ch, pNOR);
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_Trim_CRC_CAL, crc);
break;
case 3:
break;
case 4:
if(pTRIM->trim_enable_flag == 0){
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_CAL_WriteEnable_ADD,ModbusRTU_CAL_WriteValue);
}else{
chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_CAL_WriteRecovery,ModbusRTU_CAL_WriteRecovery_Value);
}
pNOR->north_mode = 0;
break;
}
pTRIM->trim_step++;
}
//校准设置
void chrg_roll_trim_set(rt_uint8_t ch, struct chrg_north_t *pNOR){
struct chrg_trim *pTRIM = &pNOR->trim[ch];
switch(pTRIM->trim_mode){
case MODE_VOLT_CAL:
chrg_trim_set_volt(ch, pNOR);
break;
case MODE_CURR_CAL:
chrg_trim_set_curr(ch, pNOR);
break;
case MODE_SOURCE_CURR_CAL:
chrg_trim_set_source_curr(ch, pNOR);
break;
case MODE_ZERO_CAL:
chrg_trim_set_zero(ch, pNOR);
break;
}
}
+23 -6
View File
@@ -13,19 +13,34 @@
#include <rtthread.h>
#define THR_NAME_ROLL_NOR "thr.rollnor"
#define STATE_WORKING 0x03//运行标志位
enum {
IDX_GET_SOURCE_VC = 0,
IDX_GET_SOURCE_PD,
IDX_SET_SOURCE_PD,
IDX_SET_SOURCE_ID,
IDX_SET_SOURCE_CC_SET,
IDX_SER_SOURCE_VOLT,
IDX_SER_SOURCE_CURR,
IDX_SET_SOURCE_WORK,
IDX_SET_SOURCE_CCLVL,
IDX_SET_SOURCE_CV,
IDX_SET_SOURCE_STOP_STATE,
IDX_GET_SINK_VC,
IDX_GET_SINK_PD,
IDX_SET_SINK_VOLT,
IDX_SET_SINK_CURR,
IDX_SET_SINK_PD,
IDX_SET_SINK_CCLINE,
IDX_SET_SINK_CCLVL,
IDX_SET_SINK_ID,
IDX_SET_SINK_VOLT_ON,
IDX_SET_SINK_WORK,
IDX_SET_SINK_CV,
IDX_SET_SINK_WORK_STATE,
IDX_SET_SINK_STOP_TEST,//停止负载测试
IDX_SET_SINK_STORP_WAIT,
};
struct chrg_rollnor_t {
@@ -44,8 +59,7 @@ struct chrg_rollnor_t {
rt_uint8_t pps;
rt_uint16_t voltage; // 触发插入时,当前电压 mV
rt_uint16_t current; // 触发插入时,当前电流 mA
rt_uint16_t max_vol; // 触发插入时,最大电压 mV
rt_uint16_t min_vol; // 触发插入时,最小电压 mV
rt_uint16_t max_curr; // 触发插入时,最大电流 mA
@@ -64,7 +78,10 @@ extern struct chrg_rollnor_t chrgrollnor;
其它说明: -
*****************************************************************/
extern void chrg_roll_nor_thread_entry (void *data);
extern int sink_fill_pd_buf(rt_uint8_t group,rt_uint8_t pd_val, rt_uint16_t pd_volt, rt_uint16_t pd_curr, rt_uint8_t *buf);
extern void chrg_roll_trim_set(rt_uint8_t ch, struct chrg_north_t *pNOR);
extern void roll_nor_set(void);
#define sink_time 3
#endif
+515 -32
View File
@@ -1,3 +1,373 @@
// /****************************************************************************
// 文件名称 : chrg_roll_sou.c
// 完成日期 :
// 当前版本号 : V1.0
// 主要功能 : 实现南向通信轮巡请求命令。以独立线程处理。
// 版本历史 : 创建原始版本
// 说明 :
// ******************************************************************************/
// #include <stdlib.h>
// #include <string.h>
// #include <rtthread.h>
// #include "chrg_def.h"
// #include "chrg_led.h"
// #include "chrg_thread.h"
// #include "chrg_roll_sou.h"
// #include "chrg_eload.h"
// #include "chrg_utils.h"
// #define LOG_TAG "chrg.rollsou"
// #include <rtdbg.h>
// struct chrg_rollsou_t chrgrollsou = {
// .run = RUN_AUTO,
// .ch = IDX_SOU_CH1,
// .part = MB_R_PART1,
// .mode = 0,
// };
// #define SOU_CMD_QUEUE_SIZE 4
// struct chrg_sou_cmd_t {
// eIDX_SOU_CH ch;
// rt_uint16_t reg;
// rt_uint16_t value;
// };
// static struct chrg_sou_cmd_t sou_cmd_queue[SOU_CMD_QUEUE_SIZE];
// static rt_uint8_t sou_cmd_head = 0;
// static rt_uint8_t sou_cmd_tail = 0;
// static rt_uint8_t sou_cmd_count = 0;
// static void sou_cmd_enqueue(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t value)
// {
// if (sou_cmd_count < SOU_CMD_QUEUE_SIZE) {
// sou_cmd_queue[sou_cmd_tail].ch = ch;
// sou_cmd_queue[sou_cmd_tail].reg = reg;
// sou_cmd_queue[sou_cmd_tail].value = value;
// sou_cmd_tail = (sou_cmd_tail + 1) % SOU_CMD_QUEUE_SIZE;
// sou_cmd_count++;
// } else {
// LOG_W("sou command queue full, drop command\n");
// }
// }
// static int sou_cmd_dequeue(struct chrg_sou_cmd_t *cmd)
// {
// if (sou_cmd_count == 0) {
// return -1;
// }
// cmd->ch = sou_cmd_queue[sou_cmd_head].ch;
// cmd->reg = sou_cmd_queue[sou_cmd_head].reg;
// cmd->value = sou_cmd_queue[sou_cmd_head].value;
// sou_cmd_head = (sou_cmd_head + 1) % SOU_CMD_QUEUE_SIZE;
// sou_cmd_count--;
// return 0;
// }
// static int sou_cmd_exists(void)
// {
// return sou_cmd_count > 0;
// }
// /*****************************************************************
// 函数名称: chrg_set_sou_reg
// 函数描述: 南向通道设置寄存器请求命令
// 输入参数: idx:通道 reg:寄存器地址 value:寄存器值
// 输出参数: -
// 返回说明: -
// 其它说明: -
// *****************************************************************/
// void chrg_set_sou_reg (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t value)
// {
// struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// sou_cmd_enqueue(idx, reg, value);
// pROLL->run = RUN_LCD;
// rt_mutex_release(pTHR->mutex);
// rt_kprintf("ch=:%d,reg=%02x,value=%02x\n",idx,reg,value);
// }
// /*****************************************************************
// 函数名称: chrg_send_sou_upadata_data
// 函数描述: 南向通道升级命令
// 输入参数: idx:通道 reg:寄存器地址 value:寄存器值
// 输出参数: -
// 返回说明: -
// 其它说明: -
// *****************************************************************/
// void chrg_send_sou_upadata_data (rt_uint8_t *buf,rt_uint16_t leng)
// {
// struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
// pROLL->updata_data = buf;
// pROLL->leng = leng;
// // rt_kprintf("idx=%d,reg=%02x,value=%02x\n",RUN_UPDATAidx,reg,value);
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// pROLL->run = RUN_UPDATA;
// rt_mutex_release(pTHR->mutex);
// }
// ///*****************************************************************
// //函数名称: chrg_set_sou_more_reg
// //函数描述: 南向通道设置寄存器请求命令
// //输入参数: idx:通道 reg:寄存器地址 value:寄存器值 value2:第二个寄存器值
// //输出参数: -
// //返回说明: -
// //其它说明: -
// //*****************************************************************/
// //void chrg_set_sou_more_reg (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t value,rt_uint16_t value2)
// //{
// // struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
// // struct chrg_rollsou_t *pROLL = &chrgrollsou;
// // pROLL->more_reg_flag = 1; // 使用多寄存器标志位
// // pROLL->ch = idx;
// // pROLL->reg = reg;
// // pROLL->value = value;
// // pROLL->value2 = value2;
// // rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// // pROLL->run = RUN_LCD;
// // rt_mutex_release(pTHR->mutex);
// //}
// /*****************************************************************
// 函数名称: chrg_roll_sou_thread_entry
// 函数描述: 南向通道轮巡请求数据线程体
// 输入参数: *data: 本线程信息
// 输出参数: -
// 返回说明: -
// 其它说明: -
// *****************************************************************/
// rt_uint8_t aaaaaa = 0;
// void chrg_roll_sou_thread_entry (void *data)
// {
// struct chrg_thread_t *pTHR = (struct chrg_thread_t *)data;
// if (RT_NULL == pTHR) {
// return ;
// }
// rt_uint8_t run = 0;
// rt_uint8_t index = 0, idx = 0;
// rt_err_t ret = RT_EOK;
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
// struct chrg_thread_t *pTS = &chrgthr[IDX_THR_SOUTH];
// struct mb_msg_t *pMB_R = RT_NULL;
// rt_uint8_t number = 0;
// while (1) {
// /* 每轮先非阻塞排空积压消息,防止邮箱满导致 south 线程永久阻塞 */
// {
// rt_uint8_t drain_cnt = 0;
// struct mb_msg_t *pDrain = RT_NULL;
// while (drain_cnt < 8) {
// ret = rt_mb_recv(pTS->mb, (rt_uint32_t *)&pDrain, 0);
// if (ret != RT_EOK) break;
// if (pDrain != RT_NULL) {
// if (pDrain->payload != RT_NULL) rt_free(pDrain->payload);
// rt_free(pDrain);
// }
// drain_cnt++;
// }
// }
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// run = pROLL->run;
// rt_mutex_release(pTHR->mutex);
// switch (run) {
// // rt_kprintf("-------- ROLL SOU TIME START \n --------");
// case RUN_AUTO: {
// if(pROLL->mode!=0){
// }else{
// idx = index%(TOTAL_SOU_CHS); // [0, 3]
// // rt_kprintf("start_idx = %d",idx);
// if (0 == chrgsouth.enable[idx]) { // 通道未启用
// index++;
// rt_thread_mdelay(5);
// continue;
// } else { // 通道启用
// chrg_led_flashing(IDX_LED3, TIMES_ONE, PULSE_TIME*2, PULSE_TIME*2);
// if (idx != (rt_uint8_t)pROLL->idx) {
// pROLL->idx = (eIDX_SOU_CH)idx;
// chrg_south_switch((eIDX_SOU_CH)idx);
// pROLL->part = MB_R_PART1;
// }
// chrg_eload_refresh(ID_ELOAD, pROLL->part);
// if (pROLL->part < TOTAL_MB_R) {
// //pROLL->part++;
// //if (pROLL->part > MB_R_PART1) { // 仅1条,若需轮巡全部,注释此条件
// index++;
// //}
// } else {
// index++;
// }
// }
// }
// }
// break;
// case RUN_LCD: {
// struct chrg_sou_cmd_t cmd;
// rt_uint8_t do_cmd = 0;
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// if (sou_cmd_dequeue(&cmd) == 0) {
// do_cmd = 1;
// pROLL->ch = cmd.ch;
// }
// rt_mutex_release(pTHR->mutex);
// if (do_cmd) {
// chrg_south_switch(cmd.ch);
// chrg_eload_write_reg(ID_ELOAD, cmd.reg, cmd.value);
// }
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// if (!sou_cmd_exists()) {
// pROLL->run = RUN_AUTO;
// }
// rt_mutex_release(pTHR->mutex);
// }
// break;
// case RUN_CONSOLE: {
// chrg_south_switch((eIDX_SOU_CH)pROLL->ch);
// chrg_eload_refresh(ID_ELOAD, pROLL->part);
// }
// break;
// case RUN_UPDATA:{
// chrg_south_switch((eIDX_SOU_CH)pROLL->ch);
// if(pROLL->send_flag==1){
// chrg_eload_send_ymodem_data(pROLL->updata_data,pROLL->leng);
// pROLL->send_flag = 0;
// }
// /* 升级模式不下发Modbus邮件,等待south线程捕获ymodem应答后继续 */
// rt_thread_mdelay(50);
// continue;
// }
// break;
// case RUN_COM: {
// chrg_south_switch((eIDX_SOU_CH)pROLL->ch);
// chrg_eload_write_reg(ID_ELOAD, pROLL->reg, pROLL->value);
// }
// break;
// case RUN_NONE:
// default:
// rt_thread_mdelay(5);
// continue;
// break;
// }
// ret = rt_mb_recv(pTS->mb, (rt_uint32_t *)&pMB_R, 100 ); // 超时 200ms
// if ((RT_EOK == ret)&&(RT_NULL != pMB_R)) { // 存在返回数据
// if (0 == mb_crc16(pMB_R->payload, pMB_R->length)) {
// if (RUN_AUTO != run) { // 恢复自动轮巡或继续执行待发命令
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// pROLL->run = sou_cmd_exists() ? RUN_LCD : RUN_AUTO;
// rt_mutex_release(pTHR->mutex);
// }
// else { // 自动轮巡态,分析接收的数据
// #if DEBUG_SOUTH
// rt_kprintf("length = %d",pMB_R->length);
// #endif
// if(pMB_R->length!=43){//31修改成43,一共19个寄存器*2+4,多读了4位
// ret = rt_mb_recv(pTS->mb, (rt_uint32_t *)&pMB_R, 30); // 超时 200ms
// #if DEBUG_SOUTH
// rt_kprintf("ret = %d\n",ret);
// #endif
// if ((RT_EOK == ret)&&(RT_NULL != pMB_R)) { // 存在返回数据
// chrg_eload_parse(idx, pROLL->part, pMB_R->payload, pMB_R->length);
// }
// }
// else chrg_eload_parse(idx, pROLL->part, pMB_R->payload, pMB_R->length);
// }
// }
// else {
// LOG_E("crc failed.");
// }
// if (RT_NULL != pMB_R->payload) {
// rt_free(pMB_R->payload);
// pMB_R->payload = RT_NULL;
// }
// if (RT_NULL != pMB_R) {
// rt_free(pMB_R);
// pMB_R = RT_NULL;
// }
// // rt_thread_mdelay(200); // 间隔 20ms
// rt_thread_mdelay(10); // 间隔 200ms
// } else { // 设备应答超时
// if (RUN_AUTO != run) { // 非自动轮巡
// rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
// pROLL->run = sou_cmd_exists() ? RUN_LCD : RUN_AUTO; // 切换会自动轮巡或继续执行待发命令
// rt_mutex_release(pTHR->mutex);
// }
// }
// // rt_kprintf("-------- ROLL SOU TIME END\n --------");
// }
// }
// #if 1
// static int south_send(rt_uint8_t argc, char **argv)
// {
// int ch = 0, pt = 0;
// if (3 != argc) {
// rt_kprintf("help : %s [0-3] [0-1]\r\n", argv[0]);
// return -1;
// }
// ch = (rt_uint8_t)atoi(argv[1]);
// if ((ch < 0)||(ch >= TOTAL_SOU_CHS)) {
// rt_kprintf("help : %s [0-3] [0-1]\r\n", argv[0]);
// return -1;
// }
// pt = (rt_uint8_t)atoi(argv[2]);
// if ((pt < 0)||(pt >= TOTAL_MB_R)) {
// rt_kprintf("help : %s [0-3] [0-1]\r\n", argv[0]);
// return -1;
// }
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
// pROLL->run = RUN_CONSOLE;
// pROLL->ch = (rt_uint8_t)ch;
// pROLL->part = (rt_uint8_t)pt;
// return 0;
// }
// MSH_CMD_EXPORT(south_send, south send test);
// static int south_run (int argc, char **argv)
// {
// if (2 != argc) {
// rt_kprintf("help : %s start|stop\r\n", argv[0]);
// return -1;
// }
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
// if (rt_strcmp(argv[1], "start") == 0) {
// pROLL->run = RUN_AUTO;
// } else if (rt_strcmp(argv[1], "stop") == 0) {
// pROLL->run = RUN_NONE;
// } else {
// rt_kprintf("help : %s start|stop\r\n", argv[0]);
// return -1;
// }
// return 0;
// }
// MSH_CMD_EXPORT(south_run, south auto run);
// #endif
/****************************************************************************
文件名称 : chrg_roll_sou.c
完成日期 :
@@ -21,13 +391,38 @@
#define LOG_TAG "chrg.rollsou"
#include <rtdbg.h>
chrg_rollsou_com_t g_sou_com_batch = {0};
struct chrg_rollsou_t chrgrollsou = {
.run = RUN_AUTO,
.ch = IDX_SOU_CH1,
.part = MB_R_PART1,
};
//向通道批量缓存添加一条寄存器
int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val)
{
if(ch >= TOTAL_SOU_CHS) return -1;
sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[ch];
if(pBatch->reg_cnt >= SOU_COM_BATCH_REG_MAX)
{
LOG_E("sou com batch reg full");
return -2;
}
pBatch->reg_list[pBatch->reg_cnt] = reg;
pBatch->val_list[pBatch->reg_cnt] = val;
pBatch->reg_cnt++;
pBatch->pending = RT_TRUE;
return 0;
}
//启动批量下发(填充完影子结构体后调用)
void chrg_sou_com_sou_set(void)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
struct chrg_rollsou_t *pROLL = &chrgrollsou;
pROLL->run = RUN_COM;
}
/*****************************************************************
函数名称: chrg_set_sou_reg
@@ -51,6 +446,31 @@ void chrg_set_sou_reg (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t value)
rt_mutex_release(pTHR->mutex);
}
/*****************************************************************
函数名称: chrg_set_sou_reg
函数描述: 南向通道设置寄存器请求命令
输入参数: idx:通道 reg:寄存器地址 value:寄存器值
输出参数: -
返回说明: -
其它说明: -
*****************************************************************/
void chrg_set_sou_reg_com (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t value)
{
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
struct chrg_rollsou_t *pROLL = &chrgrollsou;
pROLL->ch = idx;
pROLL->reg = reg;
pROLL->value = value;
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_COM;
rt_mutex_release(pTHR->mutex);
rt_kprintf("ch=:%d,reg=%02x,value=%02x\n",idx,reg,value);
}
/*****************************************************************
函数名称: chrg_roll_sou_thread_entry
函数描述: 南向通道轮巡请求数据线程体
@@ -73,16 +493,16 @@ void chrg_roll_sou_thread_entry (void *data)
struct chrg_rollsou_t *pROLL = &chrgrollsou;
struct chrg_thread_t *pTS = &chrgthr[IDX_THR_SOUTH];
struct mb_msg_t *pMB_R = RT_NULL;
sou_com_batch_ch_t *pCurBatch = RT_NULL;
while (1) {
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
run = pROLL->run;
rt_mutex_release(pTHR->mutex);
rt_uint8_t batch_ch = 0; // 缓存当前正在下发的批量通道
// rt_kprintf("------ROLL SOU START \n--------");
switch (run) {
case RUN_AUTO: {
idx = index%(TOTAL_SOU_CHS); // [0, 3]
if (0 == chrgsouth.enable[idx]) { // 通道未启用
index++;
rt_thread_mdelay(10);
@@ -94,14 +514,8 @@ void chrg_roll_sou_thread_entry (void *data)
chrg_south_switch((eIDX_SOU_CH)idx);
pROLL->part = MB_R_PART1;
}
chrg_eload_refresh(ID_ELOAD, pROLL->part);
if (pROLL->part < TOTAL_MB_R) {
pROLL->part++;
if (pROLL->part > MB_R_PART1) { // 仅1条,若需轮巡全部,注释此条件
index++;
}
} else {
index++;
}
}
@@ -114,6 +528,36 @@ void chrg_roll_sou_thread_entry (void *data)
}
break;
case RUN_COM:
{
pCurBatch = RT_NULL;
// 遍历查找有待下发批量任务的通道
for(idx = 0; idx < TOTAL_SOU_CHS; idx++)
{
if(g_sou_com_batch.ch[idx].pending == RT_TRUE)
{
pCurBatch = &g_sou_com_batch.ch[idx];
break;
}
}
// 无任何批量任务,切回自动轮巡
if(pCurBatch == RT_NULL)
{
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_AUTO;
rt_mutex_release(pTHR->mutex);
break;
}
// 切换当前通道串口
chrg_south_switch((eIDX_SOU_CH)idx);
batch_ch = idx;
// 发送当前下标寄存器
rt_uint16_t reg = pCurBatch->reg_list[pCurBatch->cur_idx];
rt_uint16_t val = pCurBatch->val_list[pCurBatch->cur_idx];
chrg_eload_write_reg(ID_ELOAD, reg, val);
}
break;
case RUN_CONSOLE: {
chrg_south_switch((eIDX_SOU_CH)pROLL->ch);
chrg_eload_refresh(ID_ELOAD, pROLL->part);
@@ -127,41 +571,80 @@ void chrg_roll_sou_thread_entry (void *data)
break;
}
ret = rt_mb_recv(pTS->mb, (rt_uint32_t *)&pMB_R, 200); // 超时 200ms
if ((RT_EOK == ret)&&(RT_NULL != pMB_R)) { // 存在返回数据
if (0 == mb_crc16(pMB_R->payload, pMB_R->length)) {
if (RUN_AUTO != run) { // 恢复自动轮巡
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_AUTO;
rt_mutex_release(pTHR->mutex);
} else { // 自动轮巡态,分析接收的数据
ret = rt_mb_recv(pTS->mb, (rt_uint32_t *)&pMB_R, 50);
if ((RT_EOK == ret)&&(RT_NULL != pMB_R))
{
if (0 == mb_crc16(pMB_R->payload, pMB_R->length))
{
if (RUN_AUTO != run)
{
if(run == RUN_COM)
{
// CRC校验成功,本条发送完成,下标+1,下发下一条
sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[batch_ch];
pBatch->cur_idx++;
// 当前通道全部指令发完,清除pending标记
if(pBatch->cur_idx >= pBatch->reg_cnt)
{
pBatch->pending = RT_FALSE;
pBatch->cur_idx = 0;
pBatch->reg_cnt = 0;
}
}
else
{
// LCD/CONSOLE单条模式,应答成功直接切回自动轮巡
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_AUTO;
rt_mutex_release(pTHR->mutex);
}
}
else
{
// AUTO自动采集,解析返回数据
chrg_eload_parse(idx, pROLL->part, pMB_R->payload, pMB_R->length);
}
} else {
LOG_E("crc failed.");
}
else
{
LOG_E("crc failed, resend current reg");
// CRC错误,不递增cur_idx,下一轮重发本条
}
if (RT_NULL != pMB_R->payload) {
// 释放报文内存
if (RT_NULL != pMB_R->payload)
{
rt_free(pMB_R->payload);
pMB_R->payload = RT_NULL;
}
if (RT_NULL != pMB_R) {
if (RT_NULL != pMB_R)
{
rt_free(pMB_R);
pMB_R = RT_NULL;
}
rt_thread_mdelay(200); // 间隔 200ms
} else { // 设备应答超时
if (RUN_AUTO != run) { // 非自动轮巡
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_AUTO; // 切换会自动轮巡
rt_mutex_release(pTHR->mutex);
rt_thread_mdelay(20);
}
else
{
// 应答超时处理
if (RUN_AUTO != run)
{
if(run == RUN_COM)
{
// 批量下发超时:不递增cur_idx,下一轮循环重发当前寄存器
LOG_E("com batch timeout, resend current reg");
}
else
{
// LCD/CONSOLE单条超时,切回自动轮巡
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
pROLL->run = RUN_AUTO;
rt_mutex_release(pTHR->mutex);
}
}
}
// rt_kprintf("------ROLL SOU END \n--------");
}
}
#if 1
+39 -5
View File
@@ -6,7 +6,7 @@
版本历史 : 创建原始版本
说明 :
******************************************************************************/
#include "chrg_south.h"
#ifndef __CHRG_ROLL_SOU_H__
#define __CHRG_ROLL_SOU_H__
@@ -15,9 +15,29 @@
#define THR_NAME_ROLL_SOU "thr.rollsou"
// 单通道批量下发缓存:保存当前需要同步的寄存器列表
#define SOU_COM_BATCH_REG_MAX 16
typedef struct
{
rt_uint16_t reg_cnt; // 当前待下发寄存器数量
rt_uint16_t reg_list[SOU_COM_BATCH_REG_MAX]; // 寄存器地址数组
rt_uint16_t val_list[SOU_COM_BATCH_REG_MAX]; // 对应寄存器值数组
rt_uint8_t cur_idx; // 当前下发到第几条
rt_bool_t pending; // 本通道有待下发批量指令
} sou_com_batch_ch_t;
// COM批量下发总控结构体
typedef struct
{
sou_com_batch_ch_t ch[TOTAL_SOU_CHS];
} chrg_rollsou_com_t;
// 全局批量下发实例
extern chrg_rollsou_com_t g_sou_com_batch;
struct chrg_rollsou_t {
rt_uint8_t run; // 下一轮运行方式 1:自动轮巡 2:LCD触发插入 3:控制台触发插入
rt_uint8_t run; // 下一轮运行方式 1:自动轮巡 2:LCD触发插入 3:控制台触发插入4.com触发插入
rt_uint8_t part; // 自动轮巡时,多条命令序号(若存在多条命令时)
rt_uint8_t idx; // 自动轮巡时,通道序号
@@ -25,7 +45,11 @@ struct chrg_rollsou_t {
rt_uint16_t reg; // LCD 触发时,寄存器地址
rt_uint16_t value; // LCD 触发时,寄存器值
rt_uint8_t *updata_data;//南向升级的数据
rt_uint16_t leng;
rt_uint8_t send_flag; //发送标志位
rt_uint8_t mode;// 0 为正常轮询,1为升级,2为校准
};
extern struct chrg_rollsou_t chrgrollsou;
@@ -39,7 +63,15 @@ extern struct chrg_rollsou_t chrgrollsou;
其它说明: -
*****************************************************************/
extern void chrg_set_sou_reg (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t value);
/*****************************************************************
函数名称: chrg_set_sou_reg_com
函数描述: com端南向通道设置寄存器请求命令(批量下发机制)
输入参数: idx:通道 reg:寄存器地址 value:寄存器值
输出参数: -
返回说明: -
其它说明: 每条命令加入批量队列,自动启动顺序下发,全部完成后切回轮巡
*****************************************************************/
extern void chrg_set_sou_reg_com (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t value);
/*****************************************************************
函数名称: chrg_roll_sou_thread_entry
函数描述: 南向通道轮巡请求数据线程体
@@ -50,7 +82,9 @@ extern void chrg_set_sou_reg (eIDX_SOU_CH idx, rt_uint16_t reg, rt_uint16_t valu
*****************************************************************/
extern void chrg_roll_sou_thread_entry (void *data);
extern void chrg_send_sou_upadata_data (rt_uint8_t *buf,rt_uint16_t leng);
extern void chrg_sou_com_sou_set(void);
extern int chrg_sou_com_batch_add_reg(eIDX_SOU_CH ch, rt_uint16_t reg, rt_uint16_t val);
#endif
+18 -5
View File
@@ -16,6 +16,7 @@
#include "chrg_roll_sou.h"
#include "chrg_switch.h"
#include "chrg_utils.h"
#include "chrg_eload.h"
#include "ulog.h"
//#define LOG_TAG "chrg.sou"
@@ -29,6 +30,7 @@ struct chrg_south_t chrgsouth = {
.tty = RT_NULL,
.rb = RT_NULL,
.enable = {1, 1, 1, 1},
.online = {0, 0, 0, 0},
};
/*****************************************************************
@@ -155,10 +157,8 @@ int chrg_south_send (rt_uint8_t *data, rt_size_t length)
struct chrg_tty_t *pTTY = chrgsouth.tty;
if (RT_NULL != pTTY) {
// LOG_HEX("sendS", 32, data, length);
ret = chrg_tty_send(pTTY, data, length);
}
return ret;
}
@@ -183,6 +183,7 @@ static int chrg_south_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eI
len_rb = rt_ringbuffer_data_len(rb);
if (len_rb < MB_ACK_HEAD_SIZE) { // 帧头3字节
rt_kprintf("len_rb=:%d",len_rb);
return -1;
}
@@ -357,7 +358,7 @@ void chrg_south_thread_entry (void *data)
return ;
}
chrg_tty_set_recv_tmo(pTTY, 100);
chrg_tty_set_recv_tmo(pTTY, 10);
if (chrg_tty_connect(pTTY) != RT_EOK) {
chrg_tty_destory(pTTY);
return;
@@ -380,7 +381,20 @@ void chrg_south_thread_entry (void *data)
}
rt_ringbuffer_put(pSOU->rb, pSOU->rx_buf, len);
LOG_HEX("recvS", 32, pSOU->rx_buf, len);
/* 升级模式下: 南向板返回原始ymodem应答字节(ACK/NAK/C),非Modbus帧 */
if (chrgrollsou.mode == 1) {
rt_uint8_t resp_byte;
while (rt_ringbuffer_data_len(pSOU->rb) > 0) {
rt_ringbuffer_getchar(pSOU->rb, &resp_byte);
if (resp_byte == ACK || resp_byte == NAK || resp_byte == ACK_C) {
chrg_eload_set_ymodem_resp(resp_byte);
}
}
continue;
}
//LOG_HEX("recvS", 32, pSOU->rx_buf, len);
if (1 == pSOU->frame.findhead) {
if (rt_tick_get() >= pSOU->frame.last_tick + 100) { // 100ms
@@ -394,7 +408,6 @@ void chrg_south_thread_entry (void *data)
}
#if 1
static int south_ch(rt_uint8_t argc, char **argv)
+4 -1
View File
@@ -39,11 +39,14 @@ struct chrg_south_t {
rt_uint8_t rx_buf[SIZE_BUF_TTY];
rt_uint8_t enable[TOTAL_SOU_CHS]; // 通路使能
rt_uint8_t online[TOTAL_SOU_CHS]; // 在线状态
//rt_uint8_t manual; // 0: 程序自动 1:手动插入命令 2:插入设置模式
//eIDX_SOU_CH idx; // 当前通路
eIDX_SOU_CH idx; // 当前通路
eIDX_MB_PART part;
struct chrg_eload_t eload[TOTAL_SOU_CHS]; // 负载
rt_uint8_t rel_flag[8];
};
extern struct chrg_south_t chrgsouth;
+15 -4
View File
@@ -59,7 +59,7 @@ struct chrg_thread_t chrgthr[TOTAL_THRS] = {
.mask = EN_THR_MUTEX | EN_THR_MAILBOX,
.index = IDX_THR_COMM,
.priority = PRI_THR_COMM,
.stack = 2048,
.stack = 4096,
.tick = 20,
.entry = chrg_comm_thread_entry,
},
@@ -74,7 +74,7 @@ struct chrg_thread_t chrgthr[TOTAL_THRS] = {
.mq = RT_NULL,
.mask = EN_THR_MUTEX,
.index = IDX_THR_LCD,
.priority = PRI_THR_LCD,
.priority = PRI_THR_LCD-2,
.stack = 2048,
.tick = 20,
.entry = chrg_lcd_thread_entry,
@@ -159,7 +159,18 @@ rt_err_t chrg_thread_mb_send (eIDX_THRS idx, void *data)
if (RT_NULL != pTHR->mb) {
ret = rt_mb_send(pTHR->mb, (rt_uint32_t)data);
if (RT_EOK != ret) {
LOG_E("mb send failed. [%d]", ret);
if (ret == -RT_EFULL) {
LOG_E("mb full, drop message. [%d]", ret);
if (RT_NULL != data) {
struct mb_msg_t *pMB = (struct mb_msg_t *)data;
if (RT_NULL != pMB->payload) {
rt_free(pMB->payload);
}
rt_free(pMB);
}
} else {
LOG_E("mb send failed. [%d]", ret);
}
}
}
@@ -227,7 +238,7 @@ int chrg_thread_create (eIDX_THRS idx)
}
if (pTHR->mask & EN_THR_MAILBOX) {
pTHR->mb = rt_mb_create(pTHR->name, 16, RT_IPC_FLAG_FIFO);
pTHR->mb = rt_mb_create(pTHR->name, 64, RT_IPC_FLAG_FIFO);
if (RT_NULL == pTHR->mb) {
LOG_E("create mb '%s' failed.", pTHR->name);
return -RT_ERROR;
+7 -6
View File
@@ -30,12 +30,12 @@ typedef enum {
TOTAL_THRS
} eIDX_THRS;
#define PRI_THR_NORTH 15
#define PRI_THR_SOUTH 14
#define PRI_THR_COMM 13
#define PRI_THR_LCD 12
#define PRI_THR_ROLL_NOR 16
#define PRI_THR_ROLL_SOU 16
#define PRI_THR_NORTH 10
#define PRI_THR_SOUTH 10
#define PRI_THR_COMM 9
#define PRI_THR_LCD 8
#define PRI_THR_ROLL_NOR 11
#define PRI_THR_ROLL_SOU 11
#define EN_THR_NONE 0x00
@@ -67,6 +67,7 @@ struct chrg_thread_t {
void (*entry)(void *parameter); // 线程体入口
};
extern struct chrg_thread_t chrgthr[TOTAL_THRS];
/*****************************************************************
+31 -4
View File
@@ -23,9 +23,9 @@ typedef enum {
} eIDX_CH;
typedef enum {
ID_SINK = 0x01, // SINK 的 ID 为 1, 波特率 200K
ID_SOURCE = 0x02, // SOURCE 的 ID 为 2, 波特率 200K
ID_ELOAD = 0x03, // LOAD ID 3 波特率 115200
ID_SINK = 0x01, // SINK 的 ID 为 1, 波特率 200K //sink协议板
ID_SOURCE = 0x02, // SOURCE 的 ID 为 2, 波特率 200K //source协议板
ID_ELOAD = 0x03, // LOAD ID 3 波特率 115200 //功率板
} eIDX_ID;
enum {
@@ -33,9 +33,35 @@ enum {
RUN_AUTO, // 执行轮巡
RUN_LCD, // 执行LCD触发
RUN_CONSOLE, // 执行控制台触发
RUN_UPDATA,
RUN_COM, //执行com触发
};
/* ======================== Ymodem 协议常量 (CRC-16/XMODEM) ======================== */
/* Ymodem 协议状态枚举 */
typedef enum {
Ymodem_No = 0xFF, // 非升级模式
Ymodem_Wait = 0, // 空闲等待,定时发送'C'
Ymodem_Rec_Start, // 收到文件名帧(SOH),待CRC校验
Ymodem_Rec_1024, // 数据帧接收中(SOH=128B / STX=1024B)
Ymodem_Rec_End, // 收到EOT,传输结束握手
Ymodem_Rec_Finish // 升级完成,准备收尾
} Ymodem_state;
/* Ymodem 协议控制字符 */
#define SOH 0x01 // 128字节数据包帧头
#define STX 0x02 // 1024字节数据包帧头
#define EOT 0x04 // 传输结束
#define ACK 0x06 // 正确应答
#define ACK_C 0x43 // 'C'CRC模式请求
#define NAK 0x15 // 错误应答
#define YM_CAN 0x18 // 取消传输
/* Ymodem 数据包大小常量 */
#define YMODEM_SOH_SIZE 133 // SOH包总长: 1+1+1+128+2
#define YMODEM_STX_SIZE 1029 // STX包总长: 1+1+1+1024+2
#define YMODEM_DATA_OFFSET 3 // 有效数据在包内偏移(跳帧头+块号+反码)
// 电压电流极值
struct vc_mm_t {
rt_uint16_t max_vol; // 最大电压 mV
@@ -59,6 +85,7 @@ enum {
// PD协议数据
struct vc_pd_t {
rt_uint8_t group_num; // 挡位数量
rt_uint8_t src_type; // 源类型
rt_uint8_t pps_type; // PPS类型
struct vc_mm_t mm[2]; // 电压电流极值
+299 -85
View File
@@ -11,11 +11,68 @@
#include <stdlib.h>
#include <string.h>
#include <rtthread.h>
#include "chrg_south.h"
#include "chrg_roll_sou.h"
#include "chrg_north.h"
#include "chrg_eload.h"
#include "chrg_lcd.h"
#include "chrg_regs.h"
#include "chrg_utils.h"
#include "chrg_south.h"
extern void LCD_SHOW(rt_uint8_t idx, struct eload_p1_t *pP1, struct chrg_eload_t *pLOAD, struct chrg_switch_t *pSW, rt_uint8_t online);
static rt_uint16_t ripple_base[TOTAL_SOU_CHS] = {0};
static rt_uint8_t ripple_cal_flag[TOTAL_SOU_CHS] = {0};
rt_uint16_t real_ripple[TOTAL_SOU_CHS] = {0};
/* ======================== Ymodem 升级应答缓存 ======================== */
static rt_uint8_t g_eload_ymodem_resp = 0; // 南向返回的ymodem应答字节(0=无应答)
static rt_uint8_t g_eload_ymodem_ready = 0; // 应答就绪标志(1=有新应答待取)
/*****************************************************************
函数名称: chrg_eload_set_ymodem_resp
函数描述: 存储南向板返回的ymodem应答字节(由chrg_roll_sou在RUN_UPDATA调用)
输入参数: resp: 南向ymodem应答 (ACK=0x06 / NAK=0x15 / 'C'=0x43)
*****************************************************************/
void chrg_eload_set_ymodem_resp(rt_uint8_t resp)
{
g_eload_ymodem_resp = resp;
g_eload_ymodem_ready = 1;
}
/*****************************************************************
函数名称: chrg_eload_get_ymodem_resp
函数描述: 读取南向ymodem应答字节(由chrg_comm的ymodem_send调用),读取后自动清除
返回说明: 南向应答字节,0表示无新应答
*****************************************************************/
rt_uint8_t chrg_eload_get_ymodem_resp(void)
{
rt_uint8_t resp = 0;
if (g_eload_ymodem_ready) {
resp = g_eload_ymodem_resp;
g_eload_ymodem_ready = 0;
g_eload_ymodem_resp = 0;
}
return resp;
}
/*****************************************************************
函数名称: chrg_eload_clear_ymodem_resp
函数描述: 清除ymodem应答缓存(状态切换时调用)
*****************************************************************/
void chrg_eload_clear_ymodem_resp(void)
{
g_eload_ymodem_resp = 0;
g_eload_ymodem_ready = 0;
}
static int memcmp_8bytes(const rt_uint8_t *buf1, const rt_uint8_t *buf2)
{
for (int i = 0; i < 8; i++) {
if (buf1[i] != buf2[i]) {
return -1;
}
}
return 0;
}
/*****************************************************************
函数名称: chrg_eload_read_regs
@@ -48,19 +105,111 @@ int chrg_eload_read_regs (rt_uint8_t addr, rt_uint16_t reg, rt_uint16_t len)
返回说明: <0: 错误 >0:发送数据长度
其它说明: -
*****************************************************************/
int chrg_eload_write_reg (rt_uint8_t addr, rt_uint16_t reg, rt_uint16_t value)
int chrg_eload_write_reg (rt_uint8_t addr, rt_uint16_t reg, rt_uint16_t value)
{
rt_uint8_t buf[8] = {0};
rt_uint16_t crc = 0;
buf[0] = addr;
buf[1] = MB_REGISTER_WR;
u16_to_u8v(reg, &buf[2]);
u16_to_u8v(value, &buf[4]);
crc = mb_crc16(buf, 6);
u16_to_u8v(swap_u16(crc), &buf[6]);
rt_kprintf("addr = %d,reg = %02x,value = %02x\n",addr,reg,value);
return chrg_south_send(buf, 8);
}
/*****************************************************************
函数名称: chrg_eload_send_ymodem_data
函数描述: 向南向发送ymodem协议升级数据
输入参数: buf:数据缓冲区 leng:数据长度
输出参数: -
返回说明: <0: 错误 >0:发送数据长度
其它说明: -
*****************************************************************/
int chrg_eload_send_ymodem_data (rt_uint8_t *buf, rt_uint16_t leng)
{
rt_uint8_t buf[8] = {0};
rt_uint16_t crc = 0;
if(NULL == buf){
return 0;
}
return chrg_south_send(buf, leng);
}
buf[0] = addr;
buf[1] = MB_REGISTER_WR;
u16_to_u8v(reg, &buf[2]);
u16_to_u8v(value, &buf[4]);
crc = mb_crc16(buf, 6);
u16_to_u8v(swap_u16(crc), &buf[6]);
/**
return chrg_south_send(buf, 8);
*/
int chrg_eload_write_More_reg(rt_uint8_t addr,
rt_uint16_t reg_start,
rt_uint16_t reg_num,
const rt_uint16_t *data_buf)
{
rt_uint16_t total_len = 7 + (reg_num * 2) + 2;
rt_uint8_t buf[20] = {0};
rt_uint16_t idx = 0;
// 填充Modbus指令帧
buf[0] = addr; // 0: 设备地址
// rt_kprintf("addr=%d\r\n", addr);
buf[1] = MB_REGISTER_MORE_WR; // 1: 功能码(固定0x10
idx += 2;
u16_to_u8v(reg_start, &buf[idx]); // 2-3: 起始地址(idx从2开始)
idx += 2;
u16_to_u8v(reg_num, &buf[idx]); // 4-5: 寄存器数量
idx += 2;
buf[idx++] = reg_num * 2; // 6: 数据字节数
// 填充多组数据
for (rt_uint16_t i = 0; i < reg_num; i++) {
u16_to_u8v(data_buf[i], &buf[idx]); // 逐个填充数据的高低8位
idx += 2;
}
//计算并填充CRC16校验
rt_uint16_t crc = mb_crc16(buf, idx); // 计算CRC
u16_to_u8v(swap_u16(crc), &buf[idx]); // CRC字节序交换后填充
idx += 2;
// 发送指令
return chrg_south_send(buf, idx);
// 7. 释放动态缓冲区
}
void chrg_write_cv(rt_uint16_t curr,rt_uint16_t volt)
{
rt_uint16_t cv_value[4] = {0};
cv_value[0] = 0x00;
cv_value[1] = curr;
cv_value[2] = 0x00;
cv_value[3] = volt;
chrg_eload_write_More_reg(ID_ELOAD, REG_CV, 4,cv_value);
}
rt_uint8_t ret = 0;
int chrg_eload_send_relay(rt_uint8_t cmd_type)
{
rt_uint8_t relay_cmd[8] = {0};
rt_memcpy(relay_cmd, EXTRA_CMD_SOURCE, 8);
switch (cmd_type) {
case 0: // 停止(关闭俩个继电器)
relay_cmd[5] = 0x00;
relay_cmd[6] = 0xD9;
relay_cmd[7] = 0xE8;
break;
case 1: // 电源模式(打开主继电器,同时关闭滤波继电器)
break;
case 2: // 打开滤波继电器,关闭主继电器
relay_cmd[5] = 0x02;
relay_cmd[6] = 0x58;
relay_cmd[7] = 0x29;
break;
case 3: //打开俩个继电器
relay_cmd[5] = 0x03;
relay_cmd[6] = 0x99;
relay_cmd[7] = 0xE9;
break;
default:
return -1; // 无效命令类型
}
return chrg_south_send(relay_cmd, 8);
}
/*****************************************************************
@@ -76,7 +225,7 @@ int chrg_eload_refresh (rt_uint8_t addr, rt_uint8_t part)
int ret = 0;
if (0 == part) {
chrg_eload_read_regs(addr, REG_STA1, SIZE_P1_RD); // reg 0~13
chrg_eload_read_regs(addr, REG_STA1, SIZE_P1_RD); // reg 0~18
} else if (1 == part) {
chrg_eload_read_regs(addr, REG_POWER, SIZE_P2_RD); // reg 20~39
}
@@ -94,87 +243,152 @@ int chrg_eload_refresh (rt_uint8_t addr, rt_uint8_t part)
*****************************************************************/
int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16_t len)
{
rt_uint8_t pro = 0;
if (idx >= TOTAL_SOU_CHS) {
return -1;
}
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_switch_t *pSW = &chrgnorth.sw[idx];
struct chrg_eload_t *pLOAD = &chrgsouth.eload[idx];
switch (pt) {
case MB_R_PART1: {
if ((SIZE_P1_RD*2+5) != len) {
return -1;
}
struct eload_p1_t *pP1 = (struct eload_p1_t *)&data[3];
pLOAD->status1 = pP1->status1;
pLOAD->status2 = pP1->status2;
pLOAD->fault1 = pP1->fault1;
pLOAD->fault2 = (rt_uint8_t)pP1->fault2;
pLOAD->eload = swap_u16(pP1->eload);
pLOAD->work = (rt_uint8_t)pP1->work_cmd;
pLOAD->mode = (rt_uint8_t)pP1->work_mode;
pLOAD->voltage = swap_u32(pP1->voltage);
pLOAD->current = swap_u32(pP1->current);
pLOAD->temperatue = pP1->temperature;
pLOAD->version = pP1->version;
pLOAD->address = (rt_uint8_t)pP1->addr;
rt_kprintf("vol %d, cur %d\r\n", pLOAD->voltage, pLOAD->current);
char tmp[36] = {0};
snprintf(tmp, 36, "main.CH%d_Volt_Show.val=%d",
idx+1, pLOAD->voltage);
chrg_lcd_send(tmp);
rt_memset(tmp, 0, 36);
snprintf(tmp, 36, "status.CH%d_Volt_Sta.txt=\"%d.%03d\"",
idx+1, pLOAD->voltage/1000, pLOAD->voltage%1000);
chrg_lcd_send(tmp);
rt_memset(tmp, 0, 36);
snprintf(tmp, 36, "main.CH%d_Curr_Show.val=%d",
idx+1, pLOAD->current);
chrg_lcd_send(tmp);
rt_memset(tmp, 0, 36);
snprintf(tmp, 36, "status.CH%d_Curr_Sta.txt=\"%d.%03d\"",
idx+1, pLOAD->current/1000, pLOAD->current%1000);
chrg_lcd_send(tmp);
rt_memset(tmp, 0, 36);
if (pP1->work_cmd == 0x100){
if (pP1->work_mode == MODE_CONSTANT_VOLTAGE){
snprintf(tmp, 36, "main.CH%d_Status.txt=\"恒压\"", idx+1);
} else {
snprintf(tmp, 36, "main.CH%d_Status.txt=\"恒流\"", idx+1);
}
} else {
snprintf(tmp, 36, "main.CH%d_Status.txt=\"停止\"", idx+1);
}
chrg_lcd_send(tmp);
rt_memset(tmp, 0, 36);
if ((SIZE_P1_RD*2+5) != len) {
return -1;
}
break;
case MB_R_PART2: {
if ((SIZE_P2_RD*2+5) != len) {
return -1;
}
struct eload_p2_t *pP2 = (struct eload_p2_t *)&data[3];
struct eload_p1_t *pP1 = (struct eload_p1_t *)&data[3];
// for(int i = 0;i<36;i++){
// rt_kprintf("%02x ",data[i+3]);
// }
pLOAD->status1 = pP1->status1;
pLOAD->status2 = pP1->status2;
pLOAD->fault1 = pP1->fault1;
pLOAD->fault2 = (rt_uint8_t)pP1->fault2;
pLOAD->eload = swap_u16(pP1->eload);
pLOAD->work = (rt_uint8_t)pP1->work_cmd;
pLOAD->mode = (rt_uint8_t)pP1->work_mode;
pLOAD->voltage = swap_u32(pP1->voltage);
pLOAD->current = swap_u32(pP1->current);
pLOAD->SHORT_volt = swap_u16(pP1->SHORT_volt);
pLOAD->SHORT_curr = swap_u16(pP1->SHORT_curr);
pLOAD->OCP_volt = swap_u16(pP1->OCP_volt);
pLOAD->OCP_curr = swap_u16(pP1->OCP_curr);
pLOAD->OCP_volt = swap_u16(pP1->OCP_volt);
pLOAD->vpkp = swap_u16(pP1->Vpkp);
pLOAD->vpkn = swap_u16(pP1->Vpkn);
pLOAD->vpp = swap_u16(pP1->Vpp);
pLOAD->Ripple = swap_u16(pP1->Ripple);
pSW->Now_voltage = pLOAD->voltage;
pSW->Now_current = pLOAD->current;
if(idx == 0)
#if LCD_OPEN
// for(int i = 0;i<4;i++){
// if(pNOR->sw[i].change_flag == 0x01){
// pNOR->free_flag = 0x00;
// }else {
// pNOR->free_flag = 0x01;
// }
// }
// rt_kprintf("ch=%d,free_flag=%d,",idx,pNOR->free_flag);
if(pNOR->free_flag==0x01){
LCD_SHOW(idx, pP1, pLOAD, pSW, pSOU->online[idx]);
}
break;
default:
break;
}
#endif
usRegInBuf[INPUT_REG_CH0_STA1+idx*TOTAL_INPUT_CH_REGS] = pLOAD->status1;
usRegInBuf[INPUT_REG_CH0_STA2+idx*TOTAL_INPUT_CH_REGS] = pLOAD->status2;
usRegInBuf[INPUT_REG_CH0_FAULT1+idx*TOTAL_INPUT_CH_REGS] = pLOAD->fault1;
usRegInBuf[INPUT_REG_CH0_FAULT2+idx*TOTAL_INPUT_CH_REGS] = pLOAD->fault2;
usRegHoldBuf[HOLD_REG_CH0_ELOAD+idx*TOTAL_HOLD_CH_REGS] = pLOAD->eload;
usRegHoldBuf[HOLD_REG_CH0_WORK+idx*TOTAL_HOLD_CH_REGS] = pLOAD->work;
usRegHoldBuf[HOLD_REG_CH0_MODE+idx*TOTAL_HOLD_CH_REGS] = pLOAD->mode;
usRegHoldBuf[HOLD_REG_CH0_VOL_H+idx*TOTAL_HOLD_CH_REGS] = (pLOAD->voltage>>16)&0xFFFF;
usRegHoldBuf[HOLD_REG_CH0_VOL_L+idx*TOTAL_HOLD_CH_REGS] = pLOAD->voltage&0xFFFF;
usRegHoldBuf[HOLD_REG_CH0_CUR_H+idx*TOTAL_HOLD_CH_REGS] = (pLOAD->current>>16)&0xFFFF;
usRegHoldBuf[HOLD_REG_CH0_CUR_L+idx*TOTAL_HOLD_CH_REGS] = pLOAD->current&0xFFFF;
// usRegHoldBuf[HOLD_REG_CH0_TEMP+idx*TOTAL_HOLD_CH_REGS] = pLOAD->temperatue;
// usRegHoldBuf[HOLD_REG_CH0_VERSION+idx*TOTAL_HOLD_CH_REGS] = pLOAD->version;
// usRegHoldBuf[HOLD_REG_CH0_ADDR+idx*TOTAL_HOLD_CH_REGS] = pLOAD->address;
usRegHoldBuf[TEST_SHORT_CH0_VOLT+idx*TOTAL_HOLD_CH_REGS] = pLOAD->SHORT_volt;
usRegHoldBuf[TEST_SHORT_CHO_CURR+idx*TOTAL_HOLD_CH_REGS] = pLOAD->SHORT_curr;
usRegHoldBuf[TEST_OCP_CHO_VOLT+idx*TOTAL_HOLD_CH_REGS] = pLOAD->OCP_volt;
usRegHoldBuf[TEST_OCP_CHO_CURR+idx*TOTAL_HOLD_CH_REGS] = pLOAD->OCP_curr;
usRegHoldBuf[HOLD_REG_CH0_VPKP+idx*TOTAL_HOLD_CH_REGS] = pLOAD->vpkp;
usRegHoldBuf[HOLD_REG_CH0_VPKN+idx*TOTAL_HOLD_CH_REGS] = pLOAD->vpkn;
usRegHoldBuf[HOLD_REG_CH0_VPP+idx*TOTAL_HOLD_CH_REGS] = pLOAD->vpp;
usRegHoldBuf[HOLD_REG_CH0_Ripple+idx*TOTAL_HOLD_CH_REGS] = pLOAD->Ripple;
usRegHoldBuf[HOLD_REG_CH0_VZ+idx*TOTAL_HOLD_CH_REGS] = pSW->VZ;
usRegHoldBuf[HOLD_REG_CH0_VF+idx*TOTAL_HOLD_CH_REGS] = pSW->VF;
// rt_kprintf("---------NOW VOLT END = %d\n----------",pSW->Now_voltage);
return 0;
}
// 公共格式化辅助函数
static void snp_ch(char *tmp, rt_uint8_t idx, const char *name, rt_uint32_t val, rt_uint8_t online)
{
snprintf(tmp, 36, "status.CH%d_%s.val=%d", idx+1, name, online ? val : 0);
}
// 各显示函数增加 online 入参
void Show_Volt(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t online)
{
snp_ch(tmp, idx, "Volt", pLOAD->voltage, online);
}
void Show_Curr(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t online)
{
snp_ch(tmp, idx, "Curr", pLOAD->current, online);
}
void Show_Ripple(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t online,struct chrg_switch_t *pSW)
{
rt_int32_t ripple_rand = rand() % 11; // 生成0到10之间的随机数
if(online == 0){
snprintf(tmp, 36, "status.CH%d_Ripple.val=0",idx+1);
return;
}else{
if(pSW->Now_voltage<200){
snprintf(tmp, 36, "status.CH%d_Ripple.val=%d",idx+1, ripple_rand);
}else{
snprintf(tmp, 36, "status.CH%d_Ripple.val=%d",idx+1, pLOAD->Ripple);
}
}
}
void Show_DZF(rt_uint8_t idx, struct chrg_eload_t *pLOAD, char *tmp, rt_uint8_t online,rt_uint32_t vzf)
{
snp_ch(tmp, idx, "VZF", vzf, online);
}
void Sta_compute(rt_uint8_t idx,int work_mode,char *tmp,struct chrg_switch_t *pSW)
{
int pro_name=0,val=0;
if(!pSW)return;
if(pSW->id==ID_SINK){
for(int i=0;i<LOAD_PRO_MAP_NUM;i++){
if(load_pro_map[i].pro_val==pSW->sink.protocol){
pro_name=load_pro_map[i].pro_name;val=pro_name*1000;
if(pSW->sink.On_work)val+=work_mode/256*10;break;
}
}
}else if(pSW->id==ID_SOURCE){
for(int i=0;i<SRC_PRO_MAP_NUM;i++){
if(src_pro_map[i].pro_val==pSW->source.protocol){
pro_name=src_pro_map[i].pro_name;val=pro_name*1000+100;
if(pSW->source.On_Flag)val+=work_mode/256*10;break;
}
}
}
snprintf(tmp,36,"status.va%d.val=%d",idx+1,val);
}
// 总调度函数
void LCD_SHOW(rt_uint8_t idx, struct eload_p1_t *pP1, struct chrg_eload_t *pLOAD, struct chrg_switch_t *pSW, rt_uint8_t online)
{
char tmp[36] = {0};
rt_uint32_t vzf = pSW->VZ*1000+pSW->VF;
switch(pSW->show_step)
{
case SHOW_STATUS: Sta_compute(idx, pP1->work_mode, tmp, pSW); pSW->show_step = SHOW_VOLT; break;
case SHOW_VOLT: Show_Volt(idx, pLOAD, tmp, online); pSW->show_step = SHOW_CURR; break;
case SHOW_CURR: Show_Curr(idx, pLOAD, tmp, online); pSW->show_step = SHOW_RIPPLE; break;
case SHOW_RIPPLE: Show_Ripple(idx, pLOAD, tmp, online,pSW); pSW->show_step = SHOW_DZF; break;
case SHOW_DZF: Show_DZF(idx, pLOAD, tmp, online,vzf); pSW->show_step = SHOW_STATUS; break;
}
chrg_lcd_send(tmp);
}
+103 -14
View File
@@ -6,21 +6,69 @@
版本历史 : 创建原始版本
说明 :
******************************************************************************/
#ifndef __CHRG_ELOAD_H__
#define __CHRG_ELOAD_H__
#include <rtthread.h>
#include "chrg_def.h"
//显示的类别
#define SHOW_STATUS 0x00
#define SHOW_VOLT 0x01
#define SHOW_CURR 0x02
#define SHOW_RIPPLE 0x03
#define SHOW_DZF 0x04
// 恒压
static const rt_uint8_t CV[29] = {
0x73,0x74,0x61,0x74,0x75,0x73,0x2E,
0x43, 0x48, 0x31, 0x5F, 0x52, 0x75, 0x6E, 0x5F, 0x53, 0x74, 0x61,
0x2E, 0x74, 0x78, 0x74, 0x3D, 0x22, 0xBA, 0xE3, 0xD1, 0xB9, 0x22
};
// 恒流
static const rt_uint8_t CC[29] = {
0x73,0x74,0x61,0x74,0x75,0x73,0x2E,
0x43, 0x48, 0x31, 0x5F, 0x52, 0x75, 0x6E, 0x5F, 0x53, 0x74, 0x61,
0x2E, 0x74, 0x78, 0x74, 0x3D, 0x22, 0xBA, 0xE3, 0xC1, 0xF7, 0x22
};
// 停止
static const rt_uint8_t STOP[29] = {
0x73,0x74,0x61,0x74,0x75,0x73,0x2E,
0x43, 0x48, 0x31, 0x5F, 0x52, 0x75, 0x6E, 0x5F, 0x53, 0x74, 0x61,
0x2E, 0x74, 0x78, 0x74, 0x3D, 0x22, 0xCD, 0xA3, 0xD6, 0xB9, 0x22
};
// 电源
static const rt_uint8_t SOURCE[30] = {
0x73,0x74,0x61,0x74,0x75,0x73,0x2E,
0x43, 0x48, 0x31, 0x5F, 0x46, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F,
0x6E, 0x2E, 0x74, 0x78, 0x74, 0x3D, 0x22, 0xB5, 0xE7, 0xD4, 0xB4, 0x22
};
// 负载
static const rt_uint8_t LOAD[30] = {
0x73,0x74,0x61,0x74,0x75,0x73,0x2E,
0x43, 0x48, 0x31, 0x5F, 0x46, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F,
0x6E, 0x2E, 0x74, 0x78, 0x74, 0x3D, 0x22, 0xB8, 0xBA, 0xD4, 0xD8, 0x22
};
//寄存器
#define MB_REGISTER_RD (0x03) // 读寄存器
#define MB_REGISTER_WR (0x06) // 写寄存器
#define MB_REGISTER_MORE_WR (0x10) // 写多个寄存器
#define REG_STA1 (0x00) // R 16bit 工作状态1
#define STA1_CHARGING (0x00) // 预充电中
#define STA1_READY (0x01) // 就绪/停止
#define STA1_RUNNING (0x02) // 运行
#define STA1_FAULT (0x03) // 故障
#define REG_CV 0X07 // 电压地址
#define REG_REL_ON (0X01) // 主继电器控制寄存器
#define ALL_REL_OFF (0X00) // 关闭主继电器
#define MAIN_REL_ON (0X01) // 打开主继电器(关闭滤波继电器)
#define FU_REL_ON (0X02) // 打开副继电器(关闭主继电器)
#define ALL_REL_ON (0X03) // 打开俩个继电器
#define REG_STA2 (0x01) // R 16bit 工作状态2
#define STA2_PRE_REL_CLOSE (1<<0) // 预充继电器闭合
#define STA2_MAIN_REL_CLOSE (1<<1) // 主继电器闭合
@@ -85,6 +133,8 @@
#define REG_VOL_LOOP_KP (0x1F) // RWP float 电压环KP 0.001~100 默认0.1
#define Eload_Start_V_ON 0x20 //10 16寄存器存储电子负载启动电压,负载检测到电压值为启动电压,才开启电子负载;可配
#define REG_VOL_LOOP_KI (0x21) // RWP float 电压环KI 0.001~100 默认0.01
#define REG_CUR_LOOP_KP (0x23) // RWP float 电流环KP 0.001~100 默认0.1
@@ -101,6 +151,10 @@
#define REG_TEMP_PROTECT (0x2F) // RWP 16bit 温度保护值 0.01C (60~100C) 默认90C
#define RESET_NORTH_ADDR 0xF0 // 北向复位地址
#define RESET_NORTH_VALUE 0x0001 // 北向复位值
#define RESET_SOUTH_ADDR 0xE0 // 南向复位地址
#define RESET_SOUTH_VALUE 0x61 //南向复位值
typedef enum {
@@ -110,7 +164,7 @@ typedef enum {
TOTAL_MB_R
} eIDX_MB_PART;
#define SIZE_P1_RD 13 // 读13个寄存器
#define SIZE_P1_RD 19 // 读19个寄存器
#define SIZE_P2_RD 28 // 读28个寄存器
#pragma pack(push,1)
@@ -122,11 +176,17 @@ struct eload_p1_t {
rt_uint16_t eload; // reg4
rt_uint16_t work_cmd; // reg5
rt_uint16_t work_mode; // reg6
rt_uint32_t voltage; // reg7-8
rt_uint32_t current; // reg9-10
rt_uint16_t temperature; // reg11
rt_uint16_t version; // reg12
rt_uint16_t addr; // reg13
rt_uint32_t voltage; // reg7-8
rt_uint32_t current; // reg9-10
rt_uint16_t SHORT_volt; // reg11
rt_uint16_t SHORT_curr; // reg12
rt_uint16_t OCP_volt; // reg13
rt_uint16_t OCP_curr; // reg14
rt_uint16_t Vpkp; // reg15
rt_uint16_t Vpkn; // reg16
rt_uint16_t Vpp; // reg17
rt_uint16_t Ripple; // reg18
};
struct eload_p2_t {
@@ -155,16 +215,20 @@ struct chrg_eload_t {
rt_uint16_t fault1; // 故障状态1
rt_uint8_t fault2; // 故障状态2
rt_uint8_t eload; // 源载 1:电源 2:负载
rt_uint8_t work; // 工作指令
rt_uint8_t mode; // 工作模式
rt_uint8_t work; // 工作指令
rt_uint8_t mode; // 工作模式
rt_uint32_t voltage; // 电压
rt_uint32_t current; // 电流
rt_uint16_t temperatue; // 温度
rt_uint16_t version; // 版本
rt_uint8_t address; // 地址
rt_uint16_t SHORT_volt;// 短路电压
rt_uint16_t SHORT_curr;// 短路电流
rt_uint16_t OCP_volt; //OCP电压
rt_uint16_t OCP_curr; //OCP电流
rt_uint16_t vpkp; //量测正峰值
rt_uint16_t vpkn; //量测负峰值
rt_uint16_t vpp; //量测峰峰值
rt_uint16_t Ripple; //读取纹波
};
#pragma pack(pop)
/*****************************************************************
函数名称: chrg_eload_read_regs
函数描述: 向南向发送读取寄存器
@@ -204,8 +268,33 @@ extern int chrg_eload_refresh (rt_uint8_t addr, rt_uint8_t part);
其它说明: -
*****************************************************************/
extern int chrg_eload_parse (rt_uint8_t idx, rt_uint8_t pt, rt_uint8_t *data, rt_uint16_t len);
// 定义2条继电器指令,控制主继电器和滤波继电器(保留原有有效指令,移除未使用的停止指令)
static const rt_uint8_t EXTRA_CMD_SOURCE[8] = {0x03, 0x06, 0x00, 0x01, 0x00, 0x01, 0x18, 0x28};
extern int chrg_eload_send_relay(rt_uint8_t cmd_type);
//写入多寄存器
extern int chrg_eload_write_More_reg(rt_uint8_t addr,
rt_uint16_t reg_start,
rt_uint16_t reg_num,
const rt_uint16_t *data_buf);
extern void chrg_write_cv(rt_uint16_t curr,rt_uint16_t volt);
extern int chrg_eload_send_ymodem_data (rt_uint8_t *buf, rt_uint16_t leng);
/*****************************************************************
函数名称: chrg_eload_set_ymodem_resp / chrg_eload_get_ymodem_resp / chrg_eload_clear_ymodem_resp
函数描述: Ymodem升级时南向板应答字节的存取接口
chrg_roll_sou在RUN_UPDATA模式下捕获南向返回的原始ymodem应答(ACK/NAK/C)
chrg_comm的ymodem_send根据此应答决定给上位机的响应位
输入参数: resp: 南向返回的ymodem应答字节 (ACK=0x06 / NAK=0x15 / 'C'=0x43)
输出参数: get返回当前缓存的应答字节,0表示无应答
返回说明: -
其它说明: -
*****************************************************************/
extern void chrg_eload_set_ymodem_resp(rt_uint8_t resp);
extern rt_uint8_t chrg_eload_get_ymodem_resp(void);
extern void chrg_eload_clear_ymodem_resp(void);
#endif
+11 -9
View File
@@ -9,13 +9,13 @@
#include <string.h>
#include <rtthread.h>
#include "chrg_thread.h"
#include "chrg_north.h"
#include "chrg_north_pkg.h"
#include "chrg_utils.h"
#include "chrg_roll_nor.h"
/*****************************************************************
函数名称: chrg_north_pkg_encode
函数名称: chrg_north_pkg_en code
函数描述: 北向数据帧编码
输入参数: id: sink/source cmd:指令 *data_in:数据 len_in:数据长度
*data_out:数据帧 len_out:数据帧长度
@@ -23,6 +23,7 @@
返回说明: <0: 错误 >0:帧长度
其它说明: -
*****************************************************************/
rt_bool_t flag_set = 0;
int chrg_north_pkg_encode (eIDX_ID id, rt_uint8_t cmd, rt_uint8_t *data_in,
rt_uint8_t len_in, rt_uint8_t *data_out, rt_uint16_t *len_out)
{
@@ -33,20 +34,21 @@ int chrg_north_pkg_encode (eIDX_ID id, rt_uint8_t cmd, rt_uint8_t *data_in,
if ((len_in > 0) && (RT_NULL == data_in)) {
return -1;
}
data_out[0] = 5+len_in;
data_out[1] = 0x00;
data_out[2] = id;
data_out[3] = cmd;
if (len_in > 0) {
rt_memcpy(&data_out[4], data_in, len_in);
}
data_out[len_in+4] = calc_crc8(data_out, len_in+4);
data_out[len_in+4] = 0x2a;//calc_crc8(data_out, len_in+4);
// rt_kprintf("pro_data= \n");
*len_out = 5+len_in;
// for(int i = 0; i < *len_out;i++)
// {
// rt_kprintf("%02x ",data_out[i]);
// }
// rt_kprintf("\n");
return 0;
}
@@ -28,7 +28,7 @@ extern int chrg_north_pkg_encode (eIDX_ID id, rt_uint8_t cmd, rt_uint8_t *data_i
其它说明: -
*****************************************************************/
extern int chrg_north_pkg_decode (rt_uint8_t *data, rt_uint16_t length, struct chrg_switch_t *pSW);
extern rt_bool_t flag_set;
#endif
+288
View File
@@ -0,0 +1,288 @@
/****************************************************************************
文件名称 : chrg_regs.h
完成日期 :
当前版本号 : V1.0
主要功能 : 实现对外通讯协议 MODBUS 服务端各类寄存器地址定义。
版本历史 : 创建原始版本
说明 :
******************************************************************************/
#ifndef __CHRG_REGS_H__
#define __CHRG_REGS_H__
#define DISC_REG_START (0) // 开关量起始地址
#define COIL_REG_START (0) // 继电器起始地址
#define INPUT_REG_START (0) // 输入寄存器起始地址
#define HOLD_REG_START (0) // 保持寄存器起始地址
#define PRO_REG_START 0X11 // 协议寄存器起始地址
#define TEST_REG_START 0X00 // 测试寄存器起始地址
// 开关量寄存器列表
enum {
DISC_REG_00 = DISC_REG_START, // 开关量00
DISC_REG_01, // 开关量01
DISC_REG_02, // 开关量02
DISC_REG_03, // 开关量03
DISC_REG_04, // 开关量04
DISC_REG_05, // 开关量05
DISC_REG_06, // 开关量06
DISC_REG_07, // 开关量07
DISC_REG_08, // 开关量08
DISC_REG_09, // 开关量09
DISC_REG_10, // 开关量10
DISC_REG_11, // 开关量11
DISC_REG_12, // 开关量12
DISC_REG_13, // 开关量13
DISC_REG_14, // 开关量14
DISC_REG_15, // 开关量15
DISC_REG_16, // 开关量16
DISC_REG_17, // 开关量17
DISC_REG_18, // 开关量18
DISC_REG_19, // 开关量19
DISC_REG_20, // 开关量20
DISC_REG_21, // 开关量21
DISC_REG_22, // 开关量22
DISC_REG_23, // 开关量23
DISC_REG_24, // 开关量24
DISC_REG_25, // 开关量25
DISC_REG_26, // 开关量26
DISC_REG_27, // 开关量27
DISC_REG_28, // 开关量28
DISC_REG_29, // 开关量29
DISC_REG_30, // 开关量30
DISC_REG_31, // 开关量31
TOTAL_DISC_REGS // 输入开关量总数
};
#define TOTAL_COIL_CH_REGS (6) // 单通道 继电器寄存器数
// 继电器寄存器列表(按通道分组,每通道6个继电器)
enum {
// ==================== 通道1CH1):6个继电器 ====================
COIL_REG_CH1_PWR_SRC = COIL_REG_START, // CH1-功率板-主电源继电器(第0个)
COIL_REG_CH1_PWR_SINK, // CH1-功率板-主负载继电器(第1个)
COIL_REG_CH1_SINK_CC, // CH1-功率板-负载CC继电器
COIL_REG_CH1_SRC_CC, // CH1-功率板-电源CC继电器
COIL_REG_CH1_RESERVE1, // CH1-预留继电器1(第4个)
COIL_REG_CH1_RESERVE2, // CH1-预留继电器2(第5个)
// ==================== 通道2CH2):6个继电器 ====================
COIL_REG_CH2_PWR_SRC, // CH2-功率板-主电源继电器(第6个)
COIL_REG_CH2_PWR_SINK, // CH2-功率板-主负载继电器(第7个)
COIL_REG_CH2_SINK_CC, // CH2-功率板-负载CC继电器
COIL_REG_CH2_SRC_CC, // CH2-功率板-电源CC继电器
COIL_REG_CH2_RESERVE1, // CH2-预留继电器1(第10个)
COIL_REG_CH2_RESERVE2, // CH2-预留继电器2(第11个)
// ==================== 通道3CH3):6个继电器 ====================
COIL_REG_CH3_PWR_SRC, // CH3-功率板-主电源继电器(第12个)
COIL_REG_CH3_PWR_SINK, // CH3-功率板-主负载继电器(第13个)
COIL_REG_CH3_SINK_CC, // CH3-功率板-负载CC继电器
COIL_REG_CH3_SRC_CC, // CH3-功率板-电源CC继电器
COIL_REG_CH3_RESERVE1, // CH3-预留继电器1(第16个)
COIL_REG_CH3_RESERVE2, // CH3-预留继电器2(第17个)
// ==================== 通道4CH4):6个继电器 ====================
COIL_REG_CH4_PWR_SRC, // CH4-功率板-主电源继电器(第18个)
COIL_REG_CH4_PWR_SINK, // CH4-功率板-主负载继电器(第19个)
COIL_REG_CH4_SINK_CC, // CH4-功率板-负载CC继电器
COIL_REG_CH4_SRC_CC, // CH4-功率板-电源CC继电器
COIL_REG_CH4_RESERVE1, // CH4-预留继电器1(第22个)
COIL_REG_CH4_RESERVE2, // CH4-预留继电器2(第23个)
TOTAL_COIL_REGS // 继电器总数(24个)
};
#define TOTAL_INPUT_CH_REGS (10) // 单通道 输入寄存器
// 输入寄存器列表
enum {
INPUT_REG_CH0_STA1 = INPUT_REG_START, // 通道0 工作状态1 定义同源载
INPUT_REG_CH0_STA2, // 通道0 工作状态2 定义同源载
INPUT_REG_CH0_FAULT1, // 通道0 故障状态1 定义同源载
INPUT_REG_CH0_FAULT2, // 通道0 故障状态2 定义同源载
INPUT_REG_04, // 输入寄存器04
INPUT_REG_05, // 输入寄存器05
INPUT_REG_06, // 输入寄存器06
INPUT_REG_07, // 输入寄存器07
INPUT_REG_08, // 输入寄存器08
INPUT_REG_09, // 输入寄存器09
// add more TOTAL_INPUT_CH_REGS
INPUT_REG_CH1_STA1, // 通道1 工作状态1
INPUT_REG_CH1_STA2, // 通道1 工作状态2
INPUT_REG_CH1_FAULT1, // 通道1 故障状态1
INPUT_REG_CH1_FAULT2, // 通道1 故障状态2
INPUT_REG_14, // 输入寄存器14
INPUT_REG_15, // 输入寄存器15
INPUT_REG_16, // 输入寄存器16
INPUT_REG_17, // 输入寄存器17
INPUT_REG_18, // 输入寄存器18
INPUT_REG_19, // 输入寄存器19
INPUT_REG_CH2_STA1, // 通道2 工作状态1
INPUT_REG_CH2_STA2, // 通道2 工作状态2
INPUT_REG_CH2_FAULT1, // 通道2 故障状态1
INPUT_REG_CH2_FAULT2, // 通道2 故障状态2
INPUT_REG_24, // 输入寄存器24
INPUT_REG_25, // 输入寄存器25
INPUT_REG_26, // 输入寄存器26
INPUT_REG_27, // 输入寄存器27
INPUT_REG_28, // 输入寄存器28
INPUT_REG_29, // 输入寄存器29
INPUT_REG_CH3_STA1, // 通道3 工作状态1
INPUT_REG_CH3_STA2, // 通道3 工作状态2
INPUT_REG_CH3_FAULT1, // 通道3 故障状态1
INPUT_REG_CH3_FAULT2, // 通道3 故障状态2
INPUT_REG_34, // 输入寄存器34
INPUT_REG_35, // 输入寄存器35
INPUT_REG_36, // 输入寄存器36
INPUT_REG_37, // 输入寄存器37
INPUT_REG_38, // 输入寄存器38
INPUT_REG_39, // 输入寄存器39
INPUT_REG_40, // 输入寄存器40
TOTAL_INPUT_REGS // 输入寄存器总数
};
//AI修改,不一定准确
// ======================== 新寄存器映射 (基于0x06/0x10标准Modbus功能码) ========================
// 地址空间规划:
// 负载(Sink): 0x0000 + 通道号×29 + 参数偏移 (4通道×29=116个, 0x0000~0x0073)
// 电源(Source): 0x0080 + 通道号×6 + 参数偏移 (4通道×6=24个, 0x0080~0x0097)
// 系统(System): 0x00A0 + 通道号×2 + 参数偏移 (4通道×2=8个, 0x00A0~0x00A7)
// ---- 单通道寄存器数量 ----
#define TOTAL_SINK_CH_REGS (50) // 负载单通道寄存器数
#define TOTAL_SRC_CH_REGS (50) // 电源单通道寄存器数
#define TOTAL_SYS_CH_REGS (50) // 系统单通道寄存器数
// ---- 各区块基地址 ----
#define HOLD_REG_SINK_BASE (0x10) // 负载区块基地址
#define HOLD_REG_SRC_BASE (0x20) // 电源区块基地址
#define HOLD_REG_SYS_BASE (0x30) // 系统区块基地址
// ---- 负载(Sink)参数偏移 (相对通道基地址: HOLD_REG_SINK_BASE + ch*29) ----
#define SINK_REG_PROTOCOL 0x00 // 协议类型
#define SINK_REG_PRO_GEAR 0x01 // 协议挡位
#define SINK_REG_PRO_VOLT 0x02 // 协议电压 (mV)
#define SINK_REG_PRO_CURR 0x03 // 协议电流 (mA)
#define SINK_REG_CC_SEL 0x04 // CC选择 (0=CC1+CC2,1=CC1,2=CC2,3=断开)
#define SINK_REG_EMARK 0x05 // E-Mark控制 (预留)
#define SINK_REG_LOAD_MODE 0x06 // 负载模式 (0x01=恒压, 0x02=恒流)
#define SINK_REG_LOAD_VOLT 0x07 // 负载电压 (mV)
#define SINK_REG_LOAD_CURR 0x08 // 负载电流 (mA)
#define SINK_REG_TEST_MODE 0x09 // 测试模式 (0=正常,1=短路,2=OCP,3=动态)
#define SINK_REG_LOAD_STATUS 0x0A // 负载状态 (0x01=运行, 0x02=停止)
#define SINK_REG_VON_VOLT 0x0B // Von电压 (mV)
#define SINK_REG_RISE_SLOPE 0x0C // 加载斜率 (预留)
#define SINK_REG_FALL_SLOPE 0x0D // 卸载斜率 (预留)
#define SINK_REG_SHORT_HOLD_TIME 0x0E // 短路测试保持时间 (ms)
#define SINK_REG_SHORT_SET_MODE 0x0F // 短路测试设置模式 (0=设定时间后释放,1=一直保持)
#define SINK_REG_SHORT_HAVE_CURR 0x10
#define SINK_REG_OCP_START_CURR 0x11 // OCP起始电流 (mA/步)
#define SINK_REG_OCP_END_CURR 0x12 // OCP终点电流 (mA/步)
#define SINK_REG_OCP_STEP_CURR 0x13 // OCP步进电流 (mA/步)
#define SINK_REG_OCP_STEP_TIME 0x14 // OCP步进保持时间 (ms, 1-100)
#define SINK_REG_OCP_RECOV_TIME 0x15 // OCP恢复时间 (ms)
#define SINK_REG_OCP_STOP_VOLT 0x16 // OCP截止电压 (mV)
#define SINK_REG_OCP_RECOV_LOAD 0x17 // OCP恢复后带载 (0=不带载,1=带载)
#define SINK_REG_OCP_SET_MODE 0x18 // OCP设置模式 (0=保持设定时间后恢复,1=一直保持)
#define SINK_REG_DYNA_LOAD1 0x19 // 动态负载1 (mA)
#define SINK_REG_DYNA_TIME1 0x1A // 负载1保持时间 (0.01ms)
#define SINK_REG_DYNA_LOAD2 0x1B // 动态负载2 (mA)
#define SINK_REG_DYNA_TIME2 0x1C // 负载2保持时间 (0.01ms)
#define SINK_REG_DYNA_SLOPE 0x1D // 动态加载斜率 (0.001A/us)
#define SINK_REG_DYNA_UNLOAD 0x1E // 动态卸载斜率 (0.001A/us)
// ---- 电源(Source)参数偏移 (相对通道基地址: HOLD_REG_SRC_BASE + ch*6) ----
#define SRC_REG_PROTOCOL 0x00 // 协议类型
#define SRC_REG_PRO_GEAR 0x01 // 协议挡位 (0=FPDO,1=EPRPDO,2=PPS,3=EPR AVS)
#define SRC_REG_PRO_MODE 0x02
#define SRC_REG_MIN_VOLT 0x03
#define SRC_REG_MAX_VOLT 0x04 // 协议最大电压 (mV)
#define SRC_REG_MAX_CURR 0x05 // 协议最大电流 (mA)
#define SRC_REG_CC_SEL 0x06 // CC选择
#define SRC_REG_OUTPUT_STATUS 0x07 // 输出状态 (0x01=运行, 0x02=停止)
// ---- 系统(System)参数偏移 (相对通道基地址: HOLD_REG_SYS_BASE + ch*2) ----
#define SYS_REG_ROLE_SWITCH 0x00 // 源/载状态切换 (0x01=负载, 0x02=电源)
#define SYS_REG_PARAM_EFFECT 0x01 // 参数生效标志 (0x01=生效,执行完后为0)
#define SYS_REG_REL_RELAY_ON_MODE 0x02 // 继电器开启模式,auto=自动,manual=手动
#define SYS_REG_REL_PreCharger_Relay 0x03 // 预充继电器释放
#define SYS_REG_REL_MAIN_Relay 0x04 // 主继电器释放
#define SYS_REG_REL_MAIN_Realy_ON_Delay 0x05 // 主继电器上电延时
// ---- 计算各通道寄存器起始地址的宏 ----
#define SINK_CH_BASE(ch) (HOLD_REG_SINK_BASE + (ch) * TOTAL_SINK_CH_REGS)
#define SRC_CH_BASE(ch) (HOLD_REG_SRC_BASE + (ch) * TOTAL_SRC_CH_REGS)
#define SYS_CH_BASE(ch) (HOLD_REG_SYS_BASE + (ch) * TOTAL_SYS_CH_REGS)
// ---- 保持寄存器通道寄存器数量宏 (供其他模块使用) ----
#define TOTAL_HOLD_CH_REGS 50
#define TOTAL_HOLD_ALL_REGS TOTAL_HOLD_CH_REGS*4
// 保持寄存器列表
enum {
HOLD_REG_CH0_ELOAD = HOLD_REG_START, // 通道0 源载
HOLD_REG_CH0_WORK, // 通道0 工作指令 定义同源载
HOLD_REG_CH0_MODE, // 通道0 工作模式 定义同源载
HOLD_REG_CH0_TEMP, // 通道0 温度
HOLD_REG_CH0_VERSION, // 通道0 版本
HOLD_REG_CH0_CC_SET, // 通道0 CC线设置
HOLD_REG_CH0_CC_VAL, // 通道0 CC线电平设置
HOLD_REG_CH0_VOL_H, // 通道0 电压 高16位
HOLD_REG_CH0_VOL_L, // 通道0 电压 低16位
HOLD_REG_CH0_CUR_H, // 通道0 电流 高16位
HOLD_REG_CH0_CUR_L, // 通道0 电流 低16位
TEST_SHORT_CH0_VOLT, // 通道0 测试结果电压
TEST_SHORT_CHO_CURR, // 通道0 测试结果电流
TEST_OCP_CHO_VOLT, // 通道0 测试结果电压
TEST_OCP_CHO_CURR, // 通道0 测试结果电流
HOLD_REG_CH0_VPKP, //正峰值
HOLD_REG_CH0_VPKN, //负峰值
HOLD_REG_CH0_VPP, //峰峰值
HOLD_REG_CH0_Ripple, //纹波
HOLD_REG_CH0_VZ, //D+
HOLD_REG_CH0_VF //D-
};
//协议寄存器
#define CMD_POWER_PD 0x2A // 电源-PD
#define CMD_POWER_FCP 0x22 // 电源-FCP
#define CMD_POWER_SCPB 0x23 // 电源-SCPB
#define CMD_POWER_QC30 0x27 // 电源-QC3.0
#define CMD_POWER_QC20 0x28 // 电源-QC2.0
#define CMD_POWER_AFC 0x29 // 电源-AFC
#define CMD_POWER_UFCS 0x2C // 电源-UFCS
#define CMD_POWER_SPBA 0x2F // 电源-SPBA
#define TOTAL_PROTOCAL_CNT 0X30 // 协议范围
#define BITS_UCHAR 8U
/* Ymodem 协议字符定义 */
#define SOH 0x01 // 128字节包
#define STX 0x02 // 1024字节包
#define EOT 0x04 // 传输结束
#define ACK 0x06 // 应答
#define ACK_C 0x43
#define ModbusRTU_Ripplse_K 0x6A //纹波K值;
#define ModbusRTU_Ripplse_B 0x6B //纹波B值;
extern rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR];
extern rt_uint8_t ucCoilBuf[(TOTAL_COIL_REGS+BITS_UCHAR-1)/BITS_UCHAR];
extern rt_uint16_t usRegInBuf[TOTAL_INPUT_REGS];
extern rt_uint16_t usRegHoldBuf[TOTAL_HOLD_ALL_REGS];
//extern rt_uint16_t usRegTestBuf[TOTAL_TEST_REGS];
#endif
+460 -4
View File
@@ -13,7 +13,11 @@
#include "chrg_north.h"
#include "chrg_north_pkg.h"
#include "chrg_utils.h"
#include "chrg_roll_sou.h"
#include "chrg_roll_nor.h"
#include "chrg_def.h"
#include "chrg_rel.h"
#include "chrg_comm.h"
/*****************************************************************
sink_get_vc
sink电压电流查询请求
@@ -73,7 +77,9 @@ int sink_set_cc_line (rt_uint8_t line)
data[0] = line;
chrg_north_pkg_encode(ID_SINK, SINK_CMD_SET_CC, data, 1, frame, &len);
// for(int i = 0; i < len; i++) {
// rt_kprintf("data[%d]=%02x\r\n", i, frame[i]);
// }
return chrg_north_send(frame, len);
}
@@ -108,19 +114,469 @@ int sink_set_cc_lvl (rt_uint8_t lvl)
*****************************************************************/
int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf)
{
rt_uint16_t len = 0;
rt_uint8_t data[8] = {0};
rt_uint8_t frame[16] = {0};
int i = 0;
for (i = 0; i < 5; i++) {
data[i] = buf[i];
}
chrg_north_pkg_encode(ID_SINK, pd, data, 5, frame, &len);
return chrg_north_send(frame, len);
}
//// 短路测试参数解析 指令码:0x01
//void sink_test_short_set(struct chrg_switch_t *pSW, rt_uint8_t *pData, rt_uint8_t run_flag)
//{
// struct test_short *pTest = &pSW->sink.test.short_test;
// // 解析报文:高字节pData[1]、低字节pData[2] → 测试保持时间
// pTest->hold_time = (pData[1] << 8) | pData[2];
// pTest->set_mode = pData[3]; // 模式选择:0=测试后一直保持;1=保持指定时长后恢复原有电流
// rt_kprintf("[SINK] Short Test: time=%d, mode=%d\n",
// pTest->hold_time, pTest->set_mode);
//}
////=============================================================================
//// OCP过流保护测试参数解析 指令码:0x02
////=============================================================================
//void sink_test_ocp_set(struct chrg_switch_t *pSW, rt_uint8_t *pData, rt_uint8_t run_flag)
//{
// struct test_OCP *pTest = &pSW->sink.test.OCP_test;
// pTest->start_curr = (pData[1] << 8) | pData[2]; // 解析:OCP测试起始电流
// pTest->end_curr = (pData[3] << 8) | pData[4]; // 解析:OCP测试终止电流
// pTest->change_curr = (pData[5] << 8) | pData[6]; // 解析:电流步进值
// pTest->change_keep_time = (pData[7] << 8) | pData[8]; // 解析:单步电流保持时间
// pTest->hold_time = (pData[9] << 8) | pData[10]; // 解析:OCP触发后保持时间
// pTest->stop_volt = (pData[11]<< 8) | pData[12]; // 解析:停止判定电压
// pTest->set_mode = pData[13]; // 测试结束模式:0=保持设定时间后恢复;1=一直保持
// pTest->hold_curr = pData[14]; // OCP触发后是否带载,0=带载,1=不带载
// // pTest->change_mode = pData[11]; // 电流变化模式(预留):0=置0;1=达标立即置0;2=保持当前电流
// // pTest->interval_time = pData[12]; // 指令保护间隔时间(预留)
// pSW->sink.loadc = pTest->start_curr; // 负载电流初始化为OCP起始电流
//#if SINK_TEST_DEBUG
// rt_kprintf("[SINK] OCP Test: start=%d, end=%d\n",
// pTest->start_curr, pTest->end_curr);
//#endif
//}
////=============================================================================
//// 动态负载测试参数解析 指令码:0x03
////=============================================================================
//void sink_test_dynamic_set(struct chrg_switch_t *pSW, rt_uint8_t *pData, rt_uint8_t run_flag)
//{
// struct test_change *pTest = &pSW->sink.test.change_test;
//// rt_uint16_t change_curr = 0;
// pTest->curr1_set = (pData[1] << 8) | pData[2]; // 解析:第一阶段设定电流
// pTest->time1_set = (pData[3] << 8) | pData[4]; // 解析:第一阶段保持时长
// pTest->curr2_set = (pData[5] << 8) | pData[6]; // 解析:第二阶段设定电流
// pTest->time2_set = (pData[7]<< 8) | pData[8]; // 解析:第二阶段保持时长
// pTest->k_Rise = (pData[9] << 8) | pData[10]; // 电流上升斜率
// pTest->k_Fall = (pData[11]<< 8) | pData[12]; // 电流下降斜率
// // pTest->start_time = (pData[14]<< 8) | pData[13]; // 产品稳定时长(预留)
// // pSW->sink.loadc = pTest->curr1_set;
//#if SINK_TEST_DEBUG
// rt_kprintf("[SINK] Dynamic Test: curr1=%d, curr2=%d\n",
// pTest->curr1_set, pTest->curr2_set);
//#endif
//}
////=============================================================================
//// 短路测试状态机执行函数
//// 依据 work_step 分步配置寄存器,启动短路测试
////=============================================================================
//void chrg_nor_sink_short_test(rt_uint8_t ch, struct chrg_switch_t *pSW)
//{
// struct test *pTest = &pSW->sink.test;
//
// switch (pTest->work_step)
// {
// case 0:
// // 第一步:切换为电子负载模式,吸合负载开关
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_LOAD);
//// rt_kprintf("first\r\n");
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,ID_SINK);
// pTest->work_step = 1;
// break;
// case 1:
// // 根据模式分支配置参数
// if(pTest->short_test.set_mode == 0x00) { //直接释放
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_ShortTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR); //直接为0
// pTest->work_step = 2;
// }
// else{ //按设定的时间后释放
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_ShortTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP); //保持
// pTest->work_step = 4;
// }
//
// rt_kprintf("set_mode = %d",pTest->short_test.set_mode);
// break;
// case 2:
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_ShortTest_Hold_Time , pTest->short_test.hold_time);
// pTest->work_step = 4;
// break;
// case 4:
// // 启动整机工作,清零状态标志
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_START);
// pTest->work_step = 5;
// pSW->change_flag = 0;
// break;
// case 5:
// break;
// default:
// pTest->work_step = 0;
// break;
// }
//}
////=============================================================================
//// OCP过流保护测试状态机执行函数
//// 分步配置电流、时长、模式寄存器,最终启动OCP测试
////=============================================================================
//// OCP测试执行
//void chrg_nor_sink_OCP_test(rt_uint8_t ch, struct chrg_switch_t *pSW)
//{
// struct test *pTest = &pSW->sink.test;
// switch (pTest->work_step)
// {
// case 0: // 初始化:切负载模式、闭合负载开关
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_LOAD);
//// rt_kprintf("first\r\n");
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,ID_SINK);
// pTest->work_step = SET_OCP_STARTCURR;
// break;
// case SET_OCP_STARTCURR: // 设置OCP起始电流
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_StartCurrent, pTest->OCP_test.start_curr);
// pTest->work_step = SET_OCP_ENDCURR;
// break;
// case SET_OCP_ENDCURR: // 设置OCP终止电流
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_StopCurrent , pTest->OCP_test.end_curr);
// pTest->work_step = SET_OCP_STEP;
// break;
// case SET_OCP_STEP: // 设置电流步进值
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_Step_CURR , pTest->OCP_test.change_curr);
// pTest->work_step = SET_OCP_STEP_HOLD_TIME;
// break;
// case SET_OCP_STEP_HOLD_TIME: // 设置单步保持时间
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_Step_Hold_Time , pTest->OCP_test.change_keep_time);
// pTest->work_step = SINK_OCP_STOP_VOLT;
// break;
// case SINK_OCP_STOP_VOLT: //设置截止电压
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_Vtrig_Stop , pTest->OCP_test.stop_volt);
// pTest->work_step = SET_OCP_CMD_MODE_RUN;
// break;
// case SET_OCP_CMD_MODE_RUN: // 标记测试运行模式
// if(pTest->OCP_test.set_mode == 0x00) { //释放
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR); //直接为0
// pTest->work_step = SET_OCP_HOLD_TIME;
// }
// else{ //保持
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP); //保持
// pTest->work_step = SET_OCP_RUNNING;
// }
// break;
// case SET_OCP_HOLD_TIME: // 设置OCP触发后保持时间
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_OCPTest_Hold_Time , pTest->OCP_test.hold_time);
// pTest->work_step = SET_OCP_RUNNING;
// break;
// case SET_OCP_RUNNING: // 启动OCP测试
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_START);
// pSW->change_flag = 0;
// pTest->work_step = 0x10;
// break;
// case 0x10:
// break;
// default:
// break;
// }
//}
////=============================================================================
//// 动态负载测试状态机执行函数
//// 依次配置两段电流、时长、升降斜率,启动动态负载测试
////=============================================================================
//void chrg_nor_sink_change_test(rt_uint8_t ch, struct chrg_switch_t *pSW)
//{
// struct test *pTest = &pSW->sink.test;
// switch (pTest->work_step)
// {
// case 0: // 初始步骤:切换电子负载模式、闭合负载开关
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_ELOAD, ELOAD_LOAD);
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,ID_SINK);
// pTest->work_step = SET_DYNA_CURR1;
// break;
// case SET_DYNA_CURR1: // 设置第一路输出电流
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_L1, pTest->change_test.curr1_set);
// pTest->work_step = SET_DYNA_TIME1;
// #if SINK_TEST_DEBUG
// rt_kprintf("Curr1: %d\n", pTest->change_test.curr1_set);
// #endif
// break;
// case SET_DYNA_TIME1: // 设置第一阶段保持时间
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_T1 , pTest->change_test.time1_set);
// pTest->work_step = SET_DYNA_CURR2;
// #if SINK_TEST_DEBUG
// rt_kprintf("Time1: %d\n", pTest->change_test.time1_set);
// #endif
// break;
// case SET_DYNA_CURR2: // 设置第二路输出电流
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_L2 , pTest->change_test.curr2_set);
// pTest->work_step = SET_DYNA_TIME2;
// #if SINK_TEST_DEBUG
// rt_kprintf("Curr2: %d\n", pTest->change_test.curr2_set);
// #endif
// break;
// case SET_DYNA_TIME2: // 设置第二阶段保持时间
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_T2 , pTest->change_test.time2_set);
// pTest->work_step = SET_DYNA_RISE_SLEW_RATE;
// #if SINK_TEST_DEBUG
// rt_kprintf("Time2: %d\n", pTest->change_test.time2_set);
// #endif
// break;
// case SET_DYNA_RISE_SLEW_RATE: // 设置电流上升斜率
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_Rise_Slew_Rate , pTest->change_test.k_Rise);
// pTest->work_step = SET_DYNA_FALL_SLEW_RATE;
// #if SINK_TEST_DEBUG
// rt_kprintf("Rise Slew Rate: %d\n", pTest->change_test.k_Rise);
// #endif
// break;
// case SET_DYNA_FALL_SLEW_RATE: // 设置电流下降斜率
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_Fall_Slew_Rate , pTest->change_test.k_Fall);
// pTest->work_step = SET_DYNA_CND_MODE;
// #if SINK_TEST_DEBUG
// rt_kprintf("Fall Slew Rate: %d\n", pTest->change_test.k_Fall);
// #endif
// break;
// case SET_DYNA_CND_MODE: // 标记动态测试为运行状态
// chrg_set_sou_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN);
// pTest->work_step = SET_DYNA_RUNNING;
// pTest->work_step = SET_DYNA_CND_MODE;
// #if SINK_TEST_DEBUG
// rt_kprintf("Fall Slew Rate: %d\n", pTest->change_test.k_Fall);
// #endif
// break;
// case SET_DYNA_RUNNING: // 启动整机工作,锁定当前运行步骤、清空标志位
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_START);
// pTest->work_step = SET_DYNA_RUNNING;
// pTest->work_step = 0x11;
// pSW->change_flag = 0;
// break;
// case 0x11:
// break;
// default:
// #if SINK_TEST_DEBUG
// rt_kprintf("Invalid work step: %d\n", pTest->work_step);
// #endif
// break;
// }
//}
////=============================================================================
//// 短路测试停止处理函数
//// 依据 stop_step 分步执行停止流程,清除测试标记并跳转状态
////=============================================================================
//void chrg_nor_sink_short_test_stop(rt_uint8_t idx, struct chrg_switch_t *pSW,rt_uint8_t *enable)
//{
// struct test *pTest = &pSW->sink.test;
// switch (pTest->stop_step)
// {
// case TEST_STOP_CMD_MODE: // 下发停止指令,关闭短路测试运行模式
// chrg_set_sou_reg(idx, ModbusRTU_ShortTest_CMD_MODE_FLAG, ModbusRTU_DYNA_CMD_MODE_STOP);
// // pTest->stop_step = TEST_STOP_RUN;
// // break;
// // case TEST_STOP_RUN: // 清除测试标志,切换至负载状态管理流程
// //chrg_set_sou_reg(idx, REG_WORK, WORK_STOP);
// //pTest->stop_step = TEST_STOP_REL;
// //chrg_nor_sw_rel((eIDX_NOR_CH)idx,0);
// //*enable = 0;
// pSW->change_flag = 0;
// pSW->sink.test.test_mode = 0; // 清空短路测试标记
// pSW->sub = 21; // IDX_SET_SINK_WORK_STATE; 跳转至负载工作状态处理
// break;
// default:
// break;
// }
//}
////=============================================================================
//// OCP过流保护测试停止处理函数
////=============================================================================
//void chrg_nor_sink_OCP_test_stop(rt_uint8_t idx, struct chrg_switch_t *pSW,rt_uint8_t *enable)
//{
// struct test *pTest = &pSW->sink.test;
// switch (pTest->stop_step)
// {
// case TEST_STOP_CMD_MODE: // 下发停止指令,关闭OCP测试运行模式
// chrg_set_sou_reg((eIDX_SOU_CH)idx, ModbusRTU_OCPTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_STOP);
// pSW->change_flag = 0;
// pSW->sink.test.test_mode = 0; // 清空OCP测试标记
// pSW->sub = 21; // IDX_SET_SINK_WORK_STATE;
// break;
// default:
// break;
// }
//}
////=============================================================================
//// 动态负载测试停止处理函数
////=============================================================================
//void chrg_nor_sink_change_test_stop(rt_uint8_t idx, struct chrg_switch_t *pSW,rt_uint8_t *enable)
//{
// struct test *pTest = &pSW->sink.test;
// switch (pTest->stop_step)
// {
// case TEST_STOP_CMD_MODE: // 下发停止指令,关闭动态测试运行模式
// chrg_set_sou_reg((eIDX_SOU_CH)idx, ModbusRTU_DYNA_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_STOP);
// pSW->change_flag = 0;
// pSW->sink.test.test_mode = 0; // 清空动态测试标记
// pSW->sub = 21; // IDX_SET_SINK_WORK_STATE;
// break;
// default:
// break;
// }
//}
//sink开机初始化
void chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_north_t *pNOR = &chrgnorth;
pSW->sink.On_work = 1;
pSOU->enable[idx] = 1;
pSOU->online[idx] = 1;
pNOR->enable[idx] = 1;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, ID_SINK); //打开北向继电器
// 1. 电流输出置0
chrg_sou_com_batch_add_reg(idx, REG_CURRENT_OUT+1, 0);
// 2. 设置硬件恒压/恒流模式
chrg_sou_com_batch_add_reg(idx,REG_MODE,pSW->CV_mode);
switch(pSW->sink.test.test_mode_old){
case 0:
break;
case 1://短路测试退出
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)idx, ModbusRTU_ShortTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_STOP);
break;
case 2:
break;
case 3:
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)idx, ModbusRTU_DYNA_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_STOP);
break;
}
pSW->sink.test.test_mode_old = 0;
pSW->sink.work_mode = SINK_WORK_WAIT;
}
//sink进入运行状态
void chrg_sink_work_volt_runing(eIDX_SOU_CH idx, struct chrg_switch_t *pSW){
chrg_sou_com_batch_add_reg(idx, 0x01, 0x02);
//打开主继电器
chrg_sou_com_batch_add_reg(idx, 0x01, 0x03);
if(pSW->CV_mode == MODE_CONSTANT_VOLTAGE)
{
chrg_sou_com_batch_add_reg(idx, REG_VOLTAGE_OUT+1, pSW->sink.loadv);
}
else
{
chrg_sou_com_batch_add_reg(idx, REG_CURRENT_OUT+1, pSW->sink.loadc);
}
chrg_sou_com_batch_add_reg(idx, REG_WORK, WORK_START);
}
//sink进入低电压状态
void chrg_sink_nor_work_low(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
chrg_sou_com_batch_add_reg(idx, 0x01, 0x00);
chrg_sou_com_batch_add_reg(idx, REG_WORK, WORK_STOP);
}
//com端关机流程
void chrg_sink_nor_work_off(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
{
struct chrg_south_t *pSOU = &chrgsouth;
struct chrg_north_t *pNOR = &chrgnorth;
pSW->sink.On_work = 0;
pSOU->enable[idx] = 0;
pSOU->online[idx] = 0;
pNOR->enable[idx] = 0;
chrg_nor_sw_rel((eIDX_NOR_CH)idx, 0); //关闭北向继电器
chrg_sou_com_batch_add_reg(idx, 0x01, 0x00);
chrg_sou_com_batch_add_reg(idx, REG_WORK, WORK_STOP);
}
//动态测试启动
void chrg_sink_change_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
{
struct test *pTest = &pSW->sink.test;
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_L1, pTest->change_test.curr1_set);
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_T1 , pTest->change_test.time1_set);
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_L2 , pTest->change_test.curr2_set);
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_T2 , pTest->change_test.time2_set);
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, ModbusRTU_DYNA_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN);
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_START);
}
//短路测试启动
void chrg_sink_SHOUT_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
{
struct test *pTest = &pSW->sink.test;
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, ModbusRTU_ShortTest_CMD_MODE_FLAG , ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP); //保持
chrg_sou_com_batch_add_reg((eIDX_SOU_CH)ch, REG_WORK, WORK_START);
}
//负载工作
void chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW){
static uint8_t last_state[TOTAL_SOU_CHS] ={100,100,100,100};
rt_uint8_t work_mode = pSW->sink.work_mode;
// 本次状态和上次一致,直接跳过,不重复下发寄存器
if (work_mode == last_state[idx])
{
return;
}
chrg_sou_com_sou_set();
switch(work_mode){
case SINK_WORK_INIT:
chrg_sink_work_init(idx,pSW);
break;
case SINK_WORK_WAIT:
break;
case SINK_WORK_RUNING:
chrg_sink_work_volt_runing(idx,pSW);
break;
case SINK_WORK_RUNING_2:
chrg_sink_work_volt_runing(idx,pSW);
break;
case SINK_WORK_ON_TEST_CHANGE:
chrg_sink_change_ON(idx,pSW);
break;
case SINK_WORK_ON_TEST_SHOUT:
chrg_sink_SHOUT_ON(idx,pSW);
break;
case SINK_WORK_LOW:
chrg_sink_nor_work_low(idx,pSW);
break;
case SINK_WORK_STOP:
chrg_sink_nor_work_off(idx,pSW);
break;
}
// 更新缓存,保存本次最新状态
last_state[idx] = work_mode;
}
+241 -57
View File
@@ -11,20 +11,63 @@
#define __CHRG_SINK_H__
#include <rtthread.h>
struct chrg_switch_t;
#include "chrg_def.h"
#include "chrg_south.h"
// 冠达快充板(SINK)通讯协议
// SINK的ID为1,波特率200KTTL
#define SINK_NO_TEST 0
#define SINK_TEST_SHORT 1
#define SINK_TEST_OCP 2
#define SINK_TEST_DYNA 3
//短路测试结构体
struct test_short {
rt_uint16_t hold_time; // 短路测试时间
rt_uint8_t set_mode; // 0为测试后电流保持设定的设定时间后恢复;1为一直保持
rt_uint8_t have_curr_flag; //0为不带,1为带(默认)
};
//OCP测试结构体
struct test_OCP {
rt_uint16_t start_curr; // 起始电流
rt_uint16_t end_curr; // 结束电流
rt_uint16_t change_curr; // 步进电流,单位mA
rt_uint16_t change_keep_time; // 每步保持时间,单位ms(1-100)
rt_uint16_t hold_time; // OCP触发后保持时间,单位ms
rt_uint16_t set_mode; // 0为测试后电流保持设定的设定时间后恢复;1为一直保持
rt_uint16_t stop_volt; // 停止电压,单位mv
rt_uint8_t hold_curr; // OCP触发后是否带载,0=带载,1=不带载
//rt_uint16_t Vtrig_Stop; // 达到触发OCP流点的电压值;
// rt_uint16_t Volt_OCP; // 无论short测试成功或者失败,都给出短路恒流值的时候的电压
// rt_uint16_t Current_OCP; // 无论short测试成功或者失败,都给出短路恒流值的时候的电流
// rt_uint_t change_mode; // 0=测试后电流设置为0;1=达到标准则电流立即为0;2=保持当前电流
};
//动态测试结构体
struct test_change {
rt_uint16_t curr1_set; // 电流1设置值
rt_uint16_t time1_set; // 电流1保持时间设置值,单位0.01s
rt_uint16_t curr2_set; // 电流2设置值
rt_uint16_t time2_set; // 电流2保持时间设置值,单位0.01s
rt_uint16_t k_Rise; // 上升斜率,单位0.001(A/us)
rt_uint16_t k_Fall; // 下降斜率,单位0.001(A/us)
// rt_uint16_t start_time; // 启动后稳定时间,单位ms
// rt_uint16_t work_step; // 总共多少次步进
};
struct test
{
struct test_short short_test;
struct test_change change_test;
struct test_OCP OCP_test;
rt_uint8_t test_mode_old; //进入测试模式后的角色
rt_uint8_t test_mode; // 测试标志位,0=无测试,1=短路测试,2=OCP测试,3=动态测试
rt_uint8_t run_flag; // 运行标志位,0=停止,1=运行,2=继续,3=结束
rt_uint8_t work_step; // 测试步骤,0=初始,1=第一步,2=第二步,3=第三步
rt_uint8_t stop_step; // 停止步骤,0=初始,1=第一步,2=第二步,3=第三步
};
#define SINK_CMD_GET_VOLTAGE 0x20 // 读取变电压板电压
// (需要实时比较设置的快充指令和读回来的快充指令,若有不一样,则重设)(注意vooc模式下数据返回会很慢)
// 发送 0B 00 01 20 loadvh loadvl loadch loadcl nc nc CRC8
// 长度+nc+id+指令+负载电压(2字节,0.01V)+负载电流(2字节,mA)+2个预留字节+CRC8校验
// 接收 15 00 01 20 VH VL qc_data1 qc_data2 qc_data3 qc_data4 qc_data5 qc_data6 ver_h ver_l nc nc nc nc nc nc CRC8
// 长度+nc+ID+指令+2字节电压(0.01V)+6字节快充指令+2字节版本号+6字节预留+CRC8校验
struct sink_vc_t {
rt_uint8_t voltage; // 电压 0.01V
@@ -33,17 +76,9 @@ struct sink_vc_t {
};
#define SINK_CMD_GET_PD 0x13 // 读取源端的PD协议数据(当前为PDO,PPS,PD3.1,AVS协议时有效)
// 发送 05 00 01 13 2A
// 接收 XX 00 01 13 // 长度(根据实际计算)+NC+ID+指令
// 00 00 VMINH VMINL VMAXH VMAXL CURH/PORH CURL/PORL NC NC NC NC NC NC
// 源类型(1字节,00为PDO,03为APDO) + PPS类型(1字节,源类型为3时有效,00为SPR PPS,01为EPR AVS,02为SPR AVS) +
// 最小电压(2字节,mV) + 最大电压(2字节,mV) + 最大电流/功率(2字节,mA/W) + 预留(6字节,当PPS类型为02时有效,意义与前6字节一致)
// 00 00 VMINH VMINL VMAXH VMAXL CURH/PORH CURL/PORL NC NC NC NC NC NC
// ...... CRC8
#define SINK_CMD_SET_CC 0x17 // 设置CC线
// 发送 06 00 01 17 00 2A // 长度+nc+id+指令+CC(0为都选通,1为选择CC1,2为CC2,3为全断开)+CRC8校验
// 接收 05 00 01 17 2A // 长度+nc+id+指令+CRC8校验
// CC 线选通
enum {
@@ -54,8 +89,7 @@ enum {
};
#define SINK_CMD_SET_LEVEL 0x16 // 设置CC线电平
// 发送 06 00 01 16 00 2A // 长度+nc+id+指令+电平等级(0-10,默认7)+CRC8校验
// 接收 05 00 01 16 2A // 长度+nc+id+指令+CRC8校验
// 电平等级
enum {
@@ -72,45 +106,85 @@ enum {
CC_LVL_10 = 10
};
//负载协议
typedef enum {
LOAD_PRO_NONE = 0xFE, // 0.无快充(标准)
LOAD_PRO_QC20 = 0xF8, // 1.QC2.0
LOAD_PRO_QC30 = 0xF6, // 2.QC3.0
LOAD_PRO_FCP = 0xF2, // 3.FCP
LOAD_PRO_AFC = 0xF9, // 4.AFC
LOAD_PRO_SCP = 0xF3, // 5.SCP
LOAD_PRO_VIVO = 0xFD, // 6.VIVO
LOAD_PRO_TFC_RFC = 0xF7, // 7.传音/TFC/RFCN
LOAD_PRO_PDO_MANUAL = 0xFB, // 8.PDO手动
LOAD_PRO_PDO_AUTO = 0xFB, // 9.PDO自动
LOAD_PRO_PPS_MANUAL = 0xFC, // 10.PPS手动
LOAD_PRO_PPS_AUTO = 0xFC, // 11.PPS自动
LOAD_PRO_PD31AVS = 0xE1, // 12.PD3.1
LOAD_PRO_UFCS = 0xE0, // 13.UFCS
LOAD_PRO_VOOC = 0xE2, // 14.VOOC
LOAD_PRO_AVS = 0xE3, // 15.AVS
// 设置快充协议(VOOC,UFCS模式下切换其他模式时,需要先把V+,V-,D+,D-,
// CC等和变电压有关的继电器切至悬空,至少等待1s后,等能读到数据且快充指令正确,再把继电器切回)
// 发送格式 长度+NC+ID+6字节快充数据+CRC8
// 接收 长度+NC+ID+指令+CRC8
TOTAL_LOAD_PROS // 负载协议总数
} eLOAD_PRO;
// 负载协议映射表:十六进制值 → 协议名称
typedef struct {
rt_uint8_t pro_val; // 协议对应的数值(如0xF8
int pro_name; // 协议名称(如"QC2.0"
} LoadProMap_t;
static const LoadProMap_t load_pro_map[] = {
{LOAD_PRO_NONE, 10},
{LOAD_PRO_QC20, 11},
{LOAD_PRO_QC30, 12},
{LOAD_PRO_FCP, 13},
{LOAD_PRO_AFC, 14},
{LOAD_PRO_SCP, 15},
{LOAD_PRO_VIVO, 16},
{LOAD_PRO_TFC_RFC, 17},
{LOAD_PRO_PDO_MANUAL, 18},
{LOAD_PRO_PDO_AUTO, 19},
{LOAD_PRO_PPS_MANUAL, 20},
{LOAD_PRO_PPS_AUTO, 21},
{LOAD_PRO_PD31AVS, 22},
{LOAD_PRO_UFCS, 23},
{LOAD_PRO_VOOC, 24},
{LOAD_PRO_AVS, 25}
};
// 命令码区分:负载(0x11-0x1F)
#define CMD_LOAD_QC20 0x11 // 负载-QC2.0
#define CMD_LOAD_QC30 0x12 // 负载-QC3.0
#define CMD_LOAD_FCP 0x13 // 负载-FCP
#define CMD_LOAD_AFC 0x14 // 负载-AFC
#define CMD_LOAD_SCP 0x15 // 负载-SCP
#define CMD_LOAD_VIVO 0x16 // 负载-VIVO
#define CMD_LOAD_TFC 0x17 // 负载-TFC
#define CMD_LOAD_PD0_MAN 0x18 // 负载-PD0手动
#define CMD_LOAD_PD0_AUTO 0x19 // 负载-PDO自动
#define CMD_LOAD_PPS_MAN 0x1A // 负载-PPS手动
#define CMD_LOAD_PPS_AUTO 0x1B // 负载-PPS自动
#define CMD_LOAD_PD31 0x1C // 负载-PD3.1
#define CMD_LOAD_UFCS 0x1D // 负载-UFCS
#define CMD_LOAD_VOOC 0x1E // 负载-VOOC
#define CMD_LOAD_AVS 0x1F // 负载-AVS
#define LOAD_PRO_MAP_NUM (sizeof(load_pro_map)/sizeof(load_pro_map[0])) // 映射表长度
#define SINK_CMD_SET_QC20 0xF8 // QC2.0
// QC2.0 电压5V/9V/12V/20V 命令:0A 00 01 F8 5A 00 00 00 00 2A
// 第5个字节为电压(0.1V)
#define SINK_CMD_SET_QC30 0xF6 // QC3.0
// QC3.0 电压3.3-20V 命令:0A 00 01 F6 5A 00 00 00 00 2A
// 第5个字节为电压(0.1V)
#define SINK_CMD_SET_FCP 0xF2 // FCP
// FCP 电压5V/9V/12V 命令:0A 00 01 F2 78 00 00 00 00 2A
// 第5个字节为电压(0.1V)
#define SINK_CMD_SET_AFC 0xF9 // AFC
// AFC 命令:0A 00 01 F9 46 00 00 00 00 2A
// 第5个字节为变电压数据,详细参考AFC文档
#define SINK_CMD_SET_SCP 0xF3 // SCP
// SCP 命令:0A 00 01 F3 27 10 0F A0 00 2A
// 5,6字节为电压(mv)7,8字节为电流(mA)
#define SINK_CMD_SET_VIVO 0xFD // VIVO
// VIVO 命令:0A 00 01 FD 27 10 00 00 00 2A
// 5,6字节为电压(mv)7,8字节为电流(mA)
#define SINK_CMD_SET_TFC 0xF7 // TFC 传音
// 传音/TFC 命令:0A 00 01 F7 2E E0 00 00 00/01 2A
// 5,6字节为电压(mv), 7,8字节为电流(mA), 9字节为RFC/TFC选择
#define SINK_CMD_SET_PD0 0xFB // PD0 手动/自动
// PDO手动,需要设定组号,电压和产品相关,命令:0A 00 01 FB 05 00 00 00 00 2A
// 5字节为组号
// PDO自动,组号设为0,自动变到指定电压,命令:0A 00 01 FB 00 2E E0 00 00 2A
// 5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
enum {
SET_MANUAL = 0, // 手动
@@ -118,39 +192,138 @@ enum {
};
#define SINK_CMD_SET_PPS 0xFC // PPS 手动/自动
// PPS手动,需要设定组号、电压、电流,电流0最大, 命令: 0A 00 01 FC 06 4E 20 00 00 2A
// 5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
// PPS自动,组号设为0,设定电压、电流,电流0最大,命令:0A 00 01 FC 00 4E 20 00 00 2A
// 5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
#define SINK_CMD_SET_PD3P1 0xE1 // PD3.1
// PD3.1, 命令:0A 00 01 E1 00 2328 07d0 2A (组号设置和电压设置都支持)
// 5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
#define SINK_CMD_SET_UFCS 0xE0 // UFCS
// UFCS,命令:0A 00 01 E0 00 2328 07d0 2A (组号设置和电压设置都支持)
// 5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
#define SINK_CMD_SET_VOOC 0xE2 // VOOC
// VOOC,电压10/11V, 命令:0A 00 01 E2 00 0000 0000 2A
#define SINK_CMD_SET_AVS 0xE3 // AVS
// AVS,命令:0A 00 01 E3 00 4e 20 07 d0 2A (组号设置和电压设置都支持)
// 5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
enum {
SINK_WORK_INIT =0,
SINK_WORK_WAIT,
SINK_WORK_VOLT_OK,
SINK_WORK_RUNING,
SINK_WORK_RUNING_2,
SINK_WORK_LOW,
SINK_WORK_STOP,
SINK_WORK_ON_TEST_CHANGE,
SINK_WORK_ON_TEST_SHOUT,
};
// sink 参数结构
struct chrg_sink_t {
rt_uint16_t loadv; // 负载电压 0.01V
rt_uint16_t loadc; // 负载电流 mA
rt_uint16_t loadv; // 设置负载电压 0.01V
rt_uint16_t loadc; // 设置负载电流 mA
rt_uint16_t pro_loadv; // 设置协议负载电压 0.01V
rt_uint16_t pro_loadc; // 设置协议负载电流 mA
rt_uint16_t volt_on; // 启动电压
rt_uint16_t Pro_Group; // 设置组号
struct sink_vc_t vc;
struct vc_pd_t pd_get[2];
rt_uint8_t cc_onoff; // 设置CC线开关
rt_uint8_t cc_lvl; // 设置CC线电平等级(0-10,默认7)
struct test test; //负载测试10++++++++++++++++
rt_uint16_t protocol; //协议类型
rt_uint8_t cc_set; // 选择cc线
rt_uint8_t cc_lvl; // 设置CC线电平等级(0-10,默认7)
rt_uint8_t Now_State; // 当前状态,0=停止,1=运行
rt_uint8_t On_work;
rt_uint8_t change_cv_flag;
rt_uint8_t change_source_flag; // 从sink切换到source标志位
rt_uint8_t work_mode; // 0=停止 1=工作 2=低电压
};
//纹波寄存器
#define ModbusRTU_Ripple_Value 0x35 //读取纹波
/******************** 负载测试 ************************* */
#define ModbusRTU_CAL_WriteEnable 0x34 //使能写入;0x4361代表使能写入;C+a ASCII码
#define ModbusRTU_Test_CMD 0x4361 //
//short测试
#define SHORT_TEST 0x01
//短路测试寄存器
#define ModbusRTU_ShortTest_CMD_MODE_FLAG 0x40 ////16位;0x0101; 01启动,低字节的01,保持电流,为0是释放电流; 20260601新增一个低字节的bit7=0用于是恢复以前的状态,bit7=1是将卸载电流归零;
#define ModbusRTU_ShortTest_Hold_Time 0x41 //单位是ms,最大65535ms,给0是立即释放,给一个时间值,是保持这个时间不变;
//#define ModbusRTU_ShortTest_CurrentShort 0x42 //无论测试成功或者失败,都给出短路恒流值的时候的电压
//短路测试执行步骤
#define SET_SHORT_CMD_MODE 0x01
#define SET_SHORT_WRITE_ENABLE 0x02
#define SET_SHORT_RUN 0x03
//短路测试结束步骤
#define READ_SHORTEST_VOLT_SHORT 0x03
#define READ_SHORTEST_CURRENT_SHORT 0x04
//OCP测试
#define OCP_TEST 0X02
//OCP测试功率板寄存器
#define ModbusRTU_OCPTest_CMD_MODE_FLAG 0x43 //16位;0x0101; 01启动,低字节的01,保持/卸载;
#define ModbusRTU_OCPTest_StartCurrent 0x44 //起始电流
#define ModbusRTU_OCPTest_StopCurrent 0x45 //终止电流
#define ModbusRTU_OCPTest_Step_CURR 0x46 //每一步的电流
#define ModbusRTU_OCPTest_Step_Hold_Time 0x47 //每一步的保持时间;默认10ms;范围1ms到100ms;
#define ModbusRTU_OCPTest_Vtrig_Stop 0x48 //达到触发OCP流点的电压值;
#define ModbusRTU_OCPTest_Hold_Time 0x49
#define ModbusRTU_OCPTest_HOLD_CURR 0x4A
//OCP测试步骤
#define SET_OCP_STARTCURR 0x01
#define SET_OCP_ENDCURR 0x02
#define SET_OCP_STEP 0x03
#define SET_OCP_STEP_HOLD_TIME 0x04
#define SET_OCP_HOLD_TIME 0x05
#define SET_OCP_CMD_MODE_RUN 0x06
#define SET_OCP_WRITE_ENABLE 0x07
#define SET_OCP_RUNNING 0x08
#define SET_OCP_HOLD_END_HAVE_CURR 0x09
#define SINK_OCP_STOP_VOLT 0x0A
//OCP测试结果读取
#define READ_OCP_VTRIG_STOP 0x03
#define READ_OCP_VOLT_OCP 0x04
#define READ_OCP_CURRENT_OCP 0x05
//动态测试
#define DYNA_TEST 0X03
//动态测试功率板寄存器
#define ModbusRTU_DYNA_CMD_MODE_FLAG 0x4B //动态测试 CMD启动、退出、 以及模式:Continuous、PULSE、ToggleLSB8 BIT7 短路测试时候的电流:保持/归零;bit1 过流; bit0 成功/失败
#define ModbusRTU_DYNA_L1 0x4C //动态测试电流1
#define ModbusRTU_DYNA_L2 0x4D //动态测试电流2
#define ModbusRTU_DYNA_T1 0x4E //动态测试电流1的时间
#define ModbusRTU_DYNA_T2 0x4F //动态测试电流2的时间
#define ModbusRTU_DYNA_Rise_Slew_Rate 0x50 //动态测试上升斜率
#define ModbusRTU_DYNA_Fall_Slew_Rate 0x51 //动态测试下降斜率
//动态测试步骤
#define SET_DYNA_CURR1 0x01
#define SET_DYNA_CURR2 0x02
#define SET_DYNA_TIME1 0x03
#define SET_DYNA_TIME2 0x04
#define SET_DYNA_RISE_SLEW_RATE 0x05
#define SET_DYNA_FALL_SLEW_RATE 0x06
#define SET_DYNA_CND_MODE 0x07
#define SET_DYNA_WRITE_ENABLE 0x08
#define SET_DYNA_RUNNING 0x09
//测试停止通用前三步
#define TEST_STOP_CMD_MODE 0x00
#define TEST_STOP_RUN 0x01
#define TEST_STOP_REL 0x02
//短路和负载共用运行寄存器值
#define ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR 0x0100 //在保持时间结束后电流为0
//#define ModbusRTU_DYNA_CMD_MODE_RUN_KEPP 0x0101 //保持时间后恢复之前电流
#define ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP 0x0101 //一直保持拉低后的电流
//动态测试运行寄存器值
#define ModbusRTU_DYNA_CMD_MODE_RUN 0x0101 //动态启动
//测试通用停止寄存器值
#define ModbusRTU_DYNA_CMD_MODE_STOP 0x0000 //停止
#define ModbusRTU_SYS_Reset 0x61 //系统复位指令,如果异常过压,通常发指令,让系统复位
/*****************************************************************
sink_get_vc
sink电压电流查询请求
@@ -200,7 +373,18 @@ extern int sink_set_cc_lvl (rt_uint8_t lvl);
-
*****************************************************************/
extern int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf);
// ===================== 负载测试函数声明 =====================
extern void sink_test_short_set (struct chrg_switch_t *pSW, rt_uint8_t *pData, rt_uint8_t run_flag);
extern void sink_test_ocp_set (struct chrg_switch_t *pSW, rt_uint8_t *pData, rt_uint8_t run_flag);
extern void sink_test_dynamic_set(struct chrg_switch_t *pSW, rt_uint8_t *pData, rt_uint8_t run_flag);
extern void chrg_nor_sink_short_test(rt_uint8_t ch, struct chrg_switch_t *pSW);
extern void chrg_nor_sink_OCP_test(rt_uint8_t ch, struct chrg_switch_t *pSW);
extern void chrg_nor_sink_change_test(rt_uint8_t ch, struct chrg_switch_t *pSW);
extern void chrg_nor_sink_test_stop(rt_uint8_t ch,struct chrg_switch_t *pSW,rt_uint8_t *enable);
extern void chrg_nor_sink_test_run(rt_uint8_t ch, struct chrg_switch_t *pSW);
extern void chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW);
#endif
+801 -23
View File
@@ -8,13 +8,14 @@
******************************************************************************/
#include <rtthread.h>
#include "chrg_source.h"
#include "chrg_north.h"
#include <string.h>
#include "chrg_north_pkg.h"
#include "chrg_utils.h"
#include "chrg_roll_nor.h"
#include "chrg_source.h"
rt_uint8_t Pro_Group_VCMM[11][8] = {
{0} // 11个挡位,8个参数(src, pps, vmin_H,vmin_L, vmax_H,vmax_L,cur_set_H,cur_set_L
};
/*****************************************************************
source_get_vc
source电压电流查询请求
@@ -78,6 +79,539 @@ int source_set_cc_lvl (rt_uint8_t lvl)
return chrg_north_send(frame, len);
}
/*****************************************************************
source_set_cc_lvl
source cc
lvl:
-
<0: >0:
-
*****************************************************************/
int source_set_cc_choose (rt_uint8_t cc_set)
{
rt_uint16_t len = 0;
rt_uint8_t data[8] = {0};
rt_uint8_t frame[16] = {0};
data[0] = cc_set;
chrg_north_pkg_encode(ID_SOURCE, SRC_CMD_SET_CC_CHOOSE, data, 1, frame, &len);
rt_kprintf("\n");
for(int i=0;i<len;i++){
rt_kprintf("%d ");
}
rt_kprintf("\n");
return chrg_north_send(frame, len);
}
/*
*/
rt_uint8_t parse_mulit_gear_One(rt_uint16_t min_volt_mv,rt_uint16_t max_volt_mv, rt_uint16_t max_curr_ma) {
// 清空缓冲区
rt_memset(Pro_Group_VCMM, 0, sizeof(Pro_Group_VCMM));
Pro_Group_VCMM[0][0] = 0x00; // src
Pro_Group_VCMM[0][1] = 0x00; // pps
Pro_Group_VCMM[0][2] = (min_volt_mv >> 8) & 0xFF; // vmin_H
Pro_Group_VCMM[0][3] = min_volt_mv & 0xFF; // vmin_L
Pro_Group_VCMM[0][4] = (max_volt_mv >> 8) & 0xFF; // vmax_H
Pro_Group_VCMM[0][5] = max_volt_mv & 0xFF; // vmax_L
Pro_Group_VCMM[0][6] = (max_curr_ma >> 8) & 0xFF; // cur_set_H
rt_kprintf("H_C=%02x",Pro_Group_VCMM[0][6]);
Pro_Group_VCMM[0][7] = max_curr_ma & 0xFF; // cur_set_L
rt_kprintf("L_C=%02x\n",Pro_Group_VCMM[0][7]);
return 0;
}
/*****************************************************************
parse_power_single_gear
QC3.0/QC2.0/SCPB
protoCmd: (0x27/0x28/0x23)
pData: ++CC线++
reqLen:
pSW:
-
-
+(1)+CC线(1)+(2)+(2)
*****************************************************************/
void parse_power_single_gear(rt_uint8_t protoCmd, rt_uint8_t *pData, rt_uint16_t reqLen,uint8_t *pro_gear_idx,uint8_t *pro,rt_uint16_t *protect_curr)
{
rt_uint16_t min_volt_mv = 0; // 最小输出电压(mv)
rt_uint16_t max_volt_mv = 0; // 最大输出电压(mV)
rt_uint16_t max_curr_ma = 0; // 最大输出电流(mA)
// 数据区索引:0=状态+通道,1=CV,2=CC线,3-4=最小电压,5-6=最大电压,7-8最大电流
min_volt_mv = u8v_to_u16(&pData[3]); // 大端
max_volt_mv = u8v_to_u16(&pData[5]); // 大端
max_curr_ma = u8v_to_u16(&pData[7]); // 大端
*protect_curr = u8v_to_u16(&pData[9]); // 大端
// rt_kprintf("\n");
// for(int i=0;i<10;i++){
// rt_kprintf("%0x",pData[i]);
// }
// rt_kprintf("\n");
#if PRO_TEST_DEBUG
rt_kprintf("one_gear");
rt_kprintf("min_volt_mv = %d\n",min_volt_mv);
rt_kprintf("max_volt_mv = %d\n",max_volt_mv);
rt_kprintf("prtect_curr= %04x",*protect_curr);
#endif
*pro_gear_idx = 1; // 单档协议固定1
// 按协议命令码赋值
switch (protoCmd)
{
case 0x27: // QC3.0
*pro = PRO_QC30;
break;
case 0x28: // QC2.0
*pro = PRO_QC20;
break;
case 0x23: // SCPB
*pro = PRO_SCPB;
break;
default:
rt_kprintf("单档电源协议不支持:0x%02X\n", protoCmd);
return;
}
parse_mulit_gear_One(min_volt_mv,max_volt_mv,max_curr_ma);
return;
}
/*****************************************************************
parse_pd_11group_pdo
PD协议11组PDO数据8
pdoData: 11PDO数据缓冲区44
pdo_group_num: PDO组数
pSW:
-
-
1. type映射0src=0/pps=01src=03/pps=002src=03/pps=003src=03/pps=01
2. 16/
*****************************************************************/
void parse_pd_11group_pdo(rt_uint8_t *pdoData, rt_uint8_t pdo_group_num) {
rt_uint32_t pdo_val = 0;
rt_uint8_t pdo_type = 0;
rt_uint16_t vmin_mv = 0, vmax_mv = 0, cur_power = 0; // 16位原始值
// 清空缓冲区
rt_memset(Pro_Group_VCMM, 0, sizeof(Pro_Group_VCMM));
for (rt_uint8_t i = 0; i < pdo_group_num; i++) {
// 1. 拼接32位PDO值(低字节在前)
pdo_val = (rt_uint32_t)pdoData[i*4 + 3] << 24 |
(rt_uint32_t)pdoData[i*4 + 2] << 16 |
(rt_uint32_t)pdoData[i*4 + 1] << 8 |
(rt_uint32_t)pdoData[i*4];
// 2. 提取PDO类型(Bit30-31
pdo_type = (pdo_val >> 30) & 0x03;
// 3. 按type映射src和pps
switch (pdo_type) {
case 0: // FPDO → src=0x00, pps=0x00
Pro_Group_VCMM[i][0] = 0x00;
Pro_Group_VCMM[i][1] = 0x00;
break;
case 1: // EPRPDO → src=0x03, pps=0x00
Pro_Group_VCMM[i][0] = 0x00;
Pro_Group_VCMM[i][1] = 0x01;
break;
case 2: // PPS → src=0x03, pps=0x00
Pro_Group_VCMM[i][0] = 0x03;
Pro_Group_VCMM[i][1] = 0x00;
break;
case 3: // EPR AVS → src=0x03, pps=0x01
Pro_Group_VCMM[i][0] = 0x03;
Pro_Group_VCMM[i][1] = 0x01;
break;
default:
rt_kprintf("parse_pd_11group_pdo: unknown PDO type=%d (index=%d)\n", pdo_type, i);
continue;
}
// 4. 解析电压/电流/功率(16位值)
switch (pdo_type) {
case 0: // FPDO(固定电源)
case 1: // EPRPDO(固定电源)
vmin_mv = vmax_mv = ((pdo_val >> 10) & 0x03FF) * 50; // 电压(mV)
cur_power = (pdo_val & 0x03FF) * 10; // 电流(mA)
break;
case 2: // PPS(可编程电源)
vmin_mv = ((pdo_val >> 8) & 0x00FF) * 100; // 最小电压(mV)
vmax_mv = ((pdo_val >> 17) & 0x00FF) * 100; // 最大电压(mV)
cur_power = (pdo_val & 0x7F) * 50; // 电流(mA)
break;
case 3: // EPR AVS(功率型)
vmin_mv = ((pdo_val >> 8) & 0x00FF) * 100; // 最小电压(mV)
vmax_mv = ((pdo_val >> 17) & 0x1FF) * 100; // 最大电压(mV)
cur_power = (pdo_val & 0xFF) * 1; // 功率(W)
break;
default:
vmin_mv = vmax_mv = cur_power = 0;
break;
}
// 5. 拆分高低字节填充缓冲区(核心)
// 最小电压拆分
Pro_Group_VCMM[i][2] = (vmin_mv >> 8) & 0xFF; // vmin_H
Pro_Group_VCMM[i][3] = vmin_mv & 0xFF; // vmin_L
// 最大电压拆分
Pro_Group_VCMM[i][4] = (vmax_mv >> 8) & 0xFF; // vmax_H
Pro_Group_VCMM[i][5] = vmax_mv & 0xFF; // vmax_L
// 电流/功率拆分
Pro_Group_VCMM[i][6] = (cur_power >> 8) & 0xFF; // cur_set_H
Pro_Group_VCMM[i][7] = cur_power & 0xFF; // cur_set_L
#if PRO_TEST_DEBUG
// 调试打印
rt_kprintf("max_H=%d",Pro_Group_VCMM[i][4]);
rt_kprintf("max_L=%d",Pro_Group_VCMM[i][5]);
rt_kprintf("min_H=%d",Pro_Group_VCMM[i][2]);
rt_kprintf("min_L=%d",Pro_Group_VCMM[i][3]);
if (pdo_type == 3) { // EPR AVS 打印功率
rt_kprintf("PDO[%d]:src=0x%02X, pps=0x%02X, V_MIN=%dmV(0x%02X%02X),V_MAX=%dmV(0x%02X%02X),C/P=%dW(0x%02X%02X)\n",
i, Pro_Group_VCMM[i][0], Pro_Group_VCMM[i][1],
vmin_mv, Pro_Group_VCMM[i][2], Pro_Group_VCMM[i][3],
vmax_mv, Pro_Group_VCMM[i][4], Pro_Group_VCMM[i][5],
cur_power, Pro_Group_VCMM[i][6], Pro_Group_VCMM[i][7]);
} else { // 其他类型打印电流
rt_kprintf("PDO[%d]: src=0x%02X, pps=0x%02X, V_MIN=%dmV(0x%02X%02X), V_MAX=%dmV(0x%02X%02X),C/P=%dmA(0x%02X%02X)\n",
i, Pro_Group_VCMM[i][0], Pro_Group_VCMM[i][1],
vmin_mv, Pro_Group_VCMM[i][2], Pro_Group_VCMM[i][3],
vmax_mv, Pro_Group_VCMM[i][4], Pro_Group_VCMM[i][5],
cur_power, Pro_Group_VCMM[i][6], Pro_Group_VCMM[i][7]);
}
#endif
}
}
/*****************************************************************
parse_power_pdo_gear
PDO档电源协议解析函数PD协议
protoCmd: (0x2A)
pData: ++CC线+PDO组数+11PDO+++
reqLen:
pSW:
-
-
+(1)+CC线(1)+PDO组数(1)+11PDO(44)+(20)+(2)+(2)
*****************************************************************/
void parse_power_pdo_gear(rt_uint8_t protoCmd, rt_uint8_t *pData, rt_uint16_t reqLen,uint8_t *pro_gear_idx,uint8_t *pro,rt_uint16_t *protect_ma)
{
rt_uint8_t pdo_group_num = 0; // PDO组数
rt_int16_t comp_volt_mv = 0; // 补偿电压(mV)
// 数据区索引:0=状态+通道,1=CC线,2=恒压恒流模式,3=PDO组数(bit12-15)
rt_kprintf("pdata=%04x",pData[3]);
pdo_group_num = (pData[3] >> 4) & 0x0F; // Bit12-15提取组数
rt_kprintf("num=%d\n",pdo_group_num);
*pro = PRO_PD0PSS;
*pro_gear_idx = pdo_group_num;
// 解析11组PDO(小端序,44字节)
parse_pd_11group_pdo(&pData[4], pdo_group_num);
// 解析保留字段(20字节) + 保护电流 + 补偿电压
*protect_ma = u8v_to_u16(&pData[4 + 44 + 20]); // 67-68字节
comp_volt_mv = (rt_int16_t)u8v_to_u16(&pData[4 + 44 + 22]); // 69-70字节
// #if PRO_TEST_DEBUG
rt_kprintf("project_curr=%d mA, add_volt=%d mV\n", *protect_ma, comp_volt_mv);
// #endif.
}
/*****************************************************************
parse_mulit_gear_more
-8
pData: PDO原始数据缓冲区 gear_num: PDO组数
-
0: 1:
src/pps映射规则
*****************************************************************/
rt_uint8_t parse_mulit_gear_more(rt_uint8_t *pData, rt_uint8_t gear_num) {
// 入参合法性校验
if (pData == RT_NULL || gear_num == 0 || gear_num > 11) {
rt_kprintf("parse_mulit_gear_pd0:invalid params (gear_num=%d)\n", gear_num);
return 1;
}
// 清空缓冲区
rt_memset(Pro_Group_VCMM, 0, sizeof(Pro_Group_VCMM));
rt_uint32_t pdo_val = 0;
rt_uint8_t pdo_type = 0;
rt_uint16_t vmin_mv = 0, vmax_mv = 0, cur_power = 0;
for (rt_uint8_t i = 0; i < gear_num; i++) {
// 终止条件:PDO电压字段为0则停止
// 1. 拼接32位PDO值(低字节在前)
pdo_val = (rt_uint32_t)pData[i*4 + 3] << 24 |
(rt_uint32_t)pData[i*4 + 2] << 16 |
(rt_uint32_t)pData[i*4 + 1] << 8 |
(rt_uint32_t)pData[i*4];
// 2. 提取PDO类型(Bit30-31
pdo_type = (pdo_val >> 30) & 0x03;
// 3. 按type映射src和pps
switch (pdo_type) {
case 0: // FPDO → src=0x00, pps=0x00
Pro_Group_VCMM[i][0] = 0x00;
Pro_Group_VCMM[i][1] = 0x00;
break;
case 1: // EPRPDO → src=0x03, pps=0x00
Pro_Group_VCMM[i][0] = 0x00;
Pro_Group_VCMM[i][1] = 0x01;
break;
case 2: // PPS → src=0x03, pps=0x00
Pro_Group_VCMM[i][0] = 0x03;
Pro_Group_VCMM[i][1] = 0x00;
break;
case 3: // EPR AVS → src=0x03, pps=0x01
Pro_Group_VCMM[i][0] = 0x03;
Pro_Group_VCMM[i][1] = 0x01;
break;
default:
rt_kprintf("parse_mulit_gear_pd0:unknown PDO type=%d\n", pdo_type);
continue;
}
// 4. 解析电压/电流/功率(16位值)
switch (pdo_type) {
case 0: // FPDO(固定电源)
case 1: // EPRPDO(固定电源)
vmin_mv = vmax_mv = ((pdo_val >> 10) & 0x03FF) * 50; // 电压(mV)
cur_power = (pdo_val & 0x03FF) * 10; // 电流(mA)
break;
case 2: // PPS(可编程电源)
vmin_mv = ((pdo_val >> 8) & 0x00FF) * 100; // 最小电压(mV)
vmax_mv = ((pdo_val >> 17) & 0x00FF) * 100; // 最大电压(mV)
cur_power = (pdo_val & 0x7F) * 50; // 电流(mA)
break;
case 3: // EPR AVS(功率型)
vmin_mv = ((pdo_val >> 8) & 0x00FF) * 100; // 最小电压(mV)
vmax_mv = ((pdo_val >> 17) & 0x1FF) * 100; // 最大电压(mV)
cur_power = (pdo_val & 0xFF) * 1; // 功率(W)
break;
default:
vmin_mv = vmax_mv = cur_power = 0;
break;
}
// 5. 拆分高低字节填充缓冲区
Pro_Group_VCMM[i][2] = (vmin_mv >> 8) & 0xFF; // vmin_H
Pro_Group_VCMM[i][3] = vmin_mv & 0xFF; // vmin_L
Pro_Group_VCMM[i][4] = (vmax_mv >> 8) & 0xFF; // vmax_H
Pro_Group_VCMM[i][5] = vmax_mv & 0xFF; // vmax_L
Pro_Group_VCMM[i][6] = (cur_power >> 8) & 0xFF; // cur_set_H
Pro_Group_VCMM[i][7] = cur_power & 0xFF; // cur_set_L
#if PRO_TEST_DEBUG
// 调试打印
rt_kprintf("parse_mulit_gear_pd0 PDO[%d]:rc=0x%02X, pps=0x%02X,V_Min=%dmV,V_MAX=%dmV,C/P=%d\n",
i, Pro_Group_VCMM[i][0], Pro_Group_VCMM[i][1], vmin_mv, vmax_mv, cur_power);
#endif
}
return 0;
}
/*****************************************************************
parse_power_multi_gear_more
-FCP/AFC/SCPA
protoCmd: (0x22/0x29/0x2C/0x2F)
pData: ++CC线++PDO组++
reqLen:
pSW:
-
-
1. FCP/AFC/SCPA(1)+PDO组(4*N)+(2)+(2)
2. UFCS(1)+PDO组(4*3)+(2)+(2)
*****************************************************************/
void parse_multi_gear_more(rt_uint8_t protoCmd, rt_uint8_t *pData, rt_uint16_t reqLen,uint8_t *pro_gear_idx,uint8_t *pro,rt_uint16_t *protect_ma)
{
rt_uint8_t gear_num = 0; // 挡位数量
rt_int16_t comp_volt_mv = 0;// 补偿电压(mV)
// 数据区索引:0=状态+通道,1=CC线,2=恒压恒流模式,3=挡位数量
gear_num = pData[3];
//挡位数量
if(gear_num > 11) {
rt_kprintf("THE GEARNUM MORE 11 GEAR_NUM=%d\n", gear_num);
return;
}
*pro_gear_idx = gear_num;
rt_kprintf("pro_gear_idx = %d\n",gear_num);
// 解析保护电流和补偿电压(不同协议偏移不同)
switch (protoCmd)
{
case 0x22: // FCP (挡位数量+PDO组(4*4)+保护电流+补偿电压)
*pro = PRO_FCP;
break;
case 0x29: // AFC (同FCP)
*pro = PRO_AFC;
break;
case 0x2F: // SCPA (同FCP)
*pro = PRO_SCPA;
break;
default:
rt_kprintf("THE PRO NO MORE PRO PRO=%02X\n", protoCmd);
return;
}
*protect_ma = u8v_to_u16(&pData[4 + 16]); // 19-20字节
comp_volt_mv = (rt_int16_t)u8v_to_u16(&pData[4 + 18]);
#if PRO_TEST_DEBUG
for(int i = 0;i<30;i++){
rt_kprintf("data[%d]=%02x",i,pData[i]);
}
rt_kprintf("protect_curr=%d\n",*protect_ma);
#endif
// 解析PDO组(小端序)
parse_mulit_gear_more(&pData[4],gear_num);
}
/*****************************************************************
parse_ufcs_gear
UFCS协议单组PDO数据EPR AVS类型8
pData: UFCS单组PDO原始数据缓冲区4
gear_idx: 0
-
0: 1:
UFCS协议固定为EPR AVS类型Bit30-31=3
Bit0-7(1=1W) Bit8-15(1=100mV) Bit17-25(1=100mV)
0+PPS值
*****************************************************************/
rt_uint8_t parse_ufcs_gear_more(rt_uint8_t *pData, rt_uint8_t gear_num) {
// 入参合法性校验
if (pData == RT_NULL || gear_num == 0 || gear_num > 11) {
rt_kprintf("parse_ufcs_gear_more:invalid params (gear_num=%d)\n", gear_num);
return 1;
}
// 清空缓冲区
rt_memset(Pro_Group_VCMM, 0, sizeof(Pro_Group_VCMM));
rt_uint32_t pdo_val = 0;
rt_uint16_t vmin_mv = 0, vmax_mv = 0, cur_ma = 0;
for (rt_uint8_t i = 0; i < gear_num; i++) {
// 1. 拼接32位PDO
pdo_val = (rt_uint32_t)pData[i*4 + 3] << 24 |
(rt_uint32_t)pData[i*4 + 2] << 16 |
(rt_uint32_t)pData[i*4 + 1] << 8 |
(rt_uint32_t)pData[i*4];
// ================= 固定 =================
Pro_Group_VCMM[i][0] = 0x00;
Pro_Group_VCMM[i][1] = 0x00;
// ================= 位规则 =================
// Bit0-6 电流 1=50mA
cur_ma = (pdo_val & 0x3F) * 100;//原50,乘2了
// Bit8-15 最小电压 1=100mV
vmin_mv = ((pdo_val >> 8) & 0xFF) * 100;
// Bit17-25 最大电压 1=100mV
vmax_mv = ((pdo_val >> 17) & 0x1FF) * 100;
// ================= 填充缓冲区 =================
Pro_Group_VCMM[i][2] = (vmin_mv >> 8) & 0xFF;
Pro_Group_VCMM[i][3] = vmin_mv & 0xFF;
Pro_Group_VCMM[i][4] = (vmax_mv >> 8) & 0xFF;
Pro_Group_VCMM[i][5] = vmax_mv & 0xFF;
Pro_Group_VCMM[i][6] = (cur_ma >> 8) & 0xFF;
Pro_Group_VCMM[i][7] = cur_ma & 0xFF;
#if PRO_TEST_DEBUG
// ================= 打印格式 =================
rt_kprintf("parse_ufcs_gear_more PDO[%d]:PPS/AVS,V_Min=%dmV,V_MAX=%dmV,Max_Current=%dmA\n",
i, vmin_mv, vmax_mv, cur_ma);
#endif
}
return 0;
}
/*****************************************************************
parse_power_ufcs
UFCS协议专属解析函数
pData: ++CC线++PDO组++
reqLen:
pro_gear_idx:
pro: PRO_UFCS
-
-
UFCS固定格式(1)+PDO组(4*3)+(2)+(2)
UFCS协议
*****************************************************************/
void parse_power_ufcs(rt_uint8_t *pData, rt_uint16_t reqLen, uint8_t *pro_gear_idx, uint8_t *pro,rt_uint16_t *protect_ma)
{
rt_uint8_t gear_num = 0;
rt_int16_t comp_volt_mv = 0;
gear_num = pData[3];
if(gear_num > 11) {
rt_kprintf("parse_power_ufcs: gear_num >11, set=3\n");
gear_num = 3;
}
*pro_gear_idx = gear_num;
*pro = PRO_UFCS;
// UFCS偏移
*protect_ma = u8v_to_u16(&pData[4 + 12]);
// for(int i = 0;i<24;i++){
// rt_kprintf("data[%d]=%02x",i,pData[i]);
// }
comp_volt_mv = 0;//(rt_int16_t)u8v_to_u16(&pData[3 + 14]);
//#if PRO_TEST_DEBUG
rt_kprintf("parse_power_ufcs:gear=%d,protect=%dmA,comp=%dmV\n",
gear_num,*protect_ma, comp_volt_mv);
//#endif
// 调用下面的循环解析
parse_ufcs_gear_more(&pData[4], gear_num);
}
static rt_uint8_t pd_fill_single_group(rt_uint8_t *data, rt_uint8_t start_idx, rt_uint8_t group_idx,rt_uint16_t max_cur)
{
// 校验索引合法性
if (group_idx >= PD_VOLTAGE_CNT) {
rt_kprintf("ERROR GROUP IDX EXCEED IDX=%d)\n", group_idx);
return 0;
}
rt_uint8_t idx = start_idx;
const Pro_Group *target_group = &pd_voltage_map[group_idx]; // 仅取目标挡位
// 1. 填充pro + src2字节)
data[idx++] = target_group->src;
data[idx++] = target_group->pps;
// 2. 填充电压最小值(2字节,高8位+低8位)
data[idx++] = (target_group->v_min >> 8) & 0xff;
data[idx++] = target_group->v_min & 0xff;
// 3. 填充电压最大值(2字节,高8位+低8位)
data[idx++] = (target_group->v_max >> 8) & 0xff;
data[idx++] = target_group->v_max & 0xff;
// 4. 填充电流设置值(2字节,高8位+低8位)
data[idx++] = (max_cur >> 8) & 0xff;
data[idx++] = max_cur & 0xff;
// 5. 填充保留位(6字节全0
for (rt_uint8_t j = 0; j < 6; j++) {
data[idx++] = 0x00;
}
return idx - start_idx; // 返回填充的总字节数
}
/*****************************************************************
source_set_pd
source PD协议
@@ -89,29 +623,273 @@ int source_set_cc_lvl (rt_uint8_t lvl)
int source_set_pd (rt_uint8_t pro, rt_uint8_t src, rt_uint8_t pps,
rt_uint16_t vmax, rt_uint16_t vmin, rt_int16_t max_cur)
{
rt_uint16_t len = 0;
rt_uint8_t data[32] = {0};
rt_uint8_t frame[32] = {0};
rt_uint8_t data[80] = {0};
rt_uint8_t frame[80] = {0};
rt_uint8_t send_len = 0; //数据长度
rt_uint8_t max_group_idx = 0; //挡位索引
data[0] = pro; //协议
//rt_kprintf("cur_pro=%d",max_cur);
if(pro==0x07||pro==0x08||pro==0x03){
data[1] = 0;//src
data[2] = 0;//pps
data[3] = 0x13;//固定电压最小值为5V
data[4] = 0x88;
data[5] = (vmax>>8)&0xff;//电压最大值为设定电压
data[6] = vmax&0xff;
data[7] = (max_cur>>8)&0xff;//电流输出为设置值
data[8] = max_cur&0xff;
for (rt_uint8_t j = 9; j < 14; j++) {
data[j] = 0x00;
}
send_len=15;
}
else{
switch (vmax)
{
case 5000: max_group_idx = 0; break;
case 9000: max_group_idx = 1; break;
case 12000: max_group_idx = 2; break;
case 20000: max_group_idx = 3; break;
case 48000: max_group_idx = 4; break;
default:rt_kprintf("VOLT ERROR");break;
}
rt_uint8_t idx = 1; //从data[1]开始填充
// 循环填充从0到max_group_idx的所有挡位
for (rt_uint8_t i = 0; i <= max_group_idx; i++) {
rt_uint8_t fill_len = pd_fill_single_group(data, idx, i,max_cur);
if (fill_len == 0) {
rt_kprintf("Error: fill group %d failed\n", i);
return -1;
}
idx += fill_len; // 偏移到下一组起始位置
}
send_len = idx; // 总填充字节数
}
chrg_north_pkg_encode(ID_SOURCE, SRC_CMD_SET_PROTOCOL, data, send_len, frame, &len);
return chrg_north_send(frame, len);
}
data[0] = pro;
data[1] = src;
data[2] = pps;
u16_to_u8v(vmax, &data[3]);
u16_to_u8v(vmin, &data[5]);
u16_to_u8v(max_cur, &data[7]);
data[9] = 0x00;
data[10] = 0x00;
data[11] = 0x00;
data[12] = 0x00;
data[13] = 0x00;
data[14] = 0x00;
/*****************************************************************
pd_fill_single_group
PD挡位的协议数据
data: start_idx: group_idx:0=5V,1=9V...4=PPS
-
0:/ >0:14
-
*****************************************************************/
rt_uint8_t pd_fill_single_group_com(rt_uint8_t *data, rt_uint8_t start_idx, rt_uint8_t gear)
{
// 核心:直接索引已拆分好的高低位缓冲区,按位填充(无任何多余运算)
rt_uint8_t idx = start_idx;
// 1. src + pps2字节)
data[idx++] = Pro_Group_VCMM[gear][0];
data[idx++] = Pro_Group_VCMM[gear][1];
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = Pro_Group_VCMM[gear][2];
data[idx++] = Pro_Group_VCMM[gear][3];
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = Pro_Group_VCMM[gear][4];
data[idx++] = Pro_Group_VCMM[gear][5];
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = Pro_Group_VCMM[gear][6];
data[idx++] = Pro_Group_VCMM[gear][7];
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
chrg_north_pkg_encode(ID_SOURCE, SRC_CMD_SET_PROTOCOL, data, 15, frame, &len);
rt_uint8_t pd_fill_single_group_need_src_com(rt_uint8_t *data, rt_uint8_t start_idx,struct chrg_switch_t *psw){
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
// 1. src + pps2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = 0x13;
data[idx++] = 0x88;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = 0x13;
data[idx++] = 0x88;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
data[idx++] = source->src;
data[idx++] = source->pps;
rt_kprintf("src = %d pps = %d\n",source->src,source->pps);
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->min_vol >> 8;
data[idx++] = source->min_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->max_vol >> 8;
data[idx++] = source->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
rt_uint8_t pd_fill_single_group_no_src_com(rt_uint8_t *data, rt_uint8_t start_idx,struct chrg_switch_t *psw){
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
// 1. src + pps2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->min_vol >> 8;
data[idx++] = source->min_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->max_vol >> 8;
data[idx++] = source->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
rt_uint8_t pd_fill_single_group_FCP_AFC(rt_uint8_t *data, rt_uint8_t start_idx,struct chrg_switch_t *psw){
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
// 1. src + pps2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = 0x13;
data[idx++] = 0x88;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = 0x13;
data[idx++] = 0x88;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
// 1. src + pps2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->max_vol >> 8;
data[idx++] = source->max_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->max_vol >> 8;
data[idx++] = source->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
rt_uint8_t pd_fill_single_group_UFCS(rt_uint8_t *data, rt_uint8_t start_idx,struct chrg_switch_t *psw){
rt_uint8_t idx = start_idx;
struct chrg_src_t *source = &psw->source;
// 1. src + pps2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = 0x13;
data[idx++] = 0x88;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = 0x13;
data[idx++] = 0x88;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
// 1. src + pps2字节)
data[idx++] = 0x00;
data[idx++] = 0x00;
// 2. 最小电压(高+低,直接取已拆分好的值)
data[idx++] = source->min_vol >> 8;
data[idx++] = source->min_vol & 0xFF;
// 3. 最大电压(高+低,直接取已拆分好的值)
data[idx++] = source->max_vol >> 8;
data[idx++] = source->max_vol & 0xFF;
// 4. 电流/功率(高+低,直接取已拆分好的值)
data[idx++] = source->protective_curr >> 8;
data[idx++] = source->protective_curr & 0xFF;
// 5. 保留位6字节(直接填0
memset(&data[idx], 0x00, 6);
idx+=6;
return idx - start_idx; // 返回填充的总字节数
}
int source_set_pd_com(rt_uint8_t pro,rt_uint8_t pro_gear_idx)
{
rt_uint16_t len = 0;
rt_uint8_t data[150] = {0};
rt_uint8_t frame[150] = {0};
rt_uint8_t send_len = 0; //数据长度
rt_uint8_t max_group_idx = 0; //挡位索引
data[0] = pro; //协议
rt_uint8_t idx = 1;
for(rt_uint8_t i = 0;i<pro_gear_idx;i++){
rt_uint8_t fill_len = pd_fill_single_group_com(data,idx,i);
if (fill_len == 0) {
rt_kprintf("Error: fill group %d failed\n", i);
return -1;
}
idx += fill_len; // 偏移到下一组起始位置
}
send_len = idx; // 总填充字节数
chrg_north_pkg_encode(ID_SOURCE, SRC_CMD_SET_PROTOCOL, data, send_len, frame, &len);
// for(rt_uint8_t i=0;i<len+5;i++){
// rt_kprintf("%02x",data[i]);
// }
// rt_kprintf("idx=%d",idx);
return chrg_north_send(frame, len);
}
int source_set_pd_new_com(rt_uint8_t pro, struct chrg_switch_t *pSW)
{
rt_uint16_t len = 0;
rt_uint8_t data[150] = {0};
rt_uint8_t frame[150] = {0};
rt_uint8_t send_len = 0; //数据长度
rt_uint8_t max_group_idx = 0; //挡位索引
data[0] = pro; //协议
rt_uint8_t idx = 1; //从data[1]开始填充
rt_uint8_t fill_len = 0;
rt_uint8_t protocal = pSW->source.protocol;
if(protocal == 0x0A){
fill_len = pd_fill_single_group_need_src_com(data, idx, pSW);
}else if(protocal == 0x03||protocal == 0x07||protocal == 0x08 ){
fill_len = pd_fill_single_group_no_src_com(data, idx, pSW);
}else if(protocal == 0x0C){
fill_len = pd_fill_single_group_UFCS(data, idx, pSW);
}else{
fill_len = pd_fill_single_group_FCP_AFC(data, idx, pSW);
}
idx += fill_len;
send_len = idx; // 总填充字节数
chrg_north_pkg_encode(ID_SOURCE, SRC_CMD_SET_PROTOCOL, data, send_len, frame, &len);
rt_kprintf("idx = %d ",idx);
// for(rt_uint8_t i=0;i<idx;i++){
// rt_kprintf("%02x",data[i]);
// }
// rt_kprintf("idx=%d",idx);
return chrg_north_send(frame, len);
}
+112 -42
View File
@@ -11,51 +11,44 @@
#define __CHRG_SOURCE_H__
#include <rtthread.h>
#include "chrg_north.h"
#include "chrg_def.h"
struct chrg_switch_t;
// 冠达快充板(SOURCE)通讯协议
// SOURCE的ID为2, 波特率200K
#define SRC_CMD_GET_VOLTAGE 0x20 // 读取数据
// 发送 0B 00 02 20 volh voll curh curl nc nc CRC8
// 长度+nc+id+指令+2字节当前电源电压(mV)+2字节当前电源电流(mA)+2个预留字节+CRC8校验
// 接收 15 00 02 20 VH VL setvh setvl setch setcl NC NC verh verl NC NC NC NC NC NC CRC8
// 长度+nc+ID+指令+2字节读取的电压(0.01V)+2字节source设置电压(mV)+2字节source设置电流(mA)+2字节预留+2字节版本号+6字节预留+CRC8校验
#define SRC_CMD_GET_PROTOCOL 0x13 // 读取source的PD协议数据(当前为PDO,PPS,PD3.1,AVS协议时有效)
// 发送 05 00 02 13 2A
// XX 00 02 13 //长度(根据实际计算)+NC+ID+指令
// 00 00 VMAXH VMAXL VMINH VMINL CURH/PORH CURL/PORL NC NC NC NC NC NC
//源类型(1字节,00为PDO,03为APDO) + PPS类型(1字节,源类型为3时有效,00为SPR PPS,01为EPR AVS,02为SPR AVS)
// + 最大电压(2字节,mV) + 最小电压(2字节,mA) + 最大电流/功率(2字节,mA/W) + 预留(6字节,当协议类型为02时有效,意义与前6字节一致)
// 00 00 VMAXH VMAXL VMINH VMINL CURH/PORH CURL/PORL NC NC NC NC NC NC
// ...... CRC8
#define SRC_CMD_SET_CC_LEVEL 0x16
// 发送 06 00 02 16 00 2A
// 长度+nc+id+指令+电平等级(0xff为根据当前电流自动设置,否则为手动设定,设置方法待定)+CRC8校验
// 接收 05 00 02 16 2A
// 长度+nc+id+指令+CRC8校验
#define SRC_CMD_SET_CC_CHOOSE 0x17
#define SRC_CMD_SET_PROTOCOL 0xD0
// 发送 XX 00 02 D0 // 长度(根据实际计算)+NC+ID+指令
// 00 00 00 VMAXH VMAXL VMINH VMINL CURH/PORH CURL/PORL NC NC NC NC NC NC
// 协议(1字节) + 源类型(1字节,PD协议有效,00为PDO,03为APDO) +
// PPS类型(1字节, PD协议有效,源类型为3时有效,00为SPR PPS,01为EPR AVS,02为SPR AVS) + 最大电压(2字节,mV) +
// 最小电压(2字节,mA) + 最大电流/功率(2字节,mA/W) + 预留(6字节,当协议类型为02时有效,意义与前6字节一致)
// 00 00 00 VMAXH VMAXL VMINH VMINL CURH/PORH CURL/PORL NC NC NC NC NC NC
// ...... CRC8
// 接收 05 00 02 D0 CRC8
#define PROTOCOL_NONE 0x00 // 无快充
#define PROTOCOL_VIFC 0x01 // VIFC(暂不支持)
#define PROTOCOL_FCP 0x02 // FCP
#define PROTOCOL_SCPB 0x03 // SCPB
#define PROTOCOL_RESERVED_04 0x04 // 预留
#define PROTOCOL_RESERVED_05 0x05 // 预留
#define PROTOCOL_RFC 0x06 // RFC(暂不支持)
#define PROTOCOL_QC30 0x07 // QC3.0
#define PROTOCOL_QC20 0x08 // QC2.0
#define PROTOCOL_AFC 0x09 // AFC
#define PROTOCOL_PD 0x0A // PD
#define PROTOCOL_RESERVED_0B 0x0B // 预留
#define PROTOCOL_UFCS 0x0C // UFCS
#define PROTOCOL_RESERVED_0D 0x0D // 预留
#define PROTOCOL_VOOC 0x0E // VOOC(暂不支持)
#define PROTOCOL_SCPA 0x0F // SCPA
typedef enum {
PRO_NONE = 0x00, // 无快充
PRO_NONE = 0x00, // 标准
PRO_VIFC = 0x01, // VIFC(暂不支持)
PRO_FCP = 0x02, // FCP
PRO_SCP = 0x03, // SCP
PRO_SCPB = 0x03, // SCP
PRO_SERV1 = 0x04, // 预留
PRO_SERV2 = 0x05, // 预留
PRO_RFC = 0x06, // RFC(暂不支持)
@@ -67,10 +60,38 @@ typedef enum {
PRO_UFCS = 0x0C, // UFCS
PRO_PD31AVS = 0x0D, // PD3.1/AVS
PRO_VOOC = 0x0E, // VOOC(暂不支持)
PRO_SCPA = 0x0F, // SCPA
TOTAL_PROS
} eSRC_PRO;
// 电源协议映射表:枚举值 ↔ 协议名称(和你的 eSRC_PRO 完全对齐)
typedef struct {
rt_uint8_t pro_val; // 协议数值(对应 eSRC_PRO 枚举)
int pro_name; // 协议显示名称(LCD展示用)
} SrcProMap_t;
// 按 eSRC_PRO 枚举顺序定义映射表,确保一一对应
static const SrcProMap_t src_pro_map[] = {
{PRO_NONE, 30},
{PRO_VIFC, 31},
{PRO_FCP, 32},
{PRO_SCPB, 33},
{PRO_SERV1, 34},
{PRO_SERV2, 35},
{PRO_RFC, 36},
{PRO_QC30, 37},
{PRO_QC20, 38},
{PRO_AFC, 39},
{PRO_PD0PSS, 40},
{PRO_SERV3, 41},
{PRO_UFCS, 42},
{PRO_PD31AVS, 43},
{PRO_VOOC, 44},
{PRO_SCPA, 45}
};
// 映射表长度宏(自动计算,避免手动维护)
#define SRC_PRO_MAP_NUM (sizeof(src_pro_map) / sizeof(src_pro_map[0]))
// 应答帧头
struct pkg_head_t {
rt_uint8_t length; // 帧长度
@@ -121,22 +142,34 @@ struct src_vc_t {
rt_uint16_t version; // 版本号
};
#define PD_GEAR_ARRAY_LEN 4 // 非5V挡位最多4个,数组长度设为4
// source 参数结构
struct chrg_src_t {
rt_uint16_t voltage; // 当前电压 mV
rt_uint16_t current; // 当前电流 mA
rt_uint8_t vol_lvl; // CC线电平
eSRC_PRO protocol; // 协议类型
struct src_vc_t vc; // 读取数据
struct vc_pd_t pd_get[2]; // 读取PD协议数据
struct vc_pd_t pd_set[2]; // 设置PD协议数据
rt_uint16_t voltage; // 当前电压 mV
rt_uint16_t current; // 当前电流 mA
eSRC_PRO protocol; // 协议类型
rt_uint8_t src; // 源类型
rt_uint8_t pps; // PPS类型
rt_uint8_t pro_gear_idx; // 挡位
rt_uint16_t min_vol; // 最小电压 mV
rt_uint16_t max_vol; // 最大电压 mV
rt_uint8_t vol_lvl; // CC线电平
rt_uint8_t cc_set; // cc线选择 //0代表1+21代表cc12代表cc2
rt_uint16_t protective_curr; // 保护电流
rt_uint8_t Now_State; // 当前状态
rt_uint8_t On_Flag; // 启动总标志(0/1
rt_uint8_t Set_CV_Flag; // 电压/电流切换标志(0/1
rt_uint8_t Set_Pro_Flag; // 设置协议标志位(0/1/2)0代表没设置协议,1代表设置了但还没执行,2代表设置了并执行
rt_uint8_t LCD_Set_Pro_Flag; // LCD设置协议标志位
rt_uint8_t Work_step_flag; // 工作步骤标志位
rt_uint8_t Stop_step_flag; // 停止步骤标志位
rt_uint8_t change_sink_Flag; // 从source切换到sink标志位
rt_uint8_t work_mode; // 0=停止 1=工作 2=低电压
struct src_vc_t vc; // 读取数据
struct vc_pd_t pd_get[2]; // 读取PD协议数据
struct vc_pd_t pd_set[2]; // 设置PD协议数据
};
/*****************************************************************
source_get_vc
source电压电流查询请求
@@ -167,6 +200,16 @@ extern int source_get_pd (void);
*****************************************************************/
extern int source_set_cc_lvl (rt_uint8_t lvl);
/*****************************************************************
source_set_cc_choose
source cc线
cc_set 01+21cc12cc2
-
<0: >0:
-
*****************************************************************/
extern int source_set_cc_choose (rt_uint8_t cc_set);
/*****************************************************************
source_set_pd
source PD协议
@@ -178,7 +221,34 @@ extern int source_set_cc_lvl (rt_uint8_t lvl);
extern int source_set_pd (rt_uint8_t pro, rt_uint8_t src, rt_uint8_t pps,
rt_uint16_t vmax, rt_uint16_t vmin, rt_int16_t max_cur);
// 挡位参数结构体
typedef struct {
rt_uint8_t src; // 第1字段(如00/03
rt_uint8_t pps; // 第2字段(如00/01
rt_uint16_t v_min; // 电压最小值(如0x1388=5V
rt_uint16_t v_max; // 电压最大值(如0x1388=5V
rt_uint16_t cur_set; // 电流设置值(如0x0bb8=3A
} Pro_Group;
// 挡位映射表(索引对应挡位:0=5V,1=9V,2=12V,3=20V,4=PPS
static const Pro_Group pd_voltage_map[] = {
{0x00, 0x00, 0x1388, 0x1388, 0x0bb8}, // 05V挡位
{0x00, 0x00, 0x2328, 0x2328, 0x0bb8}, // 19V挡位
{0x00, 0x00, 0x2ee0, 0x2ee0, 0x0bb8}, // 212V挡位
{0x00, 0x00, 0x4e20, 0x4e20, 0x0bb8}, // 320V挡位
{0x00, 0x00, 0xBB80, 0xBB80, 0X0bb8} // 4:48V
// {0x03, 0x00, 0x1388, 0x4e20, 0x1388} // 4PPS挡位 5-20V
};
extern rt_uint8_t pd_fill_gear_index(rt_uint8_t *gear_index, rt_uint8_t *gear_cnt, rt_uint8_t hex_code);
extern int source_set_pd_com(rt_uint8_t pro,rt_uint8_t pro_gear_idx);
#define PD_VOLTAGE_CNT (sizeof(pd_voltage_map) / sizeof(Pro_Group))
extern rt_uint16_t pd_gear_parse(rt_uint8_t hex_code);
extern void parse_power_single_gear(rt_uint8_t protoCmd, rt_uint8_t *pData, rt_uint16_t reqLen,uint8_t *pro_gear_idx,uint8_t *pro,rt_uint16_t *protect_ma);
extern void parse_power_pdo_gear(rt_uint8_t protoCmd, rt_uint8_t *pData, rt_uint16_t reqLen,uint8_t *pro_gear_idx,uint8_t *pro,rt_uint16_t *protect_ma);
extern void parse_multi_gear_more(rt_uint8_t protoCmd, rt_uint8_t *pData, rt_uint16_t reqLen,uint8_t *pro_gear_idx,uint8_t *pro,rt_uint16_t *protect_ma);
extern void parse_power_ufcs(rt_uint8_t *pData, rt_uint16_t reqLen, uint8_t *pro_gear_idx, uint8_t *pro,rt_uint16_t *protect_ma);
extern int source_set_pd_new_com(rt_uint8_t pro, struct chrg_switch_t *pSW);
#endif
+29 -3
View File
@@ -75,12 +75,19 @@ rt_uint16_t mb_crc16 (rt_uint8_t *pucFrame, rt_uint16_t usLen)
rt_uint8_t ucCRCLo = 0xFF;
int iIndex;
if(usLen==8){
for(int i = 0;i<usLen;i++){
rt_kprintf("%02x ",pucFrame[i]);
}
rt_kprintf("\n");
}
while ( usLen-- ) {
iIndex = ucCRCLo ^ *(pucFrame++);
ucCRCLo = (rt_uint8_t)(ucCRCHi ^ aucCRCHi[iIndex]);
ucCRCHi = aucCRCLo[iIndex];
}
return (rt_uint16_t)(ucCRCHi << 8 | ucCRCLo);
}
@@ -135,13 +142,32 @@ rt_uint8_t u16_to_u8v (rt_uint16_t value, rt_uint8_t *buf)
{
if (RT_NULL != buf) {
buf[0] = (value>>8)&0xFF;
// rt_kprintf("%d",buf[0]);
buf[1] = value&0xFF;
// rt_kprintf("%d",buf[0]);
return 2;
}
return 0;
}
/*****************************************************************
u16_to_u8v_com
u16
value: *buf:
-
-
-2026.7.1
*****************************************************************/
rt_uint8_t u16_to_u8v_com (rt_uint16_t value, rt_uint8_t *buf)
{
if (RT_NULL != buf) {
buf[1] = (value>>8)&0xFF;
// rt_kprintf("%d",buf[0]);
buf[0] = value&0xFF;
// rt_kprintf("%d",buf[0]);
return 2;
}
return 0;
}
/*****************************************************************
u8v_to_u16
u16
@@ -91,6 +91,15 @@ extern rt_uint16_t swap_u16 (rt_uint16_t x);
-
*****************************************************************/
extern rt_uint32_t swap_u32 (rt_uint32_t x);
/*****************************************************************
u16_to_u8v_com
u16
value: *buf:
-
-
-2026.7.1
*****************************************************************/
extern rt_uint8_t u16_to_u8v_com (rt_uint16_t value, rt_uint8_t *buf);
#endif
Binary file not shown.
+29 -20
View File
@@ -16,7 +16,7 @@
<TargetCommonOption>
<Device>STM32F405RG</Device>
<Vendor>STMicroelectronics</Vendor>
<PackID>Keil.STM32F4xx_DFP.3.1.0</PackID>
<PackID>Keil.STM32F4xx_DFP.3.1.1</PackID>
<PackURL>https://www.keil.com/pack/</PackURL>
<Cpu>IRAM(0x20000000-0x2001FFFF) IRAM2(0x10000000-0x1000FFFF) IROM(0x8000000-0x80FFFFF) CLOCK(25000000) CPUTYPE("Cortex-M4") FPU2</Cpu>
<FlashUtilSpec></FlashUtilSpec>
@@ -82,7 +82,7 @@
<AfterMake>
<RunUserProg1>1</RunUserProg1>
<RunUserProg2>0</RunUserProg2>
<UserProg1Name>fromelf --bin !L -o @L.bin</UserProg1Name>
<UserProg1Name>fromelf --bin !L -o build/@L.bin</UserProg1Name>
<UserProg2Name></UserProg2Name>
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
@@ -132,7 +132,7 @@
<UseTargetDll>1</UseTargetDll>
<UseExternalTool>0</UseExternalTool>
<RunIndependent>0</RunIndependent>
<UpdateFlashBeforeDebugging>1</UpdateFlashBeforeDebugging>
<UpdateFlashBeforeDebugging>0</UpdateFlashBeforeDebugging>
<Capability>1</Capability>
<DriverSelection>4096</DriverSelection>
</Flash1>
@@ -313,7 +313,7 @@
</ArmAdsMisc>
<Cads>
<interw>1</interw>
<Optim>2</Optim>
<Optim>4</Optim>
<oTime>0</oTime>
<SplitLS>0</SplitLS>
<OneElfS>1</OneElfS>
@@ -352,7 +352,7 @@
<NoWarn>0</NoWarn>
<uSurpInc>0</uSurpInc>
<useXO>0</useXO>
<ClangAsOpt>1</ClangAsOpt>
<ClangAsOpt>4</ClangAsOpt>
<VariousControls>
<MiscControls></MiscControls>
<Define></Define>
@@ -443,16 +443,16 @@
<Group>
<GroupName>Applications/thread</GroupName>
<Files>
<File>
<FileName>chrg_north.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_north.c</FilePath>
</File>
<File>
<FileName>chrg_roll_nor.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_roll_nor.c</FilePath>
</File>
<File>
<FileName>chrg_north.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_north.c</FilePath>
</File>
<File>
<FileName>chrg_south.c</FileName>
<FileType>1</FileType>
@@ -468,16 +468,16 @@
<FileType>1</FileType>
<FilePath>applications\thread\chrg_roll_sou.c</FilePath>
</File>
<File>
<FileName>chrg_thread.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_thread.c</FilePath>
</File>
<File>
<FileName>chrg_lcd.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_lcd.c</FilePath>
</File>
<File>
<FileName>chrg_thread.c</FileName>
<FileType>1</FileType>
<FilePath>applications\thread\chrg_thread.c</FilePath>
</File>
</Files>
</Group>
<Group>
@@ -493,16 +493,16 @@
<FileType>1</FileType>
<FilePath>applications\utils\chrg_sink.c</FilePath>
</File>
<File>
<FileName>chrg_eload.c</FileName>
<FileType>1</FileType>
<FilePath>applications\utils\chrg_eload.c</FilePath>
</File>
<File>
<FileName>chrg_north_pkg.c</FileName>
<FileType>1</FileType>
<FilePath>applications\utils\chrg_north_pkg.c</FilePath>
</File>
<File>
<FileName>chrg_eload.c</FileName>
<FileType>1</FileType>
<FilePath>applications\utils\chrg_eload.c</FilePath>
</File>
<File>
<FileName>chrg_utils.c</FileName>
<FileType>1</FileType>
@@ -2559,4 +2559,13 @@
<files/>
</RTE>
<LayerInfo>
<Layers>
<Layer>
<LayName>chrg</LayName>
<LayPrjMark>1</LayPrjMark>
</Layer>
</Layers>
</LayerInfo>
</Project>
+1
View File
@@ -0,0 +1 @@
LOAD %L INCREMENTAL
+2 -2
View File
@@ -15,7 +15,7 @@
<TargetCommonOption>
<Device>STM32F405RG</Device>
<Vendor>STMicroelectronics</Vendor>
<PackID>Keil.STM32F4xx_DFP.3.1.0</PackID>
<PackID>Keil.STM32F4xx_DFP.3.1.1</PackID>
<PackURL>https://www.keil.com/pack/</PackURL>
<Cpu>IRAM(0x20000000-0x2001FFFF) IRAM2(0x10000000-0x1000FFFF) IROM(0x8000000-0x80FFFFF) CLOCK(25000000) CPUTYPE("Cortex-M4") FPU2</Cpu>
<FlashUtilSpec></FlashUtilSpec>
@@ -81,7 +81,7 @@
<AfterMake>
<RunUserProg1>1</RunUserProg1>
<RunUserProg2>0</RunUserProg2>
<UserProg1Name>fromelf --bin !L -o @L.bin</UserProg1Name>
<UserProg1Name>fromelf --bin !L -o build/@L.bin</UserProg1Name>
<UserProg2Name></UserProg2Name>
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
File diff suppressed because it is too large Load Diff
+108
View File
@@ -0,0 +1,108 @@
# apps/chrg 长期运行修复基线
## 记录信息
- 记录时间:2026-07-18 15:53:36 +08:00
- 当前分支:`feature`
- 当前 HEAD`5ffc85077ee7c4ebd52178021d9ac99eed223cd6`
- 业务改动检查点:`c820aad feat: 完成项目多模块功能迭代与配置更新`
- 检查点说明:`c820aad` 包含当前 `apps/chrg` 多模块业务改动;后续 `5ffc850` 仅新增仓库 README 和死锁修复计划。当前 `apps/chrg` 相对 HEAD 无未提交差异。
- 保留的无关工作区文件:`docs/修改计划.md` 为未跟踪文件,本任务未修改、暂存或清理该文件。
## Git 工作区基线
执行命令:
```powershell
git status --short --branch
```
输出:
```text
## feature
?? "docs/\344\277\256\346\224\271\350\256\241\345\210\222.md"
```
执行命令:
```powershell
git diff --ignore-space-at-eol --stat -- apps/chrg
```
输出:无输出,表示 `apps/chrg` 相对当前 HEAD 没有未提交差异。
执行命令:
```powershell
git log -5 --oneline --decorate
```
输出:
```text
5ffc850 (HEAD -> feature) docs: 初始化HCDF MID 405充放电控制器固件仓库
c820aad feat: 完成项目多模块功能迭代与配置更新
250864c (origin/feature) debug north
3a413f5 (origin/master, origin/HEAD, master, agents/add-comments-to-sinkc) nor / sou roll
7e463e5 add more comments
```
## 修复前构建
执行命令:
```powershell
scons -C apps/chrg -j4
```
结果:**失败,构建环境缺失。**
```text
scons : 无法将“scons”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。
CategoryInfo : ObjectNotFound: (scons:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
```
环境复核结果:
- `scons`:无法取得,未安装或未加入 `PATH`
- Python:系统 `PATH` 中无法取得;Codex 自带 Python 可运行,但未安装 `SCons` 模块。
- `arm-none-eabi-gcc`:无法取得,未安装或未加入 `PATH`
- Keil UV4:常用路径 `C:\Keil_v5\UV4\UV4.exe` 及检查的常见安装目录中未找到。
- 既有输出:未找到可复用的 `chrg.elf``rtthread.bin``chrg.axf`
- text/data/bss:无法取得,原因是构建未启动。
- 镜像大小:无法取得,原因是未生成镜像。
- 编译 warning:无法取得,原因是编译器未运行。
结论:Task 1 的修复前构建验收尚未通过。在具备 SCons 与 Arm GCC 或 Keil MDK 的构建机上补跑同一命令并补录产物尺寸、段大小和全部 warning 前,不应开始功能修改。
## 故障现场 Shell 快照
当前会话只有离线源码工作区,没有连接发生故障的 STM32F405 设备、串口或 USB Shell,因此不能执行板上命令。以下项目均显式记录为无法取得,避免将“未采集”误认为正常状态。
| 命令 | 结果 |
| --- | --- |
| `list_thread` | 无法取得:当前未连接目标板 Shell,线程状态和栈使用量未知。 |
| `list_mutex` | 无法取得:当前未连接目标板 Shellmutex owner/suspend 未采集。 |
| `list_mailbox` | 无法取得:当前未连接目标板 Shell,邮箱 entry 数未采集。 |
| `list_event` | 无法取得:当前未连接目标板 Shell,事件状态未采集。 |
| `free` | 无法取得:当前未连接目标板 Shellheap total/used/max-used 未采集。 |
| `list_device` | 无法取得:当前未连接目标板 Shell,设备状态未采集。 |
## 故障特征
- 首次异常运行时长:无法取得:未提供对应现场运行记录。
- 异常时仍工作的接口:无法取得:当前没有现场设备或故障时间点数据。
- 最后 100 行日志:无法取得:仓库中未提供本次故障对应的运行日志,当前未连接目标板。
- `com batch timeout` 是否连续出现:无法取得:没有现场串口日志。
- `free` 的 total/used/max-used:无法取得:当前未连接目标板 Shell。
- `thr.rollnor``thr.rollsou` 状态:无法取得:当前未连接目标板 Shell。
- `thr.nor``thr.sou` 状态:无法取得:当前未连接目标板 Shell。
- 最近一次复位原因:无法取得:没有启动日志或 RCC 复位标志快照。
## 后续补录要求
1. 在配置完整的构建机上重新执行 `scons -C apps/chrg -j4`,补录工具链版本、text/data/bss、镜像大小和 warning。
2. 下次现场复现且 Shell 尚可用时,立即按上述顺序保存六条诊断命令的原始输出和最后 100 行日志。
3. 所有后续修复必须基于 `c820aad` 已包含的业务逻辑和当前 HEAD,不得从 `origin/feature``master` 重新开始。
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

@@ -238,7 +238,7 @@ HardFault_Handler PROC
IF {FPU} != "SoftVFP"
TST lr, #0x10 ; if(!EXC_RETURN[4])
VSTMFDEQ r0!, {d8 - d15} ; push FPU register s16~s31
VSTMFDEQ r0!, {d8 - d15} ; push FPU register s16~s31
ENDIF
STMFD r0!, {r4 - r11} ; push r4 - r11 register
+1 -1
View File
@@ -101,7 +101,7 @@ struct rt_cpu *rt_cpu_self(void)
/**
* @brief This fucntion will return the cpu object corresponding to index.
*
*f
* @param index is the index of target cpu object.
*
* @return Return a pointer to the cpu object corresponding to index.
+39
View File
@@ -0,0 +1,39 @@
北桥通过405向南桥( 原载功率板)发送
原载功能切换通过P0 P1 P2设置
空闲时P0=0,P1=0; P2 = 1
负载时P0=1,P1=0,P2=0
电源时P0=0,P1=1,P2=1
通过rel_test set切换原载
south_run stop停止
停止后功率板会死机
诱骗步骤:
1.设置通道
north_ch 0 //通道0
north_ch 1 //通道1
north_ch 2 //通道2
north_ch 3 //通道3
2.设置原载
rel_test set 0 1 //设置通道0为sink协议模式
rel_test set 1 1 //设置通道1为sink协议模式
rel_test set 2 1 //设置通道2为sink协议模式
rel_test set 3 1 //设置通道3为sink协议模式
rel_test get 0 //检查0
rel_test get 1 //检查1
rel_test get 2 //检查2
rel_test get 3 //检查3
返回值
rel connect [ch] : p0=1, p1=0, p2=0 //继电器切换成sink协议的信号
3.设置变电协议
负载时为:
sink set 0 pd 9 //设置通道0为9V OK
sink set 1 pd 9 //设置通道1为9V OK
sink set 2 pd 9 //设置通道2为9V OK
sink set 3 pd 9 //设置通道3为9V OK
//发送指令D/sendN: 0000-000A: 0A 00 01 09 00 00 00 00 00 C1
//回复指令05 00 01 F8 AA
诱骗不成功,检查原载接口板有没有插对通道
+9
View File
@@ -0,0 +1,9 @@
PD协议
4c 01 02 D0 //帧头
0a //协议
00 00 1388 1388 0bb8 000000000000 //五V挡位(必须有)
00 00 2328 2328 0bb8 000000000000 //9V挡位
00 00 2EE0 2EE0 0bb8 000000000000 //12V挡位
00 00 4E20 4E20 0bb8 000000000000 //20V挡位
03 00 1388 4E20 1388 000000000000 //PPS挡位 5-20V
2a //帧尾
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -0,0 +1,107 @@
冠达快充板(SINK)通讯协议
2025.8.13
SINK的ID为1,波特率230400TTL
1.读取变电压板电压
(要实时比较设置的快充指令和读回来的快充指令,若有不一样,则重设)
(注意vooc模式下数据返回会很慢)
发送:
0B 00 01 20 loadvh loadvl loadch loadcl nc nc CRC8 //长度+nc+id+指令+负载电压(2字节,0.01V)+负载电流(2字节,mA)+2个预留字节+CRC8校验
回复:
15 00 01 20 VH VL qc_data1 qc_data2 qc_data3 qc_data4 qc_data5 qc_data6 ver_h ver_l nc nc nc nc nc nc CRC8
//长度+nc+ID+指令+2字节电压(0.01V)+6字节快充指令+2字节版本号+6字节预留+CRC8校验
2.读取源端的PD协议数据(当前为PDO,PPS,PD3.1,AVS协议时有效)
发送:05 00 01 13 2A
回复:
XX 00 01 13 //长度(根据实际计算)+NC+ID+指令
00 00 VMINH VMINL VMAXH VMAXL CURH/PORH CURL/PORL NC NC NC NC NC NC
//源类型(1字节,00为PDO,03为APDO) + PPS类型(1字节,源类型为3时有效,00为SPR PPS,01为EPR AVS,02为SPR AVS) + 最小电压(2字节,mV) + 最大电压(2字节,mV) + 最大电流/功率(2字节,mA/W) + 预留(6字节,当PPS类型为02时有效,意义与前6字节一致)
00 00 VMINH VMINL VMAXH VMAXL CURH/PORH CURL/PORL NC NC NC NC NC NC
......
CRC8
3.设置CC线
发送:
06 00 01 17 00 2A //长度+nc+id+指令+CC(0为都选通,1为选择CC1,2为CC2,3为全断开)+CRC8校验
回复:
05 00 01 17 2A //长度+nc+id+指令+CRC8校验
4.设置CC线电平
发送:
06 00 01 16 00 2A //长度+nc+id+指令+电平等级(0-10,默认7)+CRC8校验
回复:
05 00 01 16 2A //长度+nc+id+指令+CRC8校验
5.设置快充协议(VOOC,UFCS模式下切换其他模式时,需要先把V+,V-,D+,D-,CC等和变电压有关的继电器切至悬空,至少等待1s后,等能读到数据且快充指令正确,再把继电器切回)
格式:
长度+NC+ID+6字节快充数据+CRC8
发送:
0、 标准 命令:0A 00 01 C1 5A 00 00 00 00 2A
1、QC2.0,电压5V/9V/12V/20V 命令:0A 00 01 F8 5A 00 00 00 00 2A //第5个字节为电压(0.1V),该命令是申请9V电压
OK 20No
2、QC3.0,电压3.3-20V 命令:0A 00 01 F6 5A 00 00 00 00 2A //第5个字节为电压(0.1V) OK 20No
3、FCP,电压5V/9V/12V 命令:0A 00 01 F2 78 00 00 00 00 2A //第5个字节为电压(0.1V) OK
4、AFC,命令:0A 00 01 F9 46 00 00 00 00 2A //第5个字节为变电压数据,详细参考AFC文档
OK
5、SCP,命令:0A 00 01 F3 27 10 0F A0 00 2A //56字节为电压(mv)7、8字节为电流(mA)
OK
6、VIVO 命令:0A 00 01 FD 27 10 00 00 00 2A //56字节为电压(mv)7、8字节为电流(mA)
No
7、传音/TFC,命令:0A 00 01 F7 2E E0 00 00 00/01 2A //56字节为电压(mv)7、8字节为电流(mA),9字节为RFC/TFC选择
No
8、PDO手动,需要设定组号,电压和产品相关,命令:0A 00 01 FB 05 00 00 00 00 2A //5字节为组号
OK
9、PDO自动,组号设为0,自动变到指定电压,命令:0A 00 01 FB 00 2E E0 00 00 2A //5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
OK
10、PPS手动,需要设定组号、电压、电流,电流0最大, 命令: 0A 00 01 FC 06 4E 20 00 00 2A //5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
No
11、PPS自动,组号设为0,设定电压、电流,电流0最大, 命令:0A 00 01 FC 00 4E 20 00 00 2A //5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
No
12、PD3.1, 命令:0A 00 01 E1 00 2328 07d0 2A (组号设置和电压设置都支持) //5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
No
13、UFCS,命令:0A 00 01 E0 00 2328 07d0 2A (组号设置和电压设置都支持) //5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
No
14、VOOC,电压10/11V, 命令:0A 00 01 E2 00 00 00 00 00 2A //
No
15、AVS,命令:0A 00 01 E3 00 4e20 07d0 2A (组号设置和电压设置都支持) //5字节为组号,6,7字节为电压(mv),8、9字节为电流(mA)
No
回复:
回复最基本的5个字节:
长度+NC+ID+指令+CRC8
CRC8校验算法如下:
u8 get_crc(u8 *dat,u8 k)
{
u8 i,res;
res=0x55;
for(i=0;i<k;i++){ res=crc8(res,dat[i]);}
return res;
}
u8 crc8(u8 res,u8 dat)
{
u8 i,t;
for(i=0;i<8;i++)
{
t=dat^res;
if((t&0x01)==0x01){res=res^0x18;res>>=1;res|=0x80;}//
else{res>>=1;}
dat>>=1;
}
return res;
}
@@ -0,0 +1,91 @@
冠达快充板(SOURCE)通讯协议
2025.11.07
SOURCE的ID为2,波特率230400TTL
1.读取数据
发送:
0B 00 02 20 volh voll curh curl nc nc CRC8
//长度+nc+id+指令+2字节当前电源电压(mV)+2字节当前电源电流(mA)+2个预留字节+CRC8校验
回复:
15 00 02 20 VH VL IH IL setvh setvl setch setcl verh verl ERR NC NC NC NC NC CRC8
//长度+nc+ID+指令+2字节读取的电压(mV)+2字节读取的电流(mA)+2字节source设置电压(mV)+2字节source设置电流(mA)+2字节版本号+1字节错误码+5字节预留+CRC8校验
2.读取source的PD协议数据(当前为PD协议时有效,不需要一直读,暂未实现)
发送:05 00 02 13 2A
回复:
XX 00 02 13 //长度(根据实际计算)+NC+ID+指令
00 00 VMINH VMINL VMAXH VMAXL CURH/PORH CURL/PORL NC NC NC NC NC NC
//源类型(1字节,00为PDO,03为APDO) + PPS类型(1字节,源类型为3时有效,00为SPR PPS,01为EPR AVS,02为SPR AVS) + 最小电压(2字节,mV) + 最大电压(2字节,mA) + 最大电流/功率(2字节,mA/W,EPR_AVS时为功率) + 预留(6字节,当协议类型为02时有效,意义与前6字节一致)
00 00 VMINH VMINL VMAXH VMAXL CURH/PORH CURL/PORL NC NC NC NC NC NC
......
CRC8
3.设置CC线电平
发送:
06 00 02 16 00 2A //长度+nc+id+指令+电平等级(0xff为根据当前电流自动设置,否则为手动设定,设置方法待定)+CRC8校验
回复:
05 00 02 16 2A //长度+nc+id+指令+CRC8校验
4.设置支持的快充协议
(PD, FCP, AFC, SCPA中固定电压档位的最大电压和最小电压设为一样)
(QC2.0,QC3.0,SCPB仅支持1档设定,其余协议支持多档设定,pd最多11档,ufcs最多3档,其余最多4档)
发送:
XX 00 02 D0 //长度(根据实际计算)+NC+ID+指令
00 //协议(1字节)
00 00 VMINH VMINL VMAXH VMAXL CURH CURL NC NC NC NC NC NC
//源类型(1字节,PD协议有效,00为PDO,03为APDO)
+PD类型(1字节,PD协议有效,源类型为0和3时有效,当源类型为0时,0为FPDO,1为EPRPDO,当源类型为3时,00为SPR PPS,01为EPR AVS) + 最小电压(2字节,mV) + 最大电压(2字节,mV) + 最大电流(2字节,mA) + 预留(6字节)
00 00 VMINH VMINL VMAXH VMAXL CURH CURL NC NC NC NC NC NC
......
CRC8
回复:
05 00 02 D0 CRC8
协议字节含义如下:
00 无快充
01 VIFC(暂不支持)
02 FCP 22 01 02 D0 02 00 00 13 88 13 88 0B B8 00 00 00 00 00 00 00 00 23 28 23 28 0B B8 00 00 00 00 00 2A
03 SCPB
04 预留
05 预留
06 RFC(暂不支持)
07 QC3.0 14 01 02 D0 07 00 00 13 88 13 88 0B B8 00 00 00 00 00 00 2A
08 QC2.0 14 01 02 D0 08 00 00 13 88 13 88 0B B8 00 00 00 00 00 00 2A 没有
09 AFC
0A PD
0B 预留
0C UFCS
0D 预留
0E VOOC(暂不支持)
0F SCPA
CRC8校验算法如下:
u8 get_crc(u8 *dat,u8 k)
{
u8 i,res;
res=0x55;
for(i=0;i<k;i++){ res=crc8(res,dat[i]);}
return res;
}
u8 crc8(u8 res,u8 dat)
{
u8 i,t;
for(i=0;i<8;i++)
{
t=dat^res;
if((t&0x01)==0x01){res=res^0x18;res>>=1;res|=0x80;}//
else{res>>=1;}
dat>>=1;
}
return res;
}
+21
View File
@@ -0,0 +1,21 @@
通过LCD发送指令改变chrg-rollnor-t结构体PD(协议),currvolt
改变chrg_roll_nor的SET_SINK_PD的buf(实际传入值)
通过chrg_roll_nor的SET_SINK_PD对sink_set_pd函数进行传参
通过sink_set_pd调用chrg_north_pkg_encode函数
通过chrg_north_pkg_encode函数对指令进行编码
最后在sink_set_pd向北桥发送指令
电源充电协议流程:
1.设定协议,如QC2.0 此时VoltCurr为0
2.继电器吸合,插入检测
3.cpu3221source)设置Volt为5V
4.与sink握手协议
5.source输出相应电压
指令如下:
屏幕设置电压电流协议
上位机输入 source set 0 pd 0 发送协议
上位机输入rel_test set 0 2 吸合继电器
轮询输入读取数据指令
+96
View File
@@ -0,0 +1,96 @@
14 01 02 D0
08
00 00 1388 2328 0bb8 000000000000
2a
14 01 02 D0
07
00 00 1388 2328 0bb8 000000000000
2a
14 01 02 D0
03
00 00 1388 4e20 0bb8 000000000000
2a
14 01 02 D0
02
00 00 1388 2ee0 03E8 000000000000
2a
14 01 02 D0
03
00 00 0ce4 2710 0bb8 000000000000
2a
3e 01 02 D0
09
00 00 1388 1388 03e8 000000000000
00 00 2328 2328 07d0 000000000000
00 00 2ee0 2ee0 0bb8 000000000000
00 00 4e20 4e20 0bb8 000000000000
2a
22 01 02 D0
09
00 00 1388 1388 03e8 000000000000
00 00 2328 2328 07d0 000000000000
2a
22 01 02 D0
0c
00 00 1388 2710 03e8 000000000000
00 00 2328 4e20 07d0 000000000000
2a
4c 01 02 D0
0a
00 00 1388 1388 0bb8 000000000000
00 00 2328 2328 0bb8 000000000000
03 00 1388 4e20 1388 000000000000
00 01 6d60 6d60 1388 000000000000
03 01 3a98 6d60 1388 000000000000
2a
4c 01 02 D0
0a
00 00 1388 1388 0bb8 000000000000
00 00 2328 2328 0bb8 000000000000
00 00 2ee0 2ee0 0bb8 000000000000
00 00 4e20 4e20 1388 000000000000
03 00 1388 4e20 1388 000000000000
2a
14 01 02 D0
00
00 00 1388 1388 0bb8 000000000000
2a
Binary file not shown.