1. apps/chrg/applications/thread/chrg_south.c: 新增支持YM_CAN应答类型处理 2. apps/chrg/applications/utils/chrg_source.h: 移除冗余的max_vol、protective_curr字段 3. apps/chrg/applications/utils/chrg_sink.h: 新增OCP测试状态枚举与注释 4. apps/chrg/applications/thread/chrg_comm.h: 移除废弃的sink_proto_diff_t结构体及相关声明 5. apps/chrg/applications/thread/chrg_roll_sou.h/.c: 重构南向升级逻辑,新增帧缓存与安全发送机制 6. apps/chrg/applications/utils/chrg_eload.c: 重构Ymodem应答队列,优化并发访问处理 7. apps/chrg/applications/utils/chrg_sink.c: 新增OCP测试功能实现与状态切换逻辑 8. apps/chrg/applications/utils/chrg_source.c: 替换protective_curr为vc.set_current,移除冗余enable标志 9. apps/chrg/applications/thread/chrg_north.c: 重构电源电压控制逻辑,简化快充输出流程 10. apps/chrg/applications/thread/chrg_comm.c: 全面重构Ymodem升级协议与参数差异处理逻辑
2555 lines
85 KiB
C
2555 lines
85 KiB
C
/****************************************************************************
|
||
文件名称 : chrg_comm.c
|
||
完成日期 :
|
||
当前版本号 : V1.0
|
||
主要功能 : 实现对外通讯协议 MODBUS 服务端,以独立线程等待处理响应请求数据。
|
||
版本历史 : 创建原始版本
|
||
说明 :
|
||
******************************************************************************/
|
||
|
||
#include <string.h>
|
||
#include <rtthread.h>
|
||
#include "chrg_thread.h"
|
||
#include "chrg_north.h"
|
||
#include "chrg_comm.h"
|
||
#include "chrg_utils.h"
|
||
#include "chrg_south.h"
|
||
#include "chrg_roll_sou.h"
|
||
#include "chrg_eload.h"
|
||
#include "chrg_rel.h"
|
||
#include "chrg_roll_nor.h"
|
||
#define LOG_TAG "chrg.comm"
|
||
#define DBG_LEVEL DBG_LOG
|
||
#include <rtdbg.h>
|
||
#include "chrg_north_pkg.h"
|
||
#include "chrg_lcd.h"
|
||
|
||
#include "stm32f4xx_hal.h"
|
||
|
||
rt_uint8_t ucDiscInBuf[(TOTAL_DISC_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||
rt_uint8_t ucCoilBuf[(TOTAL_COIL_REGS+BITS_UCHAR-1)/BITS_UCHAR];
|
||
rt_uint16_t usRegInBuf[TOTAL_INPUT_REGS];
|
||
rt_uint16_t usRegHoldBuf[TOTAL_HOLD_ALL_REGS] = {0}; //上位机Modbus 指令/数据保持器
|
||
rt_uint16_t usRegProBuf[TOTAL_HOLD_ALL_REGS]; //北向protcal???
|
||
//rt_uint16_t usRegTestBuf[TOTAL_TEST_REGS];
|
||
rt_uint8_t g_proto_pData[80] = {0};//上位机协议指令,控制四个通道;二维
|
||
rt_uint16_t g_proto_reqLen = 0;
|
||
rt_uint8_t mobude_ymodem_buf[MB_YMODEM_DATA_SIZE] = {0};//南向功率板升级缓冲区
|
||
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_MAX_RETRIES 3
|
||
|
||
/* COM侧升级会话状态:接收缓存负责拼帧,备份缓存负责超时/NAK重发。 */
|
||
static rt_uint16_t g_ymodem_rx_len = 0;
|
||
static rt_uint16_t g_ymodem_expected_len = 0;
|
||
static rt_uint16_t g_ymodem_last_len = 0;
|
||
static rt_uint8_t g_ymodem_waiting_ack = 0;
|
||
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;
|
||
/* 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=待生效,防止重复覆盖 */
|
||
|
||
/*
|
||
* Source和Sink共用的参数差异结果。前五项是两个角色都具备的控制属性,
|
||
* 后三项只用于Sink快充协议配置;Source比较时保持为RT_FALSE。
|
||
*/
|
||
typedef struct
|
||
{
|
||
rt_bool_t protocol_changed;
|
||
rt_bool_t cc_changed;
|
||
rt_bool_t cv_changed;
|
||
rt_bool_t voltage_changed;
|
||
rt_bool_t current_changed;
|
||
rt_bool_t group_changed;
|
||
rt_bool_t pro_voltage_changed;
|
||
rt_bool_t pro_current_changed;
|
||
} chrg_param_diff_t;
|
||
|
||
/* 首次写入影子寄存器前,从真实结构体同步,避免覆盖运行中的关键标志位 */
|
||
static void chrg_shadow_sync(rt_uint8_t ch)
|
||
{
|
||
if (g_shadow_dirty[ch] == 0) {
|
||
rt_memcpy(&g_shadow_sw[ch], &chrgnorth.sw[ch], sizeof(struct chrg_switch_t));
|
||
}
|
||
g_shadow_dirty[ch] = 1;
|
||
}
|
||
|
||
static void chrg_shadow_init(void)
|
||
{
|
||
rt_memcpy(g_shadow_sw, chrgnorth.sw, sizeof(g_shadow_sw));
|
||
rt_memset(g_shadow_dirty, 0, sizeof(g_shadow_dirty));
|
||
}
|
||
struct chrg_comm_t chrgcomm = {
|
||
.devname = DEV_NAME_COMM,
|
||
.tty = RT_NULL,
|
||
.rx_len = 0,
|
||
.tx_len = 0,
|
||
.addr = MODBUS_ADDRESS_DEFAULT,
|
||
|
||
};
|
||
|
||
static void chrg_mb_write_sink(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t sou_value, struct chrg_switch_t *pSW);
|
||
static void chrg_mb_write_src(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t sou_value, struct chrg_switch_t *pSW);
|
||
static eMBException chrg_mb_write_sys(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t sou_value, struct chrg_switch_t *pSW, struct chrg_north_t *pNOR, struct chrg_south_t *pSOU);
|
||
static int chrg_sink_diff_send_reg(eIDX_SOU_CH ch,
|
||
const chrg_param_diff_t *pDiff, struct chrg_switch_t *pReal);
|
||
|
||
/*! \brief Function to set bits in a byte buffer.
|
||
*
|
||
* This function allows the efficient use of an array to implement bitfields.
|
||
* The array used for storing the bits must always be a multiple of two
|
||
* bytes. Up to eight bits can be set or cleared in one operation.
|
||
*
|
||
* \param ucByteBuf A buffer where the bit values are stored. Must be a
|
||
* multiple of 2 bytes. No length checking is performed and if
|
||
* usBitOffset / 8 is greater than the size of the buffer memory contents
|
||
* is overwritten.
|
||
* \param usBitOffset The starting address of the bits to set. The first
|
||
* bit has the offset 0.
|
||
* \param ucNBits Number of bits to modify. The value must always be smaller
|
||
* than 8.
|
||
* \param ucValues Thew new values for the bits. The value for the first bit
|
||
* starting at <code>usBitOffset</code> is the LSB of the value
|
||
* <code>ucValues</code>
|
||
*
|
||
* \code
|
||
* ucBits[2] = {0, 0};
|
||
*
|
||
* // Set bit 4 to 1 (read: set 1 bit starting at bit offset 4 to value 1)
|
||
* mb_set_bits( ucBits, 4, 1, 1 );
|
||
*
|
||
* // Set bit 7 to 1 and bit 8 to 0.
|
||
* mb_set_bits( ucBits, 7, 2, 0x01 );
|
||
*
|
||
* // Set bits 8 - 11 to 0x05 and bits 12 - 15 to 0x0A;
|
||
* mb_set_bits( ucBits, 8, 8, 0x5A);
|
||
* \endcode
|
||
*/
|
||
void mb_set_bits (rt_uint8_t *ucByteBuf, rt_uint16_t usBitOffset,
|
||
rt_uint8_t ucNBits, rt_uint8_t ucValue)
|
||
{
|
||
rt_uint16_t usWordBuf;
|
||
rt_uint16_t usMask;
|
||
rt_uint16_t usByteOffset;
|
||
rt_uint16_t usNPreBits;
|
||
rt_uint16_t usValue = ucValue;
|
||
|
||
RT_ASSERT(ucNBits <= BITS_UCHAR);
|
||
RT_ASSERT((size_t)BITS_UCHAR == sizeof(rt_uint8_t) * 8);
|
||
|
||
/* Calculate byte offset for first byte containing the bit values starting
|
||
* at usBitOffset. */
|
||
usByteOffset = (rt_uint16_t)((usBitOffset) / BITS_UCHAR);
|
||
|
||
/* How many bits precede our bits to set. */
|
||
usNPreBits = (rt_uint16_t)(usBitOffset - usByteOffset * BITS_UCHAR);
|
||
|
||
/* Move bit field into position over bits to set */
|
||
usValue <<= usNPreBits;
|
||
|
||
/* Prepare a mask for setting the new bits. */
|
||
usMask = (rt_uint16_t)((1 << (rt_uint16_t)ucNBits) - 1);
|
||
usMask <<= usBitOffset - usByteOffset * BITS_UCHAR;
|
||
|
||
/* copy bits into temporary storage. */
|
||
usWordBuf = ucByteBuf[usByteOffset];
|
||
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR;
|
||
|
||
/* Zero out bit field bits and then or value bits into them. */
|
||
usWordBuf = (rt_uint16_t)((usWordBuf & (~usMask)) | usValue);
|
||
|
||
/* move bits back into storage */
|
||
ucByteBuf[usByteOffset] = (rt_uint8_t)(usWordBuf & 0xFF);
|
||
ucByteBuf[usByteOffset + 1] = (rt_uint8_t)(usWordBuf >> BITS_UCHAR);
|
||
}
|
||
|
||
/*! \brief Function to read bits in a byte buffer.
|
||
*
|
||
* This function is used to extract up bit values from an array. Up to eight
|
||
* bit values can be extracted in one step.
|
||
*
|
||
* \param ucByteBuf A buffer where the bit values are stored.
|
||
* \param usBitOffset The starting address of the bits to set. The first
|
||
* bit has the offset 0.
|
||
* \param ucNBits Number of bits to modify. The value must always be smaller
|
||
* than 8.
|
||
*
|
||
* \code
|
||
* rt_uint8_t ucBits[2] = {0, 0};
|
||
* rt_uint8_t ucResult;
|
||
*
|
||
* // Extract the bits 3 - 10.
|
||
* ucResult = mb_get_bits( ucBits, 3, 8 );
|
||
* \endcode
|
||
*/
|
||
rt_uint8_t mb_get_bits (rt_uint8_t * ucByteBuf, rt_uint16_t usBitOffset, rt_uint8_t ucNBits)
|
||
{
|
||
rt_uint16_t usWordBuf;
|
||
rt_uint16_t usMask;
|
||
rt_uint16_t usByteOffset;
|
||
rt_uint16_t usNPreBits;
|
||
|
||
/* Calculate byte offset for first byte containing the bit values starting
|
||
* at usBitOffset. */
|
||
usByteOffset = (rt_uint16_t)((usBitOffset) / BITS_UCHAR);
|
||
|
||
/* How many bits precede our bits to set. */
|
||
usNPreBits = (rt_uint16_t)(usBitOffset - usByteOffset * BITS_UCHAR);
|
||
|
||
/* Prepare a mask for setting the new bits. */
|
||
usMask = (rt_uint16_t)((1 << (rt_uint16_t)ucNBits) - 1);
|
||
|
||
/* copy bits into temporary storage. */
|
||
usWordBuf = ucByteBuf[usByteOffset];
|
||
usWordBuf |= ucByteBuf[usByteOffset + 1] << BITS_UCHAR;
|
||
|
||
/* throw away unneeded bits. */
|
||
usWordBuf >>= usNPreBits;
|
||
|
||
/* mask away bits above the requested bitfield. */
|
||
usWordBuf &= usMask;
|
||
|
||
return (rt_uint8_t)usWordBuf;
|
||
}
|
||
|
||
static eMBException mb_error (eMBErrorCode eErrorCode)
|
||
{
|
||
eMBException eStatus;
|
||
|
||
switch (eErrorCode) {
|
||
case MB_ENOERR:
|
||
eStatus = MB_EX_NONE;
|
||
break;
|
||
|
||
case MB_ENOREG:
|
||
eStatus = MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
break;
|
||
|
||
case MB_ETIMEDOUT:
|
||
eStatus = MB_EX_SLAVE_BUSY;
|
||
break;
|
||
|
||
default:
|
||
eStatus = MB_EX_SLAVE_DEVICE_FAILURE;
|
||
break;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
//extern eMBErrorCode Modbus_Protocal_Set(rt_uint16_t Role_value,rt_uint16_t src,rt_uint16_t pps,rt_uint16_t pro_value,rt_uint16_t volt_value,rt_uint16_t curr_value);
|
||
/*****************************************************************
|
||
函数名称: mb_reg_input_cb
|
||
函数描述: Modbus输入寄存器读取回调函数
|
||
输入参数: pucRegBuffer:输入寄存器返回数据缓冲区
|
||
usAddress:输入寄存器起始地址
|
||
usNRegs:需要读取的输入寄存器数量
|
||
输出参数: pucRegBuffer:按Modbus高字节在前格式填充寄存器数据
|
||
返回说明: MB_ENOERR:读取成功 MB_ENOREG:地址范围错误
|
||
其它说明: 只读usRegInBuf,不处理写操作;每个输入寄存器转换为两个字节
|
||
*****************************************************************/
|
||
eMBErrorCode mb_reg_input_cb (rt_uint8_t *pucRegBuffer, rt_uint16_t usAddress, rt_uint16_t usNRegs)
|
||
{
|
||
eMBErrorCode eStatus = MB_ENOERR;
|
||
rt_uint16_t iRegIndex;
|
||
|
||
if ((usAddress >= INPUT_REG_START)
|
||
&& (usAddress + usNRegs <= INPUT_REG_START + TOTAL_INPUT_REGS)) {
|
||
iRegIndex = usAddress - INPUT_REG_START;
|
||
while (usNRegs > 0) {
|
||
*pucRegBuffer++ = (rt_uint8_t)(usRegInBuf[iRegIndex] >> BITS_UCHAR);
|
||
*pucRegBuffer++ = (rt_uint8_t)(usRegInBuf[iRegIndex] & 0xFF);
|
||
iRegIndex++;
|
||
usNRegs--;
|
||
}
|
||
} else {
|
||
eStatus = MB_ENOREG;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: mb_reg_holding_cb
|
||
函数描述: Modbus保持寄存器读写回调函数
|
||
输入参数: req: 写操作时指向待写入的Modbus帧数据缓冲区(高字节在前);读操作时无意义
|
||
pucRegBuffer: 读操作时指向存储返回数据的缓冲区;写操作时无意义
|
||
usAddress: 保持寄存器起始地址(16位,基于HOLD_REG_START的偏移)
|
||
usNRegs: 需要读写的保持寄存器数量(16位寄存器个数)
|
||
eMode: 寄存器操作模式(MB_REG_READ-读寄存器,MB_REG_WRITE-写寄存器)
|
||
输出参数: -
|
||
返回说明: eMBErrorCode枚举值,MB_ENOERR表示操作成功,MB_ENOREG表示地址越界
|
||
其它说明: 1. 寄存器地址需满足HOLD_REG_START ≤ usAddress + usNRegs ≤ HOLD_REG_START + TOTAL_HOLD_ALL_REGS
|
||
2. 字节序遵循Modbus RTU标准:高字节在前,低字节在后
|
||
3. usRegHoldBuf为全局保持寄存器缓冲区,存储所有可读写配置参数
|
||
*****************************************************************/
|
||
eMBErrorCode mb_reg_holding_cb (rt_uint8_t *req, rt_uint8_t *pucRegBuffer, rt_uint16_t usAddress,
|
||
rt_uint16_t usNRegs, eMBRegisterMode eMode)
|
||
{
|
||
eMBErrorCode eStatus = MB_ENOERR;
|
||
rt_uint16_t iRegIndex;
|
||
rt_uint8_t highByte, lowByte; // 定义字节变量用于打印
|
||
if ((usAddress >= HOLD_REG_START)
|
||
&& (usAddress + usNRegs <= HOLD_REG_START + TOTAL_HOLD_ALL_REGS)) {
|
||
iRegIndex = usAddress - HOLD_REG_START;
|
||
switch (eMode) {
|
||
/* read current register values from the protocol stack. */
|
||
case MB_REG_READ:
|
||
while (usNRegs > 0) {
|
||
*pucRegBuffer++ = (rt_uint8_t)(usRegHoldBuf[iRegIndex] >> BITS_UCHAR);
|
||
*pucRegBuffer++ = (rt_uint8_t)(usRegHoldBuf[iRegIndex] & 0xFF);
|
||
// highByte = (rt_uint8_t)(usRegHoldBuf[iRegIndex] >> BITS_UCHAR);
|
||
// lowByte = (rt_uint8_t)(usRegHoldBuf[iRegIndex] & 0xFF);
|
||
// rt_kprintf("Reg[%d] = 0x%04X, High:0x%02X, Low:0x%02X\r\n",
|
||
// iRegIndex, usRegHoldBuf[iRegIndex], highByte, lowByte);
|
||
iRegIndex++;
|
||
usNRegs--;
|
||
}
|
||
break;
|
||
|
||
/* write current register values with new values from the protocol stack. */
|
||
case MB_REG_WRITE:
|
||
while (usNRegs > 0) {
|
||
// usRegHoldBuf[iRegIndex] = *req++ << BITS_UCHAR;
|
||
// usRegHoldBuf[iRegIndex] |= *req++;
|
||
// iRegIndex++;
|
||
usNRegs--;
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
eStatus = MB_ENOREG;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: mb_reg_test_cb
|
||
函数描述: Modbus测试寄存器读写回调函数,实现16位保持寄存器与8位字节流的双向转换,
|
||
支持读寄存器(返回配置参数)和写寄存器(存储主机下发的配置指令)
|
||
输入参数: req: 写操作时指向待写入的Modbus帧数据缓冲区(高字节在前);读操作时无意义
|
||
pucRegBuffer: 读操作时指向存储返回数据的缓冲区;写操作时无意义
|
||
usAddress: 保持寄存器起始地址(16位,基于HOLD_REG_START的偏移)
|
||
usNRegs: 需要读写的保持寄存器数量(16位寄存器个数)
|
||
eMode: 寄存器操作模式(MB_REG_READ-读寄存器,MB_REG_WRITE-写寄存器)
|
||
输出参数: -
|
||
返回说明: eMBErrorCode枚举值,MB_ENOERR表示操作成功,MB_ENOREG表示地址越界
|
||
其它说明: 1. 寄存器地址需满足HOLD_REG_START ≤ usAddress + usNRegs ≤ HOLD_REG_START + TOTAL_HOLD_REGS
|
||
2. 字节序遵循Modbus RTU标准:高字节在前,低字节在后
|
||
3. usRegHoldBuf为全局保持寄存器缓冲区,存储所有可读写配置参数
|
||
*****************************************************************/
|
||
eMBErrorCode mb_reg_test_cb (rt_uint8_t *req, rt_uint8_t *pucRegBuffer, rt_uint16_t usAddress,
|
||
rt_uint16_t usNRegs, eMBRegisterMode eMode)
|
||
{
|
||
eMBErrorCode eStatus = MB_ENOERR;
|
||
// rt_uint16_t iRegIndex;
|
||
|
||
// if ((usAddress >= TEST_REG_START)
|
||
// && (usAddress + usNRegs <= TEST_REG_START + TOTAL_TEST_REGS)) {
|
||
// iRegIndex = usAddress - TEST_REG_START;
|
||
// switch (eMode) {
|
||
// /* read current register values from the protocol stack. */
|
||
// case MB_REG_READ:
|
||
// while (usNRegs > 0) {
|
||
// *pucRegBuffer++ = (rt_uint8_t)(usRegTestBuf[iRegIndex] >> BITS_UCHAR);
|
||
// *pucRegBuffer++ = (rt_uint8_t)(usRegTestBuf[iRegIndex] & 0xFF);
|
||
// iRegIndex++;
|
||
// usNRegs--;
|
||
// }
|
||
// break;
|
||
// /* write current register values with new values from the protocol stack. */
|
||
// case MB_REG_WRITE:
|
||
// while (usNRegs > 0) {
|
||
// usRegTestBuf[iRegIndex] = *req++ << BITS_UCHAR;
|
||
// usRegTestBuf[iRegIndex] |= *req++;
|
||
// iRegIndex++;
|
||
// usNRegs--;
|
||
// }
|
||
// break;
|
||
// }
|
||
// } else {
|
||
// eStatus = MB_ENOREG;
|
||
// }
|
||
|
||
return eStatus;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: mb_ymodem_updata_cb
|
||
*****************************************************************/
|
||
eMBErrorCode mb_ymodem_updata_cb (rt_uint8_t *state, rt_uint8_t *pucRegBuffer)
|
||
{
|
||
if ((state == RT_NULL) || (pucRegBuffer == RT_NULL)) {
|
||
return MB_EINVAL;
|
||
}
|
||
/* Ymodem_No的枚举值为0xFF,不能用只覆盖0和1的switch生成响应。 */
|
||
pucRegBuffer[0] = (*state == Ymodem_No) ? ACK_C : ACK;
|
||
return MB_ENOERR;
|
||
}
|
||
|
||
eMBErrorCode mb_reg_coils_cb (rt_uint8_t *req, rt_uint8_t *pucRegBuffer, rt_uint16_t usAddress,
|
||
rt_uint16_t usNCoils, eMBRegisterMode eMode)
|
||
{
|
||
eMBErrorCode eStatus = MB_ENOERR;
|
||
rt_uint16_t iRegIndex = 0, iRegBitIndex = 0, iNReg = 0;
|
||
|
||
iNReg = (usNCoils+BITS_UCHAR-1)/BITS_UCHAR;
|
||
|
||
if ((usAddress >= COIL_REG_START) && (usAddress + usNCoils <= COIL_REG_START + TOTAL_COIL_REGS)) {
|
||
iRegIndex = (rt_uint16_t)(usAddress - COIL_REG_START) / BITS_UCHAR;
|
||
iRegBitIndex = (rt_uint16_t)(usAddress - COIL_REG_START) % BITS_UCHAR;
|
||
switch (eMode) {
|
||
/* read current coil values from the protocol stack. */
|
||
case MB_REG_READ:
|
||
while (iNReg > 0) {
|
||
*pucRegBuffer++ = mb_get_bits(&ucCoilBuf[iRegIndex++], iRegBitIndex, BITS_UCHAR);
|
||
iNReg--;
|
||
}
|
||
pucRegBuffer--;
|
||
/* last coils */
|
||
usNCoils = usNCoils % BITS_UCHAR;
|
||
/* filling zero to high bit */
|
||
*pucRegBuffer = *pucRegBuffer << (BITS_UCHAR - usNCoils);
|
||
*pucRegBuffer = *pucRegBuffer >> (BITS_UCHAR - usNCoils);
|
||
break;
|
||
|
||
/* write current coil values with new values from the protocol stack. */
|
||
case MB_REG_WRITE:
|
||
while (iNReg > 1) {
|
||
mb_set_bits(&ucCoilBuf[iRegIndex++], iRegBitIndex, BITS_UCHAR, *req++);
|
||
iNReg--;
|
||
}
|
||
/* last coils */
|
||
usNCoils = usNCoils % BITS_UCHAR;
|
||
/* mb_set_bits has bug when ucNBits is zero */
|
||
if (usNCoils != 0) {
|
||
mb_set_bits(&ucCoilBuf[iRegIndex++], iRegBitIndex, usNCoils, *req++);
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
eStatus = MB_ENOREG;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
eMBErrorCode mb_reg_discrete_cb (rt_uint8_t *pucRegBuffer, rt_uint16_t usAddress, rt_uint16_t usNDiscrete)
|
||
{
|
||
eMBErrorCode eStatus = MB_ENOERR;
|
||
rt_uint16_t iRegIndex , iRegBitIndex , iNReg;
|
||
|
||
iNReg = (usNDiscrete+BITS_UCHAR-1) / BITS_UCHAR;
|
||
|
||
if ((usAddress >= DISC_REG_START)
|
||
&& ((usAddress + usNDiscrete) <= (DISC_REG_START + TOTAL_DISC_REGS))) {
|
||
iRegIndex = (rt_uint16_t) (usAddress - DISC_REG_START) / BITS_UCHAR;
|
||
iRegBitIndex = (rt_uint16_t) (usAddress - DISC_REG_START) % BITS_UCHAR;
|
||
|
||
while (iNReg > 0) {
|
||
*pucRegBuffer++ = mb_get_bits(&ucDiscInBuf[iRegIndex++], iRegBitIndex, BITS_UCHAR);
|
||
iNReg--;
|
||
}
|
||
pucRegBuffer--;
|
||
/* last discrete */
|
||
usNDiscrete = usNDiscrete % BITS_UCHAR;
|
||
/* filling zero to high bit */
|
||
*pucRegBuffer = *pucRegBuffer << (BITS_UCHAR - usNDiscrete);
|
||
*pucRegBuffer = *pucRegBuffer >> (BITS_UCHAR - usNDiscrete);
|
||
} else {
|
||
eStatus = MB_ENOREG;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcReadCoils
|
||
函数描述: 读多个继电器
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
eMBException funcReadCoils (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
rt_uint16_t regAddress = 0, cntCoils = 0;
|
||
rt_uint8_t ucNBytes = 0;
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
if (4+MB_PDU_FUNC_READ_SIZE != reqLen) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
regAddress = u8v_to_u16(&reqFrame[2]);
|
||
cntCoils = u8v_to_u16(&reqFrame[4]);
|
||
|
||
if ((0 == cntCoils)||(cntCoils >= MB_PDU_FUNC_READ_COILCNT_MAX)) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
ucNBytes = (cntCoils+BITS_UCHAR-1)/BITS_UCHAR;
|
||
|
||
resFrame[1] = MB_READ_COILS;
|
||
resFrame[2] = ucNBytes;
|
||
eRegStatus = mb_reg_coils_cb(RT_NULL, &resFrame[3], regAddress, cntCoils, MB_REG_READ);
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
} else {
|
||
*resLen = 3+ucNBytes;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcReadDiscreteInputs
|
||
函数描述: 读离散输入
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
eMBException funcReadDiscreteInputs (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
rt_uint16_t regAddress = 0, cntDisCrete = 0;
|
||
rt_uint8_t ucNBytes = 0;
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
if (4+MB_PDU_FUNC_READ_SIZE != reqLen) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
regAddress = u8v_to_u16(&reqFrame[2]);
|
||
cntDisCrete = u8v_to_u16(&reqFrame[4]);
|
||
|
||
if ((0 == cntDisCrete)||(cntDisCrete >= MB_PDU_FUNC_READ_DISCCNT_MAX)) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
ucNBytes = (cntDisCrete+BITS_UCHAR-1)/BITS_UCHAR;
|
||
|
||
resFrame[1] = MB_READ_DISCRETE_INPUTS;
|
||
resFrame[2] = ucNBytes;
|
||
eRegStatus = mb_reg_discrete_cb(&resFrame[3], regAddress, cntDisCrete);
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
} else {
|
||
*resLen = 3+ucNBytes;
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcReadHoldingRegister
|
||
函数描述: 读保持寄存器
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
eMBException funcReadHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
rt_uint16_t regAddress = 0, cntReg = 0;
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
if (4+MB_PDU_FUNC_READ_SIZE != reqLen) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
regAddress = u8v_to_u16(&reqFrame[2]);
|
||
cntReg = u8v_to_u16(&reqFrame[4]);
|
||
|
||
if ((0 == cntReg)||(cntReg >= MB_PDU_FUNC_READ_REGCNT_MAX)) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
resFrame[1] = MB_READ_HOLDING_REGISTER;
|
||
resFrame[2] = cntReg * 2;
|
||
|
||
eRegStatus = mb_reg_holding_cb(RT_NULL, &resFrame[3], regAddress, cntReg, MB_REG_READ);
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
} else {
|
||
*resLen = 3+cntReg * 2;
|
||
}
|
||
rt_kprintf("------COM READ addr=%d count=%d-------\n", regAddress, cntReg);
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcReadInputRegister
|
||
函数描述: 处理Modbus功能码0x04,读取输入寄存器
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: resFrame:输入寄存器响应帧 resLen:响应帧长度
|
||
返回说明: MB_EX_NONE:读取成功 其他:Modbus异常码
|
||
其它说明: 请求地址和数量校验方式与功能码0x03保持一致;输入寄存器
|
||
数据从usRegInBuf读取,响应数据按高字节在前返回
|
||
*****************************************************************/
|
||
eMBException funcReadInputRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
rt_uint16_t regAddress = 0, cntReg = 0;
|
||
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
if (4+MB_PDU_FUNC_READ_SIZE != reqLen) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
regAddress = u8v_to_u16(&reqFrame[2]);
|
||
cntReg = u8v_to_u16(&reqFrame[4]);
|
||
|
||
if ((0 == cntReg)||(cntReg >= MB_PDU_FUNC_READ_REGCNT_MAX)) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
resFrame[1] = MB_READ_INPUT_REGISTER;
|
||
resFrame[2] = cntReg * 2;
|
||
|
||
eRegStatus = mb_reg_input_cb(&resFrame[3], regAddress, cntReg);
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
} else {
|
||
*resLen = 3+cntReg*2;
|
||
}
|
||
|
||
rt_kprintf("------COM READ INPUT addr=%d count=%d-------\n", regAddress, cntReg);
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcReadTestRegister
|
||
函数描述: 读测试寄存器
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
//eMBException funcReadTestRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
// rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
//{
|
||
// rt_uint16_t regAddress = 0,regCH=0, cntReg = 0; //读取的寄存器地址和寄存器数量
|
||
|
||
// eMBException eStatus = MB_EX_NONE;
|
||
// eMBErrorCode eRegStatus;
|
||
|
||
// if (4+MB_PDU_FUNC_READ_SIZE != reqLen) {
|
||
// return MB_EX_ILLEGAL_DATA_VALUE;
|
||
// }
|
||
// regAddress = reqFrame[3];
|
||
// regCH = reqFrame[4];
|
||
// cntReg = reqFrame[5];
|
||
// regAddress = (reqFrame[3]==1) ? 0 : 2;
|
||
//
|
||
// regAddress = regAddress+regCH*TOTAL_TEST_CH_REGS;
|
||
//
|
||
//
|
||
// if ((0 == cntReg)||(cntReg >= MB_PDU_FUNC_READ_REGCNT_MAX)) {
|
||
// return MB_EX_ILLEGAL_DATA_VALUE;
|
||
// }
|
||
|
||
// resFrame[1] = MB_READ_TEST_REGISTER;
|
||
// resFrame[2] = cntReg * 2;
|
||
|
||
// eRegStatus = mb_reg_test_cb(RT_NULL, &resFrame[3], regAddress, cntReg, MB_REG_READ);
|
||
// rt_kprintf("");
|
||
// if (eRegStatus != MB_ENOERR) {
|
||
// eStatus = mb_error(eRegStatus);
|
||
// } else {
|
||
// *resLen = 3+cntReg*2;
|
||
// }
|
||
|
||
// return eStatus;
|
||
//}
|
||
|
||
// /*****************************************************************
|
||
// 函数名称: funcWriteCoil
|
||
// 函数描述: 写单个保持继电器
|
||
// 输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
// 输出参数: -
|
||
// 返回说明: -
|
||
// 其它说明: -
|
||
// *****************************************************************/
|
||
eMBException funcWriteCoil (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
// rt_uint16_t regAddress = 0, value = 0;
|
||
// rt_uint8_t ucBuf[2] = {0};
|
||
|
||
// eMBException eStatus = MB_EX_NONE;
|
||
// eMBErrorCode eRegStatus;
|
||
|
||
// if (4+MB_PDU_FUNC_WRITE_SIZE != reqLen) {
|
||
// return MB_EX_ILLEGAL_DATA_VALUE;
|
||
// }
|
||
|
||
// regAddress = u8v_to_u16(&reqFrame[2]);
|
||
// value = u8v_to_u16(&reqFrame[4]);
|
||
|
||
// if (0x0000 == value) {
|
||
// ucBuf[0] = 0;
|
||
// ucBuf[1] = 0;
|
||
// } else if (0xFF00 == value) {
|
||
// ucBuf[0] = 1;
|
||
// ucBuf[1] = 0;
|
||
// } else {
|
||
// return MB_EX_ILLEGAL_DATA_VALUE;
|
||
// }
|
||
// //判断地址
|
||
// rt_uint16_t reg_offset = regAddress-COIL_REG_START;
|
||
// //提取通道
|
||
// eIDX_SOU_CH ch = (eIDX_SOU_CH)(reg_offset/TOTAL_COIL_CH_REGS);
|
||
// a = ch;
|
||
// //判断寄存器偏移量
|
||
// rt_uint16_t sout_reg = reg_offset%TOTAL_COIL_CH_REGS;
|
||
|
||
// if (ch > IDX_SOU_CH4) {
|
||
// rt_kprintf("[funcWriteCoil] ch out of range! ch=%d, max=%d\n", ch, IDX_SOU_CH4);
|
||
// return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
// }
|
||
// // 校验sout_reg是否在合法线圈寄存器范围
|
||
// if (sout_reg > MODBUS_COIL_SRC_CC) {
|
||
// rt_kprintf("[funcWriteCoil] sout_reg out of range! reg=%d\n", sout_reg);
|
||
// return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
// }
|
||
|
||
// //功能实现
|
||
// if(value==0x0000){
|
||
// switch(sout_reg){
|
||
// case MODBUS_COIL_PWR_SRC:
|
||
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_REL_ON, ALL_REL_OFF);
|
||
// rt_kprintf("THE_ALL_REL_OFF");
|
||
// break;
|
||
// case MODBUS_COIL_PWR_SINK:
|
||
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_REL_ON, ALL_REL_OFF);
|
||
// rt_kprintf("THE_ALL_REL_OFF");
|
||
// break;
|
||
// case MODBUS_COIL_PWR_ALL:
|
||
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_REL_ON, ALL_REL_OFF);
|
||
// rt_kprintf("THE_ALL_REL_OFF");
|
||
// break;
|
||
// case MODBUS_COIL_SINK_CC:
|
||
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,4);
|
||
// rt_kprintf("CC_SINK_OFF");
|
||
// break;
|
||
// case MODBUS_COIL_SRC_CC:
|
||
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,4);
|
||
// rt_kprintf("CC_SRC_OFF");
|
||
// break;
|
||
// default:
|
||
// return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
// break;
|
||
// }
|
||
// }
|
||
// else{
|
||
// switch(sout_reg){
|
||
// case MODBUS_COIL_PWR_SRC:
|
||
// chrg_set_sou_reg(ch, REG_REL_ON, MAIN_REL_ON);
|
||
// rt_kprintf("THE_Main_REL_ON");
|
||
// break;
|
||
// case MODBUS_COIL_PWR_SINK:
|
||
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_REL_ON, FU_REL_ON);
|
||
// rt_kprintf("THE_Main_REL_ON");
|
||
// break;
|
||
// case MODBUS_COIL_PWR_ALL:
|
||
// chrg_set_sou_reg((eIDX_SOU_CH)ch, REG_REL_ON, ALL_REL_ON);
|
||
// rt_kprintf("THE_Main_REL_ON");
|
||
// break;
|
||
// case MODBUS_COIL_SINK_CC:
|
||
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,ID_SINK);
|
||
// rt_kprintf("CC_SINK_ON");
|
||
// break;
|
||
// case MODBUS_COIL_SRC_CC:
|
||
// chrg_nor_sw_rel((eIDX_NOR_CH)ch,ID_SOURCE);
|
||
// rt_kprintf("CC_SRC_ON");
|
||
// break;
|
||
// default:
|
||
// return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
// break;
|
||
// }
|
||
// }
|
||
// // rt_memcpy(resFrame, reqFrame, reqLen);
|
||
// *resLen = reqLen-2;
|
||
|
||
// eRegStatus = mb_reg_coils_cb(RT_NULL, ucBuf, regAddress, 1, MB_REG_WRITE);
|
||
// if (eRegStatus != MB_ENOERR) {
|
||
// eStatus = mb_error(eRegStatus);
|
||
// }
|
||
return 0;
|
||
// return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
* 负载协议映射表: Modbus寄存器值 → 内部负载协议枚举
|
||
*****************************************************************/
|
||
typedef struct {
|
||
rt_uint16_t reg_val; // Modbus寄存器写入值
|
||
rt_uint16_t sink_pro; // 内部负载协议枚举 (eLOAD_PRO)
|
||
rt_uint8_t has_group; // 是否有组号 (PDO/PPS/PD3.1/UFCS/AVS)
|
||
} SinkProtoMap_t;
|
||
|
||
static const SinkProtoMap_t sink_proto_map[] = {
|
||
{0x10, LOAD_PRO_NONE, 0}, // 无协议
|
||
{0x11, LOAD_PRO_QC20, 0}, // QC2.0
|
||
{0x12, LOAD_PRO_QC30, 0}, // QC3.0
|
||
{0x13, LOAD_PRO_FCP, 0}, // FCP
|
||
{0x14, LOAD_PRO_AFC, 0}, // AFC
|
||
{0x15, LOAD_PRO_SCP, 0}, // SCP
|
||
{0x16, LOAD_PRO_VIVO, 0}, // VIVO
|
||
{0x17, LOAD_PRO_TFC_RFC, 0}, // TFC(传音)
|
||
{0x19, LOAD_PRO_PDO_AUTO, 1}, // PDO自动
|
||
{0x1B, LOAD_PRO_PPS_AUTO, 1}, // PPS自动
|
||
{0x1C, LOAD_PRO_PD31AVS, 1}, // PD3.1
|
||
{0x1D, LOAD_PRO_UFCS, 1}, // UFCS
|
||
{0x1F, LOAD_PRO_AVS, 1}, // AVS
|
||
};
|
||
#define SINK_PROTO_MAP_NUM (sizeof(sink_proto_map) / sizeof(sink_proto_map[0]))
|
||
|
||
/*****************************************************************
|
||
* 电源协议映射表: Modbus寄存器值 → 内部电源协议枚举
|
||
*****************************************************************/
|
||
typedef struct {
|
||
rt_uint16_t reg_val;
|
||
rt_uint16_t src_pro; // 内部电源协议枚举 (eSRC_PRO)
|
||
} SrcProtoMap_t;
|
||
|
||
static const SrcProtoMap_t src_proto_map[] = {
|
||
{0x20, PRO_NONE}, // 标准协议(无快充)
|
||
{0x2A, PRO_PD0PSS}, // PD
|
||
{0x22, PRO_FCP}, // FCP
|
||
{0x23, PRO_SCPB}, // SCPB
|
||
{0x27, PRO_QC30}, // QC3.0
|
||
{0x28, PRO_QC20}, // QC2.0
|
||
{0x29, PRO_AFC}, // AFC
|
||
{0x2C, PRO_UFCS}, // UFCS
|
||
};
|
||
#define SRC_PROTO_MAP_NUM (sizeof(src_proto_map) / sizeof(src_proto_map[0]))
|
||
|
||
/*****************************************************************
|
||
* 查找负载协议映射
|
||
*****************************************************************/
|
||
static rt_uint16_t sink_proto_lookup(rt_uint16_t reg_val, rt_uint8_t *has_group)
|
||
{
|
||
for (int i = 0; i < SINK_PROTO_MAP_NUM; i++) {
|
||
if (sink_proto_map[i].reg_val == reg_val) {
|
||
*has_group = sink_proto_map[i].has_group;
|
||
return sink_proto_map[i].sink_pro;
|
||
}
|
||
}
|
||
return LOAD_PRO_NONE; // 默认无协议
|
||
|
||
}
|
||
|
||
/*****************************************************************
|
||
* 查找电源协议映射
|
||
*****************************************************************/
|
||
static rt_uint16_t src_proto_lookup(rt_uint16_t reg_val)
|
||
{
|
||
for (int i = 0; i < SRC_PROTO_MAP_NUM; i++) {
|
||
if (src_proto_map[i].reg_val == reg_val) {
|
||
return src_proto_map[i].src_pro;
|
||
}
|
||
}
|
||
return PRO_NONE; // 默认无协议
|
||
}
|
||
|
||
/*****************************************************************
|
||
* 查找电源类型
|
||
*****************************************************************/
|
||
static rt_uint16_t src_type_lookup(rt_uint16_t reg_val,struct chrg_switch_t *pSW)
|
||
{
|
||
switch(reg_val){
|
||
case 0:
|
||
pSW->source.src = 0;
|
||
pSW->source.pps = 0;
|
||
break;
|
||
case 1:
|
||
pSW->source.src = 0;
|
||
pSW->source.pps = 1;
|
||
break;
|
||
case 2:
|
||
pSW->source.src = 3;
|
||
pSW->source.pps = 0;
|
||
break;
|
||
case 3:
|
||
pSW->source.src = 3;
|
||
pSW->source.pps = 1;
|
||
break;
|
||
default:
|
||
return 1;
|
||
break;
|
||
}
|
||
return 0;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: funcWriteHoldingRegister
|
||
函数描述: 写单个保持寄存器(0x06功能码)
|
||
根据寄存器地址自动识别负载/电源/系统区块,直接操作对应通道结构体
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: MB_EX_NONE=成功, 其他=Modbus异常码
|
||
其它说明: 新协议采用标准0x06功能码,寄存器地址区分功能区块:
|
||
0x0000~0x0073 负载, 0x0080~0x0097 电源, 0x00A0~0x00A7 系统
|
||
*****************************************************************/
|
||
eMBException funcWriteHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
struct chrg_north_t *pNOR = &chrgnorth;
|
||
struct chrg_south_t *pSOU = &chrgsouth;
|
||
struct chrg_switch_t *pSW = RT_NULL;
|
||
rt_uint8_t regAddress = 0;
|
||
rt_uint8_t regAddress_H = 0;
|
||
rt_uint16_t sou_value = 0;
|
||
/* ---- 提取通道号和区块内偏移 ---- */
|
||
eIDX_SOU_CH ch;
|
||
rt_uint16_t reg_offset; // 区块内寄存器偏移
|
||
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
if (4 + MB_PDU_FUNC_WRITE_SIZE != reqLen) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
regAddress_H = reqFrame[2];
|
||
regAddress = reqFrame[3];
|
||
sou_value = u8v_to_u16(&reqFrame[4]);
|
||
|
||
/* ---- 特殊地址: 系统复位 ---- */
|
||
if (regAddress_H == RESET_NORTH_ADDR && sou_value == RESET_NORTH_VALUE) {
|
||
__asm__ __volatile__("cpsid i");
|
||
__asm__ __volatile__("ldr r0, =0xE000ED0C");
|
||
__asm__ __volatile__("ldr r1, =0x05FA0004");
|
||
__asm__ __volatile__("str r1, [r0]");
|
||
while (1);
|
||
}
|
||
|
||
/* ---- 特殊地址: 南向板复位 ---- */
|
||
if (regAddress_H == RESET_SOUTH_ADDR) {
|
||
if (sou_value < 4) {
|
||
chrg_set_sou_reg((eIDX_SOU_CH)sou_value, RESET_SOUTH_VALUE, 0x01);
|
||
} else {
|
||
rt_kprintf("Value_Error\n");
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
goto build_response;
|
||
}
|
||
|
||
/* ---- 地址范围校验 ---- */
|
||
if(regAddress_H == HOLD_REG_SINK_BASE){
|
||
if (REG_IS_SINK(regAddress)) {
|
||
ch = (eIDX_SOU_CH)REG_GET_SINK_CH(regAddress);
|
||
reg_offset = REG_GET_SINK_OFFSET(regAddress);
|
||
}
|
||
}else if (regAddress_H == HOLD_REG_SRC_BASE) {
|
||
if (REG_IS_SRC(regAddress)) {
|
||
ch = (eIDX_SOU_CH)REG_GET_SRC_CH(regAddress);
|
||
reg_offset = REG_GET_SRC_OFFSET(regAddress);
|
||
}
|
||
} else if (regAddress_H == HOLD_REG_SYS_BASE) {
|
||
if (REG_IS_SYS(regAddress)) {
|
||
ch = (eIDX_SOU_CH)REG_GET_SYS_CH(regAddress);
|
||
reg_offset = REG_GET_SYS_OFFSET(regAddress);
|
||
}
|
||
} else {
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
|
||
if (ch > IDX_SOU_CH4) {
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
|
||
pSW = &g_shadow_sw[ch]; // 写入影子寄存器, 等待 PARAM_EFFECT 生效
|
||
chrg_shadow_sync(ch); // 首次写同步真实状态,防止覆盖运行标志位
|
||
/* ===== 直接根据高字节分发到对应业务函数 ===== */
|
||
if (regAddress_H == HOLD_REG_SINK_BASE)
|
||
{
|
||
chrg_mb_write_sink(ch, reg_offset, sou_value, pSW);
|
||
}
|
||
else if (regAddress_H == HOLD_REG_SRC_BASE)
|
||
{
|
||
chrg_mb_write_src(ch, reg_offset, sou_value, pSW);
|
||
}
|
||
else if (regAddress_H == HOLD_REG_SYS_BASE)
|
||
{
|
||
eStatus = chrg_mb_write_sys(ch, reg_offset, sou_value, pSW, pNOR, pSOU);
|
||
if (eStatus != MB_EX_NONE) {
|
||
return eStatus;
|
||
}
|
||
}
|
||
|
||
build_response:
|
||
/* ---- 构建响应帧 ---- */
|
||
rt_memcpy(resFrame, reqFrame, reqLen);
|
||
*resLen = reqLen - 2;
|
||
|
||
/* ---- 写入保持寄存器镜像 ---- */
|
||
eRegStatus = mb_reg_holding_cb(&reqFrame[4], &resFrame[3], regAddress, 1, MB_REG_WRITE);
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcWriteMultipleCoils
|
||
函数描述: 写多个继电器
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
eMBException funcWriteMultipleCoils (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
rt_uint16_t regAddress = 0, cntCoils = 0;
|
||
rt_uint8_t ucByteCount;
|
||
rt_uint8_t ucByteCountVerify;
|
||
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
if (4+MB_PDU_FUNC_WRITE_SIZE > reqLen) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
regAddress = u8v_to_u16(&reqFrame[2]);
|
||
cntCoils = u8v_to_u16(&reqFrame[4]);
|
||
ucByteCount = reqFrame[6];
|
||
ucByteCountVerify = (cntCoils+BITS_UCHAR-1)/BITS_UCHAR;
|
||
|
||
if ((0 == cntCoils)
|
||
||(cntCoils > MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX)
|
||
||(ucByteCountVerify != ucByteCount)) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
rt_memcpy(resFrame, reqFrame, reqLen);
|
||
*resLen = 6;
|
||
|
||
eRegStatus = mb_reg_coils_cb(reqFrame, &resFrame[3], regAddress, cntCoils, MB_REG_WRITE);
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
}
|
||
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcWriteMultipleHoldingRegister
|
||
函数描述: 写多个保持寄存器(0x10)
|
||
规则:reqFrame[2] = 分区高字节(SINK/SRC/SYS),reqFrame[3] = 分区内起始偏移
|
||
不再做全局保持寄存器地址越界判断,仅做通道合法校验,写入影子寄存器
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
*****************************************************************/
|
||
eMBException funcWriteMultipleHoldingRegister (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
struct chrg_north_t *pNOR = &chrgnorth;
|
||
struct chrg_south_t *pSOU = &chrgsouth;
|
||
struct chrg_switch_t *pSW = RT_NULL;
|
||
|
||
// 公共变量(两种模式共用)
|
||
rt_uint16_t cntReg = 0;
|
||
rt_uint8_t ucRegByteCount;
|
||
rt_uint8_t *pWriteData = RT_NULL;
|
||
eMBException eStatus = MB_EX_NONE;
|
||
eMBErrorCode eRegStatus;
|
||
|
||
// 模式私有变量
|
||
#if TRIM_NORTH
|
||
rt_uint16_t regAddress = 0;
|
||
#else
|
||
rt_uint8_t regAddress_H = 0;
|
||
rt_uint16_t regBaseOffset = 0;
|
||
#endif
|
||
|
||
// ========== 公共校验逻辑(只写一遍)==========
|
||
if (4 + MB_PDU_FUNC_WRITE_MUL_SIZE_MIN > reqLen) {
|
||
rt_kprintf("LEN ERROR");
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
cntReg = u8v_to_u16(&reqFrame[4]);
|
||
ucRegByteCount = reqFrame[6];
|
||
if ((0 == cntReg)
|
||
|| (cntReg > MB_PDU_FUNC_WRITE_MUL_COILCNT_MAX)
|
||
|| (ucRegByteCount != cntReg * 2))
|
||
{
|
||
rt_kprintf("DATA ERROR");
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
pWriteData = &reqFrame[7];
|
||
|
||
// ========== 分支业务逻辑 ==========
|
||
#if TRIM_NORTH
|
||
// 校准模式:旧连续地址逻辑
|
||
regAddress = u8v_to_u16(&reqFrame[2]);
|
||
if (regAddress < HOLD_REG_START || (regAddress + cntReg - 1) >= (HOLD_REG_START + TOTAL_HOLD_ALL_REGS)) {
|
||
rt_kprintf("ADDRESS ERROR");
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
|
||
for (int i = 0; i < cntReg; i++)
|
||
{
|
||
rt_uint16_t Curr_reg_addr = regAddress + i;
|
||
rt_uint16_t Curr_reg_offset = Curr_reg_addr - HOLD_REG_START;
|
||
eIDX_SOU_CH ch = (eIDX_SOU_CH)(Curr_reg_offset / TOTAL_HOLD_CH_REGS);
|
||
|
||
if (ch > IDX_SOU_CH4) {
|
||
rt_kprintf("WRITE MULTI REG: CH ERROR, ch=%d (reg=0x%04X)\n", ch, Curr_reg_addr);
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
if (pNOR == RT_NULL) {
|
||
rt_kprintf("WRITE MULTI REG: ERROR: pNOR is NULL! (reg=0x%04X)\n", Curr_reg_addr);
|
||
return MB_EX_SLAVE_DEVICE_FAILURE;
|
||
}
|
||
pSW = &pNOR->sw[ch];
|
||
rt_uint16_t sou_value = u8v_to_u16(&pWriteData[i * 2]);
|
||
rt_uint16_t sout_reg = Curr_reg_offset % TOTAL_HOLD_CH_REGS;
|
||
|
||
switch (sout_reg)
|
||
{
|
||
case REG_VOLTAGE_OUT + 1:
|
||
pSW->source.vc.set_voltage = sou_value;
|
||
pSW->sink.loadv = sou_value;
|
||
chrg_set_sou_reg(ch, sout_reg, sou_value);
|
||
rt_kprintf("voltage=%d", sou_value);
|
||
break;
|
||
case REG_CURRENT_OUT + 1:
|
||
pSW->source.vc.set_current = sou_value;
|
||
pSW->sink.loadc = sou_value;
|
||
chrg_set_sou_reg(ch, sout_reg, sou_value);
|
||
break;
|
||
default:
|
||
rt_kprintf("THE SOUT_REG=%d", sout_reg);
|
||
break;
|
||
}
|
||
rt_kprintf("ch=:%d,reg=%02x,value=%02x", ch, sout_reg, sou_value);
|
||
}
|
||
#else
|
||
// 正常产品模式:分区地址逻辑
|
||
regAddress_H = reqFrame[2];
|
||
regBaseOffset = reqFrame[3];
|
||
|
||
for (int i = 0; i < cntReg; i++)
|
||
{
|
||
rt_uint16_t curr_part_offset = regBaseOffset + i;
|
||
eIDX_SOU_CH ch = (eIDX_SOU_CH)(curr_part_offset / TOTAL_HOLD_CH_REGS);
|
||
rt_uint16_t ch_reg_offset = curr_part_offset % TOTAL_HOLD_CH_REGS;
|
||
|
||
if (ch > IDX_SOU_CH4) {
|
||
//rt_kprintf("WRITE MULTI REG: CH ERROR, ch=%d (part offset=%d)\n", ch, curr_part_offset);
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
pSW = &g_shadow_sw[ch];
|
||
chrg_shadow_sync(ch); // 首次写同步真实状态
|
||
rt_uint16_t sou_value = u8v_to_u16(&pWriteData[i * 2]);
|
||
|
||
if (regAddress_H == HOLD_REG_SINK_BASE)
|
||
{
|
||
chrg_mb_write_sink(ch, ch_reg_offset, sou_value, pSW);
|
||
}
|
||
else if (regAddress_H == HOLD_REG_SRC_BASE)
|
||
{
|
||
chrg_mb_write_src(ch, ch_reg_offset, sou_value, pSW);
|
||
}
|
||
else if (regAddress_H == HOLD_REG_SYS_BASE)
|
||
{
|
||
eStatus = chrg_mb_write_sys(ch, ch_reg_offset, sou_value, pSW, pNOR, pSOU);
|
||
if (eStatus != MB_EX_NONE) {
|
||
return eStatus;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//rt_kprintf("WRITE MULTI REG: INVALID PARTITION 0x%02X\n", regAddress_H);
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
// rt_kprintf("ch:%d, part_offset:%d, ch_reg:%d, val:%d\n", ch, curr_part_offset, ch_reg_offset, sou_value);
|
||
}
|
||
#endif
|
||
|
||
// ========== 公共应答帧组装、回调(只写一遍)==========
|
||
rt_memcpy(resFrame, reqFrame, reqLen);
|
||
*resLen = 6;
|
||
|
||
#if TRIM_NORTH
|
||
eRegStatus = mb_reg_holding_cb(&reqFrame[7], &resFrame[3], regAddress, cntReg, MB_REG_WRITE);
|
||
#else
|
||
eRegStatus = mb_reg_holding_cb(&reqFrame[7], &resFrame[3], regBaseOffset, cntReg, MB_REG_WRITE);
|
||
#endif
|
||
|
||
if (eRegStatus != MB_ENOERR) {
|
||
eStatus = mb_error(eRegStatus);
|
||
}
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funcTrimELoad
|
||
函数描述: 校准源载
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -只支持协议寄存器
|
||
*****************************************************************/
|
||
eMBException funcTrimELoad (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
struct chrg_north_t *pNOR = &chrgnorth;
|
||
rt_uint16_t regAddress = 0;
|
||
rt_uint8_t RegCount;
|
||
|
||
eMBException eStatus = MB_EX_NONE;
|
||
|
||
if (4+MB_PDU_FUNC_WRITE_MUL_SIZE_MIN > reqLen) {
|
||
rt_kprintf("LEN ERROR");
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
//计算寄存器地址,数量和字节
|
||
eIDX_SOU_CH ch = reqFrame[2]&0x0F;
|
||
rt_uint8_t enable_flag = (reqFrame[2]>>4)&0x0F;
|
||
regAddress = reqFrame[3];
|
||
RegCount = u8v_to_u16(&reqFrame[4]);
|
||
rt_kprintf("ch=%d,enable=%d,reg=0x%02x,count=%d\n",ch+1,enable_flag,regAddress,RegCount);
|
||
if(ch >= TOTAL_SOU_CHS){
|
||
rt_kprintf("CH ERROR");
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
if(enable_flag == 0x00 || enable_flag == 0x01){
|
||
}else{
|
||
rt_kprintf("Enable Flag ERROR");
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
//提取首指针
|
||
struct chrg_trim *pTrim = &pNOR->trim[ch];
|
||
rt_uint8_t *pWriteData = &reqFrame[7];
|
||
pTrim->trim_enable_flag = enable_flag;
|
||
pTrim->trim_mode = 0;
|
||
//多寄存器控制操作
|
||
for(int i =0;i<RegCount;i++){
|
||
//计算当前寄存器的协议地址和本地偏移
|
||
rt_uint16_t Curr_reg_addr = regAddress + i; // 当前寄存器协议地址
|
||
rt_uint16_t Curr_write_value = u8v_to_u16(pWriteData+i*2);//寄存器数值
|
||
switch (Curr_reg_addr)
|
||
{
|
||
case ModbusRTU_Volt_CAL_K_H:
|
||
pTrim->trim_mode = MODE_VOLT_CAL;
|
||
pTrim->trim_volt_k_H = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_Volt_CAL_K_L:
|
||
pTrim->trim_volt_k_L = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_Volt_CAL_B:
|
||
pTrim->trim_volt_b = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_Current_CAL_K_H:
|
||
pTrim->trim_mode = MODE_CURR_CAL;
|
||
pTrim->trim_curr_k_H = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_Current_CAL_K_L:
|
||
pTrim->trim_curr_k_L = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_Current_CAL_B:
|
||
pTrim->trim_curr_b = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_SourceCurrent_CAL_K_H:
|
||
pTrim->trim_mode = MODE_SOURCE_CURR_CAL;
|
||
pTrim->trim_source_curr_k_H = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_SourceCurrent_CAL_K_L:
|
||
pTrim->trim_source_curr_k_L = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_SourceCurrent_CAL_B:
|
||
pTrim->trim_source_curr_b = Curr_write_value;
|
||
break;
|
||
case ModbusRTU_Trim_Zero_CAL:
|
||
pTrim->trim_mode = MODE_ZERO_CAL;
|
||
pTrim->trim_zero = Curr_write_value;
|
||
break;
|
||
default:
|
||
rt_kprintf("THE CAL REG=%d",Curr_reg_addr);
|
||
break;
|
||
}
|
||
//寄存器操作
|
||
rt_kprintf("com ch=:%d,reg=%02x,value=%d\n",ch,Curr_reg_addr,Curr_write_value);
|
||
}
|
||
if (pTrim->trim_mode == 0) {
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
|
||
/*
|
||
* COM只负责解析校准数据。完整校准序列由南向模块组装并加入RUN_COM队列,
|
||
* 目标通道由队列项确定,因此无需关闭任何南向通道的正常轮询使能。
|
||
*/
|
||
if (chrg_sou_trim_submit(ch, pTrim) != 0) {
|
||
return MB_EX_SLAVE_BUSY;
|
||
}
|
||
rt_memcpy(resFrame, reqFrame, reqLen);
|
||
*resLen = 6;
|
||
return eStatus;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: funYmodemUpadata
|
||
函数描述: 南向功率板升级函数
|
||
输入参数: reqFrame: 请求数据 reqLen: 请求长度 resFrame:响应数据 resLen:响应长度
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
|
||
Ymodem_state ystate = Ymodem_No;
|
||
rt_uint8_t s_first_soh_rcvd = 0;
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_protocol_reset
|
||
函数描述: 清空COM侧Ymodem拼帧、重发和握手状态
|
||
输入参数: -
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: 不改变南向轮询模式和目标通道
|
||
*****************************************************************/
|
||
static void chrg_ymodem_protocol_reset(void)
|
||
{
|
||
rt_memset(mobude_ymodem_buf, 0, sizeof(mobude_ymodem_buf));
|
||
rt_memset(mobude_ymodem_buf_2, 0, sizeof(mobude_ymodem_buf_2));
|
||
g_ymodem_rx_len = 0;
|
||
g_ymodem_expected_len = 0;
|
||
g_ymodem_last_len = 0;
|
||
g_ymodem_waiting_ack = 0;
|
||
g_ymodem_retry_count = 0;
|
||
g_ymodem_eot_done = 0;
|
||
g_ymodem_send_tick = 0;
|
||
g_ymodem_c_tick = rt_tick_get();
|
||
s_first_soh_rcvd = 0;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_session_start
|
||
函数描述: 停止普通南向请求并进入指定通道的升级透传模式
|
||
输入参数: ch:需要升级的南向功率板通道
|
||
输出参数: -
|
||
返回说明: 0:成功 <0:线程互斥锁未就绪或加锁失败
|
||
其它说明: 不修改chrgnorth.enable和chrgsouth.enable,升级结束后恢复自动轮询
|
||
*****************************************************************/
|
||
static int chrg_ymodem_session_start(eIDX_SOU_CH ch)
|
||
{
|
||
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
|
||
struct chrg_rollsou_t *pROLL = &chrgrollsou;
|
||
struct chrg_south_t *pSOU = &chrgsouth;
|
||
|
||
if ((ch >= TOTAL_SOU_CHS) || (pTHR->mutex == RT_NULL)) {
|
||
return -1;
|
||
}
|
||
if (rt_mutex_take(pTHR->mutex, rt_tick_from_millisecond(100)) != RT_EOK) {
|
||
return -2;
|
||
}
|
||
/* 先阻止南向线程继续发普通Modbus请求,等待已发请求退出邮箱等待。 */
|
||
pROLL->run = RUN_UPDATA;
|
||
pROLL->ch = (rt_uint8_t)ch;
|
||
pROLL->send_flag = 0;
|
||
pROLL->leng = 0;
|
||
pROLL->updata_result = 0;
|
||
rt_mutex_release(pTHR->mutex);
|
||
rt_thread_mdelay(25);
|
||
|
||
chrg_eload_clear_ymodem_resp();
|
||
if (pSOU->rb != RT_NULL) {
|
||
rt_ringbuffer_reset(pSOU->rb);
|
||
}
|
||
chrg_ymodem_protocol_reset();
|
||
|
||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||
pROLL->mode = 1;
|
||
pROLL->run = RUN_UPDATA;
|
||
pROLL->ch = (rt_uint8_t)ch;
|
||
rt_mutex_release(pTHR->mutex);
|
||
ystate = Ymodem_Wait;
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_session_stop
|
||
函数描述: 退出南向升级透传模式并恢复普通自动轮询
|
||
输入参数: -
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: 清除残留升级应答,避免其被普通Modbus解析器误识别
|
||
*****************************************************************/
|
||
static void chrg_ymodem_session_stop(void)
|
||
{
|
||
struct chrg_thread_t *pTHR = &chrgthr[IDX_THR_ROLL_SOU];
|
||
struct chrg_rollsou_t *pROLL = &chrgrollsou;
|
||
struct chrg_south_t *pSOU = &chrgsouth;
|
||
|
||
if (pTHR->mutex != RT_NULL) {
|
||
rt_mutex_take(pTHR->mutex, RT_WAITING_FOREVER);
|
||
pROLL->mode = 0;
|
||
pROLL->send_flag = 0;
|
||
pROLL->leng = 0;
|
||
pROLL->updata_result = 0;
|
||
pROLL->run = RUN_AUTO;
|
||
rt_mutex_release(pTHR->mutex);
|
||
}
|
||
chrg_eload_clear_ymodem_resp();
|
||
if (pSOU->rb != RT_NULL) {
|
||
rt_ringbuffer_reset(pSOU->rb);
|
||
}
|
||
chrg_ymodem_protocol_reset();
|
||
ystate = Ymodem_No;
|
||
}
|
||
|
||
eMBException funYmodemUpadata (rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
{
|
||
rt_uint8_t ch_num;
|
||
eIDX_SOU_CH ch;
|
||
|
||
if ((reqFrame == RT_NULL) || (resFrame == RT_NULL) || (resLen == RT_NULL) ||
|
||
(reqLen != 8)) {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
ch_num = reqFrame[4];
|
||
if (ch_num >= TOTAL_SOU_CHS) {
|
||
return MB_EX_ILLEGAL_DATA_ADDRESS;
|
||
}
|
||
ch = (eIDX_SOU_CH)ch_num;
|
||
|
||
if (reqFrame[5] == 0xFF) {
|
||
if (chrg_ymodem_session_start(ch) != 0) {
|
||
return MB_EX_SLAVE_BUSY;
|
||
}
|
||
} else if (reqFrame[5] == 0x00) {
|
||
chrg_ymodem_session_stop();
|
||
} else {
|
||
return MB_EX_ILLEGAL_DATA_VALUE;
|
||
}
|
||
|
||
/* 保留原0x20响应内容,外层统一补设备地址和Modbus CRC。 */
|
||
(void)mb_ymodem_updata_cb(&ystate, resFrame);
|
||
*resLen = 1;
|
||
return MB_EX_NONE;
|
||
}
|
||
|
||
static struct code_func_t funcHandlers[MB_HANDLERS_MAX] = {
|
||
{MB_READ_COILS, funcReadCoils},
|
||
{MB_READ_DISCRETE_INPUTS, funcReadDiscreteInputs},
|
||
{MB_READ_HOLDING_REGISTER, funcReadHoldingRegister},
|
||
{MB_READ_INPUT_REGISTER, funcReadInputRegister},
|
||
{MB_WRITE_SINGLE_COIL, funcWriteCoil},
|
||
{MB_WRITE_REGISTER, funcWriteHoldingRegister}, // 0x06
|
||
{MB_WRITE_MULTIPLE_COILS, funcWriteMultipleCoils},
|
||
{MB_WRITE_MULTIPLE_REGISTERS, funcWriteMultipleHoldingRegister}, // 0x10
|
||
{MB_YMODEM_UPDATAE, funYmodemUpadata}, // 0x20 Ymodem升级
|
||
{MB_TRIM_ELOAD, funcTrimELoad}, // 0x21 校准
|
||
};
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_comm_parse_msg
|
||
函数描述: 解析请求的消息
|
||
输入参数: pCOMM: 消息结构
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: 分命令处理对应消息。地址为广播时不应答
|
||
*****************************************************************/
|
||
static int chrg_comm_parse_msg (struct chrg_comm_t *pCOMM)
|
||
{
|
||
if (RT_NULL == pCOMM) {
|
||
return -1;
|
||
}
|
||
|
||
if (pCOMM->rx_len < 8) {
|
||
return -2;
|
||
}
|
||
//校验
|
||
if (0 != mb_crc16(pCOMM->rx_buf, pCOMM->rx_len)) {
|
||
rt_uint16_t crc = mb_crc16(pCOMM->rx_buf, pCOMM->rx_len-2);
|
||
rt_uint16_t crc_r = u8v_to_u16(&pCOMM->rx_buf[pCOMM->rx_len-2]);
|
||
rt_kprintf("crc %04X vs %04X\r\n", swap_u16(crc), crc_r);
|
||
return -3;
|
||
}
|
||
|
||
int i = 0, ret = -1;
|
||
rt_uint16_t crc = 0;
|
||
eMBException eStatus = MB_EX_ILLEGAL_FUNCTION;
|
||
rt_uint8_t address = 0, functionCode = 0x00;
|
||
address = pCOMM->rx_buf[0];
|
||
functionCode = pCOMM->rx_buf[1];
|
||
//判断地址
|
||
if ((address != pCOMM->addr)&&(MB_ADDRESS_BROADCAST != address)) {
|
||
return ret;
|
||
}
|
||
|
||
struct code_func_t *pFunc = RT_NULL;
|
||
for (i = 0; i < MB_HANDLERS_MAX; i++) {
|
||
pFunc = &funcHandlers[i];
|
||
if (RT_NULL == pFunc) {
|
||
break;
|
||
}
|
||
//匹配功能码和处理函数
|
||
if ((functionCode == pFunc->code)&&(RT_NULL != pFunc->func)) {
|
||
eStatus = pFunc->func(pCOMM->rx_buf, pCOMM->rx_len,
|
||
pCOMM->tx_buf, &pCOMM->tx_len);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (MB_ADDRESS_BROADCAST != address) {
|
||
pCOMM->tx_buf[0] = address;
|
||
//异常响应
|
||
if (MB_EX_NONE != eStatus) {
|
||
pCOMM->tx_buf[1] = (functionCode | MB_FUNC_ERROR);
|
||
pCOMM->tx_buf[2] = eStatus;
|
||
pCOMM->tx_len = 3;
|
||
}
|
||
|
||
crc = mb_crc16(pCOMM->tx_buf, pCOMM->tx_len);
|
||
u16_to_u8v_com(crc, &pCOMM->tx_buf[pCOMM->tx_len]);
|
||
pCOMM->tx_len += 2;
|
||
|
||
ret = chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* 旧版Ymodem处理代码保留在下方注释中作为历史参考。当前实现使用完整拼帧、
|
||
* CRC校验和停止等待重发机制,不再依赖一次串口接收刚好得到一整帧。
|
||
*/
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_frame_size
|
||
函数描述: 根据Ymodem控制字符取得一帧应接收的总长度
|
||
输入参数: header:Ymodem帧头或控制字符
|
||
输出参数: -
|
||
返回说明: 133:SOH帧 1029:STX帧 1:EOT/CAN 0:无效字符
|
||
其它说明: -
|
||
*****************************************************************/
|
||
static rt_uint16_t chrg_ymodem_frame_size(rt_uint8_t header)
|
||
{
|
||
if (header == SOH) {
|
||
return YMODEM_SOH_SIZE;
|
||
}
|
||
if (header == STX) {
|
||
return YMODEM_STX_SIZE;
|
||
}
|
||
if ((header == EOT) || (header == YM_CAN)) {
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_crc16
|
||
函数描述: 计算Ymodem数据区使用的CRC16-XMODEM校验值
|
||
输入参数: data:数据区地址 length:数据区长度
|
||
输出参数: -
|
||
返回说明: CRC16-XMODEM校验值
|
||
其它说明: 初值0,多项式0x1021,结果按高字节在前写入Ymodem帧
|
||
*****************************************************************/
|
||
static rt_uint16_t chrg_ymodem_crc16(const rt_uint8_t *data, rt_uint16_t length)
|
||
{
|
||
rt_uint16_t crc = 0;
|
||
rt_uint16_t i;
|
||
rt_uint8_t bit;
|
||
|
||
for (i = 0; i < length; i++) {
|
||
crc ^= (rt_uint16_t)data[i] << 8;
|
||
for (bit = 0; bit < 8; bit++) {
|
||
crc = (crc & 0x8000) ? (rt_uint16_t)((crc << 1) ^ 0x1021) :
|
||
(rt_uint16_t)(crc << 1);
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_frame_valid
|
||
函数描述: 校验Ymodem数据帧长度、包号反码和数据区CRC
|
||
输入参数: frame:完整Ymodem帧 length:帧总长度
|
||
输出参数: -
|
||
返回说明: RT_TRUE:有效 RT_FALSE:无效
|
||
其它说明: EOT和CAN为单字节控制帧,不包含包号与CRC
|
||
*****************************************************************/
|
||
static rt_bool_t chrg_ymodem_frame_valid(const rt_uint8_t *frame, rt_uint16_t length)
|
||
{
|
||
rt_uint16_t data_len;
|
||
rt_uint16_t frame_crc;
|
||
|
||
if ((frame == RT_NULL) || (length != chrg_ymodem_frame_size(frame[0]))) {
|
||
return RT_FALSE;
|
||
}
|
||
if ((frame[0] == EOT) || (frame[0] == YM_CAN)) {
|
||
return RT_TRUE;
|
||
}
|
||
if ((rt_uint8_t)(frame[1] + frame[2]) != 0xFF) {
|
||
return RT_FALSE;
|
||
}
|
||
data_len = (frame[0] == SOH) ? 128 : 1024;
|
||
frame_crc = ((rt_uint16_t)frame[length - 2] << 8) | frame[length - 1];
|
||
return chrg_ymodem_crc16(&frame[YMODEM_DATA_OFFSET], data_len) == frame_crc;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_resend_last
|
||
函数描述: 将最近一次完整Ymodem帧重新提交给南向发送线程
|
||
输入参数: -
|
||
输出参数: -
|
||
返回说明: 0:提交成功 -1:无可重发帧 其它:南向提交错误
|
||
其它说明: 仅在NAK或等待应答超时时调用,始终使用原帧实际长度
|
||
*****************************************************************/
|
||
static int chrg_ymodem_resend_last(void)
|
||
{
|
||
int ret;
|
||
|
||
if (g_ymodem_last_len == 0) {
|
||
return -1;
|
||
}
|
||
ret = chrg_send_sou_upadata_data(mobude_ymodem_buf_2, g_ymodem_last_len);
|
||
if (ret == 0) {
|
||
g_ymodem_retry_count++;
|
||
g_ymodem_send_tick = rt_tick_get();
|
||
g_ymodem_waiting_ack = 1;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_ymodem_process
|
||
函数描述: 累计COM串口分段数据,形成完整Ymodem帧后提交到南向线程
|
||
输入参数: data:本次串口数据 length:本次数据长度
|
||
输出参数: -
|
||
返回说明: 0:处理完成 -2:仍在等待上一帧应答 -3:帧校验失败 -4:提交失败
|
||
其它说明: 一帧可由多次串口接收组成,只有完整且校验通过后才会发送到功率板
|
||
*****************************************************************/
|
||
static int chrg_ymodem_process(const rt_uint8_t *data, rt_uint16_t length)
|
||
{
|
||
rt_uint16_t offset = 0;
|
||
rt_uint16_t copy_len;
|
||
int ret;
|
||
|
||
if ((data == RT_NULL) || (length == 0)) {
|
||
return 0;
|
||
}
|
||
while (offset < length) {
|
||
if (g_ymodem_waiting_ack != 0) {
|
||
return -2;
|
||
}
|
||
if (g_ymodem_expected_len == 0) {
|
||
g_ymodem_expected_len = chrg_ymodem_frame_size(data[offset]);
|
||
if (g_ymodem_expected_len == 0) {
|
||
offset++;
|
||
continue;
|
||
}
|
||
g_ymodem_rx_len = 0;
|
||
}
|
||
|
||
copy_len = g_ymodem_expected_len - g_ymodem_rx_len;
|
||
if (copy_len > (length - offset)) {
|
||
copy_len = length - offset;
|
||
}
|
||
rt_memcpy(&mobude_ymodem_buf[g_ymodem_rx_len], &data[offset], copy_len);
|
||
g_ymodem_rx_len += copy_len;
|
||
offset += copy_len;
|
||
if (g_ymodem_rx_len < g_ymodem_expected_len) {
|
||
continue;
|
||
}
|
||
|
||
if (!chrg_ymodem_frame_valid(mobude_ymodem_buf, g_ymodem_rx_len)) {
|
||
g_ymodem_rx_len = 0;
|
||
g_ymodem_expected_len = 0;
|
||
return -3;
|
||
}
|
||
|
||
g_ymodem_last_len = g_ymodem_rx_len;
|
||
rt_memcpy(mobude_ymodem_buf_2, mobude_ymodem_buf, g_ymodem_last_len);
|
||
if (mobude_ymodem_buf[0] == SOH) {
|
||
if (g_ymodem_eot_done != 0) {
|
||
/* 第二次EOT之后的0号空SOH是Ymodem收尾帧,只回复ACK。 */
|
||
ystate = Ymodem_Rec_Finish;
|
||
} else if ((mobude_ymodem_buf[1] == 0) && (s_first_soh_rcvd == 0)) {
|
||
/* 只有传输开始时的0号SOH是文件名/大小帧,需要回复ACK+C。 */
|
||
ystate = Ymodem_Rec_Start;
|
||
s_first_soh_rcvd = 1;
|
||
} else {
|
||
/* 包号非0的SOH是普通128字节固件数据帧,只回复ACK。 */
|
||
ystate = Ymodem_Rec_1024;
|
||
}
|
||
} else if (mobude_ymodem_buf[0] == STX) {
|
||
ystate = Ymodem_Rec_1024;
|
||
} else if (mobude_ymodem_buf[0] == EOT) {
|
||
ystate = Ymodem_Rec_End;
|
||
} else {
|
||
ystate = Ymodem_Rec_Finish;
|
||
}
|
||
|
||
ret = chrg_send_sou_upadata_data(mobude_ymodem_buf_2, g_ymodem_last_len);
|
||
g_ymodem_rx_len = 0;
|
||
g_ymodem_expected_len = 0;
|
||
if (ret != 0) {
|
||
return -4;
|
||
}
|
||
g_ymodem_retry_count = 0;
|
||
g_ymodem_waiting_ack = 1;
|
||
g_ymodem_send_tick = rt_tick_get();
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: ymodem_send
|
||
函数描述: 处理南向Ymodem应答、生成上位机应答并执行超时重发
|
||
输入参数: pCOMM:COM通信对象
|
||
输出参数: pCOMM->tx_buf和pCOMM->tx_len
|
||
返回说明: 1:需要向上位机发送数据 0:本轮无数据发送
|
||
其它说明: 首包前每秒发送一次C;SOH首包和第二次EOT后按Ymodem规则发送ACK+C
|
||
*****************************************************************/
|
||
static rt_uint8_t ymodem_send(struct chrg_comm_t *pCOMM)
|
||
{
|
||
rt_uint8_t south_resp;
|
||
rt_uint32_t timeout_ms;
|
||
rt_tick_t now;
|
||
|
||
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;
|
||
pCOMM->tx_len = 1;
|
||
g_ymodem_c_tick = now;
|
||
return 1;
|
||
}
|
||
|
||
south_resp = chrg_eload_get_ymodem_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;
|
||
} else if (ystate == Ymodem_Rec_End) {
|
||
pCOMM->tx_buf[0] = ACK;
|
||
pCOMM->tx_buf[1] = ACK_C;
|
||
pCOMM->tx_len = 2;
|
||
g_ymodem_eot_done = 1;
|
||
ystate = Ymodem_Rec_Finish;
|
||
} else {
|
||
pCOMM->tx_buf[0] = ACK;
|
||
pCOMM->tx_len = 1;
|
||
}
|
||
return 1;
|
||
}
|
||
if (south_resp == NAK) {
|
||
if (g_ymodem_retry_count >= YMODEM_MAX_RETRIES) {
|
||
pCOMM->tx_buf[0] = YM_CAN;
|
||
pCOMM->tx_buf[1] = YM_CAN;
|
||
pCOMM->tx_len = 2;
|
||
chrg_ymodem_session_stop();
|
||
return 1;
|
||
}
|
||
(void)chrg_ymodem_resend_last();
|
||
return 0;
|
||
}
|
||
if (south_resp == ACK_C) {
|
||
/* 等待当前帧ACK时忽略功率板周期C,避免打乱上位机停止等待握手。 */
|
||
if (g_ymodem_waiting_ack != 0) {
|
||
return 0;
|
||
}
|
||
pCOMM->tx_buf[0] = ACK_C;
|
||
pCOMM->tx_len = 1;
|
||
return 1;
|
||
}
|
||
if (south_resp == YM_CAN) {
|
||
pCOMM->tx_buf[0] = YM_CAN;
|
||
pCOMM->tx_buf[1] = YM_CAN;
|
||
pCOMM->tx_len = 2;
|
||
chrg_ymodem_session_stop();
|
||
return 1;
|
||
}
|
||
|
||
if (g_ymodem_waiting_ack != 0) {
|
||
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)) {
|
||
if (g_ymodem_retry_count >= YMODEM_MAX_RETRIES) {
|
||
pCOMM->tx_buf[0] = YM_CAN;
|
||
pCOMM->tx_buf[1] = YM_CAN;
|
||
pCOMM->tx_len = 2;
|
||
chrg_ymodem_session_stop();
|
||
return 1;
|
||
}
|
||
(void)chrg_ymodem_resend_last();
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
///*****************************************************************
|
||
//函数名称: ymodem_send
|
||
//函数描述: 每隔一秒发一次0x43告诉上位机准备数据
|
||
//输入参数: pCOMM: 消息结构
|
||
//输出参数: -
|
||
//返回说明: -
|
||
//其它说明: 分命令处理对应消息。地址为广播时不应答
|
||
//*****************************************************************/
|
||
////返回1:需要发送数据;返回0:无需发送
|
||
//uint8_t y_time = 0;
|
||
//uint8_t ymodem_send(struct chrg_comm_t *pCOMM,uint8_t state){
|
||
//
|
||
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
|
||
// static rt_uint8_t number = 4;
|
||
// uint8_t need_tx = 0;
|
||
// if(pROLL->send_flag==0)y_time++;
|
||
// pCOMM->tx_len = 0; //每次进来先清空发送长度
|
||
|
||
// /* ======== 尚未收到上位机第一个数据包: 定时发送'C'握手 ======== */
|
||
// if (!s_first_soh_rcvd) {
|
||
// if (y_time > 20) {
|
||
// pCOMM->tx_buf[0] = ACK_C;
|
||
// pCOMM->tx_len = 1;
|
||
// need_tx = 1;
|
||
// y_time = 0;
|
||
// }
|
||
// return need_tx;
|
||
// }
|
||
// if(state == Ymodem_Rec_End){
|
||
// //if (y_time > 20) {
|
||
// pCOMM->tx_buf[0] = ACK;
|
||
// pCOMM->tx_buf[1] = ACK_C;
|
||
// pCOMM->tx_len = 2;
|
||
// need_tx = 1;
|
||
// y_time = 0;
|
||
// number--;
|
||
// if(number == 0)
|
||
// {
|
||
// state = Ymodem_Wait;
|
||
// number = 4;
|
||
// }
|
||
// state = Ymodem_Wait;
|
||
// //}
|
||
// }
|
||
// /* ======== 已收到上位机数据包: 南向有应答立刻转发给上位机 ======== */
|
||
// uint8_t south_resp = chrg_eload_get_ymodem_resp();
|
||
// if (south_resp != 0) {
|
||
// switch (south_resp) {
|
||
// case ACK:
|
||
// memset(mobude_ymodem_buf_2,0,sizeof(mobude_ymodem_buf));
|
||
// switch(state){
|
||
// case Ymodem_Rec_End:
|
||
// case Ymodem_Rec_Start:
|
||
// pCOMM->tx_buf[0] = ACK;
|
||
// pCOMM->tx_buf[1] = ACK_C;
|
||
// pCOMM->tx_len = 2;
|
||
// break;
|
||
// case Ymodem_Rec_1024:
|
||
// pCOMM->tx_buf[0] = ACK;
|
||
// pCOMM->tx_len = 1;
|
||
// }
|
||
// need_tx = 1;
|
||
// /* Rec_End收到ACK → 南向已完成2次EOT握手,切到收尾阶段 */
|
||
// break;
|
||
// case NAK:
|
||
// chrg_send_sou_upadata_data(mobude_ymodem_buf_2,1029);
|
||
// break;
|
||
// default:
|
||
// break;
|
||
// }
|
||
// y_time = 0;
|
||
// rt_kprintf("REC DATA = %02x\n",south_resp);
|
||
// }
|
||
//
|
||
// return need_tx;
|
||
//}
|
||
|
||
///*****************************************************************
|
||
//函数名称: Ymodem_Process
|
||
//函数描述: 处理ymodem协议数据
|
||
//输入参数: pCOMM: 消息结构
|
||
//输出参数: -
|
||
//返回说明: -
|
||
//其它说明: 分命令处理对应消息。地址为广播时不应答
|
||
//*****************************************************************/
|
||
//uint16_t len = 0;
|
||
//eMBException Ymodem_Process(rt_uint8_t *reqFrame, rt_uint16_t reqLen,
|
||
// rt_uint8_t *resFrame, rt_uint16_t *resLen)
|
||
//{
|
||
// eMBException eStatus = MB_EX_NONE;
|
||
// struct chrg_rollsou_t *pROLL = &chrgrollsou;
|
||
// rt_kprintf("TX_LEN = %d\n",reqLen);
|
||
//// for(int i=0;i<reqLen;i++){
|
||
//// rt_kprintf("%02x ",reqFrame[i]);
|
||
//// }
|
||
// len = reqLen;
|
||
// uint8_t data = reqFrame[0];
|
||
// switch(data){
|
||
// case SOH:
|
||
// memset(mobude_ymodem_buf,0,sizeof(mobude_ymodem_buf));
|
||
// ystate = Ymodem_Rec_Start;
|
||
// memcpy(mobude_ymodem_buf,reqFrame,reqLen);
|
||
// memcpy(mobude_ymodem_buf_2,reqFrame,reqLen);
|
||
// chrg_send_sou_upadata_data(mobude_ymodem_buf,reqLen);
|
||
// pROLL->send_flag = 1;
|
||
// s_first_soh_rcvd = 1;
|
||
// break;
|
||
// case STX:
|
||
// ystate = Ymodem_Rec_1024;
|
||
// memset(mobude_ymodem_buf,0,sizeof(mobude_ymodem_buf));
|
||
// memcpy(mobude_ymodem_buf,reqFrame,reqLen);
|
||
// memcpy(mobude_ymodem_buf_2,reqFrame,reqLen);
|
||
// chrg_send_sou_upadata_data(mobude_ymodem_buf,reqLen);
|
||
// pROLL->send_flag = 1;
|
||
// break;
|
||
// case EOT:
|
||
// ystate = Ymodem_Rec_End;
|
||
// memset(mobude_ymodem_buf,0,sizeof(mobude_ymodem_buf));
|
||
// memcpy(mobude_ymodem_buf,reqFrame,reqLen);
|
||
// memcpy(mobude_ymodem_buf_2,reqFrame,reqLen);
|
||
// chrg_send_sou_upadata_data(mobude_ymodem_buf,reqLen);
|
||
// pROLL->send_flag = 1;
|
||
// break;
|
||
// }
|
||
// return eStatus;
|
||
//}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_comm_updata_north
|
||
函数描述: 解析南向程序数据包
|
||
输入参数: pCOMM: 消息结构
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: 分命令处理对应消息。地址为广播时不应答
|
||
*****************************************************************/
|
||
static int chrg_comm_updata_north (struct chrg_comm_t *pCOMM)
|
||
{
|
||
int ret = 0;
|
||
int process_ret;
|
||
rt_uint8_t need_tx;
|
||
|
||
if (pCOMM == RT_NULL) {
|
||
return -1;
|
||
}
|
||
|
||
/* 升级模式中仍允许接收0x20停止命令,必须先校验地址和Modbus CRC。 */
|
||
if ((pCOMM->rx_len == 8) && (pCOMM->rx_buf[1] == MB_YMODEM_UPDATAE) &&
|
||
((pCOMM->rx_buf[0] == pCOMM->addr) ||
|
||
(pCOMM->rx_buf[0] == MB_ADDRESS_BROADCAST)) &&
|
||
(mb_crc16(pCOMM->rx_buf, pCOMM->rx_len) == 0)) {
|
||
return chrg_comm_parse_msg(pCOMM);
|
||
}
|
||
|
||
if (pCOMM->rx_len > 0) {
|
||
process_ret = chrg_ymodem_process(pCOMM->rx_buf, pCOMM->rx_len);
|
||
if ((process_ret == -3) || (process_ret == -4)) {
|
||
/* 本地校验或南向提交失败时要求上位机重发当前帧。 */
|
||
pCOMM->tx_buf[0] = NAK;
|
||
pCOMM->tx_len = 1;
|
||
return chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len);
|
||
}
|
||
}
|
||
|
||
need_tx = ymodem_send(pCOMM);
|
||
if ((need_tx != 0) && (pCOMM->tx_len > 0)) {
|
||
ret = chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len);
|
||
}
|
||
// uint8_t need_tx = 0;
|
||
// pCOMM->tx_len = 0; //进来先清空发送缓冲区
|
||
// eMBException eStatus = MB_EX_NONE;
|
||
// //收到有效数据才解析Ymodem
|
||
|
||
// if(pCOMM->rx_len == 8 && pCOMM->rx_buf[1]==0x20){
|
||
// eStatus = funYmodemUpadata(pCOMM->rx_buf, pCOMM->rx_len, pCOMM->tx_buf, &pCOMM->tx_len);
|
||
// }
|
||
// else if(pCOMM->rx_len == 8 && pCOMM->rx_buf[1]==0x06){
|
||
// eStatus = funcWriteHoldingRegister(pCOMM->rx_buf, pCOMM->rx_len, pCOMM->tx_buf, &pCOMM->tx_len);
|
||
// }
|
||
// else if(pCOMM->rx_len != 0){
|
||
// rt_kprintf("len = %d\n",pCOMM->rx_len);
|
||
// Ymodem_Process(pCOMM->rx_buf, pCOMM->rx_len, pCOMM->tx_buf, &pCOMM->tx_len);
|
||
// }
|
||
|
||
// //统一调用定时应答生成
|
||
// need_tx = ymodem_send(pCOMM,ystate);
|
||
|
||
// //只有标志为1且长度有效才发送
|
||
// if(need_tx && pCOMM->tx_len > 0){
|
||
// ret = chrg_tty_send(pCOMM->tty, pCOMM->tx_buf, pCOMM->tx_len);
|
||
// }
|
||
return ret;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: chrg_comm_thread_entry
|
||
函数描述: 对外RS485通信线程体
|
||
输入参数: *data: 本线程信息
|
||
输出参数: -
|
||
返回说明: -
|
||
其它说明: -
|
||
*****************************************************************/
|
||
void chrg_comm_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_comm_t *pCOMM = &chrgcomm;
|
||
struct chrg_rollsou_t *pROLL = &chrgrollsou;
|
||
pTTY = chrg_tty_create(pCOMM->devname, BAUD_RATE_115200, 0, 8, 1);
|
||
if (RT_NULL == pTTY) {
|
||
LOG_E("tty can't create.");
|
||
return ;
|
||
}
|
||
chrg_tty_set_recv_tmo(pTTY, 8);
|
||
if (chrg_tty_connect(pTTY) != RT_EOK) {
|
||
chrg_tty_destory(pTTY);
|
||
return;
|
||
}
|
||
chrg_tty_mode_set(pTTY, 0);
|
||
|
||
pCOMM->tty = pTTY;
|
||
|
||
/* 初始化影子寄存器, 与当前 chrgnorth.sw 同步 */
|
||
chrg_shadow_init();
|
||
|
||
while (1) {
|
||
rt_memset(pCOMM->rx_buf, 0, sizeof(pCOMM->rx_buf));
|
||
len = chrg_tty_recv(pTTY, pCOMM->rx_buf, sizeof(pCOMM->rx_buf));
|
||
// LOG_HEX("Num=", len, pCOMM->rx_buf, len);
|
||
if(pROLL->mode==0x01){ //升级模式走单独协议
|
||
pCOMM->rx_len = (len > 0) ? (rt_uint16_t)len : 0;
|
||
pCOMM->tx_len = 0;
|
||
chrg_comm_updata_north(pCOMM);
|
||
if (len <= 0) {
|
||
rt_thread_mdelay(1);
|
||
}
|
||
}else{
|
||
if (len <= 0) {
|
||
rt_thread_mdelay(1);
|
||
continue;
|
||
}
|
||
pCOMM->rx_len = len;
|
||
pCOMM->tx_len = 0;
|
||
|
||
chrg_comm_parse_msg(pCOMM);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
#if 0
|
||
|
||
int rs485snd (int argc, char **argv)
|
||
{
|
||
struct chrg_comm_t *pCOMM = &chrgcomm;
|
||
rt_uint8_t test[16] = {
|
||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
|
||
|
||
if (RT_NULL != pCOMM->tty) {
|
||
chrg_tty_send(pCOMM->tty, test, 16);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
MSH_CMD_EXPORT(rs485snd, rs485 test send);
|
||
|
||
#endif
|
||
|
||
static void chrg_mb_write_sink(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t sou_value, struct chrg_switch_t *pSW)
|
||
{
|
||
switch (reg_offset)
|
||
{
|
||
case SINK_REG_PROTOCOL: // 0x00: 协议类型
|
||
{
|
||
rt_uint8_t has_group = 0;
|
||
pSW->sink.protocol = sink_proto_lookup(sou_value, &has_group);
|
||
pSW->protoCmd = (rt_uint8_t)sou_value;
|
||
// rt_kprintf("[SINK CH%d] Protocol=0x%02X\n", ch + 1, pSW->sink.protocol);
|
||
}
|
||
break;
|
||
|
||
case SINK_REG_PRO_GEAR: // 0x01: 协议挡位(组号)
|
||
pSW->sink.Pro_Group = sou_value;
|
||
// rt_kprintf("[SINK CH%d] Gear=%d\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_PRO_VOLT: // 0x02: 协议电压 (mV)
|
||
pSW->sink.pro_loadv = sou_value;
|
||
// rt_kprintf("[SINK CH%d] ProVolt=%d mV\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_PRO_CURR: // 0x03: 协议电流 (mA)
|
||
pSW->sink.pro_loadc = sou_value;
|
||
// rt_kprintf("[SINK CH%d] ProCurr=%d mA\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_CC_SEL: // 0x04: CC选择
|
||
pSW->sink.cc_set = (rt_uint8_t)sou_value;
|
||
// rt_kprintf("[SINK CH%d] CC_SEL=%d\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_EMARK: // 0x05: E-Mark控制(预留)
|
||
break;
|
||
|
||
case SINK_REG_LOAD_MODE: // 0x06: 负载模式 (1=恒压, 2=恒流)
|
||
pSW->CV_mode = (rt_uint8_t)sou_value;
|
||
// rt_kprintf("[SINK CH%d] LoadMode=%d\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_LOAD_VOLT: // 0x07: 负载电压 (mV)
|
||
pSW->sink.loadv = sou_value;
|
||
// rt_kprintf("[SINK CH%d] LoadVolt=%d mV\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_LOAD_CURR: // 0x08: 负载电流 (mA)
|
||
pSW->sink.loadc = sou_value;
|
||
// rt_kprintf("[SINK CH%d] LoadCurr=%d mA\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_TEST_MODE: // 0x09: 测试模式
|
||
pSW->sink.test.test_mode = (rt_uint8_t)sou_value;
|
||
// rt_kprintf("[SINK CH%d] TestMode=%d\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_LOAD_STATUS: // 0x0A: 负载状态 (1=运行, 2=停止)
|
||
pSW->sink.Now_State = sou_value;
|
||
// rt_kprintf("[SINK CH%d] STATUS=%d\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_VON_VOLT: // 0x0B: Von电压
|
||
pSW->sink.volt_on = sou_value;
|
||
// rt_kprintf("[SINK CH%d] VonVolt=%d mV\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SINK_REG_RISE_SLOPE: // 0x0C: 加载斜率(预留)
|
||
case SINK_REG_FALL_SLOPE: // 0x0D: 卸载斜率(预留)
|
||
break;
|
||
case SINK_REG_SHORT_HOLD_TIME: // 0x0E: 保持时间(ms)
|
||
pSW->sink.test.short_test.hold_time = sou_value;
|
||
break;
|
||
case SINK_REG_SHORT_SET_MODE: // 0x0F: 设置模式
|
||
pSW->sink.test.short_test.set_mode = (rt_uint8_t)sou_value;
|
||
break;
|
||
case SINK_REG_SHORT_HAVE_CURR: // 0x10:设置是否带载
|
||
pSW->sink.test.short_test.have_curr_flag = (rt_uint8_t)sou_value;
|
||
break;
|
||
case SINK_REG_OCP_START_CURR: // 0x11: 起始电流
|
||
pSW->sink.test.OCP_test.start_curr = sou_value;
|
||
break;
|
||
case SINK_REG_OCP_END_CURR: // 0x12: 终点电流
|
||
pSW->sink.test.OCP_test.end_curr = sou_value;
|
||
break;
|
||
case SINK_REG_OCP_STEP_CURR: // 0x13: 步进电流
|
||
pSW->sink.test.OCP_test.change_curr = sou_value;
|
||
break;
|
||
case SINK_REG_OCP_STEP_TIME: // 0x14: 步进保持时间
|
||
pSW->sink.test.OCP_test.change_keep_time = sou_value;
|
||
break;
|
||
case SINK_REG_OCP_RECOV_TIME: // 0x15: 恢复时间
|
||
pSW->sink.test.OCP_test.hold_time = sou_value;
|
||
break;
|
||
case SINK_REG_OCP_STOP_VOLT: // 0x16: 截止电压
|
||
pSW->sink.test.OCP_test.stop_volt = sou_value;
|
||
break;
|
||
case SINK_REG_OCP_RECOV_LOAD: // 0x17: 恢复后带载
|
||
pSW->sink.test.OCP_test.hold_curr = (rt_uint8_t)sou_value;
|
||
break;
|
||
case SINK_REG_OCP_SET_MODE: // 0x18: 设置模式
|
||
pSW->sink.test.OCP_test.set_mode = (rt_uint8_t)sou_value;
|
||
break;
|
||
case SINK_REG_DYNA_LOAD1: // 0x19: 动态负载1
|
||
pSW->sink.test.change_test.curr1_set = sou_value;
|
||
break;
|
||
case SINK_REG_DYNA_TIME1: // 0x1A: 负载1保持时间
|
||
pSW->sink.test.change_test.time1_set = sou_value;
|
||
break;
|
||
case SINK_REG_DYNA_LOAD2: // 0x1B: 动态负载2
|
||
pSW->sink.test.change_test.curr2_set = sou_value;
|
||
break;
|
||
case SINK_REG_DYNA_TIME2: // 0x1C: 负载2保持时间
|
||
pSW->sink.test.change_test.time2_set = sou_value;
|
||
break;
|
||
case SINK_REG_DYNA_SLOPE: // 0x1D: 加载斜率
|
||
pSW->sink.test.change_test.k_Rise = sou_value;
|
||
break;
|
||
case SINK_REG_DYNA_UNLOAD: // 0x1E: 卸载斜率
|
||
pSW->sink.test.change_test.k_Fall = sou_value;
|
||
break;
|
||
default:
|
||
rt_kprintf("[SINK CH%d] Unknown reg offset=0x%02X\n", ch + 1, reg_offset);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void chrg_mb_write_src(rt_uint8_t ch, rt_uint16_t reg_offset, rt_uint16_t sou_value, struct chrg_switch_t *pSW)
|
||
{
|
||
switch (reg_offset)
|
||
{
|
||
case SRC_REG_PROTOCOL: // 0x00: 协议
|
||
pSW->source.protocol = src_proto_lookup(sou_value);
|
||
pSW->protoCmd = (rt_uint8_t)sou_value;
|
||
// rt_kprintf("[SRC CH%d] Protocol=0x%02X -> 0x%02X\n", ch + 1, sou_value, pSW->source.protocol);
|
||
break;
|
||
|
||
case SRC_REG_PRO_GEAR: // 0x01: 协议类型
|
||
src_type_lookup(sou_value,pSW);
|
||
//rt_kprintf("[SRC CH%d] Gear=%d\n", ch + 1, sou_value);
|
||
break;
|
||
case SRC_REG_PRO_MODE: // 0x02: 输出模式 (1=恒压, 2=恒流)
|
||
pSW->CV_mode = (rt_uint8_t)sou_value;
|
||
// rt_kprintf("[SRC CH%d] Mode=%d\n", ch + 1, sou_value);
|
||
break;
|
||
case SRC_REG_MIN_VOLT: // 0x03: 最小电压 (mV)
|
||
pSW->source.min_vol = sou_value;
|
||
//rt_kprintf("[SRC CH%d] MinVolt=%d mV\n", ch + 1, sou_value);
|
||
break;
|
||
case SRC_REG_MAX_VOLT: // 0x04: 最大电压 (mV)
|
||
pSW->source.vc.set_voltage = sou_value;
|
||
// rt_kprintf("[SRC CH%d] MaxVolt=%d mV\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SRC_REG_MAX_CURR: // 0x05: 最大电流 (mA)
|
||
pSW->source.vc.set_current = sou_value;
|
||
//rt_kprintf("[SRC CH%d] MaxCurr=%d mA\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SRC_REG_CC_SEL: // 0x06: CC选择
|
||
pSW->source.cc_set = (rt_uint8_t)sou_value;
|
||
//rt_kprintf("[SRC CH%d] CC_SEL=%d\n", ch + 1, sou_value);
|
||
break;
|
||
|
||
case SRC_REG_OUTPUT_STATUS: // 0x07: 输出状态 (1=运行, 2=停止)
|
||
pSW->source.Now_State = sou_value;
|
||
break;
|
||
|
||
default:
|
||
rt_kprintf("[SRC CH%d] Unknown reg offset=0x%02X\n", ch + 1, reg_offset);
|
||
break;
|
||
}
|
||
//rt_kprintf("ret_offset = %d, value = %d",reg_offset,sou_value);
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_comm_get_param_diff
|
||
函数描述: 按当前角色统一比较运行参数与待生效影子参数
|
||
输入参数: role:当前角色 pReal:正在运行的参数 pShadow:待生效参数
|
||
输出参数: pDiff:各公共属性及Sink专用协议属性的变化标志
|
||
返回说明: -
|
||
其它说明: 本函数只计算差异,不修改状态、不发送命令;Source的电压电流
|
||
使用vc.set_voltage/set_current,Sink使用loadv/loadc
|
||
*****************************************************************/
|
||
static void chrg_comm_get_param_diff(eIDX_ID role,
|
||
const struct chrg_switch_t *pReal,
|
||
const struct chrg_switch_t *pShadow,
|
||
chrg_param_diff_t *pDiff)
|
||
{
|
||
if ((pReal == RT_NULL) || (pShadow == RT_NULL) || (pDiff == RT_NULL)) {
|
||
return;
|
||
}
|
||
rt_memset(pDiff, 0, sizeof(*pDiff));
|
||
pDiff->cv_changed = (pReal->CV_mode != pShadow->CV_mode);
|
||
|
||
if (role == ID_SOURCE) {
|
||
pDiff->protocol_changed =
|
||
(pReal->source.protocol != pShadow->source.protocol);
|
||
pDiff->cc_changed =
|
||
(pReal->source.cc_set != pShadow->source.cc_set);
|
||
pDiff->voltage_changed =
|
||
(pReal->source.vc.set_voltage != pShadow->source.vc.set_voltage);
|
||
pDiff->current_changed =
|
||
(pReal->source.vc.set_current != pShadow->source.vc.set_current);
|
||
} else if (role == ID_SINK) {
|
||
pDiff->protocol_changed =
|
||
(pReal->sink.protocol != pShadow->sink.protocol);
|
||
pDiff->cc_changed =
|
||
(pReal->sink.cc_set != pShadow->sink.cc_set);
|
||
pDiff->voltage_changed =
|
||
(pReal->sink.loadv != pShadow->sink.loadv);
|
||
pDiff->current_changed =
|
||
(pReal->sink.loadc != pShadow->sink.loadc);
|
||
pDiff->group_changed =
|
||
(pReal->sink.Pro_Group != pShadow->sink.Pro_Group);
|
||
pDiff->pro_voltage_changed =
|
||
(pReal->sink.pro_loadv != pShadow->sink.pro_loadv);
|
||
pDiff->pro_current_changed =
|
||
(pReal->sink.pro_loadc != pShadow->sink.pro_loadc);
|
||
}
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_comm_apply_sink_effect
|
||
函数描述: 处理COM参数生效后的同角色负载启停、测试和差异更新
|
||
输入参数: ch:通道 pReal:运行参数 pShadow:待生效影子参数
|
||
输出参数: 更新pReal并按需提交南向命令
|
||
返回说明: MB_EX_NONE:成功 MB_EX_SLAVE_BUSY:命令提交失败
|
||
其它说明: 角色切换不在本函数处理,由chrg_mb_write_sys统一调度
|
||
*****************************************************************/
|
||
static eMBException chrg_comm_apply_sink_effect(rt_uint8_t ch,
|
||
struct chrg_switch_t *pReal, const struct chrg_switch_t *pShadow)
|
||
{
|
||
rt_bool_t pro_change;
|
||
rt_uint8_t curr_test;
|
||
rt_uint8_t curr_test_old;
|
||
rt_uint8_t old_cv_mode = pReal->CV_mode;
|
||
rt_uint16_t old_loadv = pReal->sink.loadv;
|
||
rt_uint16_t old_loadc = pReal->sink.loadc;
|
||
rt_uint16_t sink_work = pShadow->sink.Now_State;
|
||
chrg_param_diff_t diff_info;
|
||
|
||
chrg_comm_get_param_diff(ID_SINK, pReal, pShadow, &diff_info);
|
||
/* Sink以下属性变化时需要从CC开始重新执行北向协议配置链路。 */
|
||
pro_change = diff_info.protocol_changed || diff_info.group_changed ||
|
||
diff_info.pro_voltage_changed || diff_info.pro_current_changed ||
|
||
diff_info.cc_changed || diff_info.cv_changed;
|
||
rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t));
|
||
|
||
if (sink_work == 0x01) {
|
||
curr_test = pReal->sink.test.test_mode;
|
||
curr_test_old = pReal->sink.test.test_mode_old;
|
||
if (((curr_test_old == 0) && (curr_test != 0)) ||
|
||
((curr_test == 0) && (curr_test_old != 0))) {
|
||
if (curr_test_old == 0) {
|
||
if (curr_test == SINK_TEST_DYNA) {
|
||
pReal->sink.work_mode = SINK_WORK_ON_TEST_DYNA;
|
||
} else if (curr_test == SINK_TEST_OCP) {
|
||
pReal->sink.work_mode = SINK_WORK_ON_TEST_OCP;
|
||
} else {
|
||
pReal->sink.work_mode = SINK_WORK_ON_TEST_SHORT;
|
||
}
|
||
pReal->sink.test.test_mode_old = curr_test;
|
||
} else {
|
||
pReal->sub = IDX_SET_SINK_CCLINE;
|
||
pReal->sink.work_mode = SINK_WORK_INIT;
|
||
}
|
||
} else if (pReal->sink.On_work == 0) {
|
||
pReal->sink.On_work = 1;
|
||
pReal->sub = IDX_SET_SINK_CCLINE;
|
||
pReal->sink.work_mode = SINK_WORK_INIT;
|
||
} else if (pro_change) {
|
||
pReal->sub = IDX_SET_SINK_CCLINE;
|
||
pReal->sink.work_mode = SINK_WORK_WAIT;
|
||
} else if (chrg_sink_diff_send_reg((eIDX_SOU_CH)ch,
|
||
&diff_info, pReal) != 0) {
|
||
pReal->CV_mode = old_cv_mode;
|
||
pReal->sink.loadv = old_loadv;
|
||
pReal->sink.loadc = old_loadc;
|
||
return MB_EX_SLAVE_BUSY;
|
||
}
|
||
} else {
|
||
pReal->sink.On_work = 0;
|
||
pReal->sink.work_mode = SINK_WORK_STOP;
|
||
}
|
||
|
||
return (chrg_sink_work((eIDX_SOU_CH)ch, pReal) == 0) ?
|
||
MB_EX_NONE : MB_EX_SLAVE_BUSY;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_comm_apply_source_effect
|
||
函数描述: 处理COM参数生效后的同角色电源启停和参数差异更新
|
||
输入参数: ch:通道 pReal:运行参数 pShadow:待生效影子参数
|
||
输出参数: 更新pReal、Source状态和必要的南向寄存器
|
||
返回说明: MB_EX_NONE:成功 MB_EX_SLAVE_BUSY:命令提交失败
|
||
其它说明: 快充协议只重置北向sub并进入WAIT;标准协议直接更新CV、电压和电流
|
||
*****************************************************************/
|
||
static eMBException chrg_comm_apply_source_effect(rt_uint8_t ch,
|
||
struct chrg_switch_t *pReal, const struct chrg_switch_t *pShadow)
|
||
{
|
||
chrg_param_diff_t diff_info;
|
||
rt_uint8_t old_cv_mode = pReal->CV_mode;
|
||
rt_uint16_t old_voltage = pReal->source.vc.set_voltage;
|
||
rt_uint16_t old_current = pReal->source.vc.set_current;
|
||
rt_uint16_t source_work = pShadow->source.Now_State;
|
||
rt_uint16_t regs[3];
|
||
rt_uint16_t vals[3];
|
||
rt_uint8_t count = 0;
|
||
|
||
chrg_comm_get_param_diff(ID_SOURCE, pReal, pShadow, &diff_info);
|
||
rt_memcpy(pReal, pShadow, sizeof(struct chrg_switch_t));
|
||
|
||
if (source_work == 0x01) {
|
||
if (pReal->source.On_Flag == 0) {
|
||
/* 首次启动仍需初始化功率板、继电器和WORK_START。 */
|
||
pReal->sub = IDX_SET_SOURCE_CC_SET;
|
||
pReal->source.On_Flag = 1;
|
||
pReal->source.work_mode = SOURCE_WORK_INIT;
|
||
return (chrg_source_work_state(ch, pReal) == 0) ?
|
||
MB_EX_NONE : MB_EX_SLAVE_BUSY;
|
||
}
|
||
|
||
/* 协议或CC变化时,从CC设置开始重新执行北向配置链路。 */
|
||
if (diff_info.protocol_changed || diff_info.cc_changed) {
|
||
pReal->sub = IDX_SET_SOURCE_CC_SET;
|
||
}
|
||
|
||
pReal->source.work_mode = SOURCE_WORK_WAIT;
|
||
|
||
if (pReal->source.protocol != PRO_NONE) {
|
||
/* 快充目标值由北向协商回码决定,COM生效阶段不直接写功率板。 */
|
||
return MB_EX_NONE;
|
||
}
|
||
|
||
/* 从快充切到标准协议时强制同步全部直接控制参数。 */
|
||
if (diff_info.protocol_changed || diff_info.cv_changed) {
|
||
regs[count] = REG_MODE;
|
||
vals[count++] = pReal->CV_mode;
|
||
}
|
||
if (diff_info.protocol_changed || diff_info.voltage_changed) {
|
||
regs[count] = REG_VOLTAGE_OUT + 1;
|
||
vals[count++] = pReal->source.vc.set_voltage;
|
||
}
|
||
if (diff_info.protocol_changed || diff_info.current_changed) {
|
||
regs[count] = REG_CURRENT_OUT + 1;
|
||
vals[count++] = pReal->source.vc.set_current;
|
||
}
|
||
|
||
if ((count > 0) && (chrg_sou_com_batch_submit((eIDX_SOU_CH)ch,
|
||
regs, vals, count) != 0)) {
|
||
pReal->CV_mode = old_cv_mode;
|
||
pReal->source.vc.set_voltage = old_voltage;
|
||
pReal->source.vc.set_current = old_current;
|
||
return MB_EX_SLAVE_BUSY;
|
||
}
|
||
pReal->source.work_mode = SOURCE_WORK_RUNING;
|
||
return MB_EX_NONE;
|
||
}
|
||
|
||
pReal->source.On_Flag = 0;
|
||
pReal->source.work_mode = SOURCE_WORK_STOP;
|
||
return (chrg_source_work_state(ch, pReal) == 0) ?
|
||
MB_EX_NONE : MB_EX_SLAVE_BUSY;
|
||
}
|
||
|
||
/*****************************************************************
|
||
函数名称: chrg_mb_write_sys
|
||
函数描述: 处理系统区角色切换和影子参数生效
|
||
输入参数: ch:通道 reg_offset:系统寄存器偏移 sou_value:写入值
|
||
pSW:影子参数 pNOR:北向总控 pSOU:南向总控
|
||
输出参数: 更新角色或将参数分发给对应角色处理函数
|
||
返回说明: MB_EX_NONE:成功 MB_EX_SLAVE_BUSY:命令提交失败
|
||
其它说明: 同角色Source/Sink逻辑分别封装,角色切换仍统一走SWITCH状态
|
||
*****************************************************************/
|
||
static eMBException chrg_mb_write_sys(rt_uint8_t ch, rt_uint16_t reg_offset,
|
||
rt_uint16_t sou_value, struct chrg_switch_t *pSW,
|
||
struct chrg_north_t *pNOR, struct chrg_south_t *pSOU)
|
||
{
|
||
eMBException status;
|
||
struct chrg_switch_t *pReal;
|
||
struct chrg_thread_t *pTHR_NOR;
|
||
eIDX_ID old_id;
|
||
eIDX_ID new_id;
|
||
int switch_ret;
|
||
|
||
(void)pSOU;
|
||
switch (reg_offset) {
|
||
case SYS_REG_ROLE_SWITCH:
|
||
if (sou_value == 0x01) {
|
||
pSW->id = ID_SINK;
|
||
} else if (sou_value == 0x02) {
|
||
pSW->id = ID_SOURCE;
|
||
}
|
||
break;
|
||
|
||
case SYS_REG_PARAM_EFFECT:
|
||
if (sou_value != 0x01) {
|
||
break;
|
||
}
|
||
pReal = &pNOR->sw[ch];
|
||
pTHR_NOR = &chrgthr[IDX_THR_ROLL_NOR];
|
||
rt_mutex_take(pTHR_NOR->mutex, RT_WAITING_FOREVER);
|
||
old_id = pReal->id;
|
||
new_id = pSW->id;
|
||
g_shadow_dirty[ch] = 0;
|
||
|
||
if (old_id != new_id) {
|
||
rt_memcpy(pReal, pSW, sizeof(struct chrg_switch_t));
|
||
if (new_id == ID_SOURCE) {
|
||
pReal->source.work_mode = SOURCE_WORK_SWITCH;
|
||
switch_ret = chrg_source_work_state(ch, pReal);
|
||
} else {
|
||
pReal->sink.work_mode = SINK_WORK_SWITCH;
|
||
switch_ret = chrg_sink_work((eIDX_SOU_CH)ch, pReal);
|
||
}
|
||
status = (switch_ret == 0) ? MB_EX_NONE : MB_EX_SLAVE_BUSY;
|
||
} else if (new_id == ID_SINK) {
|
||
status = chrg_comm_apply_sink_effect(ch, pReal, pSW);
|
||
} else {
|
||
status = chrg_comm_apply_source_effect(ch, pReal, pSW);
|
||
}
|
||
|
||
if (status != MB_EX_NONE) {
|
||
g_shadow_dirty[ch] = 1;
|
||
}
|
||
rt_mutex_release(pTHR_NOR->mutex);
|
||
return status;
|
||
|
||
default:
|
||
rt_kprintf("[SYS CH%d] Unknown reg offset=0x%02X\n", ch + 1, reg_offset);
|
||
break;
|
||
}
|
||
return MB_EX_NONE;
|
||
}
|
||
/*****************************************************************
|
||
函数名称: chrg_sink_diff_send_reg
|
||
函数描述: 根据统一差异结果下发普通负载的CV、电压或电流变化
|
||
输入参数: ch:南向通道 pDiff:统一参数差异 pReal:已生效运行参数
|
||
输出参数: -
|
||
返回说明: 0:无差异或提交成功 <0:南向批量队列提交失败
|
||
其它说明: 协议、组号和CC变化由Sink生效函数重置北向sub,本函数只处理
|
||
不需要重配协议的功率参数
|
||
*****************************************************************/
|
||
static int chrg_sink_diff_send_reg(eIDX_SOU_CH ch,
|
||
const chrg_param_diff_t *pDiff, struct chrg_switch_t *pReal)
|
||
{
|
||
rt_uint16_t regs[2];
|
||
rt_uint16_t vals[2];
|
||
rt_uint8_t count = 0;
|
||
|
||
if (pReal->CV_mode == 0x01) {
|
||
if (pDiff->voltage_changed) {
|
||
regs[count] = REG_VOLTAGE_OUT + 1;
|
||
vals[count++] = pReal->sink.loadv;
|
||
}
|
||
} else if (pDiff->current_changed) {
|
||
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;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
|
||
|