382 lines
9.5 KiB
C
382 lines
9.5 KiB
C
#include "includes.h"
|
||
|
||
// 任务主体
|
||
void Modbus_Task(uint8_t c);
|
||
|
||
// modbus通讯处理
|
||
#pragma pack(push, 1)
|
||
|
||
typedef struct
|
||
{
|
||
uint8_t id; // 通信地址
|
||
uint8_t cmd; // 功能码
|
||
uint16_t reg; // 寄存器地址
|
||
union
|
||
{
|
||
uint16_t cnt; // 读取:寄存器数量
|
||
uint16_t data; // 写入:寄存器数据
|
||
};
|
||
uint16_t crc;
|
||
} modbus_request_t;
|
||
|
||
#pragma pack(pop)
|
||
|
||
// modbus rtu 数据缓冲区
|
||
#define MODBUS_1ST_REG (30001) // 第1个数据地址
|
||
#define MODBUS_LAST_REG (30016) // 最后一个数据地址
|
||
#define MODBUS_ADR_REG (40001) // 通信地址的数据地址
|
||
|
||
// modbus rtu错误代码
|
||
#define MODBUS_ERR_ILF (0x01) // 无效功能码
|
||
#define MODBUS_ERR_ILA (0x02) // 无效通讯地址
|
||
#define MODBUS_ERR_ILD (0x03) // 无效数据值
|
||
#define MODBUS_ERR_FID (0x04) // 执行失败
|
||
#define MODBUS_ERR_ACK (0x05) // ACK(命令已接受并在处理中)
|
||
#define MODBUS_ERR_BUSY (0x06) // 设备忙,拒绝
|
||
#define MODBUS_ERR_NAK (0x07) // NAK(命令无法执行)
|
||
|
||
// modbus rtu 数据缓冲区
|
||
uint8_t MODBUS_RTU_Buf[(MODBUS_LAST_REG - MODBUS_1ST_REG + 1) * 2];
|
||
|
||
void Slave_IRQHandler(USART_Handle *huart)
|
||
{
|
||
uint8_t u8DTU = (uint8_t) huart->Instance->RDR;
|
||
|
||
/* Handle received data */
|
||
Modbus_Task(u8DTU);
|
||
}
|
||
|
||
void Slave_Init()
|
||
{
|
||
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||
|
||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
|
||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
|
||
/**USART3 GPIO Configuration
|
||
PC10 ------> USART3_TX
|
||
PC11 ------> USART3_RX
|
||
PA15 ------> USART3_DE
|
||
*/
|
||
GPIO_InitStruct.Pin = LL_GPIO_PIN_10|LL_GPIO_PIN_11;
|
||
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
|
||
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
|
||
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
|
||
|
||
GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
|
||
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_MEDIUM;
|
||
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
|
||
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||
}
|
||
|
||
/*---------------------------------------------------------------------------------------------------------*/
|
||
/* RS485 Transmit Control (Address Byte: Parity Bit =1 , Data Byte:Parity Bit =0) */
|
||
/*---------------------------------------------------------------------------------------------------------*/
|
||
static void RS485_SendDataByte(uint8_t *pu8TxBuf, uint32_t u32WriteBytes)
|
||
{
|
||
/* Set UART parity as SPACE and skip baud rate setting */
|
||
// UART_SetLine_Config(UART1, 0, UART_WORD_LEN_8, UART_PARITY_SPACE, UART_STOP_BIT_1);
|
||
|
||
/* Send data */
|
||
UART_Transmit_IT(&huart3, pu8TxBuf, u32WriteBytes);
|
||
}
|
||
|
||
void Slave_Open()
|
||
{
|
||
LL_USART_InitTypeDef USART_InitStruct = {0};
|
||
|
||
huart3.RxISR = Slave_IRQHandler;
|
||
|
||
/* Peripheral clock enable */
|
||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);
|
||
|
||
/* USART3 interrupt Init */
|
||
NVIC_SetPriority(USART3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
|
||
NVIC_EnableIRQ(USART3_IRQn);
|
||
|
||
/* USER CODE BEGIN USART3_Init 1 */
|
||
|
||
/* USER CODE END USART3_Init 1 */
|
||
USART_InitStruct.BaudRate = 9600;
|
||
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
|
||
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
|
||
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
|
||
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
|
||
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
|
||
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
|
||
LL_USART_Init(USART3, &USART_InitStruct);
|
||
LL_USART_EnableDEMode(USART3);
|
||
LL_USART_SetDESignalPolarity(USART3, LL_USART_DE_POLARITY_HIGH);
|
||
LL_USART_SetDEAssertionTime(USART3, 0);
|
||
LL_USART_SetDEDeassertionTime(USART3, 0);
|
||
LL_USART_ConfigAsyncMode(USART3);
|
||
|
||
SET_BIT(USART3->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
|
||
LL_USART_Enable(USART3);
|
||
}
|
||
|
||
uint32_t Fill_MODBUS_RTU_Buf()
|
||
{
|
||
uint8_t len = 0; // 发送数据长度
|
||
uint16_t val = 0;
|
||
uint32_t val32 = 0;
|
||
|
||
// 检查数据是否准备好
|
||
if(dcBuff.sampleData.volumeTotal == 0)
|
||
return 0;
|
||
|
||
// 连接状态
|
||
if(dcBuff.sampleData.staPress.notConnect)
|
||
val |= (1 << 0);
|
||
if(dcBuff.sampleData.staExtTempr[0].notConnect)
|
||
val |= (1 << 2);
|
||
if(dcBuff.sampleData.staDPress.notConnect)
|
||
val |= (1 << 4);
|
||
// 液位传感器类型
|
||
if(dcBuff.configDisplay.op_USE_CAPACITY_SENSOR)
|
||
val |= (1 << 6);
|
||
// 测量值状态
|
||
if(dcBuff.sampleData.staPress.underFlow)
|
||
val |= (1 << 8);
|
||
else if(dcBuff.sampleData.staPress.overFlow)
|
||
val |= (2 << 8);
|
||
|
||
if(dcBuff.sampleData.staExtTempr[0].underFlow)
|
||
val |= (1 << 10);
|
||
else if(dcBuff.sampleData.staExtTempr[0].overFlow)
|
||
val |= (2 << 10);
|
||
|
||
if(dcBuff.sampleData.staDPress.underFlow)
|
||
val |= (1 << 12);
|
||
else if(dcBuff.sampleData.staDPress.overFlow)
|
||
val |= (2 << 12);
|
||
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 差压
|
||
val = (uint16_t) dcBuff.sampleData.diff;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 容积百分比
|
||
val = (uint16_t) dcBuff.sampleData.volumePct;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 体积
|
||
val32 = dcBuff.sampleData.volume;
|
||
val32 = htonl(val32);
|
||
memmove(MODBUS_RTU_Buf + len, &val32, 4);
|
||
len += 4;
|
||
|
||
// 重量
|
||
val32 = dcBuff.sampleData.weight;
|
||
val32 = htonl(val32);
|
||
memmove(MODBUS_RTU_Buf + len, &val32, 4);
|
||
len += 4;
|
||
|
||
// 高度:mmWC 或 mm
|
||
if(!dcBuff.configDisplay.op_USE_CAPACITY_SENSOR && !dcBuff.configDisplay.op_USE_HEIGHT_LEVEL && !dcBuff.configDisplay.op_USE_PCT_LEVEL)
|
||
val = (uint16_t) KPa2mmH2O(dcBuff.sampleData.diff);
|
||
else
|
||
val = (uint16_t) dcBuff.sampleData.height;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 压力
|
||
val = (uint16_t) dcBuff.sampleData.pressure;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 温度: 有符号数
|
||
memmove(&val, &dcBuff.sampleData.extTempr[0], 2);
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 介质
|
||
val = (uint16_t) dcBuff.configBottle.source;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 总有效容积
|
||
val32 = dcBuff.sampleData.volumeTotal;
|
||
val32 = htonl(val32);
|
||
memmove(MODBUS_RTU_Buf + len, &val32, 4);
|
||
len += 4;
|
||
|
||
// 真空计状态
|
||
val = 0;
|
||
if(dcBuff.sampleData.vacuum[0].staVacuum == VACUUM_STATUS_COMM_FAULT)
|
||
val |= 1;
|
||
else
|
||
{
|
||
if(dcBuff.sampleData.vacuum[0].staVacuum == VACUUM_STATUS_FAULT)
|
||
val |= (3 << 2);
|
||
else if(dcBuff.sampleData.vacuum[0].vacuum > 99.99999999)
|
||
val |= (2 << 2);
|
||
else if(dcBuff.sampleData.vacuum[0].vacuum <= 0.00999999)
|
||
val |= (1 << 2);
|
||
}
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 真空度
|
||
val = dcBuff.sampleData.vacuum[0].vacuum * 100;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
// 真空计温度
|
||
val = (int16_t) dcBuff.sampleData.vacuum[0].tempr;
|
||
val = htons(val);
|
||
memmove(MODBUS_RTU_Buf + len, &val, 2);
|
||
len += 2;
|
||
|
||
return 1;
|
||
}
|
||
|
||
// 处理modbus rtu命令(已经验证过长度和CRC的包)
|
||
void MODBUS_RTU_CMD(modbus_request_t *req)
|
||
{
|
||
static uint8_t BUFF[(MODBUS_LAST_REG - MODBUS_1ST_REG + 1) * 2 + 6]; // 响应包
|
||
uint8_t err = 0; // 错误代码
|
||
uint8_t len = 0; // 发送数据长度
|
||
|
||
uint16_t id; // 通信地址
|
||
uint16_t reg; // 数据地址
|
||
uint16_t cnt; // 数据个数
|
||
uint16_t crc;
|
||
|
||
// 检查通信地址
|
||
if(req->id != dcBuff.configBottle.addr && req->id != 0)
|
||
return;
|
||
|
||
// 清空发送帧
|
||
memset(BUFF, 0, sizeof(BUFF));
|
||
|
||
BUFF[len++] = dcBuff.configBottle.addr; // 从机地址
|
||
BUFF[len++] = req->cmd; // 命令代码
|
||
|
||
reg = ntohs(req->reg); // 数据寄存器
|
||
if(req->cmd == 0x03 || req->cmd == 0x06)
|
||
reg += 40001;
|
||
else if(req->cmd == 0x04)
|
||
reg += 30001;
|
||
else
|
||
err = MODBUS_ERR_ILF; // 无效功能码
|
||
|
||
if(err == 0)
|
||
{
|
||
if(req->cmd == 0x03) // 读保持数据
|
||
{
|
||
cnt = ntohs(req->cnt); // 参数个数
|
||
if(reg != MODBUS_ADR_REG || cnt != 1)
|
||
err = MODBUS_ERR_NAK; // 命令无法执行
|
||
else
|
||
{
|
||
// 用最新的通讯地址刷新缓冲
|
||
id = htons(dcBuff.configBottle.addr);
|
||
BUFF[len++] = 2; // 数据长度
|
||
memmove(BUFF + len, (uint8_t *) &id, 2);
|
||
len += 2;
|
||
}
|
||
}
|
||
else if(req->cmd == 0x06) // 设置数据
|
||
{
|
||
if(reg != MODBUS_ADR_REG)
|
||
err = MODBUS_ERR_NAK; // 命令无法执行
|
||
else
|
||
{
|
||
id = ntohs(req->data);
|
||
|
||
if(id < 1 || id > 247)
|
||
err = MODBUS_ERR_ILD; // 无效数据值
|
||
else
|
||
{
|
||
// 设置地址并保存
|
||
dcBuff.configBottle.addr = id;
|
||
Config_SaveConfig();
|
||
|
||
BUFF[0] = id; // 新地址
|
||
memmove(BUFF + len, &req->reg, 4); // 数据地址和通讯地址
|
||
len += 4;
|
||
}
|
||
}
|
||
}
|
||
else if(req->cmd == 0x04) // 读输入数据
|
||
{
|
||
cnt = ntohs(req->cnt); // 参数个数
|
||
if(reg < MODBUS_1ST_REG || reg > MODBUS_LAST_REG || cnt < 1 || reg + (cnt - 1) > MODBUS_LAST_REG)
|
||
err = MODBUS_ERR_NAK; // 命令无法执行
|
||
else
|
||
{
|
||
// 用最新的采集数据刷新缓冲
|
||
if(!Fill_MODBUS_RTU_Buf())
|
||
return;
|
||
|
||
BUFF[len++] = cnt * 2; // 数据长度
|
||
memmove(BUFF + len, MODBUS_RTU_Buf + (reg - MODBUS_1ST_REG) * 2, cnt * 2);
|
||
len += cnt * 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
if(err != 0)
|
||
{
|
||
BUFF[1] |= 0x80; // 错误指示
|
||
BUFF[len++] = err; // 错误代码
|
||
}
|
||
|
||
// 计算CRC
|
||
crc = MODBUS_RTU_CRC16(BUFF, len);
|
||
crc = htons(crc);
|
||
|
||
memmove(BUFF + len, &crc, 2);
|
||
len += 2;
|
||
|
||
if(req->id != 0)
|
||
{
|
||
// 发送应答
|
||
RS485_SendDataByte(BUFF, len);
|
||
}
|
||
}
|
||
|
||
// 任务主体
|
||
void Modbus_Task(uint8_t c)
|
||
{
|
||
static uint8_t BUFF[sizeof(modbus_request_t)] = {0};
|
||
static uint8_t len = 0;
|
||
|
||
// 取数据
|
||
BUFF[len++] = c;
|
||
|
||
// 验证长度和CRC值
|
||
if(len == sizeof(modbus_request_t))
|
||
{
|
||
if(MODBUS_RTU_CRC16(BUFF, sizeof(modbus_request_t)) == 0)
|
||
{
|
||
MODBUS_RTU_CMD((modbus_request_t *) BUFF);
|
||
len = 0;
|
||
}
|
||
else
|
||
{
|
||
// 丢掉第一个字节
|
||
memmove(BUFF, BUFF + 1, --len);
|
||
}
|
||
}
|
||
}
|