[C#][WPF]学習メモ(その3)~シリアル通信~

業務ツール作成にC#を触り始めたので、学習した内容をひとまずスクラップしていきます。

シリアル通信 via COMポート

シリアル通信を行う必要があったので、COMポートの叩き方を勉強しました。

参考URL:

  1. https://www.codeproject.com/Articles/130109/Serial-Communication-using-WPF-RS-and-PIC-Commu

WPFだからというハマりどころはないですが、
WinFormよりは情報が少ないのかなという印象です。

使っているライブラリは、System.Windows.IOなので、コアの部分には変更はないと思います。

COMポートの開き方+叩き方

using System.IO.Ports;

namespace ComTest
{
    public partial class MainWindow : Window
    {
        SerialPort serial = new SerialPort();
    }
    
    private void COMPort_Setup() {
       try
       {
            serial.PortName = (String)portName;//"COM#"の形式で.
            serial.BaudRate = (int)baudRate;
            serial.Handshake = System.IO.Ports.Handshake.None;
            serial.Parity = Parity.None;
            serial.DataBits = 8;
            serial.StopBits = StopBits.One;
            serial.ReadTimeout = 200;
            serial.WriteTimeout = 50;
            serial.Open();
        }
        catch
        {
                return;//
        }
            serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(COMPort_Recieve);//受信ハンドラ
    }
    
    private void COMPort_Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
    {
        string recieved_data = serial.ReadExisting();
    }

    private void COMPort_ASCII_TX(char[] data)
    {
        if (serial.IsOpen) //シリアルポートが開いている場合
        {
            try
            {
                // Send the binary data out the port
                byte[] hexstring = Encoding.ASCII.GetBytes(data);
                foreach (byte hexval in hexstring)
                {
                    byte[] _hexval = new byte[] { hexval };     // need to convert byte 
                                                            // to byte[] to write
                    serial.Write(_hexval, 0, 1);
                    Thread.Sleep(1);
                }
             }
            catch
            {
               //送信エラー処理
            }
        }
    }
}

受信は、ハードウェア割り込みが飛んできますので、それをイベントハンダラで受け取ってやります。

送信は、今回ASCII文字列を送る前提で書いてありますが、何にしてもbyte型で送ってやればOKです。

byte型の配列を定義してやれば、センサに直接コマンドを送ったりすることができます。

using System.Windows.Threading;

private void A_Command_TX()
{
    byte[] data = new byte[4]{ 0x1F, 0x43, 0x00, Convert.ToByte('\0') };
    //LCDなどに文字列として放り込むことを想定し、最後に'\0'を入れてあります。
    //ターゲットの仕様に合わせて組みます。
    Serial_TX(data, 1, 10);
}

private void Serial_Command_TX(byte[] data, int each_wait, int final_wait)
{
    try
    {
        foreach(byte hexval in data)
        {
            serial.Write(hexval, 0, 1);
            Thread.Sleep(each_wait);//ターゲットデバイスの受信バッファが溢れない為のwait
        }
    catch
    {
    }
    Thread.Sleep(final_wait);//ターゲットデバイスがコマンドを認識し処理が完了するまでのwait
}

サンプルプログラム

お試しで、参考URLの物を少し変更して、
手持ちのVFDモジュール(Noritake Itron: GU280X16G-7xxx)に文字を表示できるようにしました。

とても汚い代物ですが参考まで。

一応受信もできるようにしてあります。

xaml

<Window x:Class="ComTest.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:ComTest"
        mc:Ignorable="d"
        Title="MainWindow" 
        Background="Black" Height="490.263" Width="766.685" Closing="Window_Closing">
    <Grid Height="454" VerticalAlignment="Top" Margin="0,0,2,0">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="42,29,0,-21" Height="446" Width="668">
            <StackPanel Orientation="Horizontal" Height="30"  VerticalAlignment="Top">
                <TextBlock Foreground="White" Text="Port#" TextWrapping="Wrap" FontSize="24" Width="100"/>
                <ComboBox x:Name="_ComboBox_Port" Width="120"/>
                <Button x:Name="_Button_Connect" Background="Orange" Content="Connect" HorizontalAlignment="Left" Height="30" VerticalAlignment="Bottom" Width="97" Click="_Button_Connect_Click"/>
                <TextBlock x:Name="_TextBlock_Connected" TextWrapping="Wrap" Text="" Width="89"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Height="30"  VerticalAlignment="Top">
                <TextBlock Foreground="White" Text="Baud" TextWrapping="Wrap" FontSize="24" Width="100"/>
                <ComboBox x:Name="_ComboBox_Baud" FontSize="18" Background="white" Height ="30" HorizontalAlignment="Left" VerticalAlignment="Top" Width="300">
                    <ComboBoxItem Content="4800" />
                    <ComboBoxItem Content="9600" />
                    <ComboBoxItem Content="14400" />
                    <ComboBoxItem Content="19200" />
                    <ComboBoxItem Content="38400" />
                    <ComboBoxItem Content="57600" />
                    <ComboBoxItem Content="115200" />
                    <ComboBoxItem Content="230400" />
                    <ComboBoxItem Content="460800" />
                    <ComboBoxItem Content="921600" />
                </ComboBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Height="30"  VerticalAlignment="Top">
                <TextBlock Foreground="White" Text="TX_MES" TextWrapping="Wrap" FontSize="24" Width="100"/>
                <TextBox x:Name="_TextBox_TXMSG" TextWrapping="Wrap" Text="TXメッセージを入力" FontSize="12" Width="477"/>
                <Button x:Name="_Button_Send" Background="Orange" Content="Send" HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="97" Click="_Button_Send_Click"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Height="310"  VerticalAlignment="Top">
                <TextBlock Foreground="White" Text="AP_MES" TextWrapping="Wrap" FontSize="24" Width="100"/>
                <RichTextBox x:Name="_RichTextBox_APMES" HorizontalAlignment="Left" VerticalAlignment="Top" Height="281" Width="477" VerticalScrollBarVisibility="Auto"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.IO.Ports;
using System.Threading;
using System.Windows.Threading;

namespace ComTest
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        FlowDocument mcFlowDoc = new FlowDocument();
        Paragraph para = new Paragraph();
        SerialPort serial = new SerialPort();
        private delegate void UpdateUiTextDelegate(string text);
        private DispatcherTimer _Timer_main;

        public MainWindow()
        {
            InitializeComponent();
            DispatchTimer_Setup();
            string[] ports = SerialPort.GetPortNames();//ポート名の取得
            foreach(string port in ports)
            {
                _ComboBox_Port.Items.Add(port);//それぞれの名前をコンボボックスに登録
            }
            this._TextBlock_Connected.FontSize = 15;
        }

        private void DispatchTimer_Setup()
        {
            this._Timer_main = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(500)
            };
            this._Timer_main.Tick += (sender, e) =>
            {
          
                    if (serial != null && serial.IsOpen)
                    {
                        this._TextBlock_Connected.Background = new SolidColorBrush(Colors.Green);
                        this._TextBlock_Connected.Text = "接続OK";
                    }
                    else
                    {
                        this._TextBlock_Connected.Background = new SolidColorBrush(Colors.Orange);
                        this._TextBlock_Connected.Text = "接続NG?";
                    }

            };
            this._Timer_main.Start();
        }


        private void COMPort_Setup() {
            try
            {
                serial.PortName = _ComboBox_Port.SelectedItem.ToString();
                serial.BaudRate = Convert.ToInt32(_ComboBox_Baud.SelectionBoxItem.ToString());
                serial.Handshake = System.IO.Ports.Handshake.None;
                serial.Parity = Parity.None;
                serial.DataBits = 8;
                serial.StopBits = StopBits.One;
                serial.ReadTimeout = 200;
                serial.WriteTimeout = 50;
                serial.Open();
            }
            catch(Exception ex)
            {
                _RichTextBox_Write("Failed to Connect\n" + ex + "\n");
                    return;
            }

            serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(COMPort_Recieve);

        }
        
        private void VFD_CON()
        {
            byte[] data = new byte[4]{ 0x1F, 0x43, 0x00, Convert.ToByte('\0') };
            VFD_TX(data, 100);
        }

        private void VFD_CLF()
        {
            byte[] data = new byte[2] { 0x0A, Convert.ToByte('\0') };//カーソルを1行下げる
            VFD_TX(data, 100);
        }

        private void VFD_CCR()
        {
            byte[] data = new byte[2]{ 0x0D, Convert.ToByte('\0') }; //カーソルを同一行の一番左に移動する
            VFD_TX(data, 100);
        }

        private void VFD_CHM()
        {
            byte[] data = new byte[2]{ 0x0B, Convert.ToByte('\0') }; //カーソルをホームポジションに移動する
            VFD_TX(data, 100);
        }

        private void VFD_Clear()
        {
            byte[] data = new byte[2] { 0x0C, Convert.ToByte('\0') }; //画面をクリアする
            VFD_TX(data, 100);
        }

        private void VFD_CClear()
        {
            byte[] data = new byte[7]{ 0x1F, 0x24, 0x00, 0x00, 0x00, 0x00, Convert.ToByte('\0') };
            VFD_TX(data, 100);
        }

        private void VFD_TX(byte[] data, int wait)
        {
            try
            {
                serial.Write(data, 0, (data.Length - 1));
            }
            catch (Exception ex)
            {
                _RichTextBox_Write("Failed to SEND" + data + "\n" + ex + "\n");
            }

            Thread.Sleep(wait);
        }

        void VFD_half_TX(byte[] str, int wait)
        {
            int j = 0;
            byte[] hstr = Enumerable.Repeat<byte>(Convert.ToByte('\0'), 256).ToArray();
            byte[] lstr = Enumerable.Repeat<byte>(Convert.ToByte('\0'), 256).ToArray();
            int length = 0;

            length = str.Length;
            for (int i = 0; i < (length / 2); i++)
            {
                hstr[j] = str[i];
                j++;
            }
            hstr[j] = Convert.ToByte('\0');
            j = 0;
            for (int i = (length / 2); i < length; i++)
            {
                lstr[j] = str[i];
                j++;
            }
            VFD_TX(hstr, wait);
            VFD_TX(lstr, wait);
        }

        private void COMPort_Disconnect()
        {
            serial.Dispose();
        }

        private void _Button_Connect_Click(object sender, RoutedEventArgs e)
        {

            if (serial == null)
            {
                COMPort_Setup();
            }
            else
            {
                serial.Close();
                COMPort_Setup();
            }
        }

        private void COMPort_Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            string recieved_data = serial.ReadExisting();
            Dispatcher.Invoke(DispatcherPriority.Send, new UpdateUiTextDelegate(_RichTextBox_Write), recieved_data);
        }

         private void COMPort_ASCII_TX(char[] data)
        {
            if (serial.IsOpen)
            {
                try
                {
                    // Send the binary data out the port
                    byte[] hexstring = Encoding.ASCII.GetBytes(data);
                    foreach (byte hexval in hexstring)
                    {
                        byte[] _hexval = new byte[] { hexval };     // need to convert byte 
                                                                // to byte[] to write
                        serial.Write(_hexval, 0, 1);
                        Thread.Sleep(1);
                    }
                 }
                catch (Exception ex)
                {
                    string text = "Failed to SEND" + data + "\n" + ex + "\n";
                    _RichTextBox_Write(text);
                }
            }
        }

        private void _RichTextBox_Write(string text)
        {
            para.Inlines.Add(text);
            mcFlowDoc.Blocks.Add(para);
            _RichTextBox_APMES.Document = mcFlowDoc;
         }

        private void _TextBox_Port_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key < Key.D0 || Key.D9 < e.Key)
            {
                e.Handled = true;
            }
        }

        private void _Button_Send_Click(object sender, RoutedEventArgs e)
        {
            VFD_Clear(); //画面クリア
            VFD_CCR(); //行の先頭へカーソル移動
            COMPort_ASCII_TX(_TextBox_TXMSG.Text.ToCharArray());
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            VFD_Clear(); //画面クリア
            VFD_CCR(); //行の先頭へカーソル移動
            COMPort_ASCII_TX("__NO_CONNECTION__".ToCharArray());
        }
    }
}

関連記事:

[C#][WPF]学習メモ(その2)~タイマ~

[C#][WPF]学習メモ(その1)~イベント~

コメント

Japanese
タイトルとURLをコピーしました