串口数据帧我们学过,但到RS485是不是就卡壳了?


工作时,AB线在2.3v上做逻辑01(-2v,+2v)跳变。

RS-485的电气特性:逻辑“1”以两线间的电压差为 正(2~6)V 表示;逻辑“0”以两线间的电压差为 负(2~6)V 表示。
我们来看下:
逻辑正: A(2.3v+2v)约4v B(2.3v-2v)约0v 差值 正4V
逻辑负: A(2.3v-2v)约0v B(2.3v+2v)约4v 差值 负4V
所以 B相 是电平参考线。
//===========================
电压的问题讲完了,就该讲时序了。 

空闲时:AB都是2.3V相当于没电压,是0伏。逻辑正负切换时,波形振幅放大了。一个格子是2v,逻辑1正3.5v,逻辑0负3.5v
RS-485的电气特性:逻辑“1”以两线间的电压差为 正(2~6)V 表示;逻辑“0”以两线间的电压差为 负(2~6)V 表示。
我们来看下:
逻辑正: A(2.3v+2v)约4v B(2.3v-2v)约0v 差值 正4V
逻辑负: A(2.3v-2v)约0v B(2.3v+2v)约4v 差值 负4V
B相 是电平参考线。这样,逻辑就对上了。起始位要保持一个低电平,0伏怎么保持低电平呢?所以要先拉高4v,再拉低 (负4v)并保持一位长度。
所以,空闲是n长的0v。
一个起始位低电平:为了这个条件,所以至少拉高1个位的高电平再保持一个位的低电平。
8个数据位电平,D0D1D2D3D4D5D6D7 我发的是0xF1,

这个是(96N81)的时序图,你会发现,没有校验位的电平宽度,因为你是无校验,所以校验电平宽度就没有。
为了看校验位,我发的值要改成0x71(0111 0001),串口(96o81)奇校验

为了看校验位,我发的值要改成0x71(0111 0001),串口(96E81)偶校验

0x71(0111 0001)括号里的1数量,加 校验位电平值,是偶数就是偶校验 4+0等于4是偶数
好,下面看个无校验的(96N81)

可以看到,校验位的电平长度没了。
校验位,实际还可以强制:一直为1,或者一直为0;
打开串口,连续发送3次字节,看下时序图

我也不明白,为什么刚打开串口后,发送的第2帧会延迟。之后再连续发送就不会再延迟。可能是C#底层类库的问题。
接收:出现数据帧过短字节,可以用 com.ReceivedBytesThreshold = 1;// 单帧最低字节数
com.ReceivedBytesThreshold = 5;// 单帧最低字节数
设置5后,间歇发送的字节必须达到5字节,才能触发串口接收事件。
发送部分:
虽然是连续发单字节,实际代码有延迟。还是要用示波器,逻辑分析仪,监视
com.Write(by, 0, 1) ;


上位机上也能明显看出第3帧有延迟,我精确到毫秒4位。电脑还是不如单片机那样精准控制时间,实时性太差了。

- txshow(by, DateTime.Now.ToString("ss's'ffffff':'"));
-
-
-
-
- void txshow(byte[] bs, string tim)//日志
- {
- string num = string.Empty;
- foreach (var item in bs)
- {
- num += item.ToString("X2") + " ";
- }
-
- listBox2.Items.Insert(0, tim + num);
- //if (this.InvokeRequired)
- //{
- // this.Invoke(new Action(
- // () => { listBox2.Items.Insert(0, tim + num); }
- // ));
- //}
- //else
- //{
- // listBox2.Items.Insert(0, tim + num);
- //}
-
- }
- private void button9_Click(object sender, EventArgs e)
- {
- com.Write(new byte[3] { 0x30, 0x31, 0x32 }, 0, 3);
- }

这3字节没出现等待情况。
总之,电脑的时间实时性比较差,频繁读写,会造成卡顿。可能线程的问题。会出现字节帧过短,过长,空字节触发串口接收事件。
要么发送后,等待固定的时间,再去读取接收数据帧。或者判断载波检测,空字节可以再等待一会,再去读字节。
以下是我对串口的简单调试,写的C#,可以参考下
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.IO.Ports;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace portDemo
- {
- public partial class Form1 : Form
- {
- SerialPort com;
-
- static int wait=0;
-
- public Form1()
- {
- InitializeComponent();
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- com = new SerialPort();
- com.PortName=comboBox1.Text;
- com.BaudRate=int.Parse(comboBox2.Text );// 96
- switch (comboBox3.Text)// n
- {
- case "无": com.Parity = Parity.None; break;
- case "奇": com.Parity = Parity.Odd; break;
- case "偶": com.Parity = Parity.Even; break;
- case "高": com.Parity = Parity.Mark; break;
- case "低": com.Parity = Parity.Space; break;
-
- default:
- break;
- }
- com.DataBits=int.Parse(comboBox4.Text);
- switch (comboBox5.Text)// n
- {
- case "1": com.StopBits = StopBits.One; break;
- case "1.5": com.StopBits = StopBits.OnePointFive; break;
- case "2": com.StopBits = StopBits.Two; break;
-
- default:
- break;
- }
-
- com.ReceivedBytesThreshold = 1;// 单帧最低字节数
- com.ReadTimeout = 5;
-
-
- com.DataReceived += Com_DataReceived;//接收事件
-
- com.Open();
- button1.BackColor = Color.Green;
- }
-
- private void Com_DataReceived(object sender, SerialDataReceivedEventArgs e)
- {
- string tim = DateTime.Now.ToString("ss's'ffffff ':'"); // 毫秒
-
- byte[ ] rxbuff= new byte[com.BytesToRead];
- com.Read(rxbuff, 0, rxbuff.Length );
-
-
- rzshow(rxbuff ,tim);
-
-
- }
-
- private void button2_Click(object sender, EventArgs e)
- {
- com?.Close();
- com = null;
- button1.BackColor = Color.Gray;
- }
-
- private void button3_Click(object sender, EventArgs e)
- {
- string tim = DateTime.Now.ToString("ss's'fffff ':'"); // 毫秒
- byte by = Convert.ToByte(textBox1.Text, 16);
- com.Write(new byte[1]{ by },0,1);
- }
-
- void rzshow(byte[] bs,string tim)//日志
- {
- string num = string.Empty;
- foreach (var item in bs)
- {
- num += item.ToString("X2")+" ";
- }
-
- if(this.InvokeRequired)
- {
- this.Invoke(new Action(
- ()=> { listBox1.Items.Insert(0, tim +num); }
- ));
- }
- else
- {
- listBox1.Items.Insert(0, tim + num);
- }
-
- }
-
- private void button4_Click(object sender, EventArgs e)
- {// ↑
- com.ReadTimeout+=1;
- label7.Text= com.ReadTimeout.ToString();
- }
-
- private void button5_Click(object sender, EventArgs e)
- {
- com.ReadTimeout -= 1;
- label7.Text = com.ReadTimeout.ToString();
- }
-
- private async void button6_Click(object sender, EventArgs e)
- {//3tx
- listBox2.Items.Clear();
- label8.Text = wait.ToString();
- byte[] by = new byte[1] { Convert.ToByte(textBox1.Text, 16) };
- txshow(by, DateTime.Now.ToString("ss's'ffffff':'"));
- com.Write(by, 0, 1) ;
-
- await Task.Delay(wait);
- txshow(by, DateTime.Now.ToString("ss's'ffffff':'"));
- com.Write(by, 0, 1);
-
- await Task.Delay(wait);
- txshow(by, DateTime.Now.ToString("ss's'ffffff':'"));
- com.Write(by, 0, 1);
-
- await Task.Delay(wait);
- }
-
- void txshow(byte[] bs, string tim)//日志
- {
- string num = string.Empty;
- foreach (var item in bs)
- {
- num += item.ToString("X2") + " ";
- }
-
- listBox2.Items.Insert(0, tim + num);
- //if (this.InvokeRequired)
- //{
- // this.Invoke(new Action(
- // () => { listBox2.Items.Insert(0, tim + num); }
- // ));
- //}
- //else
- //{
- // listBox2.Items.Insert(0, tim + num);
- //}
-
- }
-
- private void button7_Click(object sender, EventArgs e)
- {// ↑
- wait += 1;
- label8.Text = wait.ToString();
-
- }
-
- private void button8_Click(object sender, EventArgs e)
- {
- wait -= 1;
- label8.Text = wait.ToString();
- }
-
- private void button9_Click(object sender, EventArgs e)
- {
- com.Write(new byte[3] { 0x30, 0x31, 0x32 }, 0, 3);
- }
- }
- }