JS `Web Serial API` `Parity`, `Stop Bits`, `Data Bits` 的硬件级通信配置

各位听众,早上好/下午好/晚上好!我是你们今天的串口通讯小喇叭,今天咱们来聊聊 Web Serial API 里那些让你头大的硬件级配置:Parity(奇偶校验)、Stop Bits(停止位)和 Data Bits(数据位)。别怕,这玩意儿听起来玄乎,其实理解起来就像你泡方便面一样简单。

开场白:串口通讯,老朋友新玩法

串口通讯,这玩意儿在嵌入式领域可是老古董了,但 Web Serial API 赋予了它新的生命。以前你得装驱动、写 C 代码、折腾半天才能和硬件设备说上话,现在好了,浏览器直接上,JavaScript 一把梭,就能搞定。但是,要想玩转 Web Serial API,就绕不开那几个硬件配置参数,它们就像方便面里的调料包,加对了味道才正。

第一部分:数据位(Data Bits):信息的宽度

想象一下,你要给朋友发短信,每个字代表一部分信息。数据位就相当于每个字的大小,也就是每个串口传输的数据包里有多少个 bit。常见的选择有 5、6、7 和 8 个 bit。

  • 5 bits: 这玩意儿现在很少见了,除非你还在用一些超级古老的设备。
  • 6 bits: 也比较少见,但某些特殊应用可能会用到。
  • 7 bits: 可以用来传输 ASCII 码,但现在通常会被 8 bits 取代。
  • 8 bits: 这是最常用的,可以传输完整的 ASCII 码和扩展 ASCII 码,还可以传输二进制数据。就像你发微信,啥表情包都能发。

代码示例:设置数据位

navigator.serial.requestPort().then(port => {
  port.open({
    baudRate: 9600,
    dataBits: 8 // 设置数据位为 8 bits
  }).then(() => {
    console.log("串口已打开,数据位:8");
  }).catch(err => {
    console.error("打开串口失败:", err);
  });
});

幽默解释: 数据位就像你家的带宽,带宽越大,一次能传的数据就越多。8 bits 就像百兆光纤,5 bits 就像拨号上网,速度差太多了!

第二部分:奇偶校验(Parity):数据传输的质检员

奇偶校验就像数据传输过程中的质检员,它能帮你检测数据在传输过程中有没有出错。想象一下,你在高速公路上开车,奇偶校验就像路上的摄像头,随时监控有没有车辆违规。

奇偶校验有几种模式:

  • None (无校验): 最简单粗暴,啥也不管,数据直接发。就像你开车直接上路,没有交通规则约束。
  • Even (偶校验): 统计数据位中 1 的个数,如果 1 的个数是奇数,就在数据后面加一个 1,让总数变成偶数。如果 1 的个数已经是偶数,就加一个 0。
  • Odd (奇校验): 和偶校验相反,如果 1 的个数是偶数,就加一个 1,让总数变成奇数。如果 1 的个数已经是奇数,就加一个 0。
  • Mark (标记校验): 在数据后面加一个 1。
  • Space (空格校验): 在数据后面加一个 0。

表格总结:奇偶校验的工作原理

校验模式 原始数据 (1 的个数) 添加的校验位 最终数据 (1 的个数)
None 1010101 (4, 偶数) 1010101 (4, 偶数)
None 1010100 (3, 奇数) 1010100 (3, 奇数)
Even 1010101 (4, 偶数) 0 10101010 (4, 偶数)
Even 1010100 (3, 奇数) 1 10101001 (4, 偶数)
Odd 1010101 (4, 偶数) 1 10101011 (5, 奇数)
Odd 1010100 (3, 奇数) 0 10101000 (3, 奇数)
Mark 1010101 (4, 偶数) 1 10101011 (5, 奇数)
Space 1010101 (4, 偶数) 0 10101010 (4, 偶数)

代码示例:设置奇偶校验

navigator.serial.requestPort().then(port => {
  port.open({
    baudRate: 9600,
    dataBits: 8,
    parity: "even" // 设置奇偶校验为偶校验
  }).then(() => {
    console.log("串口已打开,奇偶校验:偶校验");
  }).catch(err => {
    console.error("打开串口失败:", err);
  });
});

幽默解释: 奇偶校验就像你给文件打包加个校验码,如果文件在传输过程中损坏了,校验码就能告诉你哪里出错了。None 就像你直接发裸奔的文件,万一丢了数据也不知道。

第三部分:停止位(Stop Bits):数据的分割线

停止位就像数据的分割线,它告诉接收方,一个数据包已经发送完毕,可以准备接收下一个数据包了。想象一下,你在读一本书,停止位就像句号,告诉你一句话已经结束,可以开始读下一句了。

常见的选择有 1 和 2 个 bit。

  • 1 bit: 这是最常用的。
  • 2 bits: 有些老设备可能需要 2 个停止位,但现在很少见了。

代码示例:设置停止位

navigator.serial.requestPort().then(port => {
  port.open({
    baudRate: 9600,
    dataBits: 8,
    parity: "none",
    stopBits: 1 // 设置停止位为 1 bit
  }).then(() => {
    console.log("串口已打开,停止位:1");
  }).catch(err => {
    console.error("打开串口失败:", err);
  });
});

幽默解释: 停止位就像你说话时的停顿,停顿太短别人听不清,停顿太长别人以为你卡壳了。1 bit 就像正常的停顿,2 bits 就像你突然宕机了。

第四部分:综合应用:一个完整的串口配置

现在,我们把数据位、奇偶校验和停止位组合起来,创建一个完整的串口配置。

navigator.serial.requestPort().then(port => {
  port.open({
    baudRate: 115200, // 波特率
    dataBits: 8,      // 数据位
    parity: "none",   // 奇偶校验
    stopBits: 1,      // 停止位
    flowControl: "none" //流控
  }).then(() => {
    console.log("串口已打开,配置:115200-8-N-1");
    //  115200: 波特率
    //  8: 数据位
    //  N: 无校验 (None)
    //  1: 停止位
  }).catch(err => {
    console.error("打开串口失败:", err);
  });
});

重要提示:

  • 通信双方的配置必须一致! 就像你和朋友打电话,双方都得用普通话才能交流。如果你的设备用的是 8-N-1,你的 Web Serial API 也必须配置成 8-N-1,否则就乱码了。
  • 波特率也很重要! 波特率决定了数据传输的速度,如果波特率不一致,也会导致乱码。

第五部分:调试技巧:遇到乱码怎么办?

如果你发现串口通讯出现乱码,不要慌,按照下面的步骤排查:

  1. 检查波特率: 确认你的 Web Serial API 和硬件设备的波特率是否一致。
  2. 检查数据位、奇偶校验和停止位: 确认你的 Web Serial API 和硬件设备的配置是否一致。
  3. 检查编码方式: 确认你的数据是用什么编码方式发送的,例如 UTF-8 或 ASCII。
  4. 使用串口调试工具: 可以使用串口调试工具(例如 Serial Monitor)来查看原始数据,帮助你定位问题。

代码示例:使用 TextDecoder 解码数据

// 假设你已经成功打开了串口并获取了 reader

const decoder = new TextDecoder(); // 默认使用 UTF-8 解码
reader.read().then(({ value, done }) => {
  if (done) {
    console.log("读取结束");
    return;
  }
  const text = decoder.decode(value);
  console.log("接收到的数据:", text);
});

如果你需要使用其他编码方式,可以在 TextDecoder 构造函数中指定:

const decoder = new TextDecoder('gbk'); // 使用 GBK 解码

第六部分:高级话题:流控制(Flow Control)

除了数据位、奇偶校验和停止位,还有一个重要的参数是流控制。流控制用于防止数据溢出,就像你家的水管,如果水流太快,可能会爆管。

常见的流控制方式有:

  • None (无流控制): 最简单粗暴,啥也不管,数据直接发。
  • RTS/CTS (硬件流控制): 使用 RTS (Request To Send) 和 CTS (Clear To Send) 信号线来控制数据流。
  • XON/XOFF (软件流控制): 使用 XON 和 XOFF 字符来控制数据流。

代码示例:设置流控制

navigator.serial.requestPort().then(port => {
  port.open({
    baudRate: 9600,
    dataBits: 8,
    parity: "none",
    stopBits: 1,
    flowControl: "none" // 设置流控制为无流控制
  }).then(() => {
    console.log("串口已打开,流控制:无");
  }).catch(err => {
    console.error("打开串口失败:", err);
  });
});

第七部分:实际案例:使用 Web Serial API 控制 Arduino

假设我们要使用 Web Serial API 控制 Arduino 的 LED 灯。

Arduino 代码:

const int ledPin = 13;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    char command = Serial.read();
    if (command == '1') {
      digitalWrite(ledPin, HIGH); // 点亮 LED
    } else if (command == '0') {
      digitalWrite(ledPin, LOW);  // 关闭 LED
    }
  }
}

Web Serial API 代码:

let port;
let writer;

async function connectSerial() {
  try {
    port = await navigator.serial.requestPort();
    await port.open({ baudRate: 9600, dataBits: 8, parity: "none", stopBits: 1 });
    writer = port.writable.getWriter();
    console.log("串口已连接");
  } catch (error) {
    console.error("连接串口失败:", error);
  }
}

async function sendCommand(command) {
  if (!writer) {
    console.warn("串口未连接");
    return;
  }
  const data = new Uint8Array([command.charCodeAt(0)]); // 将字符转换为字节数组
  await writer.write(data);
  console.log(`发送命令: ${command}`);
}

async function disconnectSerial() {
  if (writer) {
    await writer.close();
  }
  if (port && port.readable) {
    await port.readable.cancel();
  }
  if (port) {
    await port.close();
  }
  port = null;
  writer = null;
  console.log("串口已断开");
}

// 示例用法:
document.getElementById("connectButton").addEventListener("click", connectSerial);
document.getElementById("onButton").addEventListener("click", () => sendCommand('1'));
document.getElementById("offButton").addEventListener("click", () => sendCommand('0'));
document.getElementById("disconnectButton").addEventListener("click", disconnectSerial);

代码解释:

  1. Arduino 代码: 监听串口数据,如果收到 ‘1’,就点亮 LED,如果收到 ‘0’,就关闭 LED。
  2. Web Serial API 代码:
    • connectSerial() 函数:连接串口,并配置波特率、数据位、奇偶校验和停止位。
    • sendCommand() 函数:发送命令到 Arduino。
    • disconnectSerial() 函数:断开串口连接。

总结:

Web Serial API 提供了强大的串口通讯功能,但要玩转它,你需要理解数据位、奇偶校验和停止位这些硬件级配置。希望今天的讲座能帮助你更好地使用 Web Serial API,连接你的硬件设备,创造出更多有趣的应用!记住,调试是关键,遇到问题不要怕,一步一步排查,总能找到解决方案。

最后的温馨提示:

在实际开发中,请务必参考你所使用的硬件设备的文档,了解它的串口配置要求,并根据实际情况进行调整。不同的设备可能有不同的配置要求,照搬别人的配置可能会导致通讯失败。祝大家玩得开心!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注