Files
chrg/apps/chrg/applications/thread/chrg_north.c
T
yhf 9f63dfeaf5 chore: 统一项目版本至V1.1.24并完善版本管理
1.  新增`apps/chrg/applications/utils/chrg_def.h`统一管理版本号,定义CHRG_VERSION相关宏与版本字符串
2.  更新所有业务源文件和头文件的文件头版本号为V1.1.24
3.  修改`chrg_comm.c`移除硬编码版本号,改用统一定义的CHRG_FIRMWARE_VERSION
4.  更新`main.c`添加版本头文件并打印应用版本字符串
5.  更新`修改日志.md`添加V1.1.24版本记录,说明版本管理规则
6.  新增编译日志记录本次版本统一构建结果
7.  补充AGENTS.md中的版本管理规范
8.  新增一批产品测试文档与彩页文件
2026-09-15 21:33:14 +08:00

839 lines
27 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/****************************************************************************
文件名称 : chrg_north.c
完成日期 :
当前版本号 : V1.1.24
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
版本历史 : 创建原始版本
说明 :
******************************************************************************/
#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"
#include "chrg_north.h"
#include "chrg_roll_nor.h"
#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>
#define SOURCE_VOLT_ERROR_MV 200U // Source目标电压允许误差,单位mV
#define SOURCE_VOLT_RETRY_FRAMES 3U // Source电压持续超差后的重发确认帧数
#define SOURCE_QC_MIN_VOLT_MV 5000U // QC2.0/QC3.0允许使用的最低目标电压
#define SOURCE_QC_SWITCH_ERROR 0x04U // Source协议或CC切换失败错误位
#define SINK_VOLT_STABLE_ERROR_MV 2000U // Sink实际电压与协议目标电压的最大允许误差
/* Sink电压稳定确认参数已迁移至chrg_sink.h,供chrg_sink_volt_judge使用。 */
/*****************************************************************
函数名称: chrg_source_apply_protocol_voltage_rule
函数描述: 根据电源协议类型修正北向解析得到的目标电压
输入参数: pSW:当前通道控制结构体 volt_mv:北向解析电压(mV)
输出参数: -
返回说明: 应写入Source状态机的目标电压(mV)
其它说明: QC2.0/QC3.0在完成升压协商前必须保持默认5V,因此其北向
回码低于5000mV时统一按5000mV处理;其它协议保持原值
*****************************************************************/
static rt_uint16_t chrg_source_apply_protocol_voltage_rule(
const struct chrg_switch_t *pSW, rt_uint16_t volt_mv)
{
if (pSW == RT_NULL) {
return volt_mv;
}
if (((pSW->source.protocol == PRO_QC20) ||
(pSW->source.protocol == PRO_QC30)) &&
(volt_mv < SOURCE_QC_MIN_VOLT_MV)) {
return SOURCE_QC_MIN_VOLT_MV;
}
return volt_mv;
}
/*****************************************************************
函数名称: chrg_sink_voltage_is_stable
函数描述: 判断北向返回的Sink实际电压是否已进入协议目标电压允许范围
输入参数: actual_volt_mv:北向返回的实际电压(mV)
target_volt_mv:当前协议申请的目标电压(mV)
输出参数: -
返回说明: RT_TRUE:实际电压与目标电压差值不大于允许误差
RT_FALSE:实际电压尚未到达目标范围或仍保留旧协议电压
其它说明: 使用差值的绝对值比较,避免只判断下限时将20V残压误判为5V已稳定
*****************************************************************/
static rt_bool_t chrg_sink_voltage_is_stable(rt_uint16_t actual_volt_mv,
rt_uint16_t target_volt_mv)
{
rt_uint16_t voltage_error_mv;
if (actual_volt_mv >= target_volt_mv) {
voltage_error_mv = actual_volt_mv - target_volt_mv;
} else {
voltage_error_mv = target_volt_mv - actual_volt_mv;
}
return (voltage_error_mv <= SINK_VOLT_STABLE_ERROR_MV) ?
RT_TRUE : RT_FALSE;
}
/*****************************************************************
函数名称: chrg_source_voltage_control
函数描述: 根据目标电压与实际电压统一控制Source电压下发和重试
输入参数: idx:当前通道 pSW:通道控制结构体 volt_mv:目标电压(mV)
curr_ma:目标电流(mA)
输出参数: 更新source.set_volt/set_curr、work_mode和电压重试计数
返回说明: 0:状态处理成功 <0:参数错误或命令提交失败
其它说明: set_volt同时作为上次目标,停止时会清零;目标变化时立即下发,
目标不变但实际误差连续3帧超过200mV时重发
*****************************************************************/
static int chrg_source_voltage_control(eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, rt_uint16_t volt_mv, rt_uint16_t curr_ma)
{
static rt_uint8_t volt_retry_count[TOTAL_SOU_CHS] = {0};
rt_uint16_t volt_error;
rt_uint16_t old_volt_mv;
rt_uint16_t old_curr_ma;
rt_bool_t need_set = RT_FALSE;
int ret;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return -1;
}
old_volt_mv = pSW->source.set_volt;
old_curr_ma = pSW->source.set_curr;
pSW->source.set_volt = volt_mv;
pSW->source.set_curr = curr_ma;
if (!pSW->source.On_Flag) {
return 0;
}
volt_error = (pSW->Now_voltage >= volt_mv) ?
(pSW->Now_voltage - volt_mv) :
(volt_mv - pSW->Now_voltage);
if (volt_mv != old_volt_mv) {
/* 新目标与set_volt旧值不同,需要立即写入功率板。 */
need_set = RT_TRUE;
} else if (volt_error <= SOURCE_VOLT_ERROR_MV) {
volt_retry_count[idx] = 0;
} else {
if (volt_retry_count[idx] < SOURCE_VOLT_RETRY_FRAMES) {
volt_retry_count[idx]++;
}
if (volt_retry_count[idx] >= SOURCE_VOLT_RETRY_FRAMES) {
need_set = RT_TRUE;
}
}
pSW->source.work_mode = need_set ?
SOURCE_WORK_RUNING : SOURCE_WORK_WAIT;
ret = chrg_source_work_state(idx, pSW);
if ((ret == 0) && need_set) {
volt_retry_count[idx] = 0;
} else if ((ret != 0) && need_set) {
/* 入队失败时恢复旧目标,使下一帧仍能立即识别为目标变化。 */
pSW->source.set_volt = old_volt_mv;
pSW->source.set_curr = old_curr_ma;
}
return ret;
}
/*****************************************************************
函数名称: chrg_source_parse
函数描述: 按电源协议类型处理目标电压并驱动Source状态机
输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体
pNOR:北向总控结构体
输出参数: 更新电压、电流、source.work_mode及电压重试计数
返回说明: 0:回码正常 <0:参数错误、切换错误或功率板命令提交失败
其它说明: 标准协议使用COM配置目标参与实际电压校验;快充协议读取
frame_data[8~11]中的协商目标值;frame_data[14]的bit2表示
Source板协议/CC切换失败,错误回码不得驱动功率板输出
*****************************************************************/
int chrg_source_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;
rt_uint16_t curr_ma;
(void)pNOR;
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL) ||
(frame_data == RT_NULL)) {
return -1;
}
pSW->source.qc_err = frame_data[14];
if ((pSW->source.qc_err & SOURCE_QC_SWITCH_ERROR) != 0U) {
return -2;
}
/* 标准协议不使用北向帧内目标值,但仍进入统一误差判断,确保INIT或
* COM下发的目标电压没有生效时能够自动重试。 */
if (pSW->source.protocol == PRO_NONE) {
return chrg_source_voltage_control(idx, pSW,
pSW->source.pd_set.mm.max_vol,
pSW->source.pd_set.mm.max_cur);
}
if (!pSW->source.On_Flag) {
return 0;
}
volt_mv = (frame_data[8] << 8) | frame_data[9];
curr_ma = (frame_data[10] << 8) | frame_data[11];
// /* 特殊协议规则只修正运行目标,不覆盖北向原始回码和COM协议配置。 */
// volt_mv = chrg_source_apply_protocol_voltage_rule(pSW, volt_mv);
return chrg_source_voltage_control(idx, pSW, volt_mv, curr_ma);
}
/*****************************************************************
函数名称: chrg_source_setting
函数描述: 根据电源协议类型和启停标志选择标准输出或快充解析流程
输入参数: frame_data:北向回码 idx:当前通道 pSW:通道控制结构体
pNOR:北向总控结构体
输出参数: 可能更新功率板设定值或北向子状态sub
返回说明: -
其它说明: 标准协议直接设置输出,快充协议使用协商结果驱动状态机
*****************************************************************/
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) {
/* 标准协议与快充协议都由解析入口选择各自目标值并统一校验。 */
(void)chrg_source_parse(frame_data, idx, pSW, pNOR);
}else if(pSW->source.On_Flag == 0){
//chrg_source_stop(frame_data,pSW,idx,&pNOR->enable[idx]);
pSW->sub = IDX_SET_SOURCE_STOP_STATE;
}
}
/*****************************************************************
函数名称: chrg_sink_parse
函数描述: 解析北向负载板0x20回码,提取实际电压和D+/D-电压
输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体
pNOR:北向总控结构体
输出参数: 更新pSW->Sink_voltage、VZ(D+)、VF(D-)
返回说明: -
其它说明: Sink电压状态机可通过SINK_VON_VOLTAGE_SOURCE选择使用本回码
实际电压或功率板实际电压
*****************************************************************/
void chrg_sink_parse(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
struct chrg_switch_t *pSW, struct chrg_north_t *pNOR)
{
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
return;
}
pSW->Sink_voltage = (rt_uint16_t)(((frame_data[4] << 8) |
frame_data[5]) * 10U);
pSW->Sink_voltage_seq++;
pSW->VZ = ((frame_data[14] << 8) | frame_data[15]);
pSW->VF = ((frame_data[16] << 8) | frame_data[17]);
//rt_kprintf("VZ=%d,VF=%d\n",pSW->VZ,pSW->VF);
#if NORTH_SINK_DEBUG
LOG_D("Sink[%d] real volt: %d mV", idx,
((frame_data[4] << 8) | frame_data[5]) * 10);
#endif
(void)pNOR;
}
struct chrg_north_t chrgnorth = {
.devname = DEV_NAME_NORTH, // 设备名
.tty = RT_NULL, // 串口句柄初始为空
.enable = {0, 0, 0, 0}, // 4个通道使能初始关闭
.free_flag = 1,
// 通道1(IDX_NOR_CH1)初始化
.sw[IDX_NOR_CH1] = {
.CV_mode = 2,
.change_flag = 0,
.show_step = 0,
.sub = IDX_GET_SINK_VC,
.id = ID_SINK, // 初始为负载模式
.source = { // 电源侧参数初始化
.On_Flag = 0, // 启动标志初始关闭
.work_mode = SOURCE_WORK_WAIT, // 上电保持等待,禁止自动执行Source INIT
}, // 关键:补充逗号分隔source和sink
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
},
// 通道2(IDX_NOR_CH2)初始化
.sw[IDX_NOR_CH2] = {
.show_step = 0,
.id = ID_SINK,
.CV_mode = 2,
.change_flag = 0,
.source = {
.On_Flag = 0,
.work_mode = SOURCE_WORK_WAIT, // 上电保持等待,禁止自动执行Source INIT
},
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
},
// 通道3(IDX_NOR_CH3)初始化
.sw[IDX_NOR_CH3] = {
.show_step = 0,
.CV_mode = 2,
.id = ID_SINK,
.change_flag = 0,
.source = {
.On_Flag = 0,
.work_mode = SOURCE_WORK_WAIT, // 上电保持等待,禁止自动执行Source INIT
},
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
},
// 通道4(IDX_NOR_CH4)初始化
.sw[IDX_NOR_CH4] = {
.show_step = 0,
.CV_mode = 2,
.id = ID_SINK,
.change_flag = 0,
.source = {
.On_Flag = 0,
.work_mode = SOURCE_WORK_WAIT, // 上电保持等待,禁止自动执行Source INIT
},
.sink = {
.protocol = 0xFE,
.work_mode= SINK_WORK_WAIT,
.test.test_mode_old = 0,
}
}
};
/*****************************************************************
函数名称: chrg_north_switch
函数描述: 北向通道选择
输入参数: ch: 北向通道
输出参数: -
返回说明: <0: 错误
其它说明: -
*****************************************************************/
int chrg_north_switch (eIDX_NOR_CH ch)
{
if (ch >= TOTAL_NOR_CHS) {
return -1;
}
return chrg_switch_ch(chrgios[IDX_GPO_NOR_SW_A].base,
chrgios[IDX_GPO_NOR_SW_B].base, (eIDX_CH)ch);
}
/*****************************************************************
函数名称: chrg_north_send
函数描述: 北向通道数据发送
输入参数: *data: 数据 length: 数据长度
输出参数: -
返回说明: <0: 错误 >0:发送数据长度
其它说明: -
*****************************************************************/
int chrg_north_send (rt_uint8_t *data, rt_size_t length)
{
rt_ssize_t ret = -1;
struct chrg_tty_t *pTTY = chrgnorth.tty;
if (RT_NULL != pTTY) {
#if NORTH_FRAME_DEBUG
LOG_HEX("sendN", 32, data, length);
#endif
ret = chrg_tty_send(pTTY, data, length);
}
return ret;
}
/*****************************************************************
函数名称: chrg_north_frame_shape_valid
函数描述: 检查北向回码命令和长度组合是否符合协议定义
输入参数: id:角色 length:帧长度 command:回码命令
输出参数: -
返回说明: RT_TRUE:组合合法 RT_FALSE:组合非法
其它说明: 0x20量测回码固定21字节,设置命令回码固定5字节
*****************************************************************/
static rt_bool_t chrg_north_frame_shape_valid(eIDX_ID id,
rt_uint8_t length, rt_uint8_t command)
{
if (command == SRC_CMD_GET_VOLTAGE) {
return (length == 21U) ? RT_TRUE : RT_FALSE;
}
if (command == SRC_CMD_GET_PROTOCOL) {
return (length >= MIN_FRAME_LENGTH) ? RT_TRUE : RT_FALSE;
}
if (id == ID_SOURCE) {
if ((command == SRC_CMD_SET_CC_LEVEL) ||
(command == SRC_CMD_SET_CC_CHOOSE) ||
(command == SRC_CMD_RESET) ||
(command == SRC_CMD_SET_PROTOCOL)) {
return (length == MIN_FRAME_LENGTH) ? RT_TRUE : RT_FALSE;
}
return RT_FALSE;
}
if (id == ID_SINK) {
switch (command) {
case SINK_CMD_SET_CC:
case SINK_CMD_SET_LEVEL:
case SINK_CMD_SET_QC20:
case SINK_CMD_SET_QC30:
case SINK_CMD_SET_FCP:
case SINK_CMD_SET_AFC:
case SINK_CMD_SET_SCP:
case SINK_CMD_SET_VIVO:
case SINK_CMD_SET_TFC:
case SINK_CMD_SET_PD0:
case SINK_CMD_SET_PPS:
case SINK_CMD_SET_PD3P1:
case SINK_CMD_SET_UFCS:
case SINK_CMD_SET_VOOC:
case SINK_CMD_SET_AVS:
case LOAD_PRO_NONE:
return (length == MIN_FRAME_LENGTH) ? RT_TRUE : RT_FALSE;
default:
return RT_FALSE;
}
}
return RT_FALSE;
}
/*****************************************************************
函数名称: chrg_north_ringbuffer_byte
函数描述: 不移动读指针地读取北向环形缓冲区指定偏移字节
输入参数: rb:环形缓冲区 offset:相对当前读指针偏移 data:输出字节
输出参数: data返回读取到的字节
返回说明: 0:成功 <0:参数或偏移错误
其它说明: 仅由北向接收线程调用,不与其它线程并发访问该环形缓冲区
*****************************************************************/
static int chrg_north_ringbuffer_byte(const struct rt_ringbuffer *rb,
rt_size_t offset, rt_uint8_t *data)
{
rt_size_t index;
if ((rb == RT_NULL) || (data == RT_NULL) ||
(offset >= rt_ringbuffer_data_len((struct rt_ringbuffer *)rb))) {
return -1;
}
index = rb->read_index + offset;
if (index >= rb->buffer_size) {
index -= rb->buffer_size;
}
*data = rb->buffer_ptr[index];
return 0;
}
/*****************************************************************
函数名称: chrg_north_ringbuffer_discard
函数描述: 从北向环形缓冲区丢弃指定数量字节
输入参数: rb:环形缓冲区 length:需要丢弃的字节数
输出参数: -
返回说明: -
其它说明: 用于移除帧头前噪声,同时保留末尾可能未接收完整的帧头
*****************************************************************/
static void chrg_north_ringbuffer_discard(struct rt_ringbuffer *rb,
rt_size_t length)
{
rt_uint8_t data;
while ((length > 0U) && (rt_ringbuffer_getchar(rb, &data) != 0U)) {
length--;
}
}
/*****************************************************************
函数名称: chrg_north_frame_head
函数描述: 在北向字节流中查找并提取合法四字节帧头
输入参数: rb:环形缓冲区 head:帧头输出区 id:当前通道角色
输出参数: head返回长度、固定字节、角色和命令
返回说明: >0:完整帧长度 0:暂未找到完整帧头 <0:参数或读取错误
其它说明: 未找到时保留末尾3字节,支持帧头跨多次UART接收
*****************************************************************/
static int chrg_north_frame_head(struct rt_ringbuffer *rb,
rt_uint8_t *head, eIDX_ID id)
{
rt_size_t i;
rt_size_t j;
rt_size_t len_rb;
rt_uint8_t length;
rt_uint8_t reserved;
rt_uint8_t frame_id;
rt_uint8_t command;
if ((rb == RT_NULL) || (head == RT_NULL)) {
return -1;
}
len_rb = rt_ringbuffer_data_len(rb);
if (len_rb < LEN_FRAME_HEAD) {
return 0;
}
/* 使用非破坏读取逐字节滑动,查找完整的4字节候选帧头。 */
for (i = 0; (i + LEN_FRAME_HEAD) <= len_rb; i++) {
if ((chrg_north_ringbuffer_byte(rb, i, &length) != 0) ||
(chrg_north_ringbuffer_byte(rb, i + 1U, &reserved) != 0) ||
(chrg_north_ringbuffer_byte(rb, i + 2U, &frame_id) != 0) ||
(chrg_north_ringbuffer_byte(rb, i + 3U, &command) != 0)) {
break;
}
/* 长度、固定字节、角色或命令不匹配时继续搜索下一个候选位置。 */
if ((reserved != 0x00U) || (frame_id != (rt_uint8_t)id) ||
(length < MIN_FRAME_LENGTH) ||
!chrg_north_frame_shape_valid(id, length, command)) {
continue;
}
/* 找到合法帧头后,仅丢弃它前面的噪声,再正式取出4字节帧头。 */
chrg_north_ringbuffer_discard(rb, i);
for (j = 0; j < LEN_FRAME_HEAD; j++) {
if (rt_ringbuffer_getchar(rb, &head[j]) == 0U) {
return -1;
}
}
return length;
}
/* 保留末尾可能尚未接收完整的4字节帧头,等待下一批UART数据续接。 */
if (len_rb > (LEN_FRAME_HEAD - 1U)) {
chrg_north_ringbuffer_discard(rb,
len_rb - (LEN_FRAME_HEAD - 1U));
}
return 0;
}
/*****************************************************************
函数名称: chrg_north_pickup_frame
函数描述: 北向通道数据帧提取
输入参数: *rb: 缓冲区 *pFrame: 数据帧 idx:通道
输出参数: -
返回说明: <0: 错误 >0:帧长度
其它说明: -
*****************************************************************/
int a_number;
#define NORTH_FRAME_TIMEOUT_MS 100
/*****************************************************************
函数名称: chrg_north_frame_reset
函数描述: 复位北向帧接收状态并按需释放未完成帧内存
输入参数: pFrame:帧接收上下文 release:是否释放pMB及payload
输出参数: 清零接收长度、帧头标志、超时计时和消息指针
返回说明: -
其它说明: 完整帧已移交邮箱时release应为RT_FALSE,异常中止时为RT_TRUE
*****************************************************************/
static void chrg_north_frame_reset(struct pickup_info_t *pFrame, rt_bool_t release)
{
if (RT_NULL == pFrame) {
return;
}
if (release && (RT_NULL != pFrame->pMB)) {
if (RT_NULL != pFrame->pMB->payload) {
rt_free(pFrame->pMB->payload);
}
rt_free(pFrame->pMB);
}
pFrame->last_tick = 0;
pFrame->length = 0;
pFrame->findhead = 0;
pFrame->len_msg = 0;
pFrame->pMB = RT_NULL;
}
/*****************************************************************
函数名称: chrg_north_frame_append
函数描述: 从北向环形缓冲区追加数据到当前未完成帧
输入参数: rb:北向接收环形缓冲区 pFrame:帧接收上下文
输出参数: 更新已接收长度和最后接收时间
返回说明: 1:完整帧接收完成 0:仍需等待数据 <0:参数或状态异常
其它说明: 单次仅复制当前帧剩余长度,避免读取下一帧数据
*****************************************************************/
static int chrg_north_frame_append(struct rt_ringbuffer *rb,
struct pickup_info_t *pFrame)
{
rt_size_t remaining;
rt_size_t copied;
if ((RT_NULL == rb)||(RT_NULL == pFrame)||(RT_NULL == pFrame->pMB)||
(RT_NULL == pFrame->pMB->payload)||(pFrame->len_msg <= 0)||
(pFrame->length > (rt_size_t)pFrame->len_msg)) {
return -1;
}
remaining = (rt_size_t)pFrame->len_msg - pFrame->length;
if (0 == remaining) {
return 1;
}
copied = rt_ringbuffer_data_len(rb);
if (copied > remaining) {
copied = remaining;
}
if (copied > 0) {
copied = rt_ringbuffer_get(rb,
&pFrame->pMB->payload[pFrame->length], copied);
pFrame->length += copied;
pFrame->last_tick = rt_tick_get();
}
return (pFrame->length == (rt_size_t)pFrame->len_msg) ? 1 : 0;
}
/*****************************************************************
函数名称: chrg_north_frame_dispatch
函数描述: 校验并分发已经组装完成的北向数据帧
输入参数: pFrame:包含完整pMB消息的帧接收上下文
输出参数: 合法完整帧发送到北向轮询邮箱
返回说明: -
其它说明: 本函数只负责帧结构和CRC校验,不执行Source/Sink业务解析;
校验失败时释放消息,校验成功后将消息所有权移交北向轮询线程
*****************************************************************/
static void chrg_north_frame_dispatch(struct pickup_info_t *pFrame)
{
struct mb_msg_t *pMB;
rt_uint8_t crc_calc;
rt_uint8_t crc_recv;
if (pFrame == RT_NULL) {
return;
}
pMB = pFrame->pMB;
if ((pMB == RT_NULL) || (pMB->payload == RT_NULL) ||
(pMB->length < MIN_FRAME_LENGTH) ||
(pMB->length > 0xFFU) ||
(pMB->payload[0] != (rt_uint8_t)pMB->length) ||
(pMB->payload[1] != 0x00U)) {
LOG_E("invalid north frame: len=%d", pMB ? pMB->length : 0);
chrg_north_frame_reset(pFrame, RT_TRUE);
return;
}
crc_calc = calc_crc8(pMB->payload, (rt_uint8_t)(pMB->length - 1U));
crc_recv = pMB->payload[pMB->length - 1U];
if (crc_calc != crc_recv) {
LOG_E("north crc failed: len=%d calc=%02x recv=%02x",
pMB->length, crc_calc, crc_recv);
chrg_north_frame_reset(pFrame, RT_TRUE);
return;
}
chrg_north_frame_reset(pFrame, RT_FALSE);
(void)chrg_thread_mb_send(IDX_THR_NORTH, pMB);
}
static int chrg_north_pickup_frame (struct rt_ringbuffer *rb,
struct pickup_info_t *pFrame, eIDX_NOR_CH idx)
{
int cnt = 0;
int status = 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) {
if ((pFrame->len_msg < LEN_FRAME_HEAD)||
(pFrame->len_msg >= SIZE_BUF_TTY)) {
LOG_E("invalid msg length: %d", pFrame->len_msg);
chrg_north_frame_reset(pFrame, RT_TRUE);
return -3;
}
pFrame->pMB = (struct mb_msg_t *)rt_malloc(sizeof(struct mb_msg_t));
if (RT_NULL == pFrame->pMB) {
LOG_E("malloc mb failed.");
chrg_north_frame_reset(pFrame, RT_FALSE);
return -2;
}
pFrame->pMB->length = pFrame->len_msg;
pFrame->pMB->payload = RT_NULL;
pFrame->pMB->payload = (rt_uint8_t *)rt_malloc(pFrame->len_msg);
if (RT_NULL == pFrame->pMB->payload) {
LOG_E("malloc buf failed.");
chrg_north_frame_reset(pFrame, RT_TRUE);
return -4;
}
rt_memcpy(pFrame->pMB->payload, pFrame->head, LEN_FRAME_HEAD);
pFrame->length = LEN_FRAME_HEAD;
pFrame->findhead = 1;
pFrame->last_tick = rt_tick_get();
status = chrg_north_frame_append(rb, pFrame);
if (status > 0) {
chrg_north_frame_dispatch(pFrame);
cnt++;
} else if (status < 0) {
chrg_north_frame_reset(pFrame, RT_TRUE);
return -5;
} else {
break;
}
}
} else {
status = chrg_north_frame_append(rb, pFrame);
if (status > 0) {
chrg_north_frame_dispatch(pFrame);
cnt++;
} else if (status < 0) {
chrg_north_frame_reset(pFrame, RT_TRUE);
return -5;
} else {
break;
}
}
len_rb = rt_ringbuffer_data_len(rb);
} while (len_rb >= LEN_FRAME_HEAD);
return cnt;
}
/*****************************************************************
函数名称: chrg_north_thread_entry
函数描述: 北向通道数据接收线程体
输入参数: *data: 本线程信息
输出参数: -
返回说明: -
其它说明: -
*****************************************************************/
void chrg_north_thread_entry (void *data)
{
struct chrg_thread_t *pTHR = (struct chrg_thread_t *)data;
if (RT_NULL == pTHR) {
return ;
}
int len = 0;
rt_size_t put_len = 0;
eIDX_NOR_CH frame_idx = IDX_NOR_CH1; // 当前未完成帧所属的物理通道
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, 230400, 0, -1, 1);
if (RT_NULL == pTTY) {
LOG_E("tty can't create.");
return ;
}
chrg_tty_set_recv_tmo(pTTY, 10);
if (chrg_tty_connect(pTTY) != RT_EOK) {
chrg_tty_destory(pTTY);
return ;
}
pNOR->tty = pTTY;
pNOR->rb = rt_ringbuffer_create(SIZE_BUF_TTY*2);
if (RT_NULL == pNOR->rb) {
LOG_E("create rb '%s' failed.", pNOR->devname);
return ;
}
while (1) {
if (pNOR->frame.findhead &&
((rt_tick_t)(rt_tick_get() - pNOR->frame.last_tick) >=
rt_tick_from_millisecond(NORTH_FRAME_TIMEOUT_MS))) {
chrg_north_frame_reset(&pNOR->frame, RT_TRUE);
LOG_W("partial frame timeout");
}
rt_memset(pNOR->rx_buf, 0, sizeof(pNOR->rx_buf));
len = chrg_tty_recv(pTTY, pNOR->rx_buf, sizeof(pNOR->rx_buf));
if (len <= 0) {
rt_thread_mdelay(1);
continue;
}
/*
* 四个物理通道共用一个UART组帧器。切换通道后必须终止旧通道的未完成帧,
* 防止旧帧继续吞入新通道回码。本复位只在接收线程执行,避免发送线程与
* 接收线程同时操作环形缓冲区和组帧上下文。
*/
if (frame_idx != pNOR->idx) {
rt_ringbuffer_reset(pNOR->rb);
chrg_north_frame_reset(&pNOR->frame, RT_TRUE);
frame_idx = pNOR->idx;
}
put_len = rt_ringbuffer_put(pNOR->rb, pNOR->rx_buf, len);
if (put_len != (rt_size_t)len) {
LOG_E("north ringbuffer overflow: recv=%d put=%d", len, put_len);
rt_ringbuffer_reset(pNOR->rb);
chrg_north_frame_reset(&pNOR->frame, RT_TRUE);
continue;
}
#if NORTH_FRAME_DEBUG
LOG_HEX("recvN", 32, pNOR->rx_buf, len);
#endif
chrg_north_pickup_frame(pNOR->rb, &pNOR->frame, pNOR->idx);
}
}
#if 1
static int north_ch(rt_uint8_t argc, char **argv)
{
int ret = 0;
if (2 != argc) {
rt_kprintf("help : %s [0-3]\r\n", argv[0]);
return -1;
}
ret = chrg_north_switch((eIDX_NOR_CH)atoi(argv[1]));
return ret;
}
MSH_CMD_EXPORT(north_ch, north channel select);
static int north_diag(int argc, char **argv)
{
rt_uint8_t i;
struct chrg_north_t *pNOR = &chrgnorth;
struct chrg_rollnor_t *pROLL = &chrgrollnor;
rt_kprintf("roll: run=%d idx=%d ch=%d type=%d sub=%d\r\n",
pROLL->run, pROLL->idx, pROLL->ch, pROLL->type, pROLL->sub);
rt_kprintf("rx: rb_len=%d findhead=%d frame_len=%d received=%d\r\n",
pNOR->rb ? rt_ringbuffer_data_len(pNOR->rb) : 0,
pNOR->frame.findhead, pNOR->frame.len_msg, pNOR->frame.length);
for (i = 0; i < TOTAL_NOR_CHS; i++) {
rt_kprintf("ch%d: enable=%d id=%d sub=%d\r\n", i,
pNOR->enable[i], pNOR->sw[i].id, pNOR->sw[i].sub);
}
return 0;
}
MSH_CMD_EXPORT(north_diag, show north polling and frame state);
#endif