jiecang 推杆小板代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
11 KiB

/*
* modbus.c
*
* Created on: Jul 3, 2025
* Author: shiyanlei
*/
#include "stm32g0xx_hal.h"
#include <stdint.h>
#include <stdbool.h>
#include "main.h"
#include "modbus.h"
#include "usart.h"
//uint16_t holdingRegisters[REGISTER_COUNT] = {0}; /*长度为10*/
int16_t holdingRegisters[REGISTER_COUNT] = {0}; /*长度为10*/
/* 寄存器可写标志位(1:可写,0:只读) */
uint8_t registerWritable[REGISTER_COUNT] = {
registerWritable_0, registerWritable_1, registerWritable_2, registerWritable_3,
registerWritable_4, registerWritable_5, registerWritable_6, registerWritable_7,
registerWritable_8, registerWritable_9
};
/* 全局变量 */
uint8_t modbusBuffer[MODBUS_BUFFER_SIZE];
//int8_t modbusBuffer[MODBUS_BUFFER_SIZE];
uint16_t modbusBufferIndex = 0;
uint32_t lastByteTime = 0;
bool messageComplete = false;
uint8_t response[256];
/* 函数声明 */
void Modbus_Init(void);
void Modbus_Process(void);
void Modbus_SendResponse(uint8_t *data, uint8_t length);
void Modbus_HandleFunction03(uint16_t startAddress, uint16_t registerCount);
void Modbus_HandleFunction06(uint16_t registerAddress, uint16_t value);
void Modbus_HandleFunction10(uint16_t startAddress, uint16_t registerCount, uint8_t *data);
void Modbus_SendExceptionResponse(uint8_t exceptionCode);
uint16_t CalculateCRC(uint8_t *data, uint8_t length);
/* 初始化函数 */
void Modbus_Init(void)
{
/* RS485 方向控制引脚置为接收状态 */
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET);
/* 启动串口中断接收 */
modbusBufferIndex = 0;
HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1);
}
/* Modbus处理函数 - 应在主循环中调用 */
void Modbus_Process(void)
{
uint16_t startAddress = 0;
uint16_t registerCount = 0;
uint16_t value = 0;
uint8_t byteCount = 0;
uint16_t crcReceived;
uint16_t crcCalculated;
if (modbusBufferIndex > 0 && (HAL_GetTick() - lastByteTime) > MODBUS_TIMEOUT_MS)
{
/* 超时,标记消息完成 */
messageComplete = true;
}
if (!messageComplete)
{
return;
}
/* 一个有效的 RTU 帧至少包含:地址 + 功能码 + 2 字节 CRC */
if (modbusBufferIndex < 4)
{
modbusBufferIndex = 0;
messageComplete = false;
return;
}
crcReceived = (uint16_t)((modbusBuffer[modbusBufferIndex - 1] << 8) |
modbusBuffer[modbusBufferIndex - 2]);
crcCalculated = CalculateCRC(modbusBuffer, (uint8_t)(modbusBufferIndex - 2));
if (MODBUS_CRC_OFF == 1 || crcReceived == crcCalculated)
{
if (modbusBuffer[0] == MODBUS_SLAVE_ADDRESS) /*ID 0x42*/
{
switch (modbusBuffer[1])
{
case MODBUS_FUNCTION_CODE_03:
if (modbusBufferIndex >= 8)
{
startAddress = (uint16_t)((modbusBuffer[2] << 8) | modbusBuffer[3]);
registerCount = (uint16_t)((modbusBuffer[4] << 8) | modbusBuffer[5]);
Modbus_HandleFunction03(startAddress, registerCount);
}
break;
case MODBUS_FUNCTION_CODE_06:
if (modbusBufferIndex >= 8)
{
startAddress = (uint16_t)((modbusBuffer[2] << 8) | modbusBuffer[3]);
value = (uint16_t)((modbusBuffer[4] << 8) | modbusBuffer[5]);
Modbus_HandleFunction06(startAddress, value);
}
break;
case MODBUS_FUNCTION_CODE_10:
if (modbusBufferIndex >= 9)
{
startAddress = (uint16_t)((modbusBuffer[2] << 8) | modbusBuffer[3]);
registerCount = (uint16_t)((modbusBuffer[4] << 8) | modbusBuffer[5]);
byteCount = modbusBuffer[6];
/* 检查数据长度是否正确 */
if (byteCount == (uint8_t)(registerCount * 2U) &&
(modbusBufferIndex - 2U) >= (uint16_t)(7U + byteCount))
{
Modbus_HandleFunction10(startAddress, registerCount, &modbusBuffer[7]);
}
else
{
Modbus_SendExceptionResponse(0x03);
}
}
break;
default:
Modbus_SendExceptionResponse(0x01);
break;
}
}
}
/* 重置缓冲区 */
modbusBufferIndex = 0;
messageComplete = false;
}
/* 处理功能码03 */
void Modbus_HandleFunction03(uint16_t startAddress, uint16_t registerCount)
{
/* 检查地址和数量是否合法 */
if ((startAddress + registerCount) > (REGISTER_START_ADDRESS + REGISTER_COUNT) ||
registerCount > 0x7D)
{
Modbus_SendExceptionResponse(0x02);
return;
}
uint8_t byteCount = (uint8_t)(registerCount * 2U);
uint8_t index = 0;
response[index++] = MODBUS_SLAVE_ADDRESS;
response[index++] = MODBUS_FUNCTION_CODE_03;
response[index++] = byteCount;
for (uint16_t i = 0; i < registerCount; i++)
{
uint16_t regValue = holdingRegisters[(startAddress - REGISTER_START_ADDRESS) + i];
response[index++] = (uint8_t)((regValue >> 8) & 0xFF);
response[index++] = (uint8_t)(regValue & 0xFF);
}
uint16_t crc = CalculateCRC(response, index);
response[index++] = (uint8_t)(crc & 0xFF);
response[index++] = (uint8_t)((crc >> 8) & 0xFF);
Modbus_SendResponse(response, index);
}
/* 处理功能码06 */
void Modbus_HandleFunction06(uint16_t registerAddress, uint16_t value)
{
/* 检查地址是否合法 */
if (registerAddress >= (REGISTER_START_ADDRESS + REGISTER_COUNT))
{
Modbus_SendExceptionResponse(0x02);
return;
}
uint16_t regIndex = registerAddress - REGISTER_START_ADDRESS;
uint16_t responseValue = holdingRegisters[regIndex];
/* 只有可写寄存器才更新值 */
if (registerWritable[regIndex] == 1)
{
holdingRegisters[regIndex] = value;
responseValue = value;
}
/* 准备响应帧 - 功能码06的响应是原请求帧的原样返回 */
uint8_t index = 0;
response[index++] = MODBUS_SLAVE_ADDRESS;
response[index++] = MODBUS_FUNCTION_CODE_06;
response[index++] = (uint8_t)((registerAddress >> 8) & 0xFF);
response[index++] = (uint8_t)(registerAddress & 0xFF);
response[index++] = (uint8_t)((responseValue >> 8) & 0xFF);
response[index++] = (uint8_t)(responseValue & 0xFF);
/* 计算CRC并添加到响应帧 */
uint16_t crc = CalculateCRC(response, index);
response[index++] = (uint8_t)(crc & 0xFF);
response[index++] = (uint8_t)((crc >> 8) & 0xFF);
/* 发送响应 */
Modbus_SendResponse(response, index);
}
/* 处理功能码0x10 */
void Modbus_HandleFunction10(uint16_t startAddress, uint16_t registerCount, uint8_t *data)
{
/* 检查地址和数量是否合法 */
if ((startAddress + registerCount) > (REGISTER_START_ADDRESS + REGISTER_COUNT) ||
registerCount == 0 || registerCount > 0x7B)
{
Modbus_SendExceptionResponse(0x02);
return;
}
/* 处理每个寄存器 */
for (uint16_t i = 0; i < registerCount; i++)
{
uint16_t regIndex = (startAddress - REGISTER_START_ADDRESS) + i;
uint16_t value = (uint16_t)((data[i * 2] << 8) | data[i * 2 + 1]);
/* 只有可写寄存器才更新值 */
if (registerWritable[regIndex] == 1)
{
holdingRegisters[regIndex] = value;
}
}
/* 准备响应帧 - 功能码0x10的响应包含起始地址和写入数量 */
uint8_t index = 0;
response[index++] = MODBUS_SLAVE_ADDRESS;
response[index++] = MODBUS_FUNCTION_CODE_10;
response[index++] = (uint8_t)((startAddress >> 8) & 0xFF);
response[index++] = (uint8_t)(startAddress & 0xFF);
response[index++] = (uint8_t)((registerCount >> 8) & 0xFF);
response[index++] = (uint8_t)(registerCount & 0xFF);
/* 计算CRC并添加到响应帧 */
uint16_t crc = CalculateCRC(response, index);
response[index++] = (uint8_t)(crc & 0xFF);
response[index++] = (uint8_t)((crc >> 8) & 0xFF);
/* 发送响应 */
Modbus_SendResponse(response, index);
}
/* 发送异常响应 */
void Modbus_SendExceptionResponse(uint8_t exceptionCode)
{
response[0] = MODBUS_SLAVE_ADDRESS;
response[1] = modbusBuffer[1] | 0x80;
response[2] = exceptionCode;
uint16_t crc = CalculateCRC(response, 3);
response[3] = (uint8_t)(crc & 0xFF);
response[4] = (uint8_t)((crc >> 8) & 0xFF);
Modbus_SendResponse(response, 5);
}
/* 发送响应 */
void Modbus_SendResponse(uint8_t *data, uint8_t length)
{
/* 使能RS485发送 */
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_SET);
/* DMA 发送数据,发送完成后在 HAL_UART_TxCpltCallback 中切回接收 */
HAL_UART_Transmit_DMA(&huart2, data, length);
}
/* CRC计算 */
uint16_t CalculateCRC(uint8_t *data, uint8_t length)
{
uint16_t crc = 0xFFFF;
for (uint8_t i = 0; i < length; i++)
{
crc ^= (uint16_t)data[i];
for (uint8_t j = 0; j < 8; j++)
{
if ((crc & 0x0001) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
/* UART接收中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart != &huart2)
{
return;
}
lastByteTime = HAL_GetTick();
if (modbusBufferIndex < MODBUS_BUFFER_SIZE)
{
/* HAL 已经将接收到的字节写入 modbusBuffer[modbusBufferIndex],
此处只需向后移动索引。 */
modbusBufferIndex++;
}
if (modbusBufferIndex < MODBUS_BUFFER_SIZE)
{
HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1);
}
else
{
/* 缓冲区满,丢弃并等待下一帧 */
modbusBufferIndex = 0;
HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1);
}
}
/* UART发送完成回调:切回RS485接收方向 */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2)
{
HAL_GPIO_WritePin(DIR_GPIO_Port, DIR_Pin, GPIO_PIN_RESET);
}
}
/* UART错误回调:重新同步接收 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2)
{
modbusBufferIndex = 0;
HAL_UART_Receive_IT(&huart2, (uint8_t *)&modbusBuffer[modbusBufferIndex], 1);
}
}