1. apps/chrg/debug_hot.ini: 增加调试断点配置 2. apps/chrg/applications/thread/chrg_north.h: 开启北向调试日志 3. 修正多个头文件函数返回值类型,从void改为int 4. 重构chrg_thread.c: 新增mb消息释放函数,优化邮箱发送逻辑 5. 重写chrg_south.c: 修复分包越界和内存泄漏,增加帧超时处理 6. 重构chrg_sink.c: 统一批量下发接口,修正初始化和运行逻辑 7. 重写chrg_comm.c: 修复系统寄存器写入逻辑,增加通道激活校验 8. 重构chrg_north.c: 修复北向分包越界和内存泄漏,增加超时处理和诊断命令 9. 重构chrg_roll_sou.c: 重构批量下发逻辑,增加重试机制和错误处理
488 lines
12 KiB
C
488 lines
12 KiB
C
/****************************************************************************
|
|
文件名称 : chrg_south.c
|
|
完成日期 :
|
|
当前版本号 : V1.0
|
|
主要功能 : 实现南向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
|
|
版本历史 : 创建原始版本
|
|
说明 :
|
|
******************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <rtthread.h>
|
|
|
|
#include "chrg_gpio.h"
|
|
#include "chrg_thread.h"
|
|
#include "chrg_south.h"
|
|
#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"
|
|
#define DBG_LEVEL DBG_LOG
|
|
#include <rtdbg.h>
|
|
|
|
#define SOUTH_FRAME_TIMEOUT_MS 100
|
|
|
|
#define MB_ACK_HEAD_SIZE 3 // modbus 响应头3字节
|
|
|
|
struct chrg_south_t chrgsouth = {
|
|
.devname = DEV_NAME_SOUTH,
|
|
.tty = RT_NULL,
|
|
.rb = RT_NULL,
|
|
.enable = {1, 1, 1, 1},
|
|
.online = {0, 0, 0, 0},
|
|
};
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_switch_lv1
|
|
函数描述: 南向通道选择1级
|
|
输入参数: ch: 南向通道
|
|
输出参数: -
|
|
返回说明: <0: 错误
|
|
其它说明: 南向1级选通
|
|
*****************************************************************/
|
|
static int chrg_south_switch_lv1 (eIDX_CH ch)
|
|
{
|
|
// if (ch >= 2) {
|
|
// return -1;
|
|
// }
|
|
|
|
return chrg_switch_ch(chrgios[IDX_GPO_SOU_SW_LV1_A].base,
|
|
chrgios[IDX_GPO_SOU_SW_LV1_B].base, ch);
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_switch_lv2_1
|
|
函数描述: 南向通道选择2级,左半部分
|
|
输入参数: ch: 南向通道
|
|
输出参数: -
|
|
返回说明: <0: 错误
|
|
其它说明: -
|
|
*****************************************************************/
|
|
static int chrg_south_switch_lv2_1 (eIDX_CH ch)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (IDX_CH1 == ch) {
|
|
ret = chrg_switch_ch(chrgios[IDX_GPO_SOU_SW_LV2_A1].base,
|
|
chrgios[IDX_GPO_SOU_SW_LV2_B1].base, IDX_CH3);
|
|
} else if (IDX_CH2 == ch) {
|
|
ret = chrg_switch_ch(chrgios[IDX_GPO_SOU_SW_LV2_A1].base,
|
|
chrgios[IDX_GPO_SOU_SW_LV2_B1].base, IDX_CH1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_switch_lv2_2
|
|
函数描述: 南向通道选择2级,右半部分
|
|
输入参数: ch: 南向通道
|
|
输出参数: -
|
|
返回说明: <0: 错误
|
|
其它说明: -
|
|
*****************************************************************/
|
|
static int chrg_south_switch_lv2_2 (eIDX_CH ch)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (IDX_CH1 == ch) {
|
|
ret = chrg_switch_ch(chrgios[IDX_GPO_SOU_SW_LV2_A2].base,
|
|
chrgios[IDX_GPO_SOU_SW_LV2_B2].base, IDX_CH3);
|
|
} else if (IDX_CH2 == ch) {
|
|
ret = chrg_switch_ch(chrgios[IDX_GPO_SOU_SW_LV2_A2].base,
|
|
chrgios[IDX_GPO_SOU_SW_LV2_B2].base, IDX_CH1);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_switch
|
|
函数描述: 南向通道选择
|
|
输入参数: ch: 南向通道
|
|
输出参数: -
|
|
返回说明: <0: 错误
|
|
其它说明: -
|
|
*****************************************************************/
|
|
int chrg_south_switch (eIDX_SOU_CH ch)
|
|
{
|
|
int ret = -1;
|
|
if (ch >= TOTAL_SOU_CHS) {
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
// 1级/2级通道顺序反向
|
|
if ((IDX_SOU_CH1 == ch)||(IDX_SOU_CH2 == ch)) {
|
|
ret = chrg_south_switch_lv1(IDX_CH2);
|
|
ret |= chrg_south_switch_lv2_2((eIDX_CH)ch);
|
|
} else if ((IDX_SOU_CH3 == ch)||(IDX_SOU_CH4 == ch)) {
|
|
ret = chrg_south_switch_lv1(IDX_CH1);
|
|
ret |= chrg_south_switch_lv2_1((eIDX_CH)(ch-IDX_SOU_CH3));
|
|
} else {
|
|
return -1;
|
|
}
|
|
#else
|
|
//新版注释上面的旧版切换,只要PC8-A和PC9-B控制切换
|
|
if (IDX_SOU_CH1 == ch) {
|
|
ret = chrg_south_switch_lv1(IDX_CH1);
|
|
} else if (IDX_SOU_CH2 == ch){
|
|
ret = chrg_south_switch_lv1(IDX_CH2);
|
|
} else if (IDX_SOU_CH3 == ch){
|
|
ret = chrg_south_switch_lv1(IDX_CH3);
|
|
} else if (IDX_SOU_CH4 == ch){
|
|
ret = chrg_south_switch_lv1(IDX_CH4);
|
|
} else {
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_send
|
|
函数描述: 南向数据发送
|
|
输入参数: *data: 数据 length: 数据长度
|
|
输出参数: -
|
|
返回说明: <0: 错误 >0:发送数据长度
|
|
其它说明: -
|
|
*****************************************************************/
|
|
int chrg_south_send (rt_uint8_t *data, rt_size_t length)
|
|
{
|
|
rt_ssize_t ret = -1;
|
|
struct chrg_tty_t *pTTY = chrgsouth.tty;
|
|
|
|
if (RT_NULL != pTTY) {
|
|
ret = chrg_tty_send(pTTY, data, length);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_frame_head
|
|
函数描述: 南向数据帧头提取
|
|
输入参数: *rb: 缓冲区 *head: 帧头 id:编号
|
|
输出参数: -
|
|
返回说明: <0: 错误 >0:发送数据长度
|
|
其它说明: MODBUS协议。读取 modbus 前3个字节。2个(设置)或3个字节(查询)
|
|
*****************************************************************/
|
|
static int chrg_south_frame_head (struct rt_ringbuffer *rb, rt_uint8_t *head, eIDX_ID id)
|
|
{
|
|
int i = 0;
|
|
rt_uint8_t data = 0, stage = 0, rd = 0;
|
|
rt_size_t ret = 0;
|
|
rt_size_t len_rb = 0; // ring数据长度
|
|
|
|
if (RT_NULL == rb) {
|
|
return -1;
|
|
}
|
|
|
|
len_rb = rt_ringbuffer_data_len(rb);
|
|
if (len_rb < MB_ACK_HEAD_SIZE) { // 帧头3字节
|
|
rt_kprintf("len_rb=:%d",len_rb);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < len_rb - 2; i++) { // 找头三个字节 [地址, 功能码, 长度/无]
|
|
ret = rt_ringbuffer_getchar(rb, &data);
|
|
if (0 == ret) {
|
|
continue;
|
|
}
|
|
|
|
switch (stage) {
|
|
case 0:
|
|
head[0] = data;
|
|
if (data != id) { // 地址不符
|
|
stage = 0;
|
|
continue;
|
|
}
|
|
|
|
stage = 1;
|
|
break;
|
|
|
|
case 1:
|
|
head[1] = data;
|
|
if (MB_REGISTER_RD == data) { // 读寄存器 应答
|
|
stage = 2;
|
|
rd = 1;
|
|
} else if (MB_REGISTER_WR == data) { // 写单个寄存器 应答
|
|
stage = 2;
|
|
} else {
|
|
stage = 0;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
head[2] = data;
|
|
|
|
if (1 == rd) {
|
|
if ((data == 0)||(data > 80)||(data%2 != 0)) { // 大于40个寄存器
|
|
stage = 0;
|
|
} else {
|
|
return data+5;
|
|
}
|
|
} else {
|
|
return 8; // 原命令返回
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_pickup_frame
|
|
函数描述: 南向通道数据帧提取
|
|
输入参数: *rb: 缓冲区 *pFrame: 数据帧
|
|
输出参数: -
|
|
返回说明: <0: 错误 >0:帧长度
|
|
其它说明: -
|
|
*****************************************************************/
|
|
static void chrg_south_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;
|
|
}
|
|
|
|
static int chrg_south_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;
|
|
}
|
|
|
|
static void chrg_south_frame_dispatch(struct pickup_info_t *pFrame)
|
|
{
|
|
struct mb_msg_t *pMB = pFrame->pMB;
|
|
|
|
chrg_south_frame_reset(pFrame, RT_FALSE);
|
|
chrg_thread_mb_send(IDX_THR_SOUTH, pMB);
|
|
}
|
|
|
|
static int chrg_south_pickup_frame (struct rt_ringbuffer *rb,
|
|
struct pickup_info_t *pFrame)
|
|
{
|
|
int cnt = 0;
|
|
int status = 0;
|
|
rt_size_t len_rb = 0;
|
|
|
|
if ((RT_NULL == rb)||(RT_NULL == pFrame)) {
|
|
return -1;
|
|
}
|
|
|
|
do {
|
|
if (0 == pFrame->findhead) {
|
|
pFrame->len_msg = chrg_south_frame_head(rb, pFrame->head, ID_ELOAD);
|
|
if (pFrame->len_msg > 0) {
|
|
if ((pFrame->len_msg < MB_ACK_HEAD_SIZE)||
|
|
(pFrame->len_msg >= SIZE_BUF_TTY)) {
|
|
LOG_E("invalid msg length: %d", pFrame->len_msg);
|
|
chrg_south_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_south_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_south_frame_reset(pFrame, RT_TRUE);
|
|
return -4;
|
|
}
|
|
|
|
rt_memcpy(pFrame->pMB->payload, pFrame->head, MB_ACK_HEAD_SIZE);
|
|
pFrame->length = MB_ACK_HEAD_SIZE;
|
|
pFrame->findhead = 1;
|
|
pFrame->last_tick = rt_tick_get();
|
|
|
|
status = chrg_south_frame_append(rb, pFrame);
|
|
if (status > 0) {
|
|
chrg_south_frame_dispatch(pFrame);
|
|
cnt++;
|
|
} else if (status < 0) {
|
|
chrg_south_frame_reset(pFrame, RT_TRUE);
|
|
return -5;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
status = chrg_south_frame_append(rb, pFrame);
|
|
if (status > 0) {
|
|
chrg_south_frame_dispatch(pFrame);
|
|
cnt++;
|
|
} else if (status < 0) {
|
|
chrg_south_frame_reset(pFrame, RT_TRUE);
|
|
return -5;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
len_rb = rt_ringbuffer_data_len(rb);
|
|
} while (len_rb >= MB_ACK_HEAD_SIZE);
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/*****************************************************************
|
|
函数名称: chrg_south_thread_entry
|
|
函数描述: 南向通道数据接收线程体
|
|
输入参数: *data: 本线程信息
|
|
输出参数: -
|
|
返回说明: -
|
|
其它说明: -
|
|
*****************************************************************/
|
|
void chrg_south_thread_entry (void *data)
|
|
{
|
|
struct chrg_thread_t *pTHR = (struct chrg_thread_t *)data;
|
|
|
|
if (RT_NULL == pTHR) {
|
|
return ;
|
|
}
|
|
|
|
int len = 0;
|
|
struct chrg_tty_t *pTTY = RT_NULL;
|
|
struct chrg_south_t *pSOU = &chrgsouth;
|
|
|
|
chrg_south_switch(IDX_SOU_CH1);
|
|
|
|
pTTY = chrg_tty_create(pSOU->devname, BAUD_RATE_115200, 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;
|
|
}
|
|
|
|
pSOU->tty = pTTY;
|
|
|
|
pSOU->rb = rt_ringbuffer_create(SIZE_BUF_TTY*2);
|
|
if (RT_NULL == pSOU->rb) {
|
|
LOG_E("create rb '%s' failed.", pSOU->devname);
|
|
return ;
|
|
}
|
|
|
|
while (1) {
|
|
if (pSOU->frame.findhead &&
|
|
((rt_tick_t)(rt_tick_get() - pSOU->frame.last_tick) >=
|
|
rt_tick_from_millisecond(SOUTH_FRAME_TIMEOUT_MS))) {
|
|
chrg_south_frame_reset(&pSOU->frame, RT_TRUE);
|
|
LOG_W("partial frame timeout");
|
|
}
|
|
|
|
rt_memset(pSOU->rx_buf, 0, sizeof(pSOU->rx_buf));
|
|
len = chrg_tty_recv(pTTY, pSOU->rx_buf, sizeof(pSOU->rx_buf));
|
|
if (len <= 0) {
|
|
rt_thread_mdelay(1);
|
|
continue;
|
|
}
|
|
|
|
rt_ringbuffer_put(pSOU->rb, 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);
|
|
|
|
chrg_south_pickup_frame(pSOU->rb, &pSOU->frame);
|
|
}
|
|
|
|
}
|
|
|
|
#if 1
|
|
|
|
static int south_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_south_switch((eIDX_SOU_CH)atoi(argv[1]));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
MSH_CMD_EXPORT(south_ch, south channel select);
|
|
|
|
#endif
|
|
|
|
|
|
|