C# WPF燃气报警器记录读取串口工具

C# WPF燃气报警器记录读取串口工具

  • 概要
  • 串口帧数据
  • 布局文件
  • 代码文件
  • 运行效果
  • 源码下载

概要

  • 符合国标文件《GB+15322.2-2019.pdf》串口通信协议定义;
  • 可读取燃气报警器家用版设备历史记录信息等信息;
    在这里插入图片描述
    在这里插入图片描述

串口帧数据

串口通信如何确定一帧数据接收完成是个麻烦事,本文采用最后一次数据接收完成后再过多少毫秒认为一帧数据接收完成,开始解析出来。每次接收到数据更新一次recvTimems 。定时器mTimer定时周期10毫秒,定时器回调函数里判断接收时间超过20ms(这个时间的长短和串口波特率有关)认为一帧数据接收完成。接收数据时间差未超过20ms则将接收数据追加到rxBuf数据缓冲,

long recvTimems = 0;    // 用于计算串口接收完一帧数据
int rxLen = 0;  // 串口接收到的数据长度
byte[] rxBuff = new byte[128];  // 串口数据接收缓存
private static Timer mTimer;    // 定时器,10ms执行一次

mTimer = new Timer(recvTimerCalback, null, 0, 10);  // 创建并启动定时器
 
private void recvTimerCalback(object obj)
{
    //Console.WriteLine("timer callback!" + recvTimems);
    this.Dispatcher.Invoke(new Action(()=> {
        // UI线程分离,更新UI数据显示
        if (((Environment.TickCount - recvTimems) >= 20) && (rxLen > 0))
        {
            // 串口接收时间超过X ms认为一帧数据接收完成
			
			这里解析处理接收数据.....
			
			// 一帧数据处理结束清空数据缓冲
			Array.Clear(rxBuff, 0, rxBuff.Length);
			rxLen = 0;
    	}

	}));  
}

private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    
    SerialPort comPort = (SerialPort)sender;
    try
    {
        recvTimems = Environment.TickCount; // 更新串口数据接收时间

        //rxBuff[rxLen] = mSerialPort.ReadByte();
        int readCnt = mSerialPort.BytesToRead;
        Console.WriteLine("in readCnt:" + readCnt);
        mSerialPort.Read(rxBuff, rxLen, readCnt);
        rxLen += readCnt;
        Console.WriteLine("out rxLen:" + rxLen);
    }
    catch (Exception ce)
    {
        MessageBox.Show(ce.Message);
    }            

}

布局文件

XAML

<Window x:Class="GasAlarmTestTool.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GasAlarmTestTool"
        mc:Ignorable="d"
        Title="燃气报警器接口工具" Height="600" Width="800">
    <Grid>
        <TabControl>
            <TabItem>
                <TabItem.Header>
                    <StackPanel>
                        <Label>串口设置</Label>
                    </StackPanel>
                </TabItem.Header>
                <StackPanel Orientation="Horizontal">
                    <GroupBox Header="串口" Width="200">
                        <StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Label Margin="3" Height="25" Width="50">端口号</Label>
                                <ComboBox x:Name="comboBoxCOM" Margin="3" Height="20" Width="130" DragDrop.Drop="comboBoxCOM_Drop"/>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Label Margin="3" Height="25" Width="50">波特率</Label>
                                <ComboBox x:Name="comboBoxBaudRate" Margin="3" Height="20" Width="130" SelectedIndex="0" IsEditable="True" >
                                    
                                </ComboBox>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Label Margin="3" Height="25" Width="50">数据位</Label>
                                <ComboBox x:Name="comboBoxDataBit" Margin="3" Height="20" Width="130" SelectedIndex="3">
                                    <ComboBoxItem>5</ComboBoxItem>
                                    <ComboBoxItem>6</ComboBoxItem>
                                    <ComboBoxItem>7</ComboBoxItem>
                                    <ComboBoxItem>8</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Label Margin="3" Height="25" Width="50">停止位</Label>
                                <ComboBox x:Name="comboBoxStopBit" Margin="3" Height="20" Width="130" SelectedIndex="0">
                                    <ComboBoxItem>1位</ComboBoxItem>
                                    <ComboBoxItem>1.5位</ComboBoxItem>
                                    <ComboBoxItem>2位</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Label Margin="3" Height="25" Width="50">校验位</Label>
                                <ComboBox x:Name="comboBoxSdd" Margin="3" Height="20" Width="130" SelectedIndex="0">
                                    <ComboBoxItem>无校验</ComboBoxItem>
                                    <ComboBoxItem>奇校验</ComboBoxItem>
                                    <ComboBoxItem>偶校验</ComboBoxItem>
                                    <ComboBoxItem>1 校验</ComboBoxItem>
                                    <ComboBoxItem>0 校验</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Label Margin="3" Height="25" Width="50">流控位</Label>
                                <ComboBox x:Name="comboBoxlik" Margin="3" Height="20" Width="130" SelectedIndex="0" >
                                    <ComboBoxItem>无流控</ComboBoxItem>
                                    <ComboBoxItem>RTS/CTS</ComboBoxItem>
                                    <ComboBoxItem>XON/XOFF</ComboBoxItem>
                                </ComboBox>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                                <Button x:Name="btnOpenCloseCom" Margin="3" Width="80" Height="25" Click="btnOpenCloseCom_Click">打开串口</Button>
                                <Button x:Name="btnClearRecv" Margin="3" Width="80" Height="25" Click="btnClearRecv_Click">清空接收</Button>
                            </StackPanel>
                        </StackPanel>
                    </GroupBox>
                    <GroupBox Header="接收数据" MinWidth="590" MaxWidth="1000">
                        <ScrollViewer>
                            <ScrollViewer.Content>
                                <TextBlock x:Name="textBlockRecv" MinWidth="300" MaxWidth="1000"></TextBlock>
                            </ScrollViewer.Content>
                        </ScrollViewer>
                    </GroupBox>
                </StackPanel>
            </TabItem>
            <TabItem>
                <TabItem.Header>
                    <StackPanel>
                        <Label>数据读取</Label>
                    </StackPanel>
                </TabItem.Header>
                <StackPanel Orientation="Horizontal">
                    <StackPanel Orientation="Vertical">
                        <GroupBox Header="记录总数">
                            <StackPanel Orientation="Vertical">
                                <Button x:Name="btnReadRecCount" Margin="3" Padding="5" Click="btnReadRecCount_Click">读取记录总数</Button>
                                <StackPanel Orientation="Horizontal">
                                    <Label>报警记录总数:</Label>
                                    <TextBox x:Name="txtBoxWarningCount">0</TextBox>
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Label>报警恢复记录总数:</Label>
                                    <TextBox x:Name="txtBoxWarningRecoveryCount">0</TextBox>
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Label>故障记录总数:</Label>
                                    <TextBox x:Name="txtBoxFaultCount">0</TextBox>
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Label>故障恢复记录总数:</Label>
                                    <TextBox x:Name="txtBoxFaultRecoryCount">0</TextBox>
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Label>掉电记录总数:</Label>
                                    <TextBox x:Name="txtBoxPoweroffCount">0</TextBox>
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Label>上电记录总数:</Label>
                                    <TextBox x:Name="txtBoxPoweronCount">0</TextBox>
                                </StackPanel>
                                <StackPanel Orientation="Horizontal">
                                    <Label>传感器失效记录总数:</Label>
                                    <TextBox x:Name="txtSensorInvalidCount">0</TextBox>
                                </StackPanel>
                            </StackPanel>
                        </GroupBox>
                        <GroupBox Header="日期时间">
                            <StackPanel Orientation="Vertical">
                                <Button x:Name="btnReadDateTime" Click="btnReadDateTime_Click">读取日期时间</Button>
                                <TextBox x:Name="txtBoxDateTime">0000-00-00 00:00</TextBox>
                            </StackPanel>
                        </GroupBox>
                    </StackPanel>
                    <StackPanel Orientation="Vertical">
                        <GroupBox Header="读取指定记录">
                            <StackPanel Orientation="Horizontal">
                                <ComboBox x:Name="cmboxRecordType" Width="120" SelectedIndex="0">
                                    <ComboBoxItem>报警记录</ComboBoxItem>
                                    <ComboBoxItem>报警恢复记录</ComboBoxItem>
                                    <ComboBoxItem>故障记录</ComboBoxItem>
                                    <ComboBoxItem>故障恢复记录</ComboBoxItem>
                                    <ComboBoxItem>掉电记录</ComboBoxItem>
                                    <ComboBoxItem>上电记录</ComboBoxItem>
                                    <ComboBoxItem>传感器失效记录</ComboBoxItem>
                                </ComboBox>
                                <TextBox x:Name="txtBoxNumber" Width="50">1</TextBox>
                                <Button x:Name="btnReadRecord" Click="btnReadRecord_Click" ClickMode="Press">读取记录</Button>
                                <TextBox x:Name="txtBoxRecordInfo">0000/00/00  00:00</TextBox>
                            </StackPanel>
                        </GroupBox>
                    </StackPanel>
                </StackPanel>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

代码文件

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace GasAlarmTestTool
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private int warningCnt = 0;
        private int warningRecoveryCnt = 0;
        private int faultCnt = 0;
        private int faultRecoveryCnt = 0;
        private int poweroffCnt = 0;
        private int poweronCnt = 0;
        private int sensorInvalidCnt = 0;

        long recvTimems = 0;    // 用于计算串口接收完一帧数据
        int rxLen = 0;  // 串口接收到的数据长度
        byte[] rxBuff = new byte[128];  // 串口数据接收缓存
        private static Timer mTimer;    // 定时器,10ms执行一次

        SerialPort mSerialPort = new SerialPort(); 
        private StringBuilder lineBuilder = new StringBuilder();    // 用于存放串口接收数据
        private List<string> baudrateList = new List<string> { 
            "4800", 
            "9600", 
            "19200",
            "38400",
            "57600",
            "115200",
            "256000",
            "1000000",
            "2000000",
            "3000000"
        };

        public MainWindow()
        {
            InitializeComponent();

            // 获取所有可用串口端口,并添加到comboBoxCOM
            string[] ports = System.IO.Ports.SerialPort.GetPortNames();
            comboBoxCOM.ItemsSource = ports;
            comboBoxCOM.SelectedIndex = 0;  // 默认选择索引

            comboBoxBaudRate.ItemsSource = baudrateList;    // 波特率设置combobox数据源

            mTimer = new Timer(recvTimerCalback, null, 0, 10);  // 创建并启动定时器
        }

        /// <summary>
        /// 窗口关闭处理
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {            
            if (MessageBox.Show("确定要退出吗?", "确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
            {
                // 用户选择“否”,取消关闭
                e.Cancel = true;
            }

            mSerialPort.Close();
            mTimer.Dispose();

            base.OnClosing(e);
        }

        private string RecordInfoString(byte[] buf)
        {
            if (buf.Length < 11) {
                return "";
            }

            int index = buf[4];
            int year = buf[5] << 8 | buf[6];
            int month = buf[7];
            int day = buf[8];
            int hour = buf[9];
            int minute = buf[10];
            return  year.ToString() + "/" + month.ToString() + "/" + day.ToString() + "   " + hour.ToString() + ":" + minute.ToString();            
        }

        /// <summary>
        /// 串口接收处理定时器回调
        /// </summary>
        /// <param name="obj"></param>
        private void recvTimerCalback(object obj)
        {
            //Console.WriteLine("timer callback!" + recvTimems);
            this.Dispatcher.Invoke(new Action(()=> {
                // UI线程分离,更新UI数据显示
                if (((Environment.TickCount - recvTimems) >= 20) && (rxLen > 0))
                {
                    // 串口接收时间超过X ms认为一帧数据接收完成

                    uint HEAD = rxBuff[0];
                    uint C1 = rxBuff[1];
                    uint C2 = rxBuff[2];
                    uint L = rxBuff[3];
                    uint CS = rxBuff[L + 4];
                    uint END = rxBuff[L + 5];
                    
                    // 打印串口结束缓存数据
                    uint _CS = 0;   // 计算接收数据校验和

                    uint index = 0;
                    UInt32 temp = 0;
                    for (int i = 0; i < rxLen; i++)
                    {
                        if (i < (rxLen - 2))
                        {
                            temp += rxBuff[i];
                        }
                        Console.WriteLine(rxBuff[i].ToString("X2"));
                    }
                    _CS = (uint)(temp % 0xFF);

                    if ((0xAA == rxBuff[0]) && (0x55 == END))
                    {
                        // TODO: 接收到一帧完整数据
                        Console.WriteLine("RS232 RECV ONE FRAME!" + CS.ToString("X2") + ", " + _CS.ToString("X2"));

                        if (CS == _CS)
                        {
                            // TODO: CHECKSUM8 校验和正确
                            Console.WriteLine("CheckSum OK");
                            if (0x00 == C2)
                            {
                                if (0x00 == C1)
                                {
                                    // TODO: 查询各类记录总数
                                    warningCnt = rxBuff[4]; // 探测器报警记录总数
                                    warningRecoveryCnt = rxBuff[5]; // 探测器报警恢复记录总数
                                    faultCnt = rxBuff[6];   // 探测器故障记录总数
                                    faultRecoveryCnt = rxBuff[7]; // 探测器故障恢复记录总数
                                    poweroffCnt = rxBuff[8];    // 探测器掉电记录总数
                                    poweronCnt = rxBuff[9];     // 探测器上电记录总数
                                    sensorInvalidCnt = rxBuff[10];  // 传感器失效记录总数

                                    txtBoxWarningCount.Text = warningCnt.ToString();
                                    txtBoxWarningRecoveryCount.Text = warningRecoveryCnt.ToString();
                                    txtBoxFaultCount.Text = faultCnt.ToString();
                                    txtBoxFaultRecoryCount.Text = faultRecoveryCnt.ToString();
                                    txtBoxPoweroffCount.Text = poweroffCnt.ToString();  
                                    txtBoxPoweronCount.Text = poweronCnt.ToString();
                                }
                            }
                            else if (0x01 == C2)
                            {
                                // 报警记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);
                            }
                            else if (0x02 == C2)
                            {
                                // 报警恢复记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);
                            }
                            else if (0x03 == C2)
                            {
                                // 故障记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);
                            }
                            else if (0x04 == C2)
                            {
                                // 故障恢复记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);
                            }
                            else if (0x05 == C2)
                            {
                                // 掉电记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);
                            }
                            else if (0x06 == C2)
                            {
                                // 上电记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);
                            }
                            else if (0x07 == C2)
                            {
                                // 传感器失效记录
                                txtBoxRecordInfo.Text = RecordInfoString(rxBuff);

                            }
                            else if (0x08 == C2)
                            {
                                // TODO: 日期时间
                                int year = rxBuff[4] << 8 | rxBuff[5];
                                int month = rxBuff[6];
                                int day = rxBuff[7];
                                int hour = rxBuff[8];
                                int minute = rxBuff[9];
                                txtBoxDateTime.Text = year.ToString() + "/" + month.ToString() + "/" + day.ToString() + "   "+hour.ToString() + ":"+minute.ToString();                                
                            }

                        }
                        else
                        {
                            // TODO: CHECKSUM8 校验和有误
                            Console.WriteLine("CheckSum ERR");
                        }
                    }
                    Array.Clear(rxBuff, 0, rxBuff.Length);
                    rxLen = 0;
                }

            }));                        
        }

        /// <summary>
        /// 串口数据接收函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            
            SerialPort comPort = (SerialPort)sender;
            try
            {
                recvTimems = Environment.TickCount; // 更新串口数据接收时间

                //rxBuff[rxLen] = mSerialPort.ReadByte();
                int readCnt = mSerialPort.BytesToRead;
                Console.WriteLine("in readCnt:" + readCnt);
                mSerialPort.Read(rxBuff, rxLen, readCnt);
                rxLen += readCnt;
                Console.WriteLine("out rxLen:" + rxLen);

#if false
                byte[] data = new byte[mSerialPort.BytesToRead];
                mSerialPort.Read(data, 0, data.Length);
                Console.WriteLine(data.Length);
                foreach (byte b in data)
                { 
                    Console.WriteLine(b.ToString("X2"));                    
                }
#endif
                

            }
            catch (Exception ce)
            {
                MessageBox.Show(ce.Message);
            }            

        }

        private void btnOpenCloseCom_Click(object sender, RoutedEventArgs e)
        {

            if (mSerialPort.IsOpen)
            {
                mSerialPort.Close();
                btnOpenCloseCom.Content = "打开串口";
                Console.WriteLine("关闭串口成功");
                Debug.WriteLine("关闭串口成功");
                comboBoxBaudRate.IsEnabled = true;
                comboBoxCOM.IsEnabled = true;
                comboBoxDataBit.IsEnabled = true;
                comboBoxStopBit.IsEnabled = true;
                comboBoxSdd.IsEnabled = true;
                comboBoxlik.IsEnabled = true;
            }
            else
            {
                mSerialPort.PortName = comboBoxCOM.SelectedItem.ToString();
                mSerialPort.BaudRate = 4800; // 波特率
                mSerialPort.DataBits = 8;   // 数据位
                mSerialPort.StopBits = StopBits.One;    // 停止位
                mSerialPort.Parity = Parity.None;   // 校验位
                mSerialPort.Handshake = Handshake.None;
                //mSerialPort.ReadTimeout = 1500; // 读超时
                //mSerialPort.Encoding = Encoding.UTF8; // 编码方式
                //mSerialPort.RtsEnable = true;

                mSerialPort.DataReceived += SerialPort_DataReceived;

                Console.WriteLine("baudrate SelectedIndex:" + comboBoxBaudRate.SelectedIndex);
                Console.WriteLine("baudrate SelectedValue:" + comboBoxBaudRate.SelectedValue);
                Console.WriteLine("baudrate Text:" + comboBoxBaudRate.Text);


                if (comboBoxBaudRate.SelectedIndex < 0)
                {
                    mSerialPort.BaudRate = Convert.ToInt32(comboBoxBaudRate.Text);
                }
                else 
                {
                    switch (comboBoxBaudRate.SelectedValue)
                    {
                        case "4800":
                            mSerialPort.BaudRate = 4800;
                            break;
                        case "9600":
                            mSerialPort.BaudRate = 9600;
                            break;
                        case "19200":
                            mSerialPort.BaudRate = 19200;
                            break;
                        case "38400":
                            mSerialPort.BaudRate = 38400;
                            break;
                        case "57600":
                            mSerialPort.BaudRate = 57600;
                            break;
                        case "115200":
                            mSerialPort.BaudRate = 115200;
                            break;
                        case "256000":
                            mSerialPort.BaudRate = 256000;
                            break;
                        case "1000000":
                            mSerialPort.BaudRate = 1000000;
                            break;
                        case "2000000":
                            mSerialPort.BaudRate = 2000000;
                            break;
                        case "3000000":
                            mSerialPort.BaudRate = 3000000;
                            break;                       
                        default:
                            MessageBox.Show("波特率设置有误!");
                            break;
                    }
                }

                Console.WriteLine("端口:" + mSerialPort.PortName  + ",波特率:" + mSerialPort.BaudRate);

                // mSerialPort.Write("Hello world"); // 写字符串口
                // mSerialPort.Write(new byte[] { 0xA0, 0xB0, 0xC0}, 0, 3); // 写入3个字节数据

                //Debug.WriteLine("Hello world");
                //MessageBox.Show("端口名:" + mSerialPort.PortName);

                try
                {
                    mSerialPort.Open();
                    btnOpenCloseCom.Content = "关闭串口";
                    Console.WriteLine("打开串口成功");
                    Debug.WriteLine("打开串口成功");

                    comboBoxBaudRate.IsEnabled = false;
                    comboBoxCOM.IsEnabled = false;
                    comboBoxDataBit.IsEnabled = false;
                    comboBoxStopBit.IsEnabled = false;
                    comboBoxSdd.IsEnabled = false;
                    comboBoxlik.IsEnabled = false;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

        }

        private void btnClearRecv_Click(object sender, RoutedEventArgs e)
        {
            lineBuilder.Clear();
            textBlockRecv.Text = lineBuilder.ToString();
        }

        private void comboBoxCOM_Drop(object sender, DragEventArgs e)
        {
            string[] ports = System.IO.Ports.SerialPort.GetPortNames();
            comboBoxCOM.ItemsSource = ports;
            comboBoxCOM.SelectedIndex = 0;
        }

        private void comboBoxBaudRate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            ComboBoxItem obj = (ComboBoxItem)sender;


        }

        private void btnReadRecCount_Click(object sender, RoutedEventArgs e)
        {
            if (mSerialPort.IsOpen)
            {
                mSerialPort.Write(new byte[] { 0xAA, 0x00, 0x00, 0x00, 0xAA, 0x55 }, 0, 6); // 写入3个字节数据         
            }
            else
            {
                MessageBox.Show("请先打开串口!");
            }
        }

        private void btnReadDateTime_Click(object sender, RoutedEventArgs e)
        {
            if (mSerialPort.IsOpen)
            {
                mSerialPort.Write(new byte[] { 0xAA, 0x00, 0x08, 0x00, 0xB2, 0x55 }, 0, 6);
            }
            else
            {
                MessageBox.Show("请先打开串口!");
            }
        }

        private void btnReadRecord_Click(object sender, RoutedEventArgs e)
        {            
            int index = Convert.ToInt32(txtBoxNumber.Text);
            if (index < 1 || index > 255)
            {
                MessageBox.Show("记录索引:1~255!");
            }
            else 
            {
                byte C1 = (byte)Convert.ToInt32(txtBoxNumber.Text);
                byte C2 = (byte)(cmboxRecordType.SelectedIndex);
                byte CS = (byte)((0xAA + C1 + C2)%0xFF);
                if (mSerialPort.IsOpen)
                {
                    byte[] Data = new byte[16];
                    Data[0] = 0xAA;
                    Data[1] = C1;   // 指定记录条数
                    Data[2] = (byte)(C2 + 1);   // C2 记录分类从1开始
                    Data[3] = 0x00;
                    Data[4] = CS;
                    Data[5] = 0x55;
                    mSerialPort.Write(Data, 0, 6);
                }
                else
                {
                    MessageBox.Show("请先打开串口!");
                }
            }
        }
    }
}

运行效果

在这里插入图片描述
在这里插入图片描述

源码下载

下载源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/874251.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第二证券:科创板股票交易规则,科创板新手可以买吗?

科创板是独立于现有主板商场的特别板块&#xff0c;面向的是国际科技前沿、经济主战场、国家严峻需求&#xff0c;首要服务于契合国家战略、打破要害核心技术、商场认可度高的科技立异企业。 科创板是独立于现有主板商场的特别板块&#xff0c;面向的是国际科技前沿、经济主战…

Windows安装anaconda注意事项及jupyter notebook更换目录

anaconda的介绍就不罗嗦了&#xff0c;既然准备安装了&#xff0c;说明你已经有所了解了。直入主题&#xff0c;Anaconda官网下载&#xff0c;实在太慢&#xff0c;可到https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/下载&#xff0c;注意&#xff0c;这是清华镜像站…

MySQL基础(8)- 单行函数(2)

目录 一、流程控制函数 1.IF(VALUE,VALUE1,VALUE2) 2.IFNULL(VALUE1,VALUE2) 3.CASE WHEN ... THEN ...WHEN ... THEN ... ELSE ... END 4.CASE ... WHEN ... THEN ... WHEN ... THEN ... ELSE ... END 二、加密与解密的函数 三、MySQL信息函数 四、其他函数 一、流程控…

MySQL之DQL-分组函数

1、分组函数 1. 分组函数语法 分组函数也叫聚合函数。是对表中一组记录进行操作&#xff0c;每组只返回一个结果。我们只讲如下5个常用的分组函数&#xff1a; 分组函数 含义 MAX 求最大值 MIN 求最小值 SUM 求和 AVG 求平均值 COUNT 求个数 分组函数的语法如下…

并发编程:AQS(上)

一、AQS 是什么&#xff1f; AQS 的全称为 AbstractQueuedSynchronizer &#xff0c;翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks 包下面。 AQS 就是一个抽象类&#xff0c;主要用来构建锁和同步器。 public abstract class AbstractQueuedSynch…

“健康中国 医路无忧——公益联盟”积极响应,国内首支公益陪诊师志愿队伍正式成立

在快节奏的现代生活中&#xff0c;就医不再是简单的“看病”那么简单。面对复杂的医疗流程、专业的医学术语、以及在陌生环境中的焦虑&#xff0c;患者及家属往往感到无所适从。此时&#xff0c;陪诊服务如同一束光&#xff0c;照亮了就医之路&#xff0c;它的重要性不仅体现在…

Visual Studio汇编代码高亮与自动补全

高亮插件&#xff1a;AsmDude (可以按照我的颜色进行设置&#xff0c;或者你自己改) 代码自动补全&#xff1a;CodeGeex (功能很多&#xff0c;支持的语言很多)&#xff0c;按Tab补全

骨传导耳机哪个品牌好用?精选五大高能骨传导耳机分享!

随着科技的进步和人们对健康生活的追求&#xff0c;骨传导耳机凭借其独特的设计理念和使用体验&#xff0c;迅速在运动爱好者和日常通勤族中流行起来。与传统耳机相比&#xff0c;骨传导耳机不仅能够提供清晰的音质&#xff0c;还能确保佩戴者在享受音乐的同时保持对外界环境的…

关于腾讯IM消息ID不统一的问题?服务端的MsgKey和前端的msgID不一样

角色>前端&#xff1a;web、小程序、客户端&#xff08;ios、安卓&#xff09;&#xff1b;服务端&#xff1b;腾讯IM&#xff1b; 1、背景 IM消息ID不一致&#xff0c;本地没有缓存历史数据&#xff0c;导致在调用腾讯sdk方法时&#xff0c;id不一致报错问题 2、调研目的…

HTML 揭秘:HTML 编码快速入门

HTML 揭秘&#xff1a;HTML 编码快速入门 一 . 前端知识介绍二 . HTML 介绍三 . HTML 快速入门四 . HTML 编辑器 - VSCode4.1 插件安装4.2 修改主题配色4.3 修改快捷键4.4 设置自动保存4.5 创建 HTML 文件4.5 书写 HTML 代码4.6 常见快捷键 五 . 基础标签5.1 字体标签5.1.1 col…

java实现图片爬取

开发环境 IntelliJ IDEA 2021.1.3 jdk1.8 Jsoup介绍 一款Java 的HTML解析器 jsoup 是一款Java 的HTML解析器&#xff0c;可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API&#xff0c;可通过DOM&#xff0c;CSS以及类似于jQuery的操作方法来取出和操作数…

SPI子系统

IO特性 SPI接口一般使用四条信号线通信&#xff1a; SDI&#xff08;数据输入&#xff09;&#xff0c;SDO&#xff08;数据输出&#xff09;&#xff0c;SCK&#xff08;时钟&#xff09;&#xff0c;CS&#xff08;片选&#xff09; MISO&#xff1a; 主设备输入/从设备输出…

服装|基于Java+vue的服装定制系统(源码+数据库+文档)

服装定制系统 目录 基于Javavue的服装定制系统 一、前言 二、系统设计 三、系统功能设计 系统功能实现 管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布…

【PPT】文字突然变成方框

文章目录 前言一、问题描述二、解决方案参考文献 前言 在 ppt 画图时遇到的问题 一、问题描述 在 ppt 使用过程中&#xff0c;同一字体&#xff0c;有些变成了方框&#xff0c;有些没有变&#xff08;排除字体缺失问题&#xff09; 二、解决方案 如果是页数多了&#xff0…

MyEclipse2020安装教程(图文)

本章教程主要记录如何在Windows上安装MyEclipse2020.。 一、下载安装包 通过网盘分享的文件&#xff1a;Myeclipse 2020.rar 链接: https://pan.baidu.com/s/1fD2P0S0GU_zJlUHTPeXP-A?pwdv71m 提取码: v71m 二、安装步骤 1、打开解压后的文件夹&#xff0c;鼠标右击【myeclip…

Linux日志-日志小结

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux 系统中的日志是记录系统活动和事件的重要工具&#xff0c;它们可以帮助管理员监视系统状态、调查问题以及了解系统运行…

基于C#的UDP协议消息传输

1. 服务端 internal class Program{static void Main(string[] args){//1.创建SocketSocket socketServer new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);//2.绑定IP、端口号EndPoint endPoint new IPEndPoint(IPAddress.Parse("127.0.0.1&…

【Qt】实现一个小闹钟

widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPushButton>//按钮类 #include <QLineEdit>//行编辑器 #include <QLabel>//标签类 #include <QTextEdit>//输入框 #include <QTimerEvent>//定时器事件类 #inc…

StorageSync数据缓存API

uni.setStorageSyncs参数:将 data 存储在本地缓存中指定的 key 中&#xff0c;会覆盖掉原来该 key 对应的内容&#xff0c;这是一个同步接口。 uni.setStorageSync函数里面写两个参数,分别是key和值,两个参数名称可以随便取,如果有同名的key,那么后面key的值会覆盖掉前面key的值…

详解BMP图片格式以及关于Verilog图像处理的仿真环境搭建

文章目录 一、BMP位图简介二、BMP格式分析2.1 如何用十六进制打开BMP图片2.2 头文件格式2.3 信息头格式2.4 调色板格式2.5 位图数据格式2.6 位图对齐方式三、Verilog图像处理的仿真环境搭建3.1 Verilog文件操作指令3.1.1 条件编译 (`` `ifdef``、 `` `else``、`` `endif``)3.1.…