查看: 1138|回复: 8

串口接收十六进制代码控制点灯

[复制链接]
  • TA的每日心情
    奋斗
    2021-1-26 19:45
  • 签到天数: 403 天

    [LV.9]以坛为家II

    发表于 2020-11-19 23:57 | 显示全部楼层 |阅读模式
    本帖最后由 topdog 于 2021-1-24 15:27 编辑

    设问串口接收十六进制的一串代码:00 00 00 00 00 00 FA 01,需要截取FA此段位置,即将00~FF转换成对应的十进制0~255,控制灯的亮度。

    hexchangedec.JPG

    1,从串口读取单个的字符,把它们连接成字符串inputString


    2,Arduino在处理字符串有自己独特的语法,使用起来非常方便。譬如截取F,它在inputString字符串中第18位,至第19位截取,所以写成inputString.substring(18, 19)。第一个0的位置它的序列号是0呀!


    3,十六进制FA,规范的写法是0xFA,对应的十进制值是250。十六进制的F表示15,A表示10,十六进制(HEX)转换十进制(DEC)公式:F*16^1+A*16^0=250 。


    4,此程序中使用Aduino字符串的法言法语自定义函数
    int HexChangeDec(String ch),大家可以玩味一下。String虽然截取了一个字符但是在程序中依然是字符串类型(string),不是字符类型(char)。


    const int LedPin = 9;
    String inputString = "";
    bool Status = false;
    
    void setup()
    {
      Serial.begin(9600);
      pinMode(LedPin, OUTPUT);
      analogWrite(LedPin, 0);
      inputString.reserve(200);
    }
    
    void loop()
    {
      if (Status)
      {
        //Serial.println(inputString);
        String Ten = inputString.substring(18, 19);
        String Unit = inputString.substring(19, 20);
        int Val = HexChangeDec(Ten) * 16 + HexChangeDec(Unit);
        //Serial.println(Val);
        analogWrite(LedPin, Val);
    
        inputString = "";
        Serial.flush();
    
        Status = false;
      }
    }
    
    int HexChangeDec(String ch)
    {
      String Hex0 = "0123456789ABCDEF";
      for (int i = 0; i < Hex0.length() + 1; i++)
      {
        String Hex1 = Hex0.substring(i, i + 1);
        if (ch == Hex1)
        {
          return i;
        }
      }
    }
    
    void serialEvent()
    {
      if (Serial.available())
      {
        char inChar = (char)Serial.read();
        inputString += inChar;
        if (inChar == '\n')
        {
          Status = true;
        }
      }
    }

    1_u1ssf__ascii.gif

    5,举一反三,我们还可以使用字符串处理函数charAt(),截取字节类型,再结合ASCII编码的序列计算出十进制值。charAt()应用时字符在字符串中的实际位置截取。例如:00 00 00 00 00 00 FA 01里面,截取F, 即inputString.charAt(18)。

    6,ASCII编码是由二进制表示的,譬如'0',二进制就是0011 0000,用十进制表示就是48。再如‘A’,二进制就是0100 0001用十进制表示就是65。


    7,0~9的ASCII编码减去48(字符0作为基准)一定小于10,字符编码和A字符编码之差就是十进制的值。另外A~F的ASCII编码减去48一定大于10,9到A还间隔了7个字符,所以减去7,才能求得对应的十进制值。


    8,本例(ch - '0' > 10) ? ch - '0' - 7 : ch - '0' 是三元运算符,她是软件编程中的一个固定格式,语法是“条件表达式?表达式1:表达式2”。使用这个算法可以使调用数据时逐级筛选。条件表达式为真,输出为表达式1的值;条件表达式为假,输出为表达式2的值。



    const int LedPin = 9;
    String inputString = "";
    bool Status = false;
    
    void setup()
    {
      Serial.begin(9600);
      pinMode(LedPin, OUTPUT);
      analogWrite(LedPin, 0);
      inputString.reserve(200);
    }
    
    void loop()
    {
      if (Status)
      {
        //Serial.println(inputString);
        char Ten = inputString.charAt(18);
        char Unit = inputString.charAt(19);
        int Val = HexChangeDec(Ten) * 16 + HexChangeDec(Unit);
        //Serial.println(Val);
        analogWrite(LedPin, Val);
    
        inputString = "";
        Serial.flush();
    
        Status = false;
      }
    }
    
    int HexChangeDec(char ch)
    {
      return (ch - '0' > 10) ? ch - '0' - 7 : ch - '0';
    }
    
    void serialEvent()
    {
      if (Serial.available())
      {
        char inChar = (char)Serial.read();
        inputString += inChar;
        if (inChar == '\n')
        {
          Status = true;
        }
      }
    }

    9,打开串口监视器,请注意程序里面 inChar == '\n','\n'为换行符,所以要在右下角选择换行符,这样就可以直接在串口输入行键入编码实现控制了。
    10,UNO支持analogWrite()功能的管脚是3、5、6、9、10、11,带有~符号标记。












    该用户从未签到

    发表于 2020-11-20 00:48 | 显示全部楼层
    这样似乎效率更高? int o = (ch-'0' > 10) ? ch-'A'+10 : ch-'0';
  • TA的每日心情
    奋斗
    2020-12-16 18:55
  • 签到天数: 84 天

    [LV.6]常住居民II

    发表于 2020-11-20 12:36 | 显示全部楼层
    感谢老师的讲解,非常实用的案例。
  • TA的每日心情
    奋斗
    2020-12-16 18:55
  • 签到天数: 84 天

    [LV.6]常住居民II

    发表于 2020-11-20 15:41 | 显示全部楼层
    还有个问题麻烦老师,这个例子我测试几遍,如果数据不断变化的话,必须把串口关闭,再打开才能收到新的数据。
  • TA的每日心情
    奋斗
    2021-1-26 19:45
  • 签到天数: 403 天

    [LV.9]以坛为家II

     楼主| 发表于 2020-11-21 00:22 | 显示全部楼层
    本帖最后由 topdog 于 2020-11-21 00:32 编辑
    redtxd 发表于 2020-11-20 15:41
    还有个问题麻烦老师,这个例子我测试几遍,如果数据不断变化的话,必须把串口关闭,再打开才能收到新的数据 ...

    打开串口监视器,请注意程序里面 inChar == '\n','\n'为换行符,所以要在右下角选择换行符,这样就可以直接在串口输入行键入编码实现控制了。
    你也可以改成inChar == '\r','\r'为回车符,那么就要右下角选择回车符,在你按发送的时候,IDE会自动给你加回车键。
  • TA的每日心情
    奋斗
    2021-1-26 19:45
  • 签到天数: 403 天

    [LV.9]以坛为家II

     楼主| 发表于 2020-11-21 00:23 | 显示全部楼层
    lanceli 发表于 2020-11-20 00:48
    这样似乎效率更高? int o = (ch-'0' > 10) ? ch-'A'+10 : ch-'0';

    谢谢lanceli的关心和指导,一起学习一起进步。
  • TA的每日心情
    奋斗
    2021-1-26 19:45
  • 签到天数: 403 天

    [LV.9]以坛为家II

     楼主| 发表于 2020-11-21 00:27 | 显示全部楼层
    本帖最后由 topdog 于 2020-11-21 00:48 编辑
    redtxd 发表于 2020-11-20 12:36
    感谢老师的讲解,非常实用的案例。

    串口控制的知识点还是比较多的,未能覆盖之处还请海涵,希望能够对你的学习有所帮助。
  • TA的每日心情
    奋斗
    2020-12-16 18:55
  • 签到天数: 84 天

    [LV.6]常住居民II

    发表于 2020-11-27 18:15 | 显示全部楼层
    本帖最后由 redtxd 于 2020-11-27 18:27 编辑

         谢谢老师的热心帮助,还是在你的帖子上请教吧。
          两个例子我都试验过,都能正常控制。但是加入我做的程序中就无法实现,主要还是我的基础太差

          下面详细给老师说一下我想实现的功能:
           我采用Arduino nano、CAN-TLL模块,想通过CAN模块接收CAN总线上帧ID为48的数据
        (已通过设置CAN模块实现帧ID的过滤)如下图:
    无标题.jpg
          如果CAN模块数据从软串口(2,3)输入,通过arduino的USB口接收数据正常,代码和接收情况如下:
    /*
    Arduino UNO软串口通信
    */
    #include <SoftwareSerial.h>
    SoftwareSerial CanSerial(2, 3); // RX, TX    CAN模块接软串口
    void setup()
    {
      Serial.begin(115200);
      CanSerial.begin(115200);        //初始化软串口

    }
    void loop()
    {
      if (CanSerial.available())
        Serial.write(CanSerial.read());
    }

        无标题2.jpg   
         
        但是加入取值就不行了,硬串口接收不到数据,灯也不受控,程序如下:

    #include <SoftwareSerial.h>
    SoftwareSerial CanSerial(2, 3); // RX, TX
    const int LedPin = 11;
    String inputString = "";
    bool Status = false;
    void setup()
    {
      Serial.begin(115200);
      CanSerial.begin(115200);
      pinMode(LedPin, OUTPUT);
      analogWrite(LedPin, 0);
      inputString.reserve(200);
    }
    void loop()
    {
      if (Status)
      {
            String Ten = inputString.substring(18, 19);
        String Unit = inputString.substring(19, 20);
        int Val = HexChangeDec(Ten) * 16 + HexChangeDec(Unit);
        Serial.println(Val);                      //将转换后的数据在(0,1)显示
        analogWrite(LedPin, Val);
        inputString = "";
        CanSerial.flush();
        Serial.flush();
        Status = false;
      }
    }
    int HexChangeDec(String ch)
    {
      String Hex0 = "0123456789ABCDEF";
      for (int i = 0; i < Hex0.length() + 1; i++)
      {
        String Hex1 = Hex0.substring(i, i + 1);
        if (ch == Hex1)
        {
          return i;
        }
      }
    }
    void serialEvent()
    {
      if (CanSerial.available())
      {
        char inChar = (char)CanSerial.read();
        inputString += inChar;
        if (inChar == '\n')
        {
          Status = true;
        }
      }
    }


  • TA的每日心情
    奋斗
    2021-1-26 19:45
  • 签到天数: 403 天

    [LV.9]以坛为家II

     楼主| 发表于 2020-11-27 20:15 | 显示全部楼层
    本帖最后由 topdog 于 2020-11-28 15:06 编辑
    redtxd 发表于 2020-11-27 18:15
    谢谢老师的热心帮助,还是在你的帖子上请教吧。
          两个例子我都试验过,都能正常控制。但是加入 ...

    你瞧从sscom这张图片看勾选了加回车换行,再从你的程序Serial.write(CanSerial.read())分析,两条接收的字符串都换行了,说明上位机发字符串尾部是加了回车符,文章中第九点和讨论区第五楼都提到过了,请你换成 inChar == '\n' || inChar == '\r'  就ok了!
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    热门推荐

    (开源)微信小程序控制esp8266,可远程
    (开源)微信小程序控制es
    流程说明:首先要使esp8266能够联网,能联网就可以控制,使用示例程序,修改WIFI、
    arduino封装了一个无线串口模块LC12S的操作库
    arduino封装了一个无线串
    前一段时间接触到一个比较好用的2.4G无线串口通信模块LC12S, 在淘宝上仅四五块钱,收
    【花雕动手做】快餐盒盖,极低成本搭建机器人实验平台
    【花雕动手做】快餐盒盖,
    吃完快餐粥,除了粥的味道不错之外,我对个快餐盒的圆盖子产生了兴趣,能否做个极低成
    全新AVR芯片ATMEGA328P烧录程序的教程
    全新AVR芯片ATMEGA328P烧
    潜水多年,觉得有义务贡献一些内容,此贴作为最近学习Arduino的一个总结。 玩Arduino
    【Arduino】168种传感器模块系列实验(90)--- ULN2003步进电机
    【Arduino】168种传感器模
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   
    快速回复 返回顶部 返回列表