查看: 3545|回复: 8

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

[复制链接]

签到天数: 214 天

[LV.7]常住居民III

发表于 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';

该用户从未签到

发表于 2020-11-20 12:36 | 显示全部楼层
感谢老师的讲解,非常实用的案例。

该用户从未签到

发表于 2020-11-20 15:41 | 显示全部楼层
还有个问题麻烦老师,这个例子我测试几遍,如果数据不断变化的话,必须把串口关闭,再打开才能收到新的数据。

签到天数: 214 天

[LV.7]常住居民III

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

打开串口监视器,请注意程序里面 inChar == '\n','\n'为换行符,所以要在右下角选择换行符,这样就可以直接在串口输入行键入编码实现控制了。
你也可以改成inChar == '\r','\r'为回车符,那么就要右下角选择回车符,在你按发送的时候,IDE会自动给你加回车键。

签到天数: 214 天

[LV.7]常住居民III

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

谢谢lanceli的关心和指导,一起学习一起进步。

签到天数: 214 天

[LV.7]常住居民III

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

串口控制的知识点还是比较多的,未能覆盖之处还请海涵,希望能够对你的学习有所帮助。

该用户从未签到

发表于 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;
    }
  }
}


签到天数: 214 天

[LV.7]常住居民III

 楼主| 发表于 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了!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

热门推荐

arduino解码sbus
arduino解码sbus
市面上有很多sbus接收机,它只有一根线就可以实现很多通道的通信,其本质利用了串口通
程序编译通过,运行崩溃,请大家看看
程序编译通过,运行崩溃,
编译后的程序是这么多。我也不知道占了多少,不明白这个具体意思。 我用的是esp-01s
【Arduino】168种传感器模块系列实验(146)---64位WS2812点阵屏
【Arduino】168种传感器模
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
原价299元【语音开发板套件】限时免费领!
原价299元【语音开发板套
如果NodeMcu上的GPIO引脚不够用怎么办?急!
如果NodeMcu上的GPIO引脚
我最近在做一个需要用到多个HC-SR04的项目,用Arduino IDE开发NodeMcu,参考的引脚图
Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   
快速回复 返回顶部 返回列表