Arduino(5) 使用Mega2560设计上下位机串口通信系统的下位机

2023-05-16

前言

因项目需求,我们要从PC端去控制一些外部设备,比如激光器、光放大器等,这些设备一般使用到的都是低速的串口通信,所以我们需要设计一个上下位机串口通信系统来控制这些设备。这篇文章先讲如何使用Arduino Mega2560开发板来作为下位机控制各个外部设备。

上下位机工作原理

  • 通常来说,上位机指的是PC端的控制软件,比如可以设置读取参数的界面软件,下位机则是指单片机或者带微处理器的系统,这里我们使用Mega2560来作为下位机控制板。下位机可以把一些模拟信号经过AD采集后转换为数字信号,经过处理后通过串口发送给上位机;同理上位机也可以给下位机发送一些指令或者信息。
  • 我们这里需要通过PC端的界面软件发送命令参数到外部设备上,然后再把外部设备的某些功能参数返回给界面软件。就以前一篇文章Arduino(4)为例说明,我们已经把外部设备光开关相关的功能操作写成了类库,mega2560作为下位机控制板直接去调用这个类库就可以了。

下位机软件执行流程

在编写下位机软件之前,我们可以先自定义一种串口通信的数据帧,来确保上下位机之间的通信不会出现差错。上下位机串口通信下行协议:PC -> Mega2560,共有8字节构成,采用异或校验。帧头可以自定义一字节的数据,例如0xfb;命令字表示自定义一些命令操作,例如我将设置光开关通道号的操作定义为0x10#define OPTICALSWITCH_CHANNEL_SET 0x10;4个字节的数据值区域表示携带本次命令操作设置的参数值;帧尾自定义为数据帧的末尾,例如0xfe;校验码则是对整个数据帧进行左右异或后得到的一个字节的数据。

帧头校验码命令数据值数据值帧尾
1 byte1 byte1 byte2 byte2byte1 byte

接下来就是在Arduino IDE编写数据帧的处理流程,代码如下所示:

#include <OpticalSwitch.h>
#define FRAME 8 // frame length for host command
/*********** Module Command Keyword *************/
#define OPTICALSWITCH_CHANNEL_SET 0x10
#define OPTICALSWITCH_CHANNEL_READ 0x11

/*********** Module Name Code *********************/
#define OpticalSwitch 0x03

// global varibles for com
const int baud1 = 9600;        // inital baud rate for com with host
byte rxData[FRAME];     // bytes for received frames
int comError = 0;       // error count for communications
bool newCMD = false;    // flag for incoming new command
bool response = false;  // flag for outgoing data
int inbyte;             // use it to clear other data in serial buffer

void setup() 
{
    // put your setup code here, to run once:
    Serial.begin(baud1);       //connect with host software
    delay(10);
    modComSetup();  // setup and check communications to modules sequentially
}

void loop() 
{
    // put your main code here, to run repeatedly:
    delay(300);
    int rxLength = Serial.available();  //判断接收数据的长度
    if(rxLength > 0)
    {
        if (rxLength == FRAME)    //接收数据的长度等于预设值才为指定命令帧,并将其接收下来;否则清除串口缓冲区
        {
            // get CMD in rxData
            Serial.readBytes(rxData, FRAME); //接收数据,存进指定数组
            newCMD = true;
        }
        else
        {
            // clear incoming buffer
            clearBUF();
            // setup a error flag to notify host to resend command
            comError++;
        }
    }
	
	//当有新的命令帧下发时
    if(newCMD)
    {
        newCMD = false;
         //校验码通过后才由Arduino继续下发命令给外部设备
        if (!checkCMD(rxData))  
        {
            comError = 0;
            //执行命令帧
            parseCMD(rxData);
        }
        else
        {
            comError++;
        }
    }
    clearBUF();
    //有错则重发命令
    if(comError) resendCMD();
    
    routine();
}

//在此函数内循环读取外部设备的参数
void routine()
{
}


void modComSetup()
{
    // setup com ports and registers of modules one by one
    if(OpticalSwitch_Setup()!=true)  return;           
}

void resendCMD()
{
    // in case COM error, ask host to resend the last command
    response = true;
    comError = 0;
    // prepare data to be send to host
}

void SendDataToHost(unsigned char ModuleCode)
{
    // frame data to fill up txData and send it to host    
    switch(ModuleCode)
    {
        case OpticalSwitch:
        {
            Serial.write('D');
            Serial.write(OpticalSwitch);
            Serial.write(channel);
            break;   
        } 
        default:
            break;
    }
}

void clearBUF()
{
    delay(50);
    int rxLength = Serial.available();
    // clear incoming buffer
    for (int i = 0; i < rxLength; i++)
    {
        inbyte = Serial.read();
    }
}

bool checkCMD(unsigned char *rxData)
{
    if(rxData[0]==0xfb && rxData[FRAME-1]==0xfe )
    {
        if(rxData[1] == calcBIP8(rxData))
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
    else
    {
        return 1;
    }
    // calculate to check the correctness of the received command
    // correct--> return 0;
    // uncorrect--> return 1;
}

void parseCMD(unsigned char *rxData)
{
    // parse received command and setup flags accordingly for further processing
    switch(rxData[2])
    {
        case OPTICALSWITCH_CHANNEL_SET:
        {
            OpticalSwitchChannel(rxData[4]);
            break;
        }
        case OPTICALSWITCH_CHANNEL_READ:
        {
            SendDataToHost(OpticalSwitch)
            break;
        }
        default:
            break;
    }
}

//异或校验,用于校验下发命令的数据帧是否完整
unsigned char calcBIP8(unsigned char* data)
{
    unsigned char bip16 = data[0]^(data[1]&0x00)^data[2]^data[3]^data[4]^data[5]^data[6]^data[7];
    unsigned char bip8 = ((bip16&0xf0)>>4)^(bip16&0x0f);
    return bip8;
}
  • 首先引用我们之前写好的类库#include <OpticalSwitch.h>;然后void setup()函数内初始化Arduino mega2560和PC电脑相连的串口(该串口由mega2560上自带串口转USB线和电脑上USB口连接),然后对外部设备进行初始化,这部分程序已经写在了光开关的类库里了。void loop()函数内则是数据帧的整个处理流程
  • 从上位机下发的数据命令帧被与PC相连的串口(也就是Serial)接收后,首先判断其数据长度是否等于预设值,不然就是传输过程出错了,数据包出现了丢失;接着将接收到的数据存储到rxData数组内,由异或校验函数calcBIP8()来检测数据帧的正确性;然后执行派发函数parseCMD(),这个函数根据数据帧内的命令字来执行相应的命令,调用相应模块的指定函数;如果这个过程出错了就重发命令,执行resendCMD()函数,并且清空串口缓冲区,等待下一次数据命令帧;routine()内则循环读取外部设备的参数,存进相应的数组,等待上位机读取。整个执行过程放在了loop()函数内,循环执行,有新的命令帧就执行,没有就等待新的命令到来。
  • 这样整个控制外部设备的下位机软件就已经做好了,上位机发送带有命令字的数据帧到下位机,就可以控制各种外部设备了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Arduino(5) 使用Mega2560设计上下位机串口通信系统的下位机 的相关文章

  • Arduino Nano 上的 WiFi

    我无法找到的虚拟问题 我用来将 WiFi 802 11b g n 添加到 Raspberry Pi 的扩展板也可以在 Nano 上使用吗 换句话说 向 Arduino Nano 板添加 WiFi 有多容易 可行 Thanks Arduino
  • Arduino“SerialEvent”示例代码无法在我的 Arduino Nano 上运行。我无法接收串行数据。为什么?

    我正在按照教程进行操作串行事件 https www arduino cc en Tutorial BuiltInExamples SerialEvent我正在尝试这段代码 然而 当我发送我的Arduino 纳米 https en wikip
  • Arduino:字符串到整数得到奇怪的值

    我想转换一个String to an int 我所能找到的就是你必须将 String 转换为 char 数组 然后将该数组转换为int 但我的代码产生奇怪的值 我无法弄清楚问题是什么 void ledDimm String command
  • 如何将 char 数组定义为常量?

    这里是 C C 菜鸟 我已经在头文件中定义了它 typedef unsigned char BitChar 9 8 data bytes chars and one width byte char extern BitChar BitFon
  • Arduino:使用串口和软件串口与蓝牙模块

    我的目的是使用 Arduino 通过 HC 05 蓝牙模块在 PC 和 Android 设备之间建立通信 我使用 PC 和 Arduino 串行监视器 之间的 USB 通信以及 SoftwareSerial 来连接到 HC 05 我的问题是
  • C++ Linux (Ubuntu) 正确写入串行(对于 Arduino)

    我想知道是否有一种标准方法可以与高效的串行设备进行通信 我应该使用标准库吗 如果有 是哪一个 现在我正在摆弄让 LED 根据输入的数字以给定的量亮起 Arduino 代码如下 只是练习一些东西 看我过于简单且低效的测试 include
  • 是否有通用 I2C 命令来查看设备是否仍然存在于总线上?

    是否有通用的 I2C 命令来查看设备在初始化一次后是否仍然存在于总线上 例如 OLED 显示器 我问这个的原因是为了避免主程序由于库代码中存在无限循环而冻结 当设备断开连接时 例如 Wire 库 在 MCU 启动时 我想检查设备是否可用 并
  • 带/不带类的回调函数指针 C++

    我被困 我正在尝试形成一个函数 它将吃掉无类函数指针和对象中的函数指针 这是我当前的代码 希望能解释更多 它应该在 Arduino 上运行 所以我不能使用大型库 首先 我在 Arduino 上使用这个库 SimpleTimer A time
  • 如何为 Arduino 或类似的微控制器编写 JavaScript 编译器? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我意识到这个问题会引起一些人的注意 并且我意识到 JavaScript 传统上是一种解释性语言 请让我解释一下 我是一名专门从事 We
  • 是否可以在 Arduino 上运行 ZeroMQ

    我想知道是否可以通过 ZeroMQ 与 Arduino 进行通信 我想使用 Arduino 来控制一些设备 并希望将大部分应用程序放在计算机上 为此 如果 Arduino 和控制器可以通过 Zeromq 以及 WiFi 或以太网扩展板进行通
  • 在声明对象之前在全局函数中使用类的成员函数

    我有一个事件处理函数 在该函数中调用了类的成员函数 事件处理函数在类 cpp 文件中声明 但不是类的一部分 它不是成员函数 当我编译代码时 编译器表示该函数在范围内 因为它正在调用全局事件处理函数中的成员函数 我的问题如下 有没有办法在全局
  • Arduino C++ 析构函数?

    我知道在Arduino中你不能使用delete 那么什么时候调用 C 类中定义的析构函数呢 同样 如果我想创建一个指向数组的指针 我必须使用malloc and free 当对象被销毁时 析构函数被调用 对于自动 堆栈上 变量 它在离开其作
  • 编译器:如果条件始终为真/假怎么办

    我想到了条件和编译器 我正在为 Arduino 编写一个应用程序 因此我需要该应用程序尽可能快 在我的代码中我有这个 define DEBUG false if DEBUG String pinName pinName Pin pinNam
  • Arduino串口数据解析

    我正在编写一个应用程序 通过蓝牙用我的 Android 手机控制我的机器人 一切都很顺利 数据得到回显和验证 但我在协议方面遇到了一些问题 特别是我希望我的机器人的轮子在我发送时转动一个命令 例如s 10 100 or s 30 10 数值
  • Arduino 高低

    我有一个 Arduino 我想知道到底是什么HIGH and LOW意思是 就实际值而言 它们是否有符号ints unsigned ints unsigned char是 他们的价值观是什么 我猜HIGH and LOW可能是unsigne
  • 带 firebase 的 Nodemcu(ESP8266)

    这是我上传到 ESP8266 以连接到 firebase 的代码 include
  • 云到设备 Azure IoT REST API

    我探索了如何使用 Azure REST API 将数据从设备发送到云 它运行无缝 没有任何问题 我没有找到有关使用 Azure IoT Hub REST API 向 Arduino 板发送云到设备消息的好文章 有人可以就此提供建议吗 您还可
  • Arduino sprintf float 未格式化

    我有这个arduino草图 char temperature 10 float temp 10 55 sprintf temperature f F temp Serial println temperature 温度打印为 F 关于如何格
  • 类中的易失性变量:“‘易失性’之前预期有非限定 ID”?

    我有两个static volatile我的类中定义的变量ADC 该类写为 裁剪以节省空间 pragma once include PeriodicProcess PeriodicProcess h include
  • PHP 通过 TCP/IP 发送消息

    我尝试通过 TCP IP 从 PHP 网站向 Arduino 发送消息 使用以下代码我可以从 php 网站发送消息 问题是 当第一次调用该网站时 消息不会立即发送 网站刷新几次后 消息就会到达 但逻辑上很多次 就像网站刷新量一样 已经尝试将

随机推荐

  • http请求消息体和响应消息体

    http www cnblogs com mydomain archive 2012 11 05 2754487 html HTTP 基于请求与响应模式的 无状态的 应用层的协议 xff0c 常基于TCP 的连接方式 HTTP 允许传输任意
  • HTTP Header 详解

    HTTP Header 详解 HTTP xff08 HyperTextTransferProtocol xff09 即超文本传输协议 xff0c 目前网页传输的的通用协议 HTTP协议采用了请求 响应模 型 xff0c 浏览器或其他客户端发
  • PHP模拟http请求

    http flandycheng blog 51cto com 855176 251752 方法一 xff1a 利用php的socket编程来直接给接口发送数据来模拟post的操作 建立两个文件post php getpost php po
  • HTTP 消息结构

    HTTP是基于客户端 服务端 xff08 C S xff09 的架构模型 xff0c 通过一个可靠的链接来交换信息 xff0c 是一个无状态的请求 响应协议 一个HTTP 34 客户端 34 是一个应用程序 xff08 Web浏览器或其他任
  • php 构造http头 php模拟http请求 伪装头部信息

    http jingyan baidu com article 335530daafcd6019ca41c352 html lt php 构造 http 请求 filename 61 34 http sj mjerp com dabenyin
  • 使用驱动器中J:的光盘之前需要将其格式化。

    http tieba baidu com p 3203627680 不知道神马原因致使U盘无法打开 大家千万注意 xff1a 以后遇见这种情况千万别格式化 xff08 当然如果你的U盘或者硬盘里没有重要东西那就另当别论 xff09 xff0
  • 404、500、502等HTTP状态码介绍

    一些常见的状态码为 xff1a 200 服务器成功返回网页 404 请求的网页不存在 503 服务不可用 详细分解 xff1a 1xx xff08 临时响应 xff09 表示临时响应并需要请求者继续执行操作的状态代码 代码 说明 100 x
  • 2020年电赛题目,命题专家权威解析!

    关注 星标公众号 xff0c 直达精彩内容 来源 xff1a 德州仪器TI校园计划 大学生电子设计竞赛培训网上次发了关于电赛题目前三道题目解析的视频 xff1a 2020年电赛题目 xff0c 命题专家们怎么看 xff1f xff0c 这篇
  • php curl请求信息和返回信息设置代码实例

    在用curl抓取网页内容的时候 xff0c 经常要知道 xff0c 网页返回的请求头信息 xff0c 和请求的相关信息 xff0c 特别是在请求过程中存在重定向的时候获取请求返回头信息对分析请求内容很有帮助 下面就是一个请求中存在重定向的例
  • URL特殊字符转义

    问题描述 xff1a 在工作中 xff0c 因为需要将另外一个系统的一批产品转移到自己系统 xff0c 产品基础资料什么的 xff0c 都没问题 xff0c 直接给字段插入数据库就好 xff0c 但是遇到了图片的问题 xff0c 图片是在别
  • PHP中GET/POST方法参数传递空格+逗号等特殊字符处理办法

    自己在项目开发中写了个自用接口 xff0c 用GET方法传参 xff08 用户名和密码 xff09 最近收到用户反馈 xff1a 密码为特殊字符时会出错 我一开始想是不是php的mysql real escape string函数将特殊字符
  • canvas 背景透明

    theCanvas 61 document getElementById 39 canvasOne 39 var context 61 theCanvas getContext 39 2d 39 context fillStyle 61 3
  • Http压力测试工具HttpTest4Net

    HttpTest4Net是一款基于C 实现的和HTTP压力测试工具 通过工具可以简单地对HTTP服务进行一个压力测试 虽然VS NET也集成了压力测试项目 但由于VS自身占用的资源导致了在配置不高的PC上难以做到高并发压力测试 再加上需要装
  • PHP获取今天、昨天、明天的日期

    php view plain copy lt php echo 34 今天 34 date 34 Y m d 34 34 lt br gt 34 echo 34 昨天 34 date 34 Y m d 34 strtotime 34 1 d
  • Http协议与TCP协议简单理解

    https blog csdn net sundacheng1989 article details 28239711 在C 编写代码 xff0c 很多时候会遇到Http协议或者TCP协议 xff0c 这里做一个简单的理解 TCP协议对应于
  • LINUX重启MYSQL的命令

    LINUX重启MYSQL的命令 标签 xff1a mysqllinuxservice脚本web服务server 2010 06 25 10 21 62152人阅读 评论 0 收藏 举报 分类 xff1a Linux xff08 6 xff0
  • PHP:curl模拟form表单上传文件

    lt form action 61 34 34 method 61 34 post 34 enctype 61 34 multipart form data 34 gt lt input type 61 34 file 34 name 61
  • HTTP状态码大全

    1 信息类 xff1a 表示接收到请求并且继续处理 100 客户必须继续发出请求 101 客户要求服务器根据请求转换HTTP协议版本 2 响应成功 xff1a 表示动作被成功接收 理解和接受 200 表明该请求被成功地完成 xff0c 所请
  • 如何准备电赛?19年电赛经验总结!

    关注 星标公众号 xff0c 直达精彩内容 写在前面的话 最近忙着各大厂的实习面试 xff0c 趁着准备简历的功夫回顾了一下19年电赛的比赛经历 xff0c 总体来说还算说得过去 现在把我参加电赛的经验分享出来 xff0c 希望对之后的学弟
  • Arduino(5) 使用Mega2560设计上下位机串口通信系统的下位机

    前言 因项目需求 xff0c 我们要从PC端去控制一些外部设备 xff0c 比如激光器 光放大器等 xff0c 这些设备一般使用到的都是低速的串口通信 xff0c 所以我们需要设计一个上下位机串口通信系统来控制这些设备 这篇文章先讲如何使用