Merge pull request 'Feature' (#3) from feature into master
Reviewed-on: chrg/chrg#3
This commit is contained in:
@@ -240,10 +240,10 @@ int main (void)
|
||||
VERSION_MAIN, VERSION_SUB, VERSION_FIX, BUILD_TIMESTAMP);
|
||||
#endif
|
||||
APP_Init();
|
||||
boot_JumpToAPP();
|
||||
//boot_JumpToAPP();
|
||||
while (1) {
|
||||
|
||||
//APP_Running();
|
||||
APP_Running();
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_comm.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "stm32f4xx_hal.h"
|
||||
|
||||
#define HOLD_REG_FIRMWARE_VERSION 0x0300U /* 协议板固件版本,只读 */
|
||||
#define CHRG_FIRMWARE_VERSION 0x0119U /* 当前协议板版本:V1.1.9 */
|
||||
#define CHRG_FIRMWARE_VERSION 0x011AU /* 当前协议板版本:V1.1.10 */
|
||||
|
||||
rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||||
rt_uint8_t ucCoilBuf[(TOTAL_COIL_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||||
@@ -43,6 +43,7 @@ rt_uint8_t mobude_ymodem_buf_2[MB_YMODEM_DATA_SIZE] = {0};//南向功率板升
|
||||
#define YMODEM_C_PERIOD_MS 1000
|
||||
#define YMODEM_META_ACK_TIMEOUT_MS 3000
|
||||
#define YMODEM_DATA_ACK_TIMEOUT_MS 1000
|
||||
#define YMODEM_RX_FRAME_TIMEOUT_MS 300
|
||||
#define YMODEM_MAX_RETRIES 3
|
||||
|
||||
/* COM侧升级会话状态:接收缓存负责拼帧,备份缓存负责超时/NAK重发。 */
|
||||
@@ -54,6 +55,16 @@ static rt_uint8_t g_ymodem_retry_count = 0;
|
||||
static rt_uint8_t g_ymodem_eot_done = 0;
|
||||
static rt_tick_t g_ymodem_send_tick = 0;
|
||||
static rt_tick_t g_ymodem_c_tick = 0;
|
||||
static rt_tick_t g_ymodem_rx_tick = 0;
|
||||
static rt_uint8_t g_ymodem_deferred_resp = 0;
|
||||
static rt_uint32_t g_ymodem_frame_count = 0;
|
||||
static rt_uint32_t g_ymodem_send_count = 0;
|
||||
|
||||
static const rt_uint8_t g_ymodem_update_frame[8] =
|
||||
{ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0xFF, 0xF8, 0x6B};
|
||||
static const rt_uint8_t g_ymodem_jump_frame[8] =
|
||||
{ID_ELOAD, 0x06, 0x00, 0x0F, 0x00, 0x0F, 0xF8, 0x2F};
|
||||
static void chrg_ymodem_session_stop(void);
|
||||
/* Modbus 影子寄存器: 0x06/0x10 写操作暂存到此, PARAM_EFFECT 生效后复制到实际 pNOR->sw */
|
||||
static struct chrg_switch_t g_shadow_sw[TOTAL_NOR_CHS];
|
||||
static rt_uint8_t g_shadow_dirty[TOTAL_NOR_CHS] = {0}; /* 0=已生效 1=待生效,防止重复覆盖 */
|
||||
@@ -607,7 +618,7 @@ eMBException funcReadHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||||
|
||||
/*
|
||||
* 协议板版本号使用独立的只读地址0x0300,不占用现有通道保持寄存器空间。
|
||||
* 当前版本V1.1.9编码为0x0119,Modbus响应按高字节在前返回01 19。
|
||||
* 当前版本V1.1.10编码为0x011A,Modbus响应按高字节在前返回01 1A。
|
||||
* 该特殊地址只支持一次读取一个寄存器,避免与普通连续寄存器混用。
|
||||
*/
|
||||
if (regAddress == HOLD_REG_FIRMWARE_VERSION) {
|
||||
@@ -1399,9 +1410,112 @@ static void chrg_ymodem_protocol_reset(void)
|
||||
g_ymodem_eot_done = 0;
|
||||
g_ymodem_send_tick = 0;
|
||||
g_ymodem_c_tick = rt_tick_get();
|
||||
g_ymodem_rx_tick = 0;
|
||||
g_ymodem_deferred_resp = 0;
|
||||
g_ymodem_frame_count = 0;
|
||||
g_ymodem_send_count = 0;
|
||||
s_first_soh_rcvd = 0;
|
||||
}
|
||||
|
||||
static const char *chrg_ymodem_state_name(Ymodem_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case Ymodem_Wait:
|
||||
return "WAIT";
|
||||
case Ymodem_Rec_Start:
|
||||
return "START";
|
||||
case Ymodem_Rec_1024:
|
||||
return "DATA";
|
||||
case Ymodem_Rec_End:
|
||||
return "EOT";
|
||||
case Ymodem_Rec_Finish:
|
||||
return "FINISH";
|
||||
default:
|
||||
return "IDLE";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *chrg_ymodem_resp_name(rt_uint8_t response)
|
||||
{
|
||||
switch (response) {
|
||||
case ACK:
|
||||
return "ACK";
|
||||
case NAK:
|
||||
return "NAK";
|
||||
case ACK_C:
|
||||
return "C";
|
||||
case YM_CAN:
|
||||
return "CAN";
|
||||
default:
|
||||
return "NONE";
|
||||
}
|
||||
}
|
||||
|
||||
static rt_uint8_t chrg_ymodem_take_south_resp(void)
|
||||
{
|
||||
rt_uint8_t resp;
|
||||
|
||||
if (g_ymodem_deferred_resp != 0) {
|
||||
resp = g_ymodem_deferred_resp;
|
||||
g_ymodem_deferred_resp = 0;
|
||||
return resp;
|
||||
}
|
||||
return chrg_eload_get_ymodem_resp();
|
||||
}
|
||||
|
||||
static int chrg_ymodem_wait_south_ack(rt_uint32_t timeout_ms)
|
||||
{
|
||||
rt_tick_t start = rt_tick_get();
|
||||
rt_uint8_t resp;
|
||||
|
||||
while ((rt_tick_t)(rt_tick_get() - start) <
|
||||
rt_tick_from_millisecond(timeout_ms)) {
|
||||
resp = chrg_eload_get_ymodem_resp();
|
||||
if (resp == ACK) {
|
||||
rt_kprintf("[UPG] south=%s state=%s\r\n",
|
||||
chrg_ymodem_resp_name(resp), chrg_ymodem_state_name(ystate));
|
||||
return 0;
|
||||
}
|
||||
if (resp == NAK || resp == YM_CAN) {
|
||||
rt_kprintf("[UPG] south=%s state=%s\r\n",
|
||||
chrg_ymodem_resp_name(resp), chrg_ymodem_state_name(ystate));
|
||||
return -2;
|
||||
}
|
||||
if (resp != 0 && g_ymodem_deferred_resp == 0) {
|
||||
g_ymodem_deferred_resp = resp;
|
||||
}
|
||||
rt_thread_mdelay(1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int chrg_ymodem_send_control_frame(const rt_uint8_t *frame)
|
||||
{
|
||||
rt_uint8_t retry;
|
||||
int wait_result;
|
||||
const char *control_name = (frame[5] == 0xFF) ? "ENTER" : "JUMP";
|
||||
|
||||
rt_kprintf("[UPG] CTRL %s begin\r\n", control_name);
|
||||
|
||||
for (retry = 0; retry < YMODEM_MAX_RETRIES; retry++) {
|
||||
int send_result = chrg_send_sou_upadata_data(frame, 8);
|
||||
rt_kprintf("[UPG] CTRL %s try=%u tx=%d\r\n",
|
||||
control_name, (unsigned int)(retry + 1), send_result);
|
||||
if (send_result == 0) {
|
||||
wait_result = chrg_ymodem_wait_south_ack(YMODEM_META_ACK_TIMEOUT_MS);
|
||||
if (wait_result == 0) {
|
||||
rt_kprintf("[UPG] CTRL %s done\r\n", control_name);
|
||||
return 0;
|
||||
}
|
||||
rt_kprintf("[UPG] CTRL %s wait=%d\r\n", control_name, wait_result);
|
||||
}
|
||||
}
|
||||
rt_kprintf("[UPG] CTRL %s failed\r\n", control_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void chrg_ymodem_session_stop(void);
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_ymodem_session_start
|
||||
函数描述: 停止普通南向请求并进入指定通道的升级透传模式
|
||||
@@ -1443,6 +1557,15 @@ static int chrg_ymodem_session_start(eIDX_SOU_CH ch)
|
||||
pROLL->ch = (rt_uint8_t)ch;
|
||||
rt_mutex_release(pTHR->mutex);
|
||||
ystate = Ymodem_Wait;
|
||||
rt_kprintf("[UPG] START ch=%u mode=%u run=%u state=%s\r\n",
|
||||
(unsigned int)ch, (unsigned int)pROLL->mode,
|
||||
(unsigned int)pROLL->run, chrg_ymodem_state_name(ystate));
|
||||
|
||||
/* 功率板完成擦除后才返回ACK,并立即开始发送C。 */
|
||||
if (chrg_ymodem_send_control_frame(g_ymodem_update_frame) != 0) {
|
||||
chrg_ymodem_session_stop();
|
||||
return -3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1460,6 +1583,13 @@ static void chrg_ymodem_session_stop(void)
|
||||
struct chrg_rollsou_t *pROLL = &chrgrollsou;
|
||||
struct chrg_south_t *pSOU = &chrgsouth;
|
||||
|
||||
rt_kprintf("[UPG] STOP state=%s rx_frames=%lu tx_frames=%lu retries=%u eot=%u\r\n",
|
||||
chrg_ymodem_state_name(ystate),
|
||||
(unsigned long)g_ymodem_frame_count,
|
||||
(unsigned long)g_ymodem_send_count,
|
||||
(unsigned int)g_ymodem_retry_count,
|
||||
(unsigned int)g_ymodem_eot_done);
|
||||
|
||||
if (pTHR->mutex != RT_NULL) {
|
||||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||||
pROLL->mode = 0;
|
||||
@@ -1498,14 +1628,20 @@ eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||||
return MB_EX_SLAVE_BUSY;
|
||||
}
|
||||
} else if (reqFrame[5] == 0x00) {
|
||||
chrg_eload_clear_ymodem_resp();
|
||||
if (chrg_ymodem_send_control_frame(g_ymodem_jump_frame) != 0) {
|
||||
chrg_ymodem_session_stop();
|
||||
return MB_EX_SLAVE_DEVICE_FAILURE;
|
||||
}
|
||||
chrg_ymodem_session_stop();
|
||||
} else {
|
||||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||||
}
|
||||
|
||||
/* 保留原0x20响应内容,外层统一补设备地址和Modbus CRC。 */
|
||||
(void)mb_ymodem_updata_cb(&ystate, resFrame);
|
||||
*resLen = 1;
|
||||
resFrame[0] = reqFrame[0];
|
||||
resFrame[1] = ACK;
|
||||
*resLen = 2;
|
||||
return MB_EX_NONE;
|
||||
}
|
||||
|
||||
@@ -1689,9 +1825,16 @@ static int chrg_ymodem_resend_last(void)
|
||||
}
|
||||
ret = chrg_send_sou_upadata_data(mobude_ymodem_buf_2, g_ymodem_last_len);
|
||||
if (ret == 0) {
|
||||
g_ymodem_send_count++;
|
||||
g_ymodem_retry_count++;
|
||||
g_ymodem_send_tick = rt_tick_get();
|
||||
g_ymodem_waiting_ack = 1;
|
||||
rt_kprintf("[UPG] RETX south frame=%02X pkt=%u len=%u count=%lu retry=%u state=%s\r\n",
|
||||
mobude_ymodem_buf_2[0], (unsigned int)mobude_ymodem_buf_2[1],
|
||||
(unsigned int)g_ymodem_last_len,
|
||||
(unsigned long)g_ymodem_send_count,
|
||||
(unsigned int)g_ymodem_retry_count,
|
||||
chrg_ymodem_state_name(ystate));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1724,6 +1867,7 @@ static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length)
|
||||
continue;
|
||||
}
|
||||
g_ymodem_rx_len = 0;
|
||||
g_ymodem_rx_tick = rt_tick_get();
|
||||
}
|
||||
|
||||
copy_len = g_ymodem_expected_len - g_ymodem_rx_len;
|
||||
@@ -1738,8 +1882,13 @@ static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length)
|
||||
}
|
||||
|
||||
if (!chrg_ymodem_frame_valid(mobude_ymodem_buf, g_ymodem_rx_len)) {
|
||||
rt_kprintf("[UPG] RX invalid header=0x%02X pkt=%u len=%u state=%s\r\n",
|
||||
mobude_ymodem_buf[0], (unsigned int)mobude_ymodem_buf[1],
|
||||
(unsigned int)g_ymodem_rx_len,
|
||||
chrg_ymodem_state_name(ystate));
|
||||
g_ymodem_rx_len = 0;
|
||||
g_ymodem_expected_len = 0;
|
||||
g_ymodem_rx_tick = 0;
|
||||
return -3;
|
||||
}
|
||||
|
||||
@@ -1764,13 +1913,28 @@ static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length)
|
||||
} else {
|
||||
ystate = Ymodem_Rec_Finish;
|
||||
}
|
||||
g_ymodem_frame_count++;
|
||||
rt_kprintf("[UPG] RX frame=%02X pkt=%u len=%u total=%lu state=%s\r\n",
|
||||
mobude_ymodem_buf[0], (unsigned int)mobude_ymodem_buf[1],
|
||||
(unsigned int)g_ymodem_last_len,
|
||||
(unsigned long)g_ymodem_frame_count,
|
||||
chrg_ymodem_state_name(ystate));
|
||||
|
||||
ret = chrg_send_sou_upadata_data(mobude_ymodem_buf_2, g_ymodem_last_len);
|
||||
g_ymodem_rx_len = 0;
|
||||
g_ymodem_expected_len = 0;
|
||||
g_ymodem_rx_tick = 0;
|
||||
if (ret != 0) {
|
||||
rt_kprintf("[UPG] TX south failed frame=%02X pkt=%u ret=%d\r\n",
|
||||
mobude_ymodem_buf_2[0], (unsigned int)mobude_ymodem_buf_2[1], ret);
|
||||
return -4;
|
||||
}
|
||||
g_ymodem_send_count++;
|
||||
rt_kprintf("[UPG] TX south frame=%02X pkt=%u len=%u count=%lu state=%s\r\n",
|
||||
mobude_ymodem_buf_2[0], (unsigned int)mobude_ymodem_buf_2[1],
|
||||
(unsigned int)g_ymodem_last_len,
|
||||
(unsigned long)g_ymodem_send_count,
|
||||
chrg_ymodem_state_name(ystate));
|
||||
g_ymodem_retry_count = 0;
|
||||
g_ymodem_waiting_ack = 1;
|
||||
g_ymodem_send_tick = rt_tick_get();
|
||||
@@ -1794,35 +1958,42 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM)
|
||||
|
||||
pCOMM->tx_len = 0;
|
||||
now = rt_tick_get();
|
||||
if ((s_first_soh_rcvd == 0) && (g_ymodem_waiting_ack == 0) &&
|
||||
((now - g_ymodem_c_tick) >= rt_tick_from_millisecond(YMODEM_C_PERIOD_MS))) {
|
||||
pCOMM->tx_buf[0] = ACK_C;
|
||||
if ((g_ymodem_expected_len != 0) && (g_ymodem_rx_tick != 0) &&
|
||||
((now - g_ymodem_rx_tick) >=
|
||||
rt_tick_from_millisecond(YMODEM_RX_FRAME_TIMEOUT_MS))) {
|
||||
g_ymodem_rx_len = 0;
|
||||
g_ymodem_expected_len = 0;
|
||||
g_ymodem_rx_tick = 0;
|
||||
pCOMM->tx_buf[0] = NAK;
|
||||
pCOMM->tx_len = 1;
|
||||
g_ymodem_c_tick = now;
|
||||
rt_kprintf("[UPG] RX timeout -> NAK state=%s\r\n",
|
||||
chrg_ymodem_state_name(ystate));
|
||||
return 1;
|
||||
}
|
||||
|
||||
south_resp = chrg_eload_get_ymodem_resp();
|
||||
south_resp = chrg_ymodem_take_south_resp();
|
||||
if (south_resp == ACK) {
|
||||
g_ymodem_waiting_ack = 0;
|
||||
g_ymodem_retry_count = 0;
|
||||
if (ystate == Ymodem_Rec_Start) {
|
||||
pCOMM->tx_buf[0] = ACK;
|
||||
pCOMM->tx_buf[1] = ACK_C;
|
||||
pCOMM->tx_len = 2;
|
||||
pCOMM->tx_len = 1;
|
||||
} else if (ystate == Ymodem_Rec_End) {
|
||||
pCOMM->tx_buf[0] = ACK;
|
||||
pCOMM->tx_buf[1] = ACK_C;
|
||||
pCOMM->tx_len = 2;
|
||||
pCOMM->tx_len = 1;
|
||||
g_ymodem_eot_done = 1;
|
||||
ystate = Ymodem_Rec_Finish;
|
||||
} else {
|
||||
pCOMM->tx_buf[0] = ACK;
|
||||
pCOMM->tx_len = 1;
|
||||
}
|
||||
rt_kprintf("[UPG] SOUTH ACK state=%s retries=%u\r\n",
|
||||
chrg_ymodem_state_name(ystate), (unsigned int)g_ymodem_retry_count);
|
||||
return 1;
|
||||
}
|
||||
if (south_resp == NAK) {
|
||||
rt_kprintf("[UPG] SOUTH NAK state=%s retry=%u\r\n",
|
||||
chrg_ymodem_state_name(ystate), (unsigned int)g_ymodem_retry_count);
|
||||
if (g_ymodem_retry_count >= YMODEM_MAX_RETRIES) {
|
||||
pCOMM->tx_buf[0] = YM_CAN;
|
||||
pCOMM->tx_buf[1] = YM_CAN;
|
||||
@@ -1834,6 +2005,8 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM)
|
||||
return 0;
|
||||
}
|
||||
if (south_resp == ACK_C) {
|
||||
rt_kprintf("[UPG] SOUTH C state=%s waiting=%u\r\n",
|
||||
chrg_ymodem_state_name(ystate), (unsigned int)g_ymodem_waiting_ack);
|
||||
/* 等待当前帧ACK时忽略功率板周期C,避免打乱上位机停止等待握手。 */
|
||||
if (g_ymodem_waiting_ack != 0) {
|
||||
return 0;
|
||||
@@ -1843,6 +2016,8 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM)
|
||||
return 1;
|
||||
}
|
||||
if (south_resp == YM_CAN) {
|
||||
rt_kprintf("[UPG] SOUTH CAN state=%s\r\n",
|
||||
chrg_ymodem_state_name(ystate));
|
||||
pCOMM->tx_buf[0] = YM_CAN;
|
||||
pCOMM->tx_buf[1] = YM_CAN;
|
||||
pCOMM->tx_len = 2;
|
||||
@@ -1854,6 +2029,9 @@ static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM)
|
||||
timeout_ms = (ystate == Ymodem_Rec_Start) ?
|
||||
YMODEM_META_ACK_TIMEOUT_MS : YMODEM_DATA_ACK_TIMEOUT_MS;
|
||||
if ((now - g_ymodem_send_tick) >= rt_tick_from_millisecond(timeout_ms)) {
|
||||
rt_kprintf("[UPG] ACK timeout state=%s retry=%u\r\n",
|
||||
chrg_ymodem_state_name(ystate),
|
||||
(unsigned int)g_ymodem_retry_count);
|
||||
if (g_ymodem_retry_count >= YMODEM_MAX_RETRIES) {
|
||||
pCOMM->tx_buf[0] = YM_CAN;
|
||||
pCOMM->tx_buf[1] = YM_CAN;
|
||||
@@ -2444,11 +2622,14 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||||
*/
|
||||
if (diff_info.cc_changed && protocol_data_changed) {
|
||||
/* Protocol-before-CC test flow is disabled in this version. */
|
||||
pReal->sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
pReal->sub = diff_info.cv_changed ?
|
||||
IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE :
|
||||
IDX_SET_SINK_CC_BEFORE_PD;
|
||||
} else if (diff_info.cc_changed) {
|
||||
pReal->sub = IDX_SET_SINK_CCLINE;
|
||||
} else {
|
||||
pReal->sub = IDX_SET_SINK_PD_ONLY;
|
||||
pReal->sub = diff_info.cv_changed ?
|
||||
IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD_ONLY;
|
||||
}
|
||||
pReal->sink.work_mode = SINK_WORK_HANDSHAKE_PREPARE;
|
||||
} else if (protocol_voltage_only || protocol_current_change) {
|
||||
@@ -2457,7 +2638,8 @@ static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||||
* 低电流待机本身已经WORK_STOP,无需重复提交卸载命令;运行中
|
||||
* 则等待实际电流降到安全范围后再重发协议。
|
||||
*/
|
||||
pReal->sub = IDX_SET_SINK_PD_ONLY;
|
||||
pReal->sub = diff_info.cv_changed ?
|
||||
IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD_ONLY;
|
||||
if (old_work_mode == SINK_WORK_LOAD_STANDBY) {
|
||||
pReal->sink.work_mode = SINK_WORK_WAIT;
|
||||
} else {
|
||||
@@ -2792,6 +2974,11 @@ static int chrg_sink_diff_send_reg(eIDX_SOU_CH ch,
|
||||
rt_uint16_t vals[2];
|
||||
rt_uint8_t count = 0;
|
||||
|
||||
if (pDiff->cv_changed) {
|
||||
regs[count] = REG_MODE;
|
||||
vals[count++] = pReal->CV_mode;
|
||||
}
|
||||
|
||||
if (pReal->CV_mode == 0x01) {
|
||||
if (pDiff->voltage_changed) {
|
||||
regs[count] = REG_VOLTAGE_OUT + 1;
|
||||
@@ -2801,11 +2988,6 @@ static int chrg_sink_diff_send_reg(eIDX_SOU_CH ch,
|
||||
regs[count] = REG_CURRENT_OUT + 1;
|
||||
vals[count++] = pReal->sink.loadc;
|
||||
}
|
||||
if (pDiff->cv_changed) {
|
||||
regs[count] = REG_MODE;
|
||||
vals[count++] = pReal->CV_mode;
|
||||
}
|
||||
|
||||
if ((count > 0) && (chrg_sou_com_batch_submit(ch, regs, vals, count) != 0)) {
|
||||
LOG_E("CH%d sink diff batch submit failed", ch + 1);
|
||||
return -1;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_comm.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_north.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 实现北向通信交互,包括接收命令解析,发送请求命令,切换通路。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -28,11 +28,8 @@
|
||||
#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_CURRENT_START_GRACE_MS 200U // Sink启动后暂不判断无电流的保护时间
|
||||
#define SINK_VOLT_STABLE_ERROR_MV 2000U // Sink实际电压与协议目标电压的最大允许误差
|
||||
#define SINK_VOLT_5V_TARGET_MV 5000U // VSafe5V目标电压,需延长稳定确认时间
|
||||
#define SINK_VOLT_CONFIRM_NORMAL_COUNT 5U // 非5V目标连续有效电压确认帧数
|
||||
#define SINK_VOLT_CONFIRM_5V_COUNT 15U // 5V目标连续有效电压确认帧数,避免把握手阶段VSafe5V误判为协议稳定
|
||||
/* Sink电压稳定确认参数已迁移至chrg_sink.h,供chrg_sink_volt_judge使用。 */
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_source_apply_protocol_voltage_rule
|
||||
@@ -84,22 +81,6 @@ static rt_bool_t chrg_sink_voltage_is_stable(rt_uint16_t actual_volt_mv,
|
||||
RT_TRUE : RT_FALSE;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_voltage_confirm_count
|
||||
函数描述: 根据Sink协议目标电压选择进入RUNING前所需的连续稳定帧数
|
||||
输入参数: target_volt_mv:当前协议申请的目标电压,单位mV
|
||||
输出参数: -
|
||||
返回说明: 5V目标返回延长确认帧数,其它目标返回普通确认帧数
|
||||
其它说明: 快充协议切换时总线会先经过VSafe5V;当目标本身也是5V时,
|
||||
单凭电压值无法区分过渡阶段和协议稳定阶段,因此增加确认时间,
|
||||
防止电子负载提前启动后导致协议板复位
|
||||
*****************************************************************/
|
||||
static rt_uint8_t chrg_sink_voltage_confirm_count(rt_uint16_t target_volt_mv)
|
||||
{
|
||||
return (target_volt_mv == SINK_VOLT_5V_TARGET_MV) ?
|
||||
SINK_VOLT_CONFIRM_5V_COUNT : SINK_VOLT_CONFIRM_NORMAL_COUNT;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_source_voltage_control
|
||||
函数描述: 根据目标电压与实际电压统一控制Source电压下发和重试
|
||||
@@ -231,110 +212,29 @@ void chrg_source_setting(rt_uint8_t *frame_data, eIDX_SOU_CH idx,
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_parse
|
||||
函数描述: 解析北向负载板回码并根据电压、电流反馈驱动Sink状态机
|
||||
函数描述: 解析北向负载板0x20回码,提取D+/D-电压到VZ/VF
|
||||
输入参数: frame_data:完整北向回码 idx:当前通道 pSW:通道控制结构体
|
||||
pNOR:北向总控结构体
|
||||
输出参数: 更新VZ、VF、低压计数、稳定电压计数、无电流计数及sink.work_mode
|
||||
输出参数: 更新pSW->VZ(D+)、pSW->VF(D-)
|
||||
返回说明: -
|
||||
其它说明: 电压连续低于门槛进入LOW;掉电处理恢复副继电器后,电压重新
|
||||
出现时先重设协议和CC;一帧有效电压达到目标附近即进入RUNING
|
||||
其它说明: Sink电压状态机判定已迁移至chrg_sink_volt_judge,
|
||||
由北向轮询线程基于eload.c更新的Now_voltage驱动
|
||||
*****************************************************************/
|
||||
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;
|
||||
rt_uint8_t stable_confirm_count;
|
||||
static rt_uint8_t volt_low[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t no_curr[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t volt_set_ok[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t volt_stable_count[TOTAL_SOU_CHS] = {0};
|
||||
static rt_tick_t running_start_tick[TOTAL_SOU_CHS] = {0};
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return;
|
||||
}
|
||||
stable_confirm_count = chrg_sink_voltage_confirm_count(
|
||||
pSW->sink.pro_loadv);
|
||||
|
||||
pSW->VZ = ((frame_data[14] << 8) | frame_data[15]);
|
||||
pSW->VF = ((frame_data[16] << 8) | frame_data[17]);
|
||||
|
||||
#if NORTH_SINK_DEBUG
|
||||
LOG_D("Sink[%d] real volt: %d mV", idx, volt_mv);
|
||||
LOG_D("Sink[%d] real volt: %d mV", idx,
|
||||
((frame_data[4] << 8) | frame_data[5]) * 10);
|
||||
#endif
|
||||
|
||||
if(pSW->sink.On_work == 1 && pSW->sink.test.test_mode == 0) {
|
||||
if (volt_mv <= WORK_MIN_VOLT) { // 电压过低,禁止进入工作状态机
|
||||
volt_stable_count[idx] = 0;
|
||||
if (volt_set_ok[idx] == 1) {
|
||||
if (++volt_low[idx] >= 50) {
|
||||
volt_set_ok[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
//pSW->sink.work_mode = SINK_WORK_LOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{ // 电压稳定时执行工作状态机(继电器/工作模式切换)
|
||||
volt_low[idx] = 0;
|
||||
volt_set_ok[idx] = 1;
|
||||
pSW->change_flag = 0;
|
||||
if (pSW->sink.work_mode == SINK_WORK_LOW_MONITOR) {
|
||||
/* 副继电器恢复后首次重新检测到输入电压,只启动协议重握手。
|
||||
* 当前回码不计入稳定电压次数,防止跳过协议设置直接带载。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
//pSW->sub = IDX_SET_SINK_CC_BEFORE_PD;
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
} else if ((pSW->sink.work_mode == SINK_WORK_LOW) ||
|
||||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) {
|
||||
/* 掉电安全批次完成前,即使收到残留回码也不得进入RUNING。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
else if (chrg_sink_voltage_is_stable(volt_mv,
|
||||
pSW->sink.pro_loadv)) {
|
||||
if ((pSW->sink.work_mode != SINK_WORK_RUNING) &&
|
||||
(pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY)) {
|
||||
if (volt_stable_count[idx] < stable_confirm_count) {
|
||||
volt_stable_count[idx]++;
|
||||
}
|
||||
if (volt_stable_count[idx] >= stable_confirm_count) {
|
||||
/* 回码已经过CRC、ID、命令和配置版本校验,并连续达到当前
|
||||
* 目标对应的确认帧数;5V采用更长窗口过滤VSafe5V过渡阶段。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
pSW->sink.work_mode = SINK_WORK_RUNING;
|
||||
running_start_tick[idx] = rt_tick_get();
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
} else if (pSW->sink.work_mode == SINK_WORK_LOAD_STANDBY) {
|
||||
/* 协议保持在线但电子负载主动停止,不执行无电流重试。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
} else if ((pSW->Now_current > 200) ||
|
||||
(pSW->sink.loadc < 200)) {
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
} else if ((rt_tick_t)(rt_tick_get() - running_start_tick[idx]) >=
|
||||
rt_tick_from_millisecond(SINK_CURRENT_START_GRACE_MS)) {
|
||||
volt_stable_count[idx] = 0;
|
||||
if (++no_curr[idx] >= 2) {
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
volt_low[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
volt_set_ok[idx] = 0;
|
||||
volt_stable_count[idx] = 0;
|
||||
}
|
||||
chrg_sink_work(idx,pSW);
|
||||
(void)pNOR;
|
||||
}
|
||||
|
||||
struct chrg_north_t chrgnorth = {
|
||||
|
||||
@@ -21,12 +21,12 @@
|
||||
|
||||
#define LEN_FRAME_HEAD (4) // [帧长, 0x00, id, 命令]
|
||||
#define MIN_FRAME_LENGTH (5) // 帧最短5字节
|
||||
#define WORK_MIN_VOLT 3000 // 工作电压下限,单位mV
|
||||
#define WORK_MIN_VOLT 500 // 工作电压下限,单位mV
|
||||
#define NORTH_FRAME_DEBUG 0x00 // 北向UART原始收发帧
|
||||
#define NORTH_SOURCE_DEBUG 0x01 // Source协议解析及组帧
|
||||
#define NORTH_SINK_DEBUG 0x00 // Sink协议解析及组帧
|
||||
#define NORTH_SINK_DEBUG 0x01 // Sink协议解析及组帧
|
||||
#define SOUTH_FRAME_DEBUG 0x00 // 南向UART原始收发帧
|
||||
#define SOUTH_COMMAND_DEBUG 0x01 // 南向寄存器写命令
|
||||
#define SOUTH_COMMAND_DEBUG 0x00 // 南向寄存器写命令
|
||||
#define COMM_FRAME_DEBUG 0x00 // COM UART原始收发帧
|
||||
#define COMM_DEBUG 0x00 // COM寄存器读写过程
|
||||
#define COMM_TRIM_DEBUG 0x00 // COM校准参数解析
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_nor.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -250,8 +250,33 @@ static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch,
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
if (pSW->sub == IDX_SET_SINK_MODE_AFTER_PD) {
|
||||
rt_uint16_t reg = REG_MODE;
|
||||
rt_uint16_t value = pSW->CV_mode;
|
||||
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, ®, &value, 1U) != 0) {
|
||||
return RT_TRUE;
|
||||
}
|
||||
pSW->sub = IDX_WAIT_SINK_MODE_AFTER_PD;
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
if (pSW->sub == IDX_WAIT_SINK_MODE_AFTER_PD) {
|
||||
if (chrg_sou_com_batch_is_pending((eIDX_SOU_CH)ch)) {
|
||||
return RT_TRUE;
|
||||
}
|
||||
if (chrg_sou_com_batch_get_result((eIDX_SOU_CH)ch) != 1) {
|
||||
pSW->sub = IDX_SET_SINK_MODE_AFTER_PD;
|
||||
return RT_TRUE;
|
||||
}
|
||||
pSW->sub = IDX_GET_SINK_VC;
|
||||
chrg_roll_nor_sink_query_wait_start(ch);
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
if ((pSW->sub != IDX_WAIT_SINK_CC) &&
|
||||
(pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD)) {
|
||||
(pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD) &&
|
||||
(pSW->sub != IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE)) {
|
||||
/* COM在等待期间提交新配置时,清理旧事务留下的CC计时记录。 */
|
||||
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
|
||||
g_sink_cc_wait_start[ch] = 0U;
|
||||
@@ -262,8 +287,10 @@ static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch,
|
||||
}
|
||||
} else {
|
||||
if ((g_sink_cc_wait_mask & bit) == 0U) {
|
||||
if (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) {
|
||||
pSW->sub = IDX_SET_SINK_PD;
|
||||
if ((pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) ||
|
||||
(pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE)) {
|
||||
pSW->sub = (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE) ?
|
||||
IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD;
|
||||
} else {
|
||||
pSW->sub = IDX_GET_SINK_VC;
|
||||
chrg_roll_nor_sink_query_wait_start(ch);
|
||||
@@ -275,14 +302,16 @@ static rt_bool_t chrg_roll_nor_sink_wait_process(rt_uint8_t ch,
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
if (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) {
|
||||
if ((pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD) ||
|
||||
(pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE)) {
|
||||
/* CC先设置完成后进入协议设置,不立即查询电压。 */
|
||||
g_sink_query_throttle[ch] = RT_FALSE;
|
||||
g_sink_query_first_pending[ch] = RT_FALSE;
|
||||
g_sink_query_tick[ch] = 0U;
|
||||
g_sink_cc_wait_mask &= (rt_uint8_t)~bit;
|
||||
g_sink_cc_wait_start[ch] = 0U;
|
||||
pSW->sub = IDX_SET_SINK_PD;
|
||||
pSW->sub = (pSW->sub == IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE) ?
|
||||
IDX_SET_SINK_PD_WITH_MODE : IDX_SET_SINK_PD;
|
||||
return RT_TRUE;
|
||||
}
|
||||
|
||||
@@ -545,12 +574,14 @@ static rt_uint8_t chrg_roll_nor_expected_cmd(const struct chrg_switch_t *pSW)
|
||||
return SINK_CMD_GET_PD;
|
||||
case IDX_SET_SINK_PD:
|
||||
case IDX_SET_SINK_PD_ONLY:
|
||||
case IDX_SET_SINK_PD_WITH_MODE:
|
||||
#if 0
|
||||
case IDX_SET_SINK_PD_BEFORE_CC:
|
||||
#endif
|
||||
return pSW->sink.protocol;
|
||||
case IDX_SET_SINK_CCLINE:
|
||||
case IDX_SET_SINK_CC_BEFORE_PD:
|
||||
case IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE:
|
||||
return SINK_CMD_SET_CC;
|
||||
case IDX_SET_SINK_CCLVL:
|
||||
return SINK_CMD_SET_LEVEL;
|
||||
@@ -762,6 +793,9 @@ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,
|
||||
case IDX_SET_SINK_CC_BEFORE_PD:
|
||||
*next_sub = IDX_WAIT_SINK_CC_BEFORE_PD;
|
||||
return sink_set_cc_line(pSW->sink.cc_set);
|
||||
case IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE:
|
||||
*next_sub = IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE;
|
||||
return sink_set_cc_line(pSW->sink.cc_set);
|
||||
case IDX_SET_SINK_CCLVL: //CC电平
|
||||
return sink_set_cc_lvl(pSW->sink.cc_lvl);
|
||||
case IDX_SET_SINK_PD:
|
||||
@@ -771,6 +805,9 @@ static int chrg_roll_nor_set(rt_uint8_t ch, struct chrg_switch_t *pSW,
|
||||
case IDX_SET_SINK_PD_ONLY:
|
||||
*next_sub = IDX_WAIT_SINK_PD_ACK;
|
||||
return chrg_roll_nor_sink_pro_set(pSW);
|
||||
case IDX_SET_SINK_PD_WITH_MODE:
|
||||
*next_sub = IDX_SET_SINK_MODE_AFTER_PD;
|
||||
return chrg_roll_nor_sink_pro_set(pSW);
|
||||
#if 0
|
||||
case IDX_SET_SINK_PD_BEFORE_CC:
|
||||
/* 协议回码确认后再设置CC,不提前查询电压。 */
|
||||
@@ -885,7 +922,8 @@ static int chrg_roll_nor_auto_request(rt_uint8_t idx,
|
||||
(rt_uint8_t)pSW->source.protocol);
|
||||
} else if ((expected_id == ID_SINK) &&
|
||||
((request_sub == IDX_SET_SINK_CCLINE) ||
|
||||
(request_sub == IDX_SET_SINK_CC_BEFORE_PD))) {
|
||||
(request_sub == IDX_SET_SINK_CC_BEFORE_PD) ||
|
||||
(request_sub == IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE))) {
|
||||
/* 北向Sink继电器保持接通,仅等待目标CC配置稳定。 */
|
||||
chrg_roll_nor_sink_cc_wait_start(idx);
|
||||
} else if ((expected_id == ID_SINK) &&
|
||||
@@ -992,8 +1030,17 @@ void chrg_roll_nor_thread_entry (void *data)
|
||||
* 协议切换等待只推进时间状态,不发送串口命令。该检查位于enable
|
||||
* 过滤前,使停止或角色切换后的通道也能及时清理残留事务记录。
|
||||
*/
|
||||
source_waiting = chrg_roll_nor_source_wait_process(idx, pSW);
|
||||
sink_waiting = chrg_roll_nor_sink_wait_process(idx, pSW);
|
||||
source_waiting = chrg_roll_nor_source_wait_process(idx, pSW);
|
||||
sink_waiting = chrg_roll_nor_sink_wait_process(idx, pSW);
|
||||
|
||||
/* 全通道Sink电压判定:使用eload实际电压Now_voltage与V_On比较,
|
||||
* 判定后统一由chrg_sink_work执行;短路测试由该入口负责WORK_START保活。 */
|
||||
for (i = 0; i < TOTAL_NOR_CHS; i++) {
|
||||
struct chrg_switch_t *psw = &pNOR->sw[i];
|
||||
if ((psw->id == ID_SINK) && psw->sink.On_work) {
|
||||
(void)chrg_sink_volt_judge((eIDX_SOU_CH)i, psw);
|
||||
}
|
||||
}
|
||||
rt_mutex_release(pTHR->mutex);
|
||||
if (state_work_ret != 0) {
|
||||
/* 南向状态动作未成功入队时保留原状态,下轮重试且禁止北向先行。 */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_roll_nor.h
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 实现北向通信轮巡请求命令。以独立线程处理。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
@@ -52,6 +52,11 @@ enum {
|
||||
IDX_SET_SINK_CC_BEFORE_PD, // 先设置CC,稳定后再设置协议
|
||||
IDX_WAIT_SINK_CC_BEFORE_PD, // 等待CC稳定后进入协议设置
|
||||
IDX_WAIT_SINK_PD_ACK, // 协议回码确认后的非发送过渡状态
|
||||
IDX_SET_SINK_PD_WITH_MODE,
|
||||
IDX_SET_SINK_CC_BEFORE_PD_WITH_MODE,
|
||||
IDX_WAIT_SINK_CC_BEFORE_PD_WITH_MODE,
|
||||
IDX_SET_SINK_MODE_AFTER_PD,
|
||||
IDX_WAIT_SINK_MODE_AFTER_PD,
|
||||
#if 0
|
||||
IDX_SET_SINK_PD_BEFORE_CC, // 先设置协议,协议回码后再设置CC
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "chrg_thread.h"
|
||||
#include "chrg_roll_sou.h"
|
||||
#include "chrg_north.h"
|
||||
#include "chrg_sink.h"
|
||||
#include "chrg_eload.h"
|
||||
#include "chrg_utils.h"
|
||||
|
||||
@@ -25,6 +26,8 @@
|
||||
#define SOU_MUTEX_TIMEOUT_MS 100
|
||||
#define SOU_RESPONSE_TIMEOUT_MS 20
|
||||
#define SOU_COM_MAX_ATTEMPTS 3
|
||||
#define SOU_COM_BURST_MAX 10U
|
||||
#define SOU_COM_RETRY_YIELD_MS 1U
|
||||
#define SOU_REL_SWITCH_DELAY_MS 100U
|
||||
#define SOU_CHANNEL_BIT(ch) ((rt_uint8_t)(1U << (ch)))
|
||||
|
||||
@@ -44,6 +47,8 @@ static rt_uint8_t g_sou_version_ready[TOTAL_SOU_CHS] = {0U};
|
||||
* 回码错误或超时时游标保持不变,使同一条命令优先完成重发,避免破坏
|
||||
* 本通道批次内的寄存器执行顺序。 */
|
||||
static rt_uint8_t g_sou_com_batch_rr_next_ch = 0U;
|
||||
static rt_uint8_t g_sou_com_batch_burst_ch = TOTAL_SOU_CHS;
|
||||
static rt_uint8_t g_sou_com_batch_burst_count = 0U;
|
||||
|
||||
struct chrg_rollsou_t chrgrollsou = {
|
||||
.run = RUN_AUTO,
|
||||
@@ -534,6 +539,29 @@ static int sou_com_batch_next(rt_uint8_t *ch, rt_uint16_t *reg, rt_uint16_t *val
|
||||
}
|
||||
|
||||
now = rt_tick_get();
|
||||
/* 短路保活的WORK_START达到周期后优先下发,避免长批次挤占30ms保活窗口。 */
|
||||
for (offset = 0; offset < TOTAL_SOU_CHS; offset++) {
|
||||
idx = (rt_uint8_t)((g_sou_com_batch_rr_next_ch + offset) %
|
||||
TOTAL_SOU_CHS);
|
||||
sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[idx];
|
||||
if ((pBatch->pending != RT_TRUE) ||
|
||||
(pBatch->cur_idx >= pBatch->reg_cnt) ||
|
||||
(pBatch->reg_list[pBatch->cur_idx] != REG_WORK) ||
|
||||
(pBatch->val_list[pBatch->cur_idx] != WORK_START) ||
|
||||
(chrgnorth.sw[idx].id != ID_SINK) ||
|
||||
(chrgnorth.sw[idx].sink.work_mode != SINK_WORK_ON_TEST_SHORT) ||
|
||||
(chrgnorth.sw[idx].sink.test.test_mode != SINK_TEST_SHORT) ||
|
||||
((pBatch->next_send_tick != 0U) &&
|
||||
((rt_int32_t)(now - pBatch->next_send_tick) < 0))) {
|
||||
continue;
|
||||
}
|
||||
pBatch->next_send_tick = 0U;
|
||||
*ch = idx;
|
||||
*reg = pBatch->reg_list[pBatch->cur_idx];
|
||||
*value = pBatch->val_list[pBatch->cur_idx];
|
||||
rt_mutex_release(pTHR->mutex);
|
||||
return 1;
|
||||
}
|
||||
/* 从上一次成功命令的下一个通道开始查找,使多通道批次按
|
||||
* "CH1一条 -> CH2一条 -> ..." 交替执行,而不是低编号通道独占总线。 */
|
||||
for (offset = 0; offset < TOTAL_SOU_CHS; offset++) {
|
||||
@@ -592,9 +620,6 @@ static int sou_com_batch_complete(rt_uint8_t ch, rt_uint16_t reg, rt_uint16_t va
|
||||
}
|
||||
|
||||
pBatch->cur_idx++;
|
||||
/* 当前寄存器已收到匹配写回码。下一次调度从下一个通道开始;
|
||||
* 当前通道的下一条命令会在其它就绪通道各执行一条后继续发送。 */
|
||||
g_sou_com_batch_rr_next_ch = (rt_uint8_t)((ch + 1U) % TOTAL_SOU_CHS);
|
||||
/* 暂时关闭副继电器与主继电器之间的固定延时;需要恢复时重新启用本段。 */
|
||||
if ((reg == REG_REL_ON) && (value == ALL_REL_OFF) &&
|
||||
(pBatch->cur_idx < pBatch->reg_cnt) &&
|
||||
@@ -615,6 +640,19 @@ static int sou_com_batch_complete(rt_uint8_t ch, rt_uint16_t reg, rt_uint16_t va
|
||||
pBatch->cur_idx = 0;
|
||||
pBatch->reg_cnt = 0;
|
||||
pBatch->next_send_tick = 0U;
|
||||
g_sou_com_batch_burst_ch = TOTAL_SOU_CHS;
|
||||
g_sou_com_batch_burst_count = 0U;
|
||||
g_sou_com_batch_rr_next_ch = (rt_uint8_t)((ch + 1U) % TOTAL_SOU_CHS);
|
||||
} else if (g_sou_com_batch_burst_ch != ch) {
|
||||
g_sou_com_batch_burst_ch = ch;
|
||||
g_sou_com_batch_burst_count = 1U;
|
||||
g_sou_com_batch_rr_next_ch = ch;
|
||||
} else if (++g_sou_com_batch_burst_count >= SOU_COM_BURST_MAX) {
|
||||
g_sou_com_batch_burst_ch = TOTAL_SOU_CHS;
|
||||
g_sou_com_batch_burst_count = 0U;
|
||||
g_sou_com_batch_rr_next_ch = (rt_uint8_t)((ch + 1U) % TOTAL_SOU_CHS);
|
||||
} else {
|
||||
g_sou_com_batch_rr_next_ch = ch;
|
||||
}
|
||||
rt_mutex_release(pTHR->mutex);
|
||||
return 0;
|
||||
@@ -635,6 +673,12 @@ int chrg_sou_com_batch_cancel(eIDX_SOU_CH ch)
|
||||
pBatch->cur_idx = 0;
|
||||
pBatch->reg_cnt = 0;
|
||||
pBatch->next_send_tick = 0U;
|
||||
if (g_sou_com_batch_burst_ch == ch) {
|
||||
g_sou_com_batch_burst_ch = TOTAL_SOU_CHS;
|
||||
g_sou_com_batch_burst_count = 0U;
|
||||
g_sou_com_batch_rr_next_ch =
|
||||
(rt_uint8_t)((ch + 1U) % TOTAL_SOU_CHS);
|
||||
}
|
||||
/* 已取消事务对应的优先P1量测同样失效,后续状态会重新申请新量测。 */
|
||||
g_sou_measure_once_pending_mask &= (rt_uint8_t)~SOU_CHANNEL_BIT(ch);
|
||||
g_sou_measure_once_active_mask &= (rt_uint8_t)~SOU_CHANNEL_BIT(ch);
|
||||
@@ -734,6 +778,23 @@ static void sou_com_command_failed(rt_uint8_t ch, rt_uint16_t reg, rt_uint16_t v
|
||||
|
||||
(*retry_count)++;
|
||||
if (*retry_count < SOU_COM_MAX_ATTEMPTS) {
|
||||
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
|
||||
if (rt_mutex_take(pTHR->mutex,
|
||||
rt_tick_from_millisecond(SOU_MUTEX_TIMEOUT_MS)) == RT_EOK) {
|
||||
sou_com_batch_ch_t *pBatch = &g_sou_com_batch.ch[ch];
|
||||
if ((pBatch->pending == RT_TRUE) &&
|
||||
(pBatch->cur_idx < pBatch->reg_cnt) &&
|
||||
(pBatch->reg_list[pBatch->cur_idx] == reg) &&
|
||||
(pBatch->val_list[pBatch->cur_idx] == value)) {
|
||||
pBatch->next_send_tick = rt_tick_get() +
|
||||
rt_tick_from_millisecond(SOU_COM_RETRY_YIELD_MS);
|
||||
g_sou_com_batch_rr_next_ch =
|
||||
(rt_uint8_t)((ch + 1U) % TOTAL_SOU_CHS);
|
||||
g_sou_com_batch_burst_ch = TOTAL_SOU_CHS;
|
||||
g_sou_com_batch_burst_count = 0U;
|
||||
}
|
||||
rt_mutex_release(pTHR->mutex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -765,7 +826,7 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
rt_uint8_t run = 0;
|
||||
rt_uint8_t index = 0, idx = 0;
|
||||
rt_uint8_t batch_ch = 0;
|
||||
rt_uint8_t retry_count = 0;
|
||||
rt_uint8_t retry_count[TOTAL_SOU_CHS] = {0};
|
||||
rt_uint8_t command_ch = 0;
|
||||
rt_uint8_t single_ch = 0;
|
||||
rt_uint8_t single_part = 0;
|
||||
@@ -782,6 +843,7 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
struct chrg_thread_t *pTS = &chrgthr[IDX_THR_SOUTH];
|
||||
struct mb_msg_t *pMB_R = RT_NULL;
|
||||
while (1) {
|
||||
chrg_sink_short_keepalive_process();
|
||||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||||
run = pROLL->run;
|
||||
single_ch = pROLL->ch;
|
||||
@@ -794,7 +856,7 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
version_probe = RT_FALSE;
|
||||
response_part = MB_R_PART1;
|
||||
if ((run != RUN_COM) && (run != RUN_UPDATA)) {
|
||||
retry_count = 0;
|
||||
rt_memset(retry_count, 0, sizeof(retry_count));
|
||||
}
|
||||
// rt_kprintf("------ROLL SOU START \n--------");
|
||||
switch (run) {
|
||||
@@ -853,7 +915,6 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
{
|
||||
int next_ret = sou_com_batch_next(&command_ch, &command_reg, &command_value);
|
||||
if (next_ret <= 0) {
|
||||
retry_count = 0;
|
||||
rt_thread_mdelay(1);
|
||||
continue;
|
||||
}
|
||||
@@ -917,10 +978,10 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
if (response_valid) {
|
||||
if (run == RUN_COM) {
|
||||
if (sou_com_batch_complete(batch_ch, command_reg, command_value) == 0) {
|
||||
retry_count = 0;
|
||||
retry_count[batch_ch] = 0;
|
||||
} else {
|
||||
sou_com_command_failed(batch_ch, command_reg, command_value,
|
||||
"batch changed", &retry_count);
|
||||
"batch changed", &retry_count[batch_ch]);
|
||||
}
|
||||
} else if (run != RUN_AUTO) {
|
||||
sou_single_command_finish(run);
|
||||
@@ -938,7 +999,7 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
}
|
||||
} else if (run == RUN_COM) {
|
||||
sou_com_command_failed(batch_ch, command_reg, command_value,
|
||||
"invalid response", &retry_count);
|
||||
"invalid response", &retry_count[batch_ch]);
|
||||
} else {
|
||||
LOG_E("crc failed");
|
||||
}
|
||||
@@ -954,7 +1015,7 @@ void chrg_roll_sou_thread_entry (void *data)
|
||||
}
|
||||
if ((run == RUN_COM) && command_valid) {
|
||||
sou_com_command_failed(batch_ch, command_reg, command_value,
|
||||
"timeout", &retry_count);
|
||||
"timeout", &retry_count[batch_ch]);
|
||||
} else if (run != RUN_AUTO) {
|
||||
sou_single_command_finish(run);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#define SINK_UNLOAD_QUERY_INTERVAL_MS 20U // 卸载期间两次优先量测请求的最小间隔
|
||||
#define SINK_UNLOAD_CURRENT_MAX_MA 200U // 判定负载已安全卸载的最大实际电流
|
||||
#define SINK_UNLOAD_CONFIRM_COUNT 2U // 连续满足低电流条件的量测帧数
|
||||
#define SINK_SHORT_KEEPALIVE_MS 30U // 短路测试WORK_START保活下发周期
|
||||
|
||||
/* 主继电器闭合跟踪:1=已提交ALL_REL_ON(主继电器应已闭合) 0=未闭合/已断开。
|
||||
* 用于0V硬保护区分"首次开载/握手恢复"(主继电器未闭合,Now_voltage=0属正常)
|
||||
@@ -38,6 +39,8 @@ static rt_uint32_t g_sink_unload_sample_seq[TOTAL_SOU_CHS] = {0};
|
||||
static rt_bool_t g_sink_unload_batch_confirmed[TOTAL_SOU_CHS] = {RT_FALSE};
|
||||
static rt_tick_t g_sink_unload_query_tick[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t g_sink_unload_confirm_count[TOTAL_SOU_CHS] = {0};
|
||||
/* 记录各通道短路测试最近一次WORK_START的发送时间。 */
|
||||
static rt_tick_t g_sink_short_start_tick[TOTAL_SOU_CHS] = {0};
|
||||
|
||||
enum {
|
||||
SINK_UNLOAD_NEXT_RUNNING = 0,
|
||||
@@ -244,11 +247,10 @@ int sink_set_pd (rt_uint8_t pd, rt_uint8_t *buf)
|
||||
static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
{
|
||||
struct chrg_north_t *pNOR = &chrgnorth;
|
||||
rt_uint16_t regs[5] = {REG_REL_ON, REG_CURRENT_OUT + 1,
|
||||
REG_MODE, Eload_Start_V_ON, 0};
|
||||
rt_uint16_t vals[5] = {FU_REL_ON, 0, 0, 0, 0};
|
||||
rt_uint16_t regs[6];
|
||||
rt_uint16_t vals[6];
|
||||
rt_bool_t first_start;
|
||||
rt_uint8_t count = 4U;
|
||||
rt_uint8_t count;
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
@@ -256,17 +258,31 @@ static int chrg_sink_work_init(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
/* COM进入INIT前已经设置On_work,使用原工作状态区分首次提交和后台重试。 */
|
||||
first_start = (pSW->sink.work_mode != SINK_WORK_INIT_AUX_WAIT) ?
|
||||
RT_TRUE : RT_FALSE;
|
||||
vals[2] = pSW->CV_mode;
|
||||
vals[3] = pSW->sink.volt_on <= 500 ? 500 : pSW->sink.volt_on;
|
||||
if (pSW->sink.test.test_mode_old == SINK_TEST_SHORT) {
|
||||
regs[count] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
} else if (pSW->sink.test.test_mode_old == SINK_TEST_OCP) {
|
||||
regs[count] = ModbusRTU_OCPTest_CMD_MODE_FLAG;
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
} else if (pSW->sink.test.test_mode_old == SINK_TEST_DYNA) {
|
||||
regs[count] = ModbusRTU_DYNA_CMD_MODE_FLAG;
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
/* 短路测试退出:先断开全部继电器,再打开辅助继电器,最后停止短路测试。 */
|
||||
regs[0] = REG_REL_ON; vals[0] = ALL_REL_OFF;
|
||||
regs[1] = REG_REL_ON; vals[1] = FU_REL_ON;
|
||||
regs[2] = REG_CURRENT_OUT + 1; vals[2] = 0;
|
||||
regs[3] = REG_MODE; vals[3] = pSW->CV_mode;
|
||||
regs[4] = Eload_Start_V_ON; vals[4] =
|
||||
(pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
|
||||
regs[5] = ModbusRTU_ShortTest_CMD_MODE_FLAG;
|
||||
vals[5] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
count = 6U;
|
||||
} else {
|
||||
regs[0] = REG_REL_ON; vals[0] = FU_REL_ON;
|
||||
regs[1] = REG_CURRENT_OUT + 1; vals[1] = 0;
|
||||
regs[2] = REG_MODE; vals[2] = pSW->CV_mode;
|
||||
regs[3] = Eload_Start_V_ON; vals[3] =
|
||||
(pSW->sink.volt_on <= 500) ? 500 : pSW->sink.volt_on;
|
||||
count = 4U;
|
||||
if (pSW->sink.test.test_mode_old == SINK_TEST_OCP) {
|
||||
regs[count] = ModbusRTU_OCPTest_CMD_MODE_FLAG;
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
} else if (pSW->sink.test.test_mode_old == SINK_TEST_DYNA) {
|
||||
regs[count] = ModbusRTU_DYNA_CMD_MODE_FLAG;
|
||||
vals[count++] = ModbusRTU_DYNA_CMD_MODE_STOP;
|
||||
}
|
||||
}
|
||||
if (chrg_sou_com_batch_submit(idx, regs, vals, count) != 0) {
|
||||
return -1;
|
||||
@@ -814,16 +830,150 @@ static int chrg_sink_TEST_SHORT_ON(rt_uint8_t ch, struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint16_t regs[3] = {ModbusRTU_ShortTest_Hold_Time,ModbusRTU_ShortTest_CMD_MODE_FLAG, REG_WORK};
|
||||
rt_uint16_t vals[3] = {0,0,0};
|
||||
if(pSW->sink.test.short_test.set_mode == 0)
|
||||
{
|
||||
vals[0] = pSW->sink.test.short_test.hold_time;
|
||||
vals[1] = ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR;
|
||||
}
|
||||
else{
|
||||
vals[1] = ModbusRTU_DYNA_CMD_MODE_RUN_ALWAYS_KEPP;
|
||||
}
|
||||
/* 外部工程固定短路测试保持60秒且不带载。 */
|
||||
vals[0] = 60000;
|
||||
vals[1] = ModbusRTU_DYNA_CMD_MODE_RUN_NO_CURR;
|
||||
vals[2] = WORK_START;
|
||||
return chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 3);
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, regs, vals, 3) == 0) {
|
||||
g_sink_short_start_tick[ch] = rt_tick_get();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 短路测试保持期间只补发WORK_START,避免重复写入测试配置并堆积批次。 */
|
||||
static int chrg_sink_TEST_SHORT_KEEPALIVE(rt_uint8_t ch)
|
||||
{
|
||||
rt_uint16_t reg = REG_WORK;
|
||||
rt_uint16_t val = WORK_START;
|
||||
rt_tick_t now = rt_tick_get();
|
||||
|
||||
if ((rt_tick_t)(now - g_sink_short_start_tick[ch]) <
|
||||
rt_tick_from_millisecond(SINK_SHORT_START_INTERVAL_MS)) {
|
||||
return 0;
|
||||
}
|
||||
if (chrg_sou_com_batch_is_pending((eIDX_SOU_CH)ch)) {
|
||||
return 0;
|
||||
}
|
||||
if (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch, ®, &val, 1U) == 0) {
|
||||
g_sink_short_start_tick[ch] = now;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_voltage_confirm_count
|
||||
函数描述: 根据Sink协议目标电压选择进入RUNING前所需的连续稳定帧数
|
||||
输入参数: target_volt_mv:当前协议申请的目标电压,单位mV
|
||||
输出参数: -
|
||||
返回说明: 5V目标返回延长确认帧数,其它目标返回普通确认帧数
|
||||
其它说明: 由chrg_sink_volt_judge调用
|
||||
*****************************************************************/
|
||||
static rt_uint8_t chrg_sink_voltage_confirm_count(rt_uint16_t target_volt_mv)
|
||||
{
|
||||
return (target_volt_mv == SINK_VOLT_5V_TARGET_MV) ?
|
||||
SINK_VOLT_CONFIRM_5V_COUNT : SINK_VOLT_CONFIRM_NORMAL_COUNT;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
函数名称: chrg_sink_volt_judge
|
||||
函数描述: 使用南向功率板实际电压Now_voltage与V_On比较,驱动负载进入RUNING
|
||||
输入参数: idx:南向通道 pSW:通道控制结构体
|
||||
输出参数: 更新sink.work_mode并调用chrg_sink_work
|
||||
返回说明: 0:处理完成 <0:参数错误
|
||||
*****************************************************************/
|
||||
int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
{
|
||||
rt_uint8_t stable_confirm_count;
|
||||
static rt_uint8_t volt_low[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t no_curr[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t volt_set_ok[TOTAL_SOU_CHS] = {0};
|
||||
static rt_uint8_t volt_stable_count[TOTAL_SOU_CHS] = {0};
|
||||
static rt_tick_t running_start_tick[TOTAL_SOU_CHS] = {0};
|
||||
|
||||
if ((idx >= TOTAL_SOU_CHS) || (pSW == RT_NULL)) {
|
||||
return -1;
|
||||
}
|
||||
stable_confirm_count = chrg_sink_voltage_confirm_count(
|
||||
pSW->sink.pro_loadv);
|
||||
|
||||
if ((pSW->sink.On_work == 1) && (pSW->sink.test.test_mode == 0)) {
|
||||
if (pSW->Now_voltage <= WORK_MIN_VOLT) {
|
||||
volt_stable_count[idx] = 0;
|
||||
if (volt_set_ok[idx] == 1 && ++volt_low[idx] >= 50) {
|
||||
volt_set_ok[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
} else {
|
||||
volt_low[idx] = 0;
|
||||
volt_set_ok[idx] = 1;
|
||||
pSW->change_flag = 0;
|
||||
if (pSW->sink.work_mode == SINK_WORK_LOW_MONITOR) {
|
||||
/* 副继电器恢复后首次重新检测到输入电压,只启动协议重握手。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
} else if ((pSW->sink.work_mode == SINK_WORK_LOW) ||
|
||||
(pSW->sink.work_mode == SINK_WORK_LOW_AUX_WAIT)) {
|
||||
/* 掉电安全批次完成前,不得进入RUNING。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
} else if (pSW->Now_voltage > pSW->sink.volt_on) {
|
||||
if ((pSW->sink.work_mode != SINK_WORK_RUNING) &&
|
||||
(pSW->sink.work_mode != SINK_WORK_LOAD_STANDBY)) {
|
||||
if (volt_stable_count[idx] < stable_confirm_count) {
|
||||
volt_stable_count[idx]++;
|
||||
}
|
||||
if (volt_stable_count[idx] >= stable_confirm_count) {
|
||||
/* 连续达到当前目标对应的确认帧数后进入RUNING。 */
|
||||
volt_stable_count[idx] = 0;
|
||||
pSW->sink.work_mode = SINK_WORK_RUNING;
|
||||
running_start_tick[idx] = rt_tick_get();
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
} else if (pSW->sink.work_mode == SINK_WORK_LOAD_STANDBY) {
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
} else if ((pSW->Now_current > 200) || (pSW->sink.loadc < 200)) {
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
} else if ((rt_tick_t)(rt_tick_get() - running_start_tick[idx]) >=
|
||||
rt_tick_from_millisecond(SINK_CURRENT_START_GRACE_MS)) {
|
||||
volt_stable_count[idx] = 0;
|
||||
if (++no_curr[idx] >= 2) {
|
||||
pSW->sink.work_mode = SINK_WORK_WAIT;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
volt_stable_count[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
volt_low[idx] = 0;
|
||||
no_curr[idx] = 0;
|
||||
volt_set_ok[idx] = 0;
|
||||
volt_stable_count[idx] = 0;
|
||||
}
|
||||
return chrg_sink_work(idx, pSW);
|
||||
}
|
||||
|
||||
void chrg_sink_short_keepalive_process(void)
|
||||
{
|
||||
eIDX_SOU_CH idx;
|
||||
struct chrg_switch_t *pSW;
|
||||
|
||||
for (idx = IDX_SOU_CH1; idx < TOTAL_SOU_CHS; idx++) {
|
||||
pSW = &chrgnorth.sw[idx];
|
||||
if ((pSW->id != ID_SINK) ||
|
||||
(pSW->sink.work_mode != SINK_WORK_ON_TEST_SHORT) ||
|
||||
(pSW->sink.test.test_mode != SINK_TEST_SHORT)) {
|
||||
g_sink_short_start_tick[idx] = 0U;
|
||||
continue;
|
||||
}
|
||||
(void)chrg_sink_TEST_SHORT_KEEPALIVE(idx);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
@@ -877,14 +1027,21 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
}
|
||||
work_mode = pSW->sink.work_mode;
|
||||
workmode = work_mode;
|
||||
if (work_mode != SINK_WORK_ON_TEST_SHORT) {
|
||||
g_sink_short_start_tick[idx] = 0;
|
||||
}
|
||||
/* 异步等待状态需要持续检查批次或新量测,其余相同状态保持去重。 */
|
||||
if ((work_mode == last_state[idx]) &&
|
||||
(work_mode != SINK_WORK_ON_TEST_SHORT) &&
|
||||
(work_mode != SINK_WORK_INIT_AUX_WAIT) &&
|
||||
(work_mode != SINK_WORK_STOP_WAIT) &&
|
||||
(work_mode != SINK_WORK_STOP_RELAY_WAIT) &&
|
||||
(work_mode != SINK_WORK_UNLOAD_WAIT) &&
|
||||
(work_mode != SINK_WORK_LOW_AUX_WAIT) &&
|
||||
(work_mode != SINK_WORK_HANDSHAKE_WAIT)) {
|
||||
if (work_mode == SINK_WORK_ON_TEST_SHORT) {
|
||||
return chrg_sink_TEST_SHORT_KEEPALIVE(idx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
switch(work_mode){
|
||||
@@ -904,7 +1061,11 @@ int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW)
|
||||
ret = chrg_sink_TEST_DYNA_ON(idx,pSW);
|
||||
break;
|
||||
case SINK_WORK_ON_TEST_SHORT:
|
||||
ret = chrg_sink_TEST_SHORT_ON(idx,pSW);
|
||||
if (work_mode != last_state[idx]) {
|
||||
ret = chrg_sink_TEST_SHORT_ON(idx, pSW);
|
||||
} else {
|
||||
ret = chrg_sink_TEST_SHORT_KEEPALIVE(idx);
|
||||
}
|
||||
break;
|
||||
case SINK_WORK_ON_TEST_OCP:
|
||||
ret = chrg_sink_TEST_OCP_ON(idx,pSW);
|
||||
|
||||
@@ -21,6 +21,8 @@ struct chrg_switch_t;
|
||||
#define SINK_TEST_SHORT 1
|
||||
#define SINK_TEST_OCP 2
|
||||
#define SINK_TEST_DYNA 3
|
||||
/* 短路测试期间按该周期补发WORK_START,防止功率板因超时停止。 */
|
||||
#define SINK_SHORT_START_INTERVAL_MS 30U
|
||||
//短路测试结构体
|
||||
struct test_short {
|
||||
rt_uint16_t hold_time; // 短路测试时间
|
||||
@@ -208,6 +210,12 @@ enum {
|
||||
*/
|
||||
#define SINK_LOAD_ENABLE_MIN_MA 100U
|
||||
|
||||
/* Sink电压稳定确认参数,供chrg_sink_volt_judge使用。 */
|
||||
#define SINK_VOLT_5V_TARGET_MV 5000U
|
||||
#define SINK_VOLT_CONFIRM_NORMAL_COUNT 5U
|
||||
#define SINK_VOLT_CONFIRM_5V_COUNT 15U
|
||||
#define SINK_CURRENT_START_GRACE_MS 200U
|
||||
|
||||
/* 主继电器闭合后的0V持续监控:由北向轮询线程每轮调用,掉电立即转掉电保护。 */
|
||||
extern int chrg_sink_volt_low_protect(eIDX_SOU_CH idx, struct chrg_switch_t *pSW);
|
||||
|
||||
@@ -409,6 +417,8 @@ extern void chrg_nor_sink_test_run(rt_uint8_t ch, struct chrg_switch_t *pSW);
|
||||
/* 判断普通负载的当前设定是否允许启动电子负载。 */
|
||||
extern rt_bool_t chrg_sink_load_should_start(const struct chrg_switch_t *pSW);
|
||||
extern int chrg_sink_work(eIDX_SOU_CH idx, struct chrg_switch_t *pSW);
|
||||
extern int chrg_sink_volt_judge(eIDX_SOU_CH idx, struct chrg_switch_t *pSW);
|
||||
extern void chrg_sink_short_keepalive_process(void);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_source.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 北向数据source请求数据填充及发送。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
文件名称 : chrg_source.c
|
||||
完成日期 :
|
||||
当前版本号 : V1.1.9
|
||||
当前版本号 : V1.1.10
|
||||
主要功能 : 北向数据source请求数据填充及发送,命令字定义。
|
||||
版本历史 : 创建原始版本
|
||||
说明 :
|
||||
|
||||
+921
-2487
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
+2519
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
(ExpressProject ""
|
||||
(ProjectVersion "19981106")
|
||||
(Folder "Design Resources"
|
||||
(Folder "Library")
|
||||
(File ".\hc_dfcd_protocol.dsn"
|
||||
(Type "Schematic Design"))
|
||||
(BuildFileAddedOrDeleted "x")
|
||||
(CompileFileAddedOrDeleted "x")
|
||||
(NoModify))
|
||||
(Folder "Outputs")
|
||||
(Folder "Referenced Projects")
|
||||
(PartMRUSelector)
|
||||
(GlobalState
|
||||
(FileView
|
||||
(Path "Design Resources")
|
||||
(Path "Design Resources" ".\hc_dfcd_protocol.dsn")
|
||||
(Path "Design Resources" ".\hc_dfcd_protocol.dsn" "SCHEMATIC1")
|
||||
(Select "Design Resources" ".\hc_dfcd_protocol.dsn" "SCHEMATIC1" "01_MCU"))
|
||||
(HierarchyView)
|
||||
(Doc
|
||||
(Type "COrCapturePMDoc")
|
||||
(Frame
|
||||
(Placement "44 2 3 -1 -1 -8 -31 0 200 0 150"))
|
||||
(Tab 0))))
|
||||
@@ -0,0 +1,167 @@
|
||||
# 功率板升级问题与修改计划
|
||||
|
||||
## 1. 升级链路
|
||||
|
||||
```text
|
||||
上位机
|
||||
↓
|
||||
协议板 App
|
||||
↓ 进入升级指令
|
||||
协议板南向 UART
|
||||
↓
|
||||
功率板 Boot
|
||||
↓ 0x06、0x43
|
||||
协议板 App
|
||||
↓
|
||||
上位机
|
||||
进入升级控制帧:
|
||||
03 06 00 0F 00 FF F8 6B
|
||||
跳转 App 控制帧:
|
||||
03 06 00 0F 00 0F F8 2F
|
||||
功率板 Boot 使用裸 BIN + YModem 接收升级数据。
|
||||
2. 已确认的问题
|
||||
2.1 协议板发送日志含义不准确
|
||||
协议板日志:
|
||||
[UPG] CTRL ENTER try=1 tx=0
|
||||
tx=0 只表示发送请求已经提交到南向升级发送流程,不能证明 UART 已经在物理线上发送成功。
|
||||
需要通过串口逻辑分析仪确认实际线路是否出现完整控制帧。
|
||||
2.2 功率板 Boot 控制帧接收依赖一次完整 DMA 回调
|
||||
当前 Boot 主要在以下条件同时满足时识别进入升级帧:
|
||||
received_len == 8
|
||||
usart1_dma_rx_buf[0] == 0x03
|
||||
UPDATE_FRAME_MATCH
|
||||
如果控制帧被 DMA 空闲中断拆成多次接收,当前逻辑可能无法在拼接完成后重新判断控制帧。
|
||||
2.3 功率板 Boot 只有擦除成功后才发送应答
|
||||
处理流程:
|
||||
收到完整控制帧
|
||||
↓
|
||||
Flash_Erase_App_Region()
|
||||
↓
|
||||
擦除成功:发送 0x06、0x43
|
||||
擦除失败:发送 0x15
|
||||
因此:
|
||||
- 完全没有响应:优先检查 UART 接收、帧匹配、通道切换和线路;
|
||||
- 返回 0x15:重点检查 Flash 擦除;
|
||||
- 返回 0x06 但协议板没有收到:检查南向返回线路和协议板接收线程。
|
||||
2.4 Flash 分区注释与实际大小不一致
|
||||
当前配置:
|
||||
APP_FLASH_BASE_ADDR = 0x08003800
|
||||
APP_FLASH_SIZE = 0x0000B000
|
||||
0xB000 实际为 44 KB,而代码注释写成了 40 KB。
|
||||
同时擦除配置为:
|
||||
NbPages = 22
|
||||
PAGE_SIZE = 0x800
|
||||
22 页也是 44 KB。需要确认这是否符合实际功率板 Flash 分区规划。
|
||||
2.5 南向通道和收发方向需要单独确认
|
||||
协议板升级前会执行:
|
||||
chrg_south_switch(ch)
|
||||
串口发送前后还会切换 RS485 收发方向。通道选择错误、方向控制延迟不足或线路未接通,都可能导致功率板无法收到控制帧或协议板收不到 0x06。
|
||||
3. 修改计划
|
||||
3.1 完善功率板 Boot 控制帧接收
|
||||
在 DMA 接收回调中:
|
||||
1. 累积多次接收到的数据;
|
||||
2. 达到 8 字节后统一匹配进入升级帧和跳转帧;
|
||||
3. 匹配成功后设置对应 pending 标志;
|
||||
4. 匹配失败时清理缓存并等待下一帧;
|
||||
5. 增加接收长度和帧内容调试输出,或使用 GPIO 翻转辅助定位。
|
||||
3.2 完善协议板发送状态
|
||||
协议板需要区分:
|
||||
- 发送请求提交成功;
|
||||
- UART 实际发送完成;
|
||||
- 功率板返回 0x06;
|
||||
- 功率板返回 0x15;
|
||||
- 等待超时。
|
||||
日志中应明确打印实际发送长度和发送帧内容。
|
||||
3.3 完善应答重发机制
|
||||
控制帧和 YModem 数据帧均应保留最近一次发送内容:
|
||||
- 等待 ACK 超时后重发;
|
||||
- 收到 NAK 后重发;
|
||||
- 超过最大重试次数后退出升级;
|
||||
- 收到 CAN 后立即中止;
|
||||
- 半帧超时后清理接收缓存并等待下一帧。
|
||||
3.4 核对 Flash 配置
|
||||
需要确认:
|
||||
- 功率板 MCU 型号;
|
||||
- Flash 页面大小;
|
||||
- Boot 起始地址;
|
||||
- App 起始地址;
|
||||
- App 最大容量;
|
||||
- Boot 工程链接地址;
|
||||
- App 工程链接地址;
|
||||
- YModem 文件最大长度。
|
||||
确认后统一修正宏定义、擦除页数和注释。
|
||||
3.5 保持升级状态期间停止普通南向轮询
|
||||
进入升级模式后:
|
||||
- 停止普通 Modbus 轮询;
|
||||
- 锁定目标功率板通道;
|
||||
- 只转发控制帧、YModem 数据帧和单字节应答;
|
||||
- 升级结束或失败后恢复普通轮询。
|
||||
4. 待验证项目
|
||||
4.1 控制帧发送
|
||||
使用逻辑分析仪确认协议板实际发送:
|
||||
03 06 00 0F 00 FF F8 6B
|
||||
确认:
|
||||
- 波特率为 115200;
|
||||
- 数据格式为 8-N-1;
|
||||
- 通道选择正确;
|
||||
- RS485 发送方向正确;
|
||||
- 帧没有被截断。
|
||||
4.2 功率板接收
|
||||
在功率板 Boot 的:
|
||||
HAL_UARTEx_RxEventCallback()
|
||||
检查:
|
||||
Size
|
||||
usart1_dma_rx_buf[0..7]
|
||||
期望:
|
||||
Size = 8
|
||||
03 06 00 0F 00 FF F8 6B
|
||||
4.3 功率板帧处理
|
||||
确认以下变量和流程:
|
||||
g_update_prepare_pending = 1
|
||||
Check_Updata()
|
||||
Ymodem_HandleControlFrame()
|
||||
Flash_Erase_App_Region()
|
||||
记录 Flash 擦除返回值。
|
||||
4.4 功率板应答
|
||||
确认功率板 UART 线上依次发送:
|
||||
06 43
|
||||
其中:
|
||||
- 0x06 表示控制帧处理成功;
|
||||
- 0x43 表示进入 YModem 接收等待。
|
||||
4.5 协议板接收
|
||||
确认协议板南向接收线程能识别:
|
||||
0x06
|
||||
0x15
|
||||
0x43
|
||||
0x18
|
||||
并将应答正确放入 YModem 应答队列。
|
||||
4.6 YModem 数据传输
|
||||
确认:
|
||||
- 上位机发送的是裸 BIN;
|
||||
- 首包为 YModem 元数据包;
|
||||
- 数据包号连续;
|
||||
- 功率板每包返回 0x06;
|
||||
- 丢包或 CRC 错误时返回 0x15;
|
||||
- 重发后包号状态正确;
|
||||
- EOT 流程能够正常结束。
|
||||
4.7 异常场景
|
||||
需要测试:
|
||||
- 控制帧拆包;
|
||||
- 控制帧丢失;
|
||||
- 0x06 丢失;
|
||||
- YModem 数据包丢失;
|
||||
- YModem 数据包 CRC 错误;
|
||||
- 升级中途断电;
|
||||
- 升级中途复位;
|
||||
- BIN 文件超过 App 分区;
|
||||
- Flash 擦除失败;
|
||||
- 跳转帧发送失败。
|
||||
5. 暂停调试时的结论
|
||||
当前暂不进行硬件调试。后续恢复调试时,优先使用逻辑分析仪确认以下最小链路:
|
||||
协议板发送控制帧
|
||||
→ 功率板 DMA 收到 8 字节
|
||||
→ 功率板识别控制帧
|
||||
→ Flash 擦除成功
|
||||
→ 功率板发送 0x06
|
||||
→ 协议板接收到 0x06
|
||||
只有这条链路确认成功后,再继续验证 YModem 数据包传输。
|
||||
Reference in New Issue
Block a user