ZNY_Pakistan/Anjiehui7_ZNY/User/modbus_slave.c

382 lines
9.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}
}
}