文本的串口通信协议设计以及在Arduino上的应用

Arduino介绍

Arduino是一种开放源代码的硬件平台,可以用于制作各种电子项目和原型设计。它基于易于使用的软件和硬件接口,使得非专业人士也能够轻松创建交互式电子设备。

Arduino的主要特性包括:

  1. 简单易用:Arduino使用C/C++编程语言,并提供了简单的API,使得编程变得容易上手。它还有一个友好的集成开发环境(IDE),具有代码编辑器、编译器和调试工具等功能。
  2. 开放源代码:Arduino的相关软件和硬件设计文件都是开放源代码的,可以自由获取和修改。这意味着用户可以根据自己的需求进行定制和扩展,共享自己的创意和成果。
  3. 丰富的库和示例:Arduino社区提供了大量的库和示例代码,涵盖了各种传感器、执行器和通信模块等常用组件。这些库和示例代码可以帮助用户快速构建项目,并提供了学习和参考的资源。
  4. 强大的兼容性:Arduino兼容性广泛,支持各种扩展板(称为"Shields")和传感器模块,如Wi-Fi模块、蓝牙模块、LCD显示屏等。这些扩展板可以轻松地插入到Arduino主板上,扩展其功能。

Arduino Uno介绍:

它一款广泛使用的Arduino开发板,以下是其核心技术参数:

  • 微控制器:ATmega328P
  • 工作电压:5V
  • 输入电压(建议范围):7-12V
  • 输入电压(极限):6-20V
  • 数字输入/输出引脚:14个(其中6个可用作PWM输出)
  • 模拟输入引脚:6个
  • 直流电流每个I/O引脚:20 mA
  • 直流电流所有I/O引脚总和:200 mA
  • Flash存储器容量:32 KB(其中约0.5 KB由引导程序占用)
  • SRAM容量:2 KB
  • EEPROM容量:1 KB
  • 时钟频率:16 MHz
  • USB接口:用于编程和供电
  • 推荐编程环境:Arduino IDE(集成开发环境)
  • 支持操作系统:Windows、Mac OS X、Linux

串口通信协议

实现功能

  • 读取三路0.0V~5.0V模拟量输入,读取端口A0~A2
  • 设置三路0.0V~5.0V的模拟量输出,输出管脚3,5,6
  • 读取三路数字量输入,读取管脚2,4,7
  • 设置三路数字量输出,输出管脚8,12,13

协议格式

 1)读取三路0.0V~5.0V模拟量输入:

发送:VOLTS:GET?\r\n

接受:VOLTS:GET:2.5:1.5:3.2\r\n

2)设置三路0.0V~5.0V的模拟量输出:

发送:VOLTS:SET:2.1:2.2:3.2\r\n

接受:VOLTS:SET:2.1:2.2:3.2\r\n

3)读取三路数字量输入:

发送:DIGITS:GET?\r\n

接受:DIGITS:GET:1:1:0\r\n

4) 设置三路数字量输出:

发送:DIGITS:SET:1:0:1\r\n

接受:DIGITS:SET:1:0:1\r\n

实现代码如下

/*
  Serial Event 示例
  当新串行数据到达时,这个程序添加它到一个String。
  当一个新行被接收时,loop打印这个字符串并且清除它。

  注意:serialEvent()特性在the Leonardo, Micro和其他基于ATmega32U4的版块上不可用。
*/
#define CHANNELS 3

String inputString = "";      // 一个保存到来数据的String
String outputString = "";    //一个保存发送数据的String
String num = "";
bool stringComplete = false;  // 字符串是否结束

const int inputAs[CHANNELS] ={A0 , A1, A2};
float voltages[CHANNELS] = {0.0, 0.0, 0.0};

const int outputAs[CHANNELS] = {3, 5, 6};
int voltagesout[CHANNELS];

const int inputDs[CHANNELS] = {2, 4, 7};
byte dis[CHANNELS];

const int outputDs[CHANNELS] = {8, 12, 13};
byte dos[CHANNELS] = {0, 0, 0};

float convertToVoltage(int input)
{
  int range = constrain(input, 0, 1023);

  return 5.0 / 1024 * range;
}

int convertToIntensity(float voltage)
{
   float range = constrain(voltage, 0.0, 5.0);

   return (int)(range / 5.0 * 255);
}

float inverseIntensity(int intensity)
{
  int range = constrain(intensity, 0, 255);

  return 5.0 * intensity / 255;
}

void setup() {
  for (int i = 0; i < CHANNELS; i++){
    pinMode(outputAs[i], OUTPUT);
    analogWrite(outputAs[i], 0.0);
    pinMode(inputDs[i], INPUT);
    pinMode(outputDs[i], OUTPUT);
    digitalWrite(outputDs[i], LOW);
  }
  // 初始化串口:
  Serial.begin(9600);
  // 为inputString保留200个字节
  inputString.reserve(200);
  outputString.reserve(200);
  num.reserve(20);
}

void loop() {
    int index;

    for (int i = 0; i < CHANNELS; i++){
      voltages[i] = convertToVoltage(analogRead(inputAs[i]));
      dis[i] = digitalRead(inputDs[i]);
    }
  // 当新行到达时,打印这个字符串
  if (stringComplete) {
  // Serial.println(inputString);

    if (inputString.startsWith("VOLTS:GET?")){
      outputString = "VOLTS:GET:";
      for (int i = 0; i < CHANNELS; i++){
        outputString += voltages[i];
        if (i < CHANNELS - 1)
        {
          outputString += ":";
        }
      }
      outputString += "\r\n";
    }
    else if (inputString.startsWith("DIGITS:GET?"))
    {
      outputString = "DIGITS:GET:";
      for (int i = 0; i < CHANNELS; i++){
        outputString += dis[i];
        if (i < CHANNELS - 1)
        {
          outputString += ":";
        }
      }
      outputString += "\r\n";
    }
    else if (inputString.startsWith("DIGITS:SET:")){
      int end, start;
      outputString = inputString;
      inputString.replace("DIGITS:SET:", "");
      inputString.replace("\r\n", "");

      start = 0;
      for (int i = 0; i < CHANNELS; i++)
      {
        end = inputString.indexOf(":");
        inputString.setCharAt(end, ' ');
        // Serial.print("end:");
        // Serial.println(end);
        if (end != -1){
          num = inputString.substring(start, end);
          start = end + 1;
        }
        else{
          num = inputString.substring(start);
        }
        // Serial.println(num);

        if (num.toInt() == 0 && dos[i] == 1){
          dos[i] = LOW;
          digitalWrite(outputDs[i], dos[i]);
        }
        else if (num.toInt() != 0 && dos[i] == 0){
          dos[i] = HIGH;
          digitalWrite(outputDs[i], dos[i]);
        }
      }
    }
    else if ( inputString.startsWith("VOLTS:SET:")){
      int start, end;
      float vol;
      outputString  = "VOLTS:SET:";
      int intensity;

      inputString.replace("VOLTS:SET:", "");
      inputString.replace("\r\n", "");

      start = 0;
      for (int i = 0; i < CHANNELS; i++){
        end =  inputString.indexOf(":");
        inputString.setCharAt(end, ' ');

        if (end != -1){
          num = inputString.substring(start, end);
          start = end + 1;
        }
        else{
          num = inputString.substring(start);
        }
       vol = num.toFloat();

       intensity = convertToIntensity(vol);

       if (intensity != voltagesout[i]){
        voltagesout[i] = intensity;
        analogWrite(outputAs[i], voltagesout[i]);
       }

       outputString += inverseIntensity(voltagesout[i]);

       if (i < CHANNELS - 1){
        outputString += ":";
       }
      }

      outputString += "\r\n";
    }
    
    Serial.println(outputString);
    
    // 清除这个字符串:
    inputString = "";
    stringComplete = false;
  }
}

/*
  serialEvent在一个数据在硬件串口RX上出现时发生。在每次loopp()运行之间,SerialEvent发生,因此
  在loop中使用delay可以延时响应。多个字节数据可以获取。
*/
void serialEvent() {
  while (Serial.available()) {
    // 获取字符:
    char inChar = (char)Serial.read();
    // 添加它到inputString:
    inputString += inChar;
    // 如果到来的字符串时一个新行,设置一个标记,因此,主循环可以对它做某件事
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}

测试

pyserial的功能简介

1、打开/dev/ttyXX并设置波特率为9600, 只适用于Linux

serial = serial.Serial(‘/dev/ttyXXX’, 9600)

2、往串口里面写数据

serial.write(b“hello”) 

3、读一行数据,以/n结束,要是没有/n就一直读,阻塞。

data = serial.readline() 

使用python串口包pyserial进行测试 

 基于以上Arduino的响应消息,在Arduino上实现了基于字符串消息通信的功能。

控制客户端

基于以上串口通信协议,编写客户端控制程序,实现了对模拟量和数字量的读取和设置,如下:

  • 1、DO0~DO2对应设置Arduino的数字量输出-管脚[8,12,13]。
  • 2、DI0~DI2对应读取Arduino的数字量输入-管脚[2,4,7]。
  • 3、AO0~AO2对应设置Arduino的模拟量输出-管脚[3,5,6]。
  • 4、AI0~AI2对应设置Arduino的模拟量输入-管脚[A0,A1,A2]。

控制客户端使用EPICS IOC,具体实现请见EPICS和Arduino Uno之间基于串行文本协议的控制开发-CSDN博客

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值