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

空闲状态:AB线悬浮在2.3V的样子。GND是0V,+5V是4.75v

工作时,AB线在2.3v上做逻辑01(-2v,+2v)跳变。
这图是不是还不太好理解?我把B线下移3伏,给大家看看。

黄色是A相,绿色是B相。值是0x71
电平逻辑正好相反。永远对称。
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相 是电平参考线。
//===========================
电压的问题讲完了,就该讲时序了。 
我们先把示波器的GND接到B相上。

这2图是同一个波形,只是GND参考线换成了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长度高电平,一个起始位低电平,8个数据位电平,一个校验位,一个停止位。
所以,空闲是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;
注意:串口刚打开后,发送的第2帧有延迟,之后会没这个问题。
打开串口,连续发送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);
        }
    }
}



















