单片机
返回首页

stm32 DMA+IDLE modbus_rtu

2025-11-05 来源:bilibili

没有做3.5T延时判断,直接用IDLE中断,所以时间间距会更小。

此程序只使用了两个功能码:03查询数据,06修改从机地址。

需要发送的数据直接放在data[]数组里面,协议直接从此数组中取数据然后发送。

00为广播地址,当忘记从机地址时使用此地址发送命令修改从机地址。

modbus_slave.c

#include "stm32f10x.h"

#include "modbus_slave.h"

#include "bsp_usart_dma.h"

#include "bsp_user_lib.h"

static void Modbus_SendWithCRC(uint8_t *_pBuf, uint8_t Len);

static void Modbus_SendAckErr(uint8_t ErrCode);

static void Modbus_AnalyzeApp(void);

static void Modbus_03H(void);

static void Modbus_06H(void);

u8 RspCode;

u8 data[DATA_SIZE];

static void Modbus_SendWithCRC(uint8_t *_pBuf, uint8_t Len)

{

    uint16_t crc;

    crc = CRC16_Modbus(_pBuf, Len);

    TxBuffer[Len++] = crc >> 8;

    TxBuffer[Len++] = crc;

    uart_dma_send_enable(Len);

}

static void Modbus_SendAckErr(uint8_t ErrCode)

{

    TxBuffer[0] = RxBuffer[0];        //从机地址

    TxBuffer[1] = RxBuffer[1] | 0x80; //异常的功能码

    TxBuffer[2] = ErrCode;            // 错误代码

    Modbus_SendWithCRC(TxBuffer, 3);

}

void Modbus_Poll(void)

{

    uint16_t crc16;

    // Modbus命令至少需要包括从机号、命令号和CRC字段,否则是无效帧

    if (RxBufferSize < 4)

    {

        goto end;

    }

    // 从机地址检查 0是广播地址

    if ((RxBuffer[0] != (FLASH_R(FLASH_START_ADDR) & 0xff)) && (RxBuffer[0] != 0))

    {

        goto end;

    }

    // 帧校验检查

    if (CRC16_Modbus(RxBuffer, RxBufferSize) != 0)

    {

        Modbus_SendAckErr(MODBUS_CRC_ERROR);

        goto end;

    }

    switch (RxBuffer[1]) // 功能码

    {

    case 0x03: //读取数据

        Modbus_03H();

        break;

    case 0x06: //写入从机地址

        Modbus_06H();

        break;

    default:

        Modbus_SendAckErr(MODBUS_ILLEGAL_CMD); //返回功能码错误

        break;

    }

end:;

}

static void Modbus_03H(void)

{

    uint16_t u16Addr;

    uint16_t u16Num;

    uint16_t i;

    RspCode = MODBUS_OK;

    if (RxBufferSize != 8) /* 03H命令必须是8个字节 */

    {

        RspCode = MODBUS_ILLEGAL_DATA; /* 数据值域错误 */

        goto ret;

    }

    u16Addr = BEBufToUint16(&RxBuffer[2]); /* 寄存器号 */

    u16Num = BEBufToUint16(&RxBuffer[4]);  /* 寄存器个数 */

    if ((u16Addr < 0) || (u16Addr > (DATA_SIZE - 5))) //1位从机地址+1位功能码+1位寄存器数+2位crc校验码

    {

        RspCode = MODBUS_ILLEGAL_ADDR; /* 寄存器地址错误 */

        goto ret;

    }

    if ((u16Num > ((DATA_SIZE - 5) / 2)) || (u16Num == 0))

    {

        RspCode = MODBUS_ILLEGAL_DATA; /* 数据值域错误 */

        goto ret;

    }

ret:

    if (RspCode == MODBUS_OK) /* 正确应答 */

    {

        uint16_t TxCount = 0;

        uint16_t j = u16Addr;

        TxBuffer[TxCount++] = FLASH_R(FLASH_START_ADDR) & 0xff; //从机地址

        TxBuffer[TxCount++] = RxBuffer[1];                      //功能码

        TxBuffer[TxCount++] = u16Num * 2;                       // 返回字节数

//        TxBuffer[TxCount++] = 0x00;

//        TxBuffer[TxCount++] = 0x01;  // 传感器类型

//        for (i = 0; i < (u16Num - 1); i++) //(u16Num-1) 1个字节固定用于传感器类型

        for (i = 0; i < u16Num; i++)

        {

            TxBuffer[TxCount++] = data[j++];

            TxBuffer[TxCount++] = data[j++];

        }

        Modbus_SendWithCRC(TxBuffer, TxCount); /* 发送正确应答 */

    }

    else

    {

        Modbus_SendAckErr(RspCode); /* 发送错误应答 */

    }

}

static void Modbus_06H(void)

{

    uint16_t u16Addr;

    uint16_t u16Num;

    uint16_t i;

    RspCode = MODBUS_OK;

    if (RxBufferSize != 8) /* 03H命令必须是8个字节 */

    {

        RspCode = MODBUS_ILLEGAL_DATA; /* 数据值域错误 */

        goto ret;

    }

    u16Addr = BEBufToUint16(&RxBuffer[2]); /* 寄存器号 */

    u16Num = BEBufToUint16(&RxBuffer[4]);  /* 寄存器个数 */

    if ((u16Addr < 0) || (u16Addr > (DATA_SIZE - 5))) //1位从机地址+1位功能码+1位寄存器数+2位crc校验码

    {

        RspCode = MODBUS_ILLEGAL_ADDR; /* 寄存器地址错误 */

        goto ret;

    }

    if ((u16Num > ((DATA_SIZE - 5) / 2)) || (u16Num == 0))

    {

        RspCode = MODBUS_ILLEGAL_DATA; /* 数据值域错误 */

        goto ret;

    }

ret:

    if (RspCode == MODBUS_OK) /* 正确应答 */

    {

        uint16_t TxCount = 0;

        uint16_t j = 0;

        FLASH_W(FLASH_START_ADDR, ((uint16_t)(RxBuffer[4] << 8) | RxBuffer[5])); //写从机地址

        TxBuffer[TxCount++] = FLASH_R(FLASH_START_ADDR) & 0xff;                  //从机地址,上电初始化为0,即广播地址

        TxBuffer[TxCount++] = RxBuffer[1];                                       //功能码

        //此程序中06号功能码只用于写从机地址,所以寄存器地址可以为任意数

        TxBuffer[TxCount++] = RxBuffer[2];                                       //寄存器地址高8位

        TxBuffer[TxCount++] = RxBuffer[3];                                       //寄存器地址低8位

        TxBuffer[TxCount++] = RxBuffer[4];                                       //寄存器数据高8位

        TxBuffer[TxCount++] = RxBuffer[5];                                       //寄存器数据低8位

        Modbus_SendWithCRC(TxBuffer, TxCount); /* 发送正确应答 */

    }

    else

    {

        Modbus_SendAckErr(RspCode); /* 发送错误应答 */

    }

}

modbus_slave.h

#ifndef __MODBUY_SLAVE_H

#define __MODBUY_SLAVE_H

#include "stm32f10x.h"

#define MODBUS_OK 0           // 其它未定义的错误

#define MODBUS_ILLEGAL_CMD 1  // 不支持的功能码

#define MODBUS_ILLEGAL_ADDR 2 // 非法数据地址,主站试图读取非法的寄存器地址

#define MODBUS_ILLEGAL_DATA 3 // 非法数据值,数据不完整或字节数错

#define MODBUS_CRC_ERROR 4    // CRC错误

#define MODBUS_ADDR_NUM 2

#define DATA_SIZE (DMA_SIZE - 5)

extern u8 data[];

void Modbus_Poll(void);

#endif


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦四级调频发射机

  • 500W MOS场效应管电源逆变器,12V转110V/220V

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • 12V转110V/220V 500W逆变器

  • DS1669数字电位器

    相关电子头条文章