STM32基于HAL库带FreeRTOS系统的Freemodbus移植

2023-10-27

移植前提

下载所需源码

github地址
本项目地址

可能的win10 IAR设置

设置快捷键
ctrl+shift+l变为find in file,原ctrl+shift+f与win10输入法冲突会切换繁体输入

从站注意定义寄存器数量大小

在这里插入图片描述

效果查询报文

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200406184232910.png

效果回复报文

在这里插入图片描述

移植事件、定时器、串口

对应实现
在这里插入图片描述
开关临界区
e_port.c

void EnterCriticalSection(void)
{
    taskENTER_CRITICAL();
}

void ExitCriticalSection(void)
{
    taskEXIT_CRITICAL();
}

事件移植

portevent.c

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
volatile uint32_t Modbus_Event_ALL = 0;
void Set_Event_Port(void);   
/* ----------------------- Variables ----------------------------------------*/
static StaticEventGroup_t   xSlaveOsEvent;      /*  事件存储,事件组  */
static EventGroupHandle_t   slave_event_Handle; /*  事件标志组句柄    */
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
    /*创建事件组,成功返回句柄*/
    printf("创建事件组,成功返回句柄\n");
    slave_event_Handle = xEventGroupCreateStatic(&xSlaveOsEvent);
    if(slave_event_Handle == NULL)
    {
        printf("创建事件组 失败!\n");
    }
    return TRUE;
}

BOOL
xMBPortEventPost( eMBEventType eEvent )
{
    /*设置事件标志组 eEvent 置1*/
    printf("设置事件标志组 eEvent:0x%04X\n",eEvent);
    Modbus_Event_ALL |= eEvent;
        
    return TRUE;
}

BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    /* waiting forever OS event */
    uint32_t recvedEvent;
    recvedEvent = xEventGroupGetBits(slave_event_Handle);
    switch (recvedEvent)
    {
    case EV_READY:
        *eEvent = EV_READY;
        printf("读取事件标志组 EV_READY\n");
        break;
    case EV_FRAME_RECEIVED:
        *eEvent = EV_FRAME_RECEIVED;
        printf("读取事件标志组 EV_FRAME_RECEIVED\n");
        break;
    case EV_EXECUTE:
        *eEvent = EV_EXECUTE;
        printf("读取事件标志组 EV_EXECUTE\n");
        break;
    case EV_FRAME_SENT:
        *eEvent = EV_FRAME_SENT;
        printf("读取事件标志组 EV_FRAME_SENT\n");
        break;
    }
    xEventGroupClearBits(slave_event_Handle ,recvedEvent);
    return TRUE;
}

/**
  ******************************************************************
  * @brief   循环设置事件接口,原中断中设置事件存在问题
  * @author  aron566
  * @version v1.0
  * @date    2020/4/5
  ******************************************************************
  */
void Set_Event_Port(void)
{
    uint32_t ret = 0;
    if(Modbus_Event_ALL)
    {
        ret = xEventGroupSetBits(slave_event_Handle, Modbus_Event_ALL);
        if(ret != Modbus_Event_ALL)
        {
            printf("设置事件失败\n");
        }
        else
        {
            Modbus_Event_ALL = 0;
        }
    }
}

串口移植

串口使用的是带有环形缓冲区,实现方法参考博文
portserial.c

/* ----------------------- Static variables ---------------------------------*/
/* software simulation serial transmit IRQ handler thread stack */
static uint32_t serial_soft_trans_irq_stack[128];
/* software simulation serial transmit IRQ handler thread */
static osThreadId SlaveSerial_soft_trans_irq_Handle;
static osStaticThreadDef_t slave_transControlBlock;


/* serial event */
static StaticEventGroup_t   Slaveevent_serial;           /*事件存储,事件组*/
static EventGroupHandle_t   Slaveevent_serial_Handle;    /*事件标志组句柄*/
/* modbus slave serial device */
static UART_HandleTypeDef *huart_x;
/* ----------------------- Defines ------------------------------------------*/
/* serial transmit event */
#define EVENT_SERIAL_TRANS_START    (1<<0)

/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);

static void prvvUARTRxISR(void);
static BOOL serial_rx_ind(UART_HandleTypeDef *dev, uint16_t size);
static void serial_soft_trans_irq(void const *parameter);
void Slave_Serial_rx_ind(uint16_t size);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
        eMBParity eParity)
{
    /**
     * set 485 mode receive and transmit control IO
     * @note MODBUS_SLAVE_RT_CONTROL_PIN_INDEX need be defined by user
     */

    /* set serial name */
    switch (ucPORT)
    {
      case 1:
        huart_x = &huart1;
        break;
      case 2:
        huart_x = &huart2;
        break;
      case 3:
        
        break;
      default:
        return FALSE;
        break;
    }
    /*创建事件组,成功返回句柄*/
    Slaveevent_serial_Handle = xEventGroupCreateStatic(&Slaveevent_serial);
    if(Slaveevent_serial_Handle == NULL)
    {
        printf("创建事件组 失败!\n");
    }
    printf("创建串口事件组 Slaveevent_serial_Handle\n");
    osThreadStaticDef(slave_trans, serial_soft_trans_irq, osPriorityNormal, 0, 128, serial_soft_trans_irq_stack, &slave_transControlBlock);
    SlaveSerial_soft_trans_irq_Handle = osThreadCreate(osThread(slave_trans), NULL);
    return TRUE;
}

void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
//    uint32_t recvedEvent;
    if (xRxEnable)
    {
        /* enable RX interrupt */
//        __HAL_UART_ENABLE_IT(huart_x, UART_FLAG_IDLE);
        printf("开启串口接收中断 UART_IT_RXNE\n");
        /* switch 485 to receive mode */
        /*拉低RS485_SEL脚,RS485为接收状态*/
        HAL_GPIO_WritePin(RS485_SEL_GPIO_Port, RS485_SEL_Pin, GPIO_PIN_RESET);		
    }
    else
    {
        /* switch 485 to transmit mode */
        /*拉高RS485_SEL脚,RS485为发送状态*/
        HAL_GPIO_WritePin(RS485_SEL_GPIO_Port, RS485_SEL_Pin, GPIO_PIN_SET);		
        /* disable RX interrupt */
        printf("关闭串口接收中断 UART_FLAG_IDLE\n");
//        HAL_UART_AbortReceive_IT(huart_x);
//        __HAL_UART_DISABLE_IT(huart_x ,UART_FLAG_IDLE);

    }
    if (xTxEnable)
    {
        /* start serial transmit */
//        __HAL_UART_ENABLE_IT(huart_x ,UART_IT_TXE);
        printf("开启串口发送中断 UART_IT_TXE\n");
        /*设置事件标志组 EVENT_SERIAL_TRANS_START 置1*/
        xEventGroupSetBits(Slaveevent_serial_Handle, EVENT_SERIAL_TRANS_START);    
    }
    else
    {
        /* stop serial transmit */
//        recvedEvent = xEventGroupGetBits(Slaveevent_serial_Handle);
        printf("关闭串口发送中断 UART_IT_TXE\n");
//        HAL_UART_AbortTransmit_IT(huart_x);
//        __HAL_UART_DISABLE_IT(huart_x ,UART_IT_TXE);
    }
}

void vMBPortClose(void)
{
    HAL_UART_Abort(huart_x);
}

BOOL xMBPortSerialPutByte(CHAR ucByte)
{
    HAL_UART_Transmit_IT(huart_x,(uint8_t *)&ucByte, 1);
    return TRUE;
}

BOOL xMBPortSerialGetByte(CHAR * pucByte)
{
//    HAL_UART_Receive_IT(huart_x, (uint8_t*)pucByte, 1);
/*这里使用环形缓冲区,作为数据的提取*/
    CQ_getData(cb, (uint8_t*)pucByte, 1);
    return TRUE;
}

/* 
 * Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte( ) to send the character.
 */
void prvvUARTTxReadyISR(void)
{
    pxMBFrameCBTransmitterEmpty();
}

/* 
 * Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
void prvvUARTRxISR(void)
{
    pxMBFrameCBByteReceived();
}

/**
 * Software simulation serial transmit IRQ handler.
 *
 * @param parameter parameter
 */
static void serial_soft_trans_irq(void const *parameter) 
{
    uint32_t recved_event;
    
    while (1)
    {
        /* waiting for serial transmit start */
        recved_event = xEventGroupGetBits(Slaveevent_serial_Handle);
        /* execute modbus callback */
        if(recved_event == EVENT_SERIAL_TRANS_START)
        {
            prvvUARTTxReadyISR();
            /*这里本应清除事件位,由于协议栈使用的是单字节发送,需要检测此事件标志位否则发送完一字节就不发了,偷懒没有清除,后期可以优化成发完清除标志*/
//            xEventGroupClearBits(Slaveevent_serial_Handle ,EVENT_SERIAL_TRANS_START);
        }
        osDelay(1);
    }
}

/**
 * This function is serial receive callback function
 *
 * @param dev the device of serial
 * @param size the data size that receive
 *
 * @return return RT_EOK
 */
static BOOL serial_rx_ind(UART_HandleTypeDef *dev, uint16_t size) {
    prvvUARTRxISR();
    return TRUE;
}

void Slave_Serial_rx_ind(uint16_t size)
{
    serial_rx_ind(huart_x ,size);
}

定时器移植

porttimer.c
硬件设置:50us定时
在这里插入图片描述

/* ----------------------- static functions ---------------------------------*/
static TIM_HandleTypeDef timer;
static void prvvTIMERExpiredISR(void);
void Slave_timer_timeout_ind(void* parameter);
uint32_t slave_timer_tick;
static USHORT RT_TICK_PER_SECOND = 50;

extern uint32_t TIM2CurrentTicks;
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{
    timer = htim2;
    slave_timer_tick = (50 * usTim1Timerout50us) / RT_TICK_PER_SECOND;
    return TRUE;
}

void vMBPortTimersEnable()
{
    printf("开启定时器\n");
    TIM2CurrentTicks = 0;
    __HAL_TIM_SET_COUNTER(&timer, 0);
    HAL_TIM_Base_Start_IT(&timer);
    
}

void vMBPortTimersDisable()
{
    printf("关闭定时器\n");
    HAL_TIM_Base_Stop_IT(&timer);
}

void prvvTIMERExpiredISR(void)
{
    (void) pxMBPortCBTimerExpired();
}

void Slave_timer_timeout_ind(void* parameter)
{
    prvvTIMERExpiredISR();
}

中断
在这里插入图片描述

线程中调用

eMBInit和eMBEnable调用顺序别搞反

    eMBInit(MB_RTU, 1, 1, 115200, MB_PAR_NONE);
    eMBEnable();
    /* Infinite loop */
    for(;;)
    {
        eMBPoll();
        Set_Event_Port();
        /*缓冲区数据不为空,则调用协议栈取数据*/
        if(CQ_getLength(cb))
        {
            Slave_Serial_rx_ind(1);
        }
        osDelay(1);
    }

Master移植类似参考从机协议portxx.c文件修改修改名称即可,问题不大

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32基于HAL库带FreeRTOS系统的Freemodbus移植 的相关文章

  • 优化 ARM Cortex M3 代码

    我有一个 C 函数 它尝试将帧缓冲区复制到 FSMC RAM 这些函数将游戏循环的帧速率降低至 10FPS 我想知道如何分析反汇编的函数 我应该计算每个指令周期吗 我想知道CPU把时间花在哪里 在哪个部分 我确信该算法也是一个问题 因为它的
  • 133-基于stm32单片机停车场车位管理系统Proteus仿真+源程序

    资料编号 133 一 功能介绍 1 采用stm32单片机 4位数码管 独立按键 制作一个基于stm32单片机停车场车位管理系统Proteus仿真 2 通过按键进行模拟车辆进出 并且通过程序计算出当前的剩余车位数量 3 将剩余的车位数量显示到
  • 137-基于stm32单片机智能保温杯控制装置Proteus仿真+源程序

    资料编号 137 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DS18B20传感器 电机 制作一个基于stm32单片机智能保温杯控制装置Proteus仿真 2 通过DS18b20传感器检测当前保温杯水的温度 并且
  • 136-基于stm32单片机家庭温湿度防漏水系统设计Proteus仿真+源程序

    资料编号 136 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 蜂鸣器 制作一个基于stm32单片机家庭温湿度防漏水系统设计Proteus仿真 2 通过DHT11传感器检测当前温湿度 并且显示到L
  • rt-thread studio中新建5.0不能用

    文章目录 一 版本对比 二 文件和文件夹打斜杠 在使用RT Thread studio创建新工程5 0版本的时候 结果发现新建完成之后程序不能正常运行 但是创建4 10版本的时候却能运行 那肯定是新版本出现了BUG 一 版本对比 首先对比了
  • [MM32硬件]搭建灵动微MM32G0001A6T的简易开发环境

    作为学习单片机的经典 自然是通过GPIO点亮LED 或者是响应按钮的外部中断例程 这我们看看SOP8封装的芯片MM32G0001A6T得引脚 除了VDD和GND固定外 我们可以使用PA14 PA1 PA13 PA15 PA2 PA3这六个G
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • VS Code 有没有办法导入 Makefile 项目?

    正如标题所说 我可以从现有的 Makefile 自动填充 c cpp properties json 吗 Edit 对于其他尝试导入 makefile 的人 我找到了一组脚本 它们完全可以实现我想要实现的目标 即通过 VS Code 管理
  • 在 Atollic TrueStudio、STM32CubeMX 中导入 C 库

    我目前正在开发 STM32F767ZI Nucleo 板和一个小安全芯片 microchip atecc508a 通过 i2c 连接进行连接 该芯片有一个可用的库加密验证库 https github com MicrochipTech cr
  • 锂电池管理系统(BMS)

    引言 在现代科技的推动下 锂电池已经成为各种电动设备和能源存储系统的首选能源媒介 然而 锂电池在充电和放电过程中存在一系列潜在的安全隐患 同时其性能和寿命也受到一些限制 为了解决这些问题 锂电池管理系统 BMS 应运而生 BMS不仅仅是一个
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • STM32F207 I2C 测试失败

    我正在使用 STM32F207 微控制器在 STM3220G EVAL 板上学习嵌入式开发 我尝试通过连接同一芯片上的两个 I2C2 和 I2C3 模块并发送 接收字符来测试 I2C 接口 这是我当前编写的代码 使用 mdk arm 5 i
  • CMSIS & STM32,如何开始? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在 STM32 上使用 CMSIS 启动项目 网上一搜 没找到具体的教程 有些使用 SPL 开始项
  • 嵌入式开发--STM32G4系列片上FLASH的读写

    这个玩意吧 说起来很简单 就是几行代码的事 但楞是折腾了我大半天时间才搞定 原因后面说 先看代码吧 读操作 读操作很简单 以32位方式读取的时候是这样的 data IO uint32 t 0x0800F000 需要注意的是 当以32位方式读
  • 库函数点亮Led

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • systick定时器

    systick定时器 文章目录 前言 一 前期疑惑 二 解答 1 关于systick是阻塞的吗 2 非阻塞 三 软件编写 总结 前言 这边记录systick相关知识点 一 前期疑惑 在学习systick志气啊 其实对于systick还是一脸
  • STM32内部时钟

    我对 STM32F7 设备 意法半导体的 Cortex M7 微控制器 上的时钟系统感到困惑 参考手册没有充分阐明这些时钟之间的差异 SYSCLK HCLK FCLK 参考手册中阅读章节 gt RCC 为 Cortex 系统定时器 SysT
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • 使用 STM32 USB 设备库将闪存作为大容量存储设备

    我的板上有这个闪存IC 它连接到我的STM32F04 ARM处理器 处理器的USB端口可供用户使用 我希望我的闪存在通过 USB 连接到 PC 时被检测为存储设备 作为第一步 我在程序中将 USB 类定义为 MSC 效果很好 因为当我将主板
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能

随机推荐

  • 基于streamlit的表格展示-完美解决方案

    问题 用streamlit开发web app非常实用 但是streamlit的表格展示非常不友好 只有两个简单的接口函数 st table df 和st dataframe df 对于字段稍微比较多的dataframe显示效果相当不友好 s
  • linux安装中文、中文输入法以及火狐浏览器中文

    emm 今天突然想用下linux版本的web项目 发现进入浏览器后无法输入中文 鹅且也不知道中文输入法的入口 OTZ 摸索了一会 整理出来分享下 这里是分割线 啦啦啦啦 第一步 设置中文 话不多说直接看图 保存好 退出终端 重启系统 界面啥
  • 报错 LINK : fatal error LNK1181: 无法打开输入文件“cxcore.lib”

    转自 http www cnblogs com lxt287994374 archive 2012 11 23 2785274 html vs2010报错 1 gt LINK fatal error LNK1181 无法打开输入文件 cxc
  • C++头文件容器库——vector

    vector的使用 首先添加头文件 include
  • GDI+使用步骤

    相比较GDI GDI 增加了渐变的画刷 支持多种图像格式等 不过最大的变化 还是编程模型上的变化 GDI 使用了面向对象的思想 对接口进行了类封装 使用更加方便 在应用程序中使用GDI 库应该遵循一下步骤 1 引入Gdiplus h头文件
  • 辟谣、催债、倒闭.....2018年后,将再无创业黄金期!

    导读 上下游没钱了 信贷机构没钱了 风险投资人也没钱了 中小企业成本和资金压力大 企业违约 到期没法还债 非银行金融机构爆雷 大量出问题 股市非理性下滑 所有人进入新一轮升级性迷茫 由于各项成本的抬升 未来 不缺钱也不缺资源的头部企业将变得
  • Spark 基础教程

    Spark是基于内存计算的大数据并行计算框架 可用于构建大型的 低延迟的数据分析应用程序 Spark特点 运行速度快 Spark使用先进的DAG Directed Acyclic Graph 有向无环图 执行引擎 以支持循环数据流与内存计算
  • 软件发布之版本命名

    简介软件的版本命名与编号 img src http blog csdn net ppbage aggbug 747919 aspx width 1 height 1
  • unity--触屏游戏中如何判断点击的位置的左右&触屏游戏中如何判断点击的位置的左右&通过反转对象,让左侧运动的动画应用于右侧运动&通过代码改变图层覆盖顺序(Sorting Layer)

    文章目录 触屏游戏中如何判断点击的位置的左右 使用获取到的触碰坐标来进行左右移动 通过反转对象 让左侧运动的动画应用于右侧运动 通过代码改变图层覆盖顺序 Sorting Layer 触屏游戏中如何判断点击的位置的左右 我们先要有一个可以反馈
  • osg学习(四十)osg::Viewer的realize创建窗体的几种方式

    能够根据屏幕数 创建不同位置的窗口 void Viewer realize 在某一个屏幕上创建无边框窗口 在某一个屏幕上创建正常窗口 在所有屏幕上创建正常窗口 一个窗口 窗口位置可以跨屏幕 osgViewer SingleWindow实现
  • promise笔记

    promise笔记 以下笔记主要参考axios官网里的promise教程 写在这里方便以后复习或者查找 Promise javascript info 可以通过这个链接进行学习 更加详细 文章目录 promise笔记 1 介绍 2 基本语法
  • 将字符串格式的时间格式化

    时间格式化 param Number date 时间戳 param DateString fmt 时间格式 dateFormat yyyy MM dd hh mm ss S gt 2016 03 12 20 13 32 232 return
  • Object.create 以及 Object.setPrototypeOf

    第一部分 Object crate 方法是es5中的关于原型的方法 这个方法会使用指定的原型对象以及属性去创建一个新的对象 语法 Object create proto propertiesObjecy 参数 proto 必须的 一个对象
  • 云计算简介

    什么是 云 迁移至云端 在云中运行 在云中存储 从云端访问 当今时代 似乎一切都在 云 中进行 但是 云 究竟是一个什么样的概念 简单来说 云就是互联网连接的另一端 你可以从云端访问各种应用程序和服务 也可以在云端安全存储你的数据 云 之所
  • 扫描二维码 跳转到小程序指定页面

    注意 必须发布代码之后才能使用扫描二维码跳转 规则 1 二维码规则 填写你要生成的二维码的链接 2 小程序功能页面 要跳转的小程序的页面 3 测试链接 也填同样的链接 4 将上面的链接生成一个二维码 测试链接 5 通过微信扫描这个二维码跳转
  • 安装Apex库

    在Linux系统下安装Apex库 1 安装流程 按顺序使用如下命令 git clone https github com NVIDIA apex cd apex pip3 install v no cache dir 注意 不能直接使用pi
  • iOS 多线程

    1 怎么用GCD实现多读单写 dispatch barrier async 2 ios系统为我们提供的几种多程序技术各自的特点是怎样的 GCD 实现一些简单的线程同步 子线程的分派 包括一些类似于多读单写 nsoperation 比如adn
  • 2022年最佳的9种逆向工程工具[持续更新]

    逆向是复杂的 然而 软件开发人员经常在面临一项具有挑战性的任务时转向反向工程 增强软件安全性 使软件与第三方组件兼容 维护遗留代码 等等 在本文中 我们将描述我们的软件逆向程序在工作中所依赖的主要工具 并展示如何使用这些工具的实际示例 本文
  • python输入多组测试数据_python ddt数据驱动实例代码分享

    python ddt数据驱动最简实例 在接口自动化测试中 往往一个接口的用例需要考虑 正确的 错误的 异常的 边界值等诸多情况 然后你需要写很多个同样代码 参数不同的用例 如果测试接口很多 不但需要写大量的代码 测试数据和代码柔合在一起 可
  • STM32基于HAL库带FreeRTOS系统的Freemodbus移植

    STM32基于HAL库移植带FreeRTOS系统的Freemodbus移植 移植前提 下载所需源码 可能的win10 IAR设置 从站注意定义寄存器数量大小 效果查询报文 效果回复报文 移植事件 定时器 串口 事件移植 串口移植 定时器移植