Modbus协议每次发送的内容根据传输模式不同,其报文结构也会有所差异,主要分为RTU、ASCII、TCP三种。这些模式在帧分隔、编码、数据域和校验方式上各有不同。

模式 典型场景 编码方式 核心区分特征
RTU 工业现场、PLC、变频器 二进制(紧凑高效) 依靠时间间隔(3.5字符)分隔帧,使用 CRC-16 校验
ASCII 系统调试、人眼分析数据 ASCII文本(可读性强) 使用 冒号(:) 和 CRLF 标识帧,使用 LRC 校验
TCP 现代工业网络、物联网、高速采集 二进制(最高效) 依赖 TCP 可靠性,无额外校验,通过 MBAP 报文头替代RTU帧头

在通信中,主站(客户端)发送请求报文,从站(服务器)处理后返回响应报文。异常响应时,功能码会加上0x80,例如原本的功能码0x01会变为0x81。

Modbus RTU 模式

RTU是工业中最常用的模式,采用紧凑的二进制编码,数据密度高。

  • 帧分隔:没有固定的起始/结束字符,依靠总线上的静默时间(至少3.5个字符时间)来分隔报文。

  • 报文结构:完整的RTU报文由地址、功能码、数据、CRC四个部分组成,并以特定顺序发送。

  • CRC校验:使用CRC-16循环冗余校验,保障传输数据的完整性。

  • 报文示例01 03 00 00 00 02 C4 0B

    字节 数值 (Hex) 含义
    1 01 地址:目标从站地址为 1
    2 03 功能码:读保持寄存器 (Read Holding Registers)
    3 00 数据域
    4 00 数据域:寄存器起始地址为 0x0000
    5 00 数据域
    6 02 数据域:读取 2 个寄存器
    7-8 C4 0B CRC校验:低字节 0xC4 在前,高字节 0x0B 在

    帧长度限制:整个RTU帧的最大长度为256字节。因此,数据域(N)的最大长度受此限制,通常为252字节(256 – 地址1 – 功能码1 – CRC2)。

  • 时序要求

      帧间间隔 (Inter-frame Delay) :前后两帧之间必须保持至少3.5个字符时间的静默期。接收方以此来判断一帧的结束和新一帧的开始。

      帧内间隔:帧内各个字节之间的传输间隔应小于1.5个字符时间,否则接收方可能认为帧传输不完整。

      “字符时间”取决于波特率(Baud Rate)。例如,在9600波特率下,传输1个字符(包括起始位、数据位、停止位,通常11位)的时间约为1.14ms。因此,3.5个字符时间约为4ms。

CRC表计算方式:

所有的 CRC 算法背后,都有一个行业规定好的方法(专业术语叫:生成多项式)。 对于 Modbus 协议来说,这个方法规定死了是:0xA001

生成表里任意一个数字(比如索引 ii 的范围是 0~255)的规则如下,一共分三步:

  1. 把索引 i 放进一个 16位 的大盒子里

  2. 循环 8 次(因为一个字节有 8 位)

    • 看看盒子最右边的那一位(最低位)是 1 还是 0

    • 把盒子里所有的数字向右推移一位(最右边的那个数字被挤掉出去了)。

    • 如果刚才被挤出去的那个数字是 1:就把盒子里的数字拿出来,和魔法数字 0xA001 做一次“异或(XOR)”运算,放回盒子里。

    • 如果被挤出去的数字是 0:什么都不做,继续下一次循环。

  3. 8 次循环结束后,盒子里剩下的 16位 数字,就是这个索引最终的 CRC 结果。把它劈成两半,高八位存入 TabH,低八位存入 TabL

先准备好咱们的初始条件:

  • 初始盒子里的数字: 10(十六进制写成 0x000A,二进制写成 0000 0000 0000 1010)。

  • 方法(多项式): 0xA001(二进制写成 1010 0000 0000 0001)。

接下来,开启 8 次“看尾巴 -> 推移 -> 异或”的循环:

  • 第 1 次循环: 当前盒子:0000 0000 0000 1010

  • 👉 看尾巴: 最右边是 0

  • 👉 动作: 因为是 0,只向右推移一位,不异或。最左边补 0。

  • ➡️ 结果: 0000 0000 0000 0101(十六进制 0x0005

  • 第 2 次循环: 当前盒子:0000 0000 0000 0101

  • 👉 看尾巴: 最右边是 1

  • 👉 动作: 既然是 1,就要搞大动作了!

    1. 先向右推移一位:变成 0000 0000 0000 00100x0002

    2. 再跟魔法数字 0xA001 进行“异或”运算:   0000 0000 0000 0010  ^ 1010 0000 0000 0001 (异或规则:相同为0,不同为1)  

    3. ➡️ 结果: 1010 0000 0000 0011(十六进制 0xA003

  • 第 3 次循环: 当前盒子:1010 0000 0000 0011

  • 👉 看尾巴: 最右边还是 1。继续大动作!

    1. 先向右推移一位:变成 0101 0000 0000 00010x5001

    2. 再跟魔法数字 0xA001 异或:   0101 0000 0000 0001  ^ 1010 0000 0000 0001  

    3. ➡️ 结果: 1111 0000 0000 0000(十六进制 0xF000

  • 第 4 次循环: 当前盒子:1111 0000 0000 0000

  • 👉 看尾巴: 最右边是 0

  • 👉 动作: 太平了,只向右推移一位。

  • ➡️ 结果: 0111 1000 0000 0000(十六进制 0x7800

  • 第 5 次循环: 当前盒子:0111 1000 0000 0000(看最右边是0,只移位)

  • ➡️ 结果: 0011 1100 0000 0000(十六进制 0x3C00

  • 第 6 次循环: 当前盒子:0011 1100 0000 0000(看最右边是0,只移位)

  • ➡️ 结果: 0001 1110 0000 0000(十六进制 0x1E00

  • 第 7 次循环: 当前盒子:0001 1110 0000 0000(看最右边是0,只移位)

  • ➡️ 结果: 0000 1111 0000 0000(十六进制 0x0F00

  • 第 8 次循环: 当前盒子:0000 1111 0000 0000(看最右边是0,最后一次移位)

  • ➡️ 结果: 0000 0111 1000 0000(十六进制 0x0780

见证奇迹的时刻

8 次循环终于跑完了,最后留在盒子里的 16位 最终结果是:0x0780

我们把它一刀劈成两半:

  • 高八位是:0x07

  • 低八位是:0x80

代码实现

modbus.c

#include "modbus.h"
#include "uart.h" 

// 初始化变量
Uint16 regGroup[100] = {0};
Uint16 modbus_rx_buf[MODBUS_BUF_SIZE];
Uint16 modbus_tx_buf[MODBUS_BUF_SIZE];
Uint16 modbus_rx_cnt = 0;
Uint16 modbus_rx_ready = 0;
Uint16 modbus_idle_timer = 0;

// --- CRC 表 (使用静态数组节省空间) ---
static const Uint16 TabH[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};

static const Uint16 TabL[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04,
    0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
    0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
    0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
    0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0,
    0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,
    0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
    0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98,
    0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};

// --- CRC16 计算函数 ---
Uint16 GetCRC16(Uint16 *ptr, Uint16 len)
{
    Uint16 index;
    Uint16 crch = 0xFF;
    Uint16 crcl = 0xFF;
    while (len--)
    {
        index = crch ^ (*ptr++ & 0xFF);
        crch = crcl ^ TabH[index];
        crcl = TabL[index];
    }
    return ((crch << 8) | crcl);
}

// --- 初始化一些测试数据供电脑读取 ---
void Modbus_Init_Regs(void)
{
    regGroup[0] = 10000; // 地址 0: 目标功率 (例如 10kW = 10000W)
    regGroup[1] = 2906;  // 地址 1: 谐振频率 (例如 2906Hz)
    regGroup[2] = 1;     // 地址 2: 运行状态 (1:运行, 0:停机)

}

// --- 核心:处理接收到的一帧完整报文 ---
void Modbus_Process(void)
{
    Uint16 i, cnt;
    Uint16 crc_cal, crc_recv;
    Uint16 tx_size = 0;
    Uint16 reg_addr, reg_val;

    // 1. 如果没有收到完整的一句话,或者数据太短,直接退出
    if (modbus_rx_ready == 0 || modbus_rx_cnt < 8) return;

    // 2. 检查设备地址是不是呼叫本机
    if (modbus_rx_buf[0] != SLAVE_ADDR) goto clear_frame;

    // 3. 校验 CRC 是否正确
    crc_cal = GetCRC16(modbus_rx_buf, modbus_rx_cnt - 2);

    // 按照 (低位<<8 | 高位) 来拼接接收到的 CRC
    crc_recv = (modbus_rx_buf[modbus_rx_cnt - 2] << 8) | modbus_rx_buf[modbus_rx_cnt - 1];

    // DSP字节序验证
    if (crc_cal != crc_recv) goto clear_frame;

    // 4. 解析功能码
    switch (modbus_rx_buf[1])
    {
        case 0x03:  // 读取保持寄存器
            reg_addr = (modbus_rx_buf[2] << 8) | modbus_rx_buf[3]; // 寄存器起始地址
            cnt = (modbus_rx_buf[4] << 8) | modbus_rx_buf[5];      // 读取数量

            // 安全限制:防止读取越界
            if ((reg_addr + cnt) <= 100)
            {
                modbus_tx_buf[0] = SLAVE_ADDR;
                modbus_tx_buf[1] = 0x03;
                modbus_tx_buf[2] = cnt * 2;  // 返回的字节数
                tx_size = 3;

                for(i = 0; i < cnt; i++)
                {
                    modbus_tx_buf[tx_size++] = (regGroup[reg_addr + i] >> 8) & 0xFF; // 寄存器高八位
                    modbus_tx_buf[tx_size++] = regGroup[reg_addr + i] & 0xFF;        // 寄存器低八位
                }
            }
            break;

        case 0x06:  // 写入单个寄存器
            reg_addr = (modbus_rx_buf[2] << 8) | modbus_rx_buf[3];
            reg_val = (modbus_rx_buf[4] << 8) | modbus_rx_buf[5];

            if (reg_addr <= 100)
            {
                regGroup[reg_addr] = reg_val; // 修改寄存器的值

                // 06 功能码的正常回复是:原样返回接收到的报文
                for(i = 0; i < 6; i++) { modbus_tx_buf[i] = modbus_rx_buf[i]; }
                tx_size = 6;
            }
            break;

        default: break; // 其他功能码暂不处理
    }

    // 5. 如果有数据需要回复,计算发送数据的 CRC 并发送
    if (tx_size > 0)
        {
            crc_cal = GetCRC16(modbus_tx_buf, tx_size);

            // 【修改这里】:因为 crc_cal 把低位放在了高八位,所以要先发它的高八位
            modbus_tx_buf[tx_size++] = (crc_cal >> 8) & 0xFF;  // 实际发送的是低字节
            modbus_tx_buf[tx_size++] = crc_cal & 0xFF;         // 实际发送的是高字节

            for (i = 0; i < tx_size; i++)
            {
                uarta_SendChar(modbus_tx_buf[i]);
            }
        }

clear_frame:
    // 6. 清理战场,准备接收下一句话
    modbus_rx_cnt = 0;
    modbus_rx_ready = 0;
}

// --- 超时断句逻辑 (非常重要) ---
// 工业上,如果总线静默超过 3.5 个字符的时间(比如 9600波特率下约 4ms)
// 就认为上位机的一句话说完了。
void Modbus_Tick_1ms(void)
{
    if (modbus_rx_cnt > 0 && modbus_rx_ready == 0)
    {
        modbus_idle_timer++;
        if (modbus_idle_timer >= 5) // 5ms 没有收到新字节,认为一帧结束
        {
            modbus_rx_ready = 1;
            modbus_idle_timer = 0;
        }
    }
}

modbus.h 

#ifndef __MODBUS_H
#define __MODBUS_H

#include "headfile.h"  

// 定义本机的 Modbus 地址
#define SLAVE_ADDR  0x01

// Modbus 缓冲数组大小
#define MODBUS_BUF_SIZE 128

// 全局变量声明
extern Uint16 regGroup[100];      // 你的样机状态寄存器组
extern Uint16 modbus_rx_buf[MODBUS_BUF_SIZE];
extern Uint16 modbus_tx_buf[MODBUS_BUF_SIZE];
extern Uint16 modbus_rx_cnt;
extern Uint16 modbus_rx_ready;    // 接收完成一帧的标志
extern Uint16 modbus_idle_timer;  // 空闲计时器

// 函数声明
void Modbus_Init_Regs(void);
void Modbus_Process(void);
void Modbus_Tick_1ms(void);
Uint16 GetCRC16(Uint16 *ptr, Uint16 len);

#endif

uart.c

#include "uart.h"
#include "headfile.h"
#include "modbus.h"

void uarta_Init(Uint32 baud)
{
    EALLOW;
    PieVectTable.SCIRXINTA = &uart_IRQn;
    EDIS;

    //计算波特率
    unsigned char scihbaud=0; // 存放波特率寄存器的高 8 位
    unsigned char scilbaud=0; // 存放波特率寄存器的低 8 位
    Uint16 scibaud=0;         // 存放计算出的完整波特率数值(16位)

    scibaud = 37500000 / (8 * baud) - 1;      // 公式:BRR = (LSPCLK / (波特率 * 8)) - 1   (以 LSPCLK = 37.5MHz 为前提)

    scihbaud = scibaud >> 8;
    scilbaud = scibaud & 0xff;

    //开启SCIA时钟,调用GPIO复用配置
    InitSciaGpio();
    // 3. 初始化 SCI FIFO (硬件缓存区)
    SciaRegs.SCIFFTX.all = 0xE040; //1110 0000 0100 0000  (开启 FIFO 功能,复位发送 FIFO,清除中断标志)
//    SciaRegs.SCIFFRX.all = 0x204f;//0010 0000 0100 1111 (复位接收 FIFO,开启接收 FIFO 中断,设置接收中断级别为 15,即收到 15 个字符才进一次中断)
    SciaRegs.SCIFFRX.all = 0x2021;//0010 0000 0010 0001
    SciaRegs.SCIFFCT.all = 0x0;// 设置 FIFO 传输延迟为 0
    // 4. 基础通信格式配置
    SciaRegs.SCICCR.all = 0x0007;//0000 0000 0000 0111   1个停止位, 无校验, 8位数据位, 异步模式
    SciaRegs.SCICTL1.all = 0x0003;//0000 0000 0000 0011  开启 TX (发送) 和 RX (接收) 功能
    // 5. 开启中断
    SciaRegs.SCICTL2.all = 0x0003;//0000 0000 0000 0011  允许发送和接收产生中断标志
//    SciaRegs.SCICTL2.bit.TXINTENA =1;
//    SciaRegs.SCICTL2.bit.RXBKINTENA =1;
    // 6. 写入波特率并启动
    SciaRegs.SCIHBAUD = scihbaud;
    SciaRegs.SCILBAUD = scilbaud;

    //回环测试 如果打开(写1),DSP 会把自己发送的 TX 直接在内部连到接收 RX 上,用于不接线时测试代码逻辑是否正常。
    //  SciaRegs.SCICCR.bit.LOOPBKENA = 1;

    // 退出复位状态,正式启动 SCI 串口
    SciaRegs.SCICTL1.all = 0x0023;// 0000 0000 0010 0011

    PieCtrlRegs.PIEIER9.bit.INTx1 = 1;
    IER |=M_INT9;
    EINT;



}

// ==========================================
// 2. 发送单个字符函数
// ==========================================
void uarta_SendChar(int a)
{
    // 等待发送 FIFO 缓冲里有空位
    while (SciaRegs.SCIFFTX.bit.TXFFST != 0) {}
    SciaRegs.SCITXBUF = a;
}

// ==========================================
// 3. 发送字符串函数
// ==========================================
void uarta_SendString(char *msg)
{
    int i = 0;
    while(msg[i] != '\0')
    {
        uarta_SendChar(msg[i]);
        i++;
    }
}
//回显测试
//interrupt void uart_IRQn()
//{
//    Uint16 receivedChar;
//
//    // 1. 读取接收到的数据
//    receivedChar = SciaRegs.SCIRXBUF.all;
//
//    // 2. 你的业务逻辑 (比如直接发回给电脑)
//    uarta_SendChar(receivedChar);
//
//    // 3. 清除硬件上的中断标志位 (必须要有)
//    SciaRegs.SCIFFRX.bit.RXFFOVRCLR = 1;   // 清除接收 FIFO 溢出标志
//    SciaRegs.SCIFFRX.bit.RXFFINTCLR = 1;   // 清除接收 FIFO 中断标志
//
//    // 4. 响应 PIE 控制器,告诉它本组中断处理完毕
//    PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
//}

interrupt void uart_IRQn()
{
    Uint16 receivedChar;

    // 1. 读取接收到的单个字节数据
    receivedChar = SciaRegs.SCIRXBUF.all;

    // 2. 将数据存入 Modbus 接收缓冲池,并让计数器加一
    if(modbus_rx_cnt < MODBUS_BUF_SIZE)
    {
        modbus_rx_buf[modbus_rx_cnt++] = receivedChar & 0xFF;
    }

    // 3. 【极其关键】只要收到哪怕一个字节,就重置空闲计时器!
    modbus_idle_timer = 0;
    modbus_rx_ready = 0;

    // 4. 清除硬件中断标志,准备接收下一个字节
    SciaRegs.SCIFFRX.bit.RXFFOVRCLR = 1;//清除接收FIFO溢出标志
    SciaRegs.SCIFFRX.bit.RXFFINTCLR = 1;//清除接收FIFO中断标志
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP9;
}

uart.h

#ifndef UART_H_
#define UART_H_
#include "headfile.h"

void uarta_SendChar(int a);
void uarta_SendString(char *msg);
interrupt void uart_IRQn();
void uarta_Init(Uint32 baud);

#endif /* APP_INC_UART_H_ */

main.c

#include "headfile.h"
#include "LEDS.h"
#include "KEY.h"
#include "timer.h"
#include "exit.h"
#include "adc.h"
#include "epwm.h"
#include "uart.h"
#include "modbus.h"

uint16_t key = 0,i = 50;

/**
 * main.c
 */
int main(void)
{
    InitSysCtrl();
    MemCopy(&RamfuncsLoadStart,&RamfuncsLoadEnd,&RamfuncsRunStart);
//  1.先关闭所有中断
    InitPieCtrl();              // 初始化PIE控制寄存器
    IER = 0x0000;               // 清除CPU级中断使能
    IFR = 0x0000;               // 清除CPU级中断标志
    InitPieVectTable();         // 初始化PIE中断向量表

    timer_Init(150,1000);
    exit1_Init();
    led_Init();
    key_Init();
    epwm1_Init(14999);
//    epwm2_Init(14999);
    uarta_Init(9600);
    Modbus_Init_Regs();
//    uarta_SendString("\r\nUART Module Initialized Successfully!\r\n");

    for(;;)
    {
        if(led_flag ==1)
        {
            led_flag =0;
            led_toggle(5);
        }
        if(modbus_rx_ready == 1)
                {
                    Modbus_Process();  // 验证 CRC -> 读/写寄存器 -> 自动回复屏幕
                     if(regGroup[2] == 1) {
                         led_on(2);
                     } else {
                         led_off(2);
                     }
                }
    }
}




Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐