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

news2024/12/28 19:11:19

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("请先打开串口!");
                }
            }
        }
    }
}

运行效果

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

源码下载

下载源码

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

golang学习笔记06——怎么实现本地文件及目录监控-fsnotify

推荐学习文档 基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总golang学习笔记01——基本数据类型golang学习笔记02——gin框架及基本原理golang学习笔记03——gin框架的核心数据结构golang学习笔记04——如何真正写好Golang代码&…

在嵌入式板子上搭建和自定义live555服务器---编译问题和方法整理

live555 官方网站 点我直达&#xff0c;live555是一个简单的专注于实现RTSP服务器的开源库。它自带解析H264 H265 mp3等源的API&#xff0c;有一个简单的推流文件参考RTSP服务器例程testH264VideoStreamer也有官方实现的LIVE555 Media Server。无论是命令行使用还是用API实现定…

【最新华为OD机试E卷-支持在线评测】分糖果(100分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

pyautogui进行点击失效,pyautogui.click()失效

背景&#xff1a;在Pycharm里&#xff0c;使用pythonpyautogui调用 .exe程序文件时候&#xff0c;当程序界面出来之后&#xff0c;鼠标失去反应&#xff0c;用pyautogui进行点击。后面尝试使用图片相似也无法实行点击。 解决方法&#xff1a;运行Pycharm或者其他ide的时候选择…

鸿蒙(API 12 Beta6版)超帧功能开发【顶点标记】

超帧提供两种运动估计模式供开发者选择&#xff1a;分别为基础模式和增强模式。其中增强模式需要对绘制顶点的Draw Call命令进行额外的标记&#xff0c;在相机和物体快速运动的游戏场景超帧效果较基础模式更优&#xff0c;能够有效改善拖影问题。本章主要介绍增强模式的运动估计…

【VB6|第27期】如何在VB6中使用Shell函数实现同步执行

日期&#xff1a;2024年9月1日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xff…

算法打卡——田忌赛马问题

问题简介&#xff1a;就是一个贪心的思想&#xff0c;下面上题目 要求示例输出输入 大体上先比较快马&#xff0c;田的快马与王的快马 其次比较田的慢马与王的慢马&#xff0c; 两处边界比较完全之后可以直接贪心了 几份示例的代码 代码一 #include <bits/stdc.h> …

【数据结构-二维前缀和】力扣1504. 统计全 1 子矩形

给你一个 m x n 的二进制矩阵 mat &#xff0c;请你返回有多少个 子矩形 的元素全部都是 1 。 示例 1&#xff1a; 输入&#xff1a;mat [[1,0,1],[1,1,0],[1,1,0]] 输出&#xff1a;13 解释&#xff1a; 有 6 个 1x1 的矩形。 有 2 个 1x2 的矩形。 有 3 个 2x1 的矩形。 有…

ia复习笔记

HCIA 常用配置以及快捷键&#xff1a;! 查看时间&#xff1a;display clock&#xff1b;修改时间&#xff1a;clock datetime 11:11:11 2023-1-1 查看设备当前的配置&#xff1a;display current-configuration&#xff1b;查看已保存的配置&#xff1a;display saved-config…

水晶连连看 - 无限版软件操作说明书

水晶连连看 – 无限版游戏软件使用说明书 文章目录 水晶连连看 – 无限版游戏软件使用说明书1 引言1.1 编写目的1.2 项目名称1.3 项目背景1.4 项目开发环境 2 概述2.1 目标2.2 功能2.3 性能 3 运行环境3.1 硬件3.2 软件 4 使用说明4.1 游戏开始界面4.2 游戏设定4.2.1 游戏帮助4…

docker拉取redis5.0.5并建立redis集群

1.配置文件 mkdir -p redis-cluster/7001/ mkdir -p redis-cluster/7002/ mkdir -p redis-cluster/7003/ mkdir -p redis-cluster/7004/ mkdir -p redis-cluster/7005/ mkdir -p redis-cluster/7006/cd redis-clustervim 7001/redis.confbind 0.0.0.0port 7001cluster-enabled…

传统CV算法——图像特征算法概述

文章目录 传统CV——图像特征算法概述1. 概述1.1 图像特征概述1. 2 局部特征1.2.1 定义1.2.2 特点1.2.3 常见方法1.2.4 应用 1.3 全局特征1.3.1 定义1.3.2 特点1.3.3 常见方法1.3.4 应用 1.4 局部特征与全局特征的比较1.5 局部特征点1.5.1 斑点与角点 1. 定义2. 特征3. 应用4. …

批量处理PDF神器:快速转换、压缩,提升工作效率

现在PDF格式的文件流通率越来越高了&#xff0c;因为它可以完整的保存文档原有的格式而被大家所使用。但是这个格式的文档编辑对很多人来说还比较陌生。这次我介绍几款pdf软件让你实现轻松编辑。 1.福昕PDF编辑器 链接一下>>https://editor.foxitsoftware.cn 这家公司…

Dance with Compiler - EP2

今天来熟悉汇编指令。 基本指令特点 str: store value to memory ldr: load value from memory stp: store register value to stack ldp: load stack value to register 更新寄存器的操作&#xff0c;一般结果寄存器是左操作数。 写内存的操作&#xff08;str&#xff09;&…

不同工况下的迁移轴承故障诊断,融合SE注意力机制的Resnet18迁移学习,附MATLAB代码...

概要 迁移学习&#xff08;Transfer Learning&#xff09;是一种在机器学习中广泛应用的技术&#xff0c;它利用在一个任务上获得的知识来帮助解决另一个相关任务。迁移学习尤其适用于数据量有限或训练成本较高的情况。它可以显著提高模型的性能和训练效率。 本期采用MATLAB语…

新材料 金属3D打印发展的加速器

在金属3D打印的广阔舞台上&#xff0c;材料性能犹如舞台的台柱&#xff0c;直接决定了打印工件性能的优劣。从强度、硬度到耐腐蚀性、抛光性及导热性&#xff0c;每一项指标都紧密关联着材料的选择&#xff0c;而优质材料正是推动这项技术跨越边界、深入更多行业领域的核心动力…

一个vue重新回顾,好多年前写的

在校期间简单跟着视频学习的代码&#xff0c;后面上传到github仓库就一直没有使用了&#xff0c;今天重新加载&#xff0c;重新启动。下面是启动时候遇到的问题&#xff0c;主要原因是我这部电脑是新电脑&#xff0c;很多环境还没有搭建。 成功启动后的页面效果 这里采用的思…

oauth2 方式获取outlook邮箱收件箱(python)

1.在Azure 门户注册应用程序 微软文档地址 重定向的地址配置(微软地址)&#xff1a; https://login.microsoftonline.com/common/oauth2/nativeclient 注册应用地址 2.程序代码 #安装包以及需要的驱动 pip3 install playwrightplaywright install import base64 import jso…

服务器模型 Reactor 和 Proactor

Proactor 具体流程如下&#xff1a; 处理器发起异步操作&#xff0c;并关注 IO 完成事件&#xff1b;事件分离器等待操作完成事件&#xff1b;分离器等待过程中&#xff0c;内核并行执行实际的 IO 操作&#xff0c;并将结果存储入用户自定义的缓冲区&#xff0c;最后通知事件分…

零工市场小程序:自由职业者的日常工具

零工市场小程序多功能且便捷&#xff0c;提供了前所未有的灵活性和工作效率。这类小程序不仅改变了自由职业者的工作方式&#xff0c;也重塑了劳动力市场的格局。 一、零工市场小程序的特点 即时匹配&#xff1a;利用先进的数据算法&#xff0c;零工市场小程序能够快速匹配自由…