2011年7月31日星期日

C#与Arduino的通讯试验

        这几天一直在搞PC机作为上位机,通过与Arduino单片机的通讯,来控制LED闪烁或者渐明渐暗。之前有一篇博客记录了试验过程中遇到的问题,不过之前的描述不怎么准确,这两天又重写了Arduino程序,并对C#上位机的程序也做了一些修改,终于可以达到预期功能。

功能需求:
        PC机作为上位机,通过串口发送控制指令 F(ading) or B(link) 给Arduino,根据不同指令来实现LED闪烁或者渐明渐暗。

实现方式:
        单片机选用Arduino MEGA 1280,PWM10脚作为输出口,接220欧限流电阻至高亮LED,再与GND脚共地。
       


        上位机用C#作为编程语言,IDE选择VS2010,.Net版本选择了4.0,不过应该能够兼容其它版本的.net framework。之前上位机跟单片机通信时,传递的是字符串,方法是先将PWM值视为ASCII码,转换成对应的Char,然后将Char再转换成String类型。由于ANSI ASCII码表只支持128个字符,所以128-255之间的数在传递的时候就遇到难题,通过串口监视发现,超过127的ASCII发给arduino之后,都被修改为63,导致传输失效。

        现在的方法改为直接传递byte类型的无符号数,解决了这个问题。

        运行界面如下:


        上位机源代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class frmMain : Form
       {
        int FlashTimes;
        int FlashCounts;
        Label FlashingLED;
        string TextRead;
        public frmMain()
        {
            InitializeComponent();
        }
        private int  FillSerialPorts()
        {
            int PortCount;
            PortCount = 0;
            //加载串口列表
            SerialPort _tempPort;
            String[] Portname = SerialPort.GetPortNames();
            foreach (string str in Portname)
            {
                try
                {
                    _tempPort = new SerialPort(str);
                    _tempPort.Open();
                    if (_tempPort.IsOpen)
                    {
                        cboCOMPorts.Items.Add(str);
                        _tempPort.Close();
                        PortCount++;
                    }
                }
                catch (Exception ex)
                {
                    tsMessage.Text = ex.ToString();
                }
            }
            if (!(PortCount ==0))
                cboCOMPorts.SelectedIndex =0;
            tsCOMPort.Text = cboCOMPorts.Text;
            return PortCount;
        }
        private void trkPWM_ValueChanged(object sender, EventArgs e)
        {
            lblPWM.Text = trkPWM.Value.ToString ();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            txtRead.Clear();
            if (cboCOMPorts.Text == "")
            {
                tsMessage.Text = "尚未选择串口!";
            }
            else
            {
                if (!serialPort1.IsOpen)
                {
                    serialPort1.PortName = cboCOMPorts.Text;
                    serialPort1.Open();
                    try
                    {
                        tsCOMPort.Text = cboCOMPorts.Text;
                        tsCOMState.Text = "开启";
                    }
                    catch (Exception ex)
                    {
                        tsMessage.Text = ex.ToString(); // "串口打开过程中遇到错误,串口不存在或者已经被占用!";
                        tsCOMPort.Text = "";
                        tsCOMState.Text = "已断开";
                    }
                }
                if (serialPort1.IsOpen)
                {
                    if (rbFading.Checked)
                    {
                        serialPort1.Write("F");
                        byte[] bytesToSend = new byte[1] ;
                        bytesToSend[0] =Convert.ToByte ( trkPWM.Value *2.55 ); //疑问:当Value=100,ToByte=255,但是((int)(trkPWM.Value * 2.55)).ToString()却是254?
                        serialPort1.Write(bytesToSend, 0, 1);
                       
                        //serialPort1.Write( ((Char ) ((int) trkPWM.Value *2.55 )).ToString () ); //这个方法不能传递高于127的数
                        //tsMessage.Text = ((int)(trkPWM.Value * 2.55)).ToString();
                    }
                    else
                    {
                        serialPort1.Write("B");
                        serialPort1.Write(Convert.ToChar(Convert.ToInt16(trkPWM.Value)).ToString());
                    }
                    FlashLED(lblRX, 10);
                }
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            FillSerialPorts();
            trkPWM.Value = 80;
        }
        private void FlashLED(Label LED, int Count)
        {
            FlashingLED = LED;
            FlashCounts = Count;
            timer1.Enabled = true;
        }
        private void DisplayText(object sender, EventArgs e)
        {
            txtRead.AppendText(TextRead);
        }
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (chkReceive.Checked)
            {
                TextRead = serialPort1.ReadExisting();
                this.Invoke(new EventHandler(DisplayText));
                FlashLED(lblRX, 10);
            }
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            //add flash times
            if (FlashTimes <= FlashCounts)
            {
                FlashingLED.Visible = !FlashingLED.Visible;
                FlashTimes++;
            }
            else
            {
                timer1.Enabled = false;
                FlashCounts = 0;
                FlashTimes = 0;
                FlashingLED.Visible = true;
            }
        }

        private void frmMain_Activated(object sender, EventArgs e)
        {
            if (cboCOMPorts.Items.Count ==0 )
                FillSerialPorts ();
        }
        private void button2_Click(object sender, EventArgs e)
        {
            serialPort1.Close();
            timer1.Enabled = false;
        }
        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            pictureBox1.Invalidate();
            int yOffset=100;
            int yBase = 40;
            int xOffset = 40;
            int i ;
            int x=0;
            Pen p = new Pen(Color.Yellow ,1);
            Graphics g = e.Graphics;
            if (trkPWM.Value ==100)
                g.DrawLine(p, pictureBox1.Left , pictureBox1.Height - yOffset , pictureBox1.Left + 10 , pictureBox1.Height - yOffset ); // __
            else
                g.DrawLine(p, pictureBox1.Left , pictureBox1.Height - yBase, pictureBox1.Left + 10 , pictureBox1.Height - yBase); // __
            for (i = 0; i <= 5; i++)
            {
                x = 100 * i;
                if (!((trkPWM.Value ==0 )||(trkPWM.Value ==100)))
                    g.DrawLine(p, pictureBox1.Left + 10 + x, pictureBox1.Height - yBase, pictureBox1.Left + 10 + x, pictureBox1.Height - yOffset); // |
                //p.Color = Color.Blue ;
                xOffset = trkPWM.Value;
                g.DrawLine(p, pictureBox1.Left + 10 + x, pictureBox1.Height - yOffset, pictureBox1.Left + 10 + xOffset + x, pictureBox1.Height - yOffset);//--
                if (!((trkPWM.Value == 0) || (trkPWM.Value == 100)))
                    g.DrawLine(p, pictureBox1.Left + 10 + xOffset + x, pictureBox1.Height - yOffset, pictureBox1.Left + 10 + xOffset + x, pictureBox1.Height - yBase);//|
                g.DrawLine(p,pictureBox1.Left + 10 + xOffset + x, pictureBox1.Height - yBase, pictureBox1.Left + 10 + xOffset + x + 100 -trkPWM.Value , pictureBox1.Height - yBase);//__
                e.Graphics.DrawString(trkPWM.Value.ToString () + "%", panel1.Font , new SolidBrush(Color.White ), panel1.Left +  120 , 5, StringFormat.GenericDefault);
            }
        }
        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort1.IsOpen)
                serialPort1.Close();
        }
        private void 清空ToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            txtRead.Clear();
        }
    }
}

Arduino程序源代码:

//COPYRIGHT DECLARATION
//THIS APPLICATION IS A DEMOSTRATION APPLICATON FOR EDUCATION PURPOSE ONLY. YOU CAN USE OR
//REDISTRIBUTE IT FREELY ON CONDITION THAT YOU WOULD NOT MAKE MONEY OF IT.
    
//THE DESKTOP APPLICATON WHICH IS PROGRAMMED IN C# WOULD SEND CONTROL CODE AND PARAMETERS, IF ANY,
//TO THE ARDUINO, THE MICROCONTROLLER WOULD MAKE THE LED FADING OR BLINKING IN ACCORDANCE
//WITH THE CONTROL CODE, THEN.
//SINCE THIS IS MY FIRST APPLICATION IN EITHER C# AND ARDUINO, THERE MIGHT BE SOME BUGS IN IT,
//IF YOU RUN INTO ANY EXCEPTIONS, PLEASE REPORT THEM TO ME AT JZOPEN@YEAH.NET.

//HISTORY
//VER 0.101 1:32 2011-7-31
//Add Fade function to the application
//VER 0.100 16:45 2011-7-30
//Add blink function to the application

char CMD;           //command
int pwmValue;       //PWM value
int PinOUT=10;      //output pin
int Interval = 10;

void setup()
{
    Serial.begin(9600);
    pinMode(PinOUT, OUTPUT);    //set the pin 10 as output pin�
    CMD = 'F';
    pwmValue = 50;
}
void loop()
{
    if (Serial.available())
    {
        char PrevCMD;
        PrevCMD = CMD;
        CMD = Serial.read();
        switch(CMD)
        {
            case 'B':   //Blink
                //read PWM value in readPWMValue routine
                pwmValue = readPWMValue();
                break ;
            //any other CASE statement�
            case 'F':   //Fading
                pwmValue = readPWMValue();
                break;
            default :
                CMD = PrevCMD; //当没有按键或者控制指令无效时,继续执行原来的指令
               break;
        }
    }
   
    //if there is no chars income, then execute the last command
    switch(CMD)
    {
       case 'B':
          setPWM(pwmValue);
          break;
       case 'F':
          ledFading(pwmValue);
          break;
      
       default:
          break;
    }
}
int readPWMValue()
{  
    int Value;
    while (!Serial.available())
    {
        //wait for another incoming char�
    }
    Value=Serial.read();
   
    Serial.print("Parameters received: ");
    Serial.println(Value);
   
    return Value;
}
void setPWM(int Value)
{
    int i;
   
    Serial.println(Value);
   
    for (i=0; i<=100; i++)
      { 
        //debug
        Serial.print(i);
        Serial.print(" / ");
        if (i<= Value)
        {
          digitalWrite(PinOUT,HIGH);
         
          //Debug
          Serial.println("HIGH");
        }
        else
        {
           digitalWrite(PinOUT,LOW);
          
           //DEBUG
           Serial.println("LOW");
        }
        delay(Interval);
    }
}
void ledFading(int Value)
{  
    Serial.print("Fading Value: ");
    Serial.println(Value);
    int i;
    /*
    if (Value>=255)
      Value = 255;
    else
      if (Value<=0);
        Value =0;
    */
   
    //fade in
    for (i=0; i<=Value; i++)
    {
       analogWrite(PinOUT, i);
       Serial.println(i);
       delay(Interval);
    }
   
    delay(500);
   
    //fade out
    for (i=Value; i>=0; i--)
    {
       analogWrite(PinOUT,i);
       Serial.println(i);
       delay(Interval);
    }
   
    delay(500);
   
}


       当然上面的代码只是为了演示和学习需要,所以会很不完善甚至有错误的地方。

没有评论:

发表评论