​        在自动化领域,我们经常会有一些设备人为无法去进行操作,比如高压闸门、具有腐蚀性的的液体阀门等等。通常情况下,我们会使用电脑软件去控制这些设备,通过运行在PC端的上位机软件+单片机的方式来实现。

项目需求

​         设计一款控制器,该控制器有四个继电器。我们需要通过电脑端的软件来实现对继电器的控制。

需求分析

​         在这个需求当中,我们需要设计的有两部分组成,一个是单片机控制器,一个是电脑端的软件。电脑端有八个按键,分别控制四个继电器的开和关两种状态。

​         这里大概的一个框架是这样的,我们需要实现一个PC与MCU之间的通讯控制,通过串口。当MCU接收到PC给的数据时候,MCU判断接收到的内容,做出处理。

​ 在项目开始之前,我们先来制定下通讯的内容。如下:

波特率 9600 bps(Bit Per Second)
数据位
停止位 1
校验位 0

​         这个是关于通讯的定义,这个也是常用的一个格式9600,8N1的模式。

​         然后是我们通讯的内容,我们做出如下的规则。

一号继电器开启 1
一号继电器关闭 2
二号继电器开启 3
二号继电器关闭 4
三号继电器开始 5
三号继电器关闭 6
四号继电器开启 7
四号继电器关闭 8

        ​ 我们做出规定,当PC给MCU发“1”的时候,一号继电器开启;当PC给MCU发“2”的时候,一号继电器关闭,依此类推。

​         接下来,我们会从电路、单片机程序、上位机程序三个领域给大家一一阐述设计。

​         我们需要来设计单片机的驱动电路和继电器的驱动电路。你可以选择使用Arduino单片机+四个LED灯来实现模拟继电器的效果。

聊一聊继电器

​         继电器(Relay),也称电驿,是一种电子控制器件,它具有控制系统(又称输入回路)和被控制系统(又称输出回路),通常应用于自动控制电路中,它实际上是用较小的电流去控制较大电流的一种“自动开关”。故在电路中起着自动调节、安全保护、转换电路等作用。继电器线圈在电路中用一个长方框符号表示,如果继电器有两个线圈,就画两个并列的长方框。同时在长方框内或长方框旁标上继电器的文字符号“J”。继电器的触点有两种表示方法:一种是把它们直接画在长方框一侧,这种表示法较为直观。

​         我在这里使用的是光耦继电器,继电器是一个低电压、弱电流去控制一个高电压、强电流的元件,在使用的过程当中,我们为了避免高低压击穿MCU,所以使用了一款光耦IC_C817。这个IC正如大家看到的,由放光二极管和光敏二极管组成,我们的驱动信号点亮发光二极管之后,光敏二极管检测到光源,从而导通。电阻R3为限流电阻,保护IO口和GND;电阻R5为下拉电阻,保证IO默认处于低电平。

​        在右侧的驱动电路当中,我们使用了S8550三极管来作为驱动,当C817的光敏二极管导通,VCC的电流可经过Q1三极管来导致继电器的闭合。电阻R2作为上拉电阻,保证默认状态下Q1处于截至状态,电阻R4为限流电阻,起到保护光耦的作用。电阻R1和LED1作为状态指示,电阻R1为LED1的限流电阻,保护LED1。继电器在闭合的时候是有电流经过线圈的,在这里我们要把线圈当成电感来处理,在继电器释放的瞬间,是会在电流产生反向电流的,极大的可能性会烧毁整个电路,在这里的二极管D1就是起到了保护的作用。

​        然后我们来谈谈Arduino,Arduino是一款便捷灵活、方便上手的开源电子原型平台。包含硬件(各种型号的Arduino板)和软件(ArduinoIDE)。

​        它构建于开放原始码simple I/O介面版,并且具有使用类似Java、C语言的Processing/Wiring开发环境。主要包含两个的部分:硬件部分是可以用来做电路连接的Arduino电路板;另外一个则是Arduino IDE,你的计算机中的程序开发环境。你只要在IDE中编写程序代码,将程序上传到Arduino电路板后,程序便会告诉Arduino电路板要做些什么了。

​        Arduino能通过各种各样的传感器来感知环境,通过控制灯光、马达和其他的装置来反馈、影响环境。板子上的微控制器可以通过Arduino的编程语言来编写程序,编译成二进制文件,烧录进微控制器。对Arduino的编程是通过 Arduino编程语言 (基于 Wiring)和Arduino开发环境(基于 Processing)来实现的。基于Arduino的项目,可以只包含Arduino,也可以包含Arduino和其他一些在PC上运行的软件,他们之间进行通信 (比如 Flash, Processing, MaxMSP)来实现。

​        我们使用Arduino的任意版本即可,在这里我使用Arduino UNO。我在这里使用LED来模拟继电器实现效果。

​        这是一个仿真网站https://www.tinkercad.com/,大家可自行前去体验。

下位机驱动代码

​         我们已经规定了通讯频率和通讯格式,现在我们需要来写MCU的代码。

​         我们的下位机使用的是Arduino UNO,这个编程语法及下载方式都是很简单的。我们需要先来做一个串口读取的功能,在屏幕打印当前串口的数据。

void setup()
{
Serial.begin(9600); //初始化串口,波特率为9600bps,默认8N1模式
}

void loop()
{
if(Serial.available()>0) //判定当前串口缓冲区是否有数据
{
//如果有
char msg = Serial.read(); //读取串口的数据保存到msg变量下
Serial.print("当前数据:"); //串口监视器打印提示内容
Serial.println(msg); ////串口监视器打印当前的内容
}
delay(100); //延时100ms
}

​         这个是Arduino串口接收的程序,它会将串口读取的内容,在前面加上提示给你显示在串口监视器里面。

​         这个是我在仿真下测试的内容,是没有问题的。乱码的原因是因为当前浏览器不支持汉字的Unicode编码格式。

​         我们现在再对代码进行一次,在现有代码的基础上,增加一个输入内容的判断。如下:


void setup()
{
Serial.begin(9600);
 
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
}

void loop()
{
if(Serial.available()>0) //判定当前串口缓冲区是否有数据
{
//如果有
char msg = Serial.read(); //读取串口的数据保存到msg变量下
Serial.print("当前数据:"); //串口监视器打印提示内容
Serial.println(msg); ////串口监视器打印当前的内容

//判断msg的内容是什么
if(msg == '1')
{
    digitalWrite(2,HIGH);
}else if(msg == '2')
{
    digitalWrite(2,LOW);
}else if(msg == '3')
{
    digitalWrite(3,HIGH);
}else if(msg == '4')
{
    digitalWrite(3,LOW);
}else if(msg == '5')
{
    digitalWrite(4,HIGH);
}else if(msg == '6')
{
    digitalWrite(4,LOW);
}else if(msg == '7')
{
    digitalWrite(5,HIGH);
}else if(msg == '8')
{
    digitalWrite(5,LOW);
}

}
delay(100); //延时100ms
}

​         我现在串口输入了1,3,7并按下回车,可以看到对应的LED亮起,这里我们换成继电器就可以实现工业控制。我们现在在仿真里面测试是没有问题的,接下来我们可以购买材料进行实物的测试。清单如下:

Arduino开发板 1个
继电器模块 4个
杜邦线 若干

​上位机开发

​        C#是微软公司发布的一种由C和C++衍生出来的面向对象的编程语言、运行于.NET Framework和.NET Core(完全开源,跨平台)之上的高级程序设计语言。并定于在微软职业开发者论坛(PDC)上登台亮相。C#是微软公司研究员Anders Hejlsberg的最新成果。C#看起来与Java有着惊人的相似;它包括了诸如单一继承、接口、与Java几乎同样的语法和编译成中间代码再运行的过程。但是C#与Java有着明显的不同,它借鉴了Delphi的一个特点,与COM(组件对象模型)是直接集成的,而且它是微软公司 .NET windows网络框架的主角。

​        我们在Visual Studio下面建立一个Windows窗体应用程序,如下:

​        我们按照下面的布局,拉出UI界面。

​        在这里我再次介绍下信号与槽机制。在我们的这个上位机当中,我们按下不同的按键需要发送出去不同的指令。我们在按下按键的时候,会产生一个信号,程序里面会有专门处理这个信号的部分,做出对应的执行,所以这部分也可以叫做信号触发与槽执行机制。

private void Form1_Load(object sender, EventArgs e)
{
  for (int i = 1; i < 20; i++)
  {
      comboBox1.Items.Add("COM" + i.ToString()); //循环将内容加入comboBox1内
  }
  comboBox1.Text = "COM1"; //comboBox1的内容更改为COM1
  comboBox2.Text = "9600"; //comboBox2的内容更改为9600
}

​        我们在这里定义了一个函数叫做Form1_Load,这个是我们在启动的时候进行页面初始化的功能。我们将一些端口配置信息加入到对应得选项框内。这部分代码我们加在了Form1控件得load事件内。

​        然后是我们八个按键来控制继电器,我们按下按键后,需要通过代码发送对应得数据,如下:

//当按键1按下时执行
private void button1_Click(object sender, EventArgs e)
{
  serialPort1.Write("1");//发送数据1,控制继电器1开启
}

//当按键2按下时执行
private void button2_Click(object sender, EventArgs e)
{
  serialPort1.Write("2");//发送数据1,控制继电器1关闭
}

//当按键3按下时执行
private void button3_Click(object sender, EventArgs e)
{
  serialPort1.Write("3");//发送数据3,控制继电器2开启
}

//当按键4按下时执行
private void button4_Click(object sender, EventArgs e)
{
  serialPort1.Write("4");//发送数据4,控制继电器2关闭
}

//当按键5按下时执行
private void button5_Click(object sender, EventArgs e)
{
  serialPort1.Write("5");//发送数据5,控制继电器3开启
}

//当按键6按下时执行
private void button6_Click(object sender, EventArgs e)
{
  serialPort1.Write("6");//发送数据6,控制继电器3关闭
}

//当按键7按下时执行
private void button7_Click(object sender, EventArgs e)
{
  serialPort1.Write("7");//发送数据7,控制继电器4开启
}

//当按键8按下时执行
private void button8_Click(object sender, EventArgs e)
{
  serialPort1.Write("8");//发送数据8,控制继电器4关闭
}

​ 这部分就是8个按键槽执行的内容,通过串口发送相应的内容。

​ 最后就是关于端口的打开和关闭操作,由按键9和按键10负责。内容如下:


//当按键9按下时执行
private void button9_Click(object sender, EventArgs e)
{
  try
  {
      serialPort1.PortName = comboBox1.Text;//串口名字是选项框1的内容
      serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);//通讯频率是选项框2的内容
      serialPort1.Open();//打开串口
      button3.Enabled = false;//禁用按钮3,打开通讯的按钮,不能使用
      button4.Enabled = true;//启用按钮4,关闭通讯的按钮,可以使用
  }
  catch
  {
      MessageBox.Show("端口错误,请查看端口.", "错误");//try内的语句有任何一句无法执行的时候,执行
  }
}

//当按键10按下时执行
private void button10_Click(object sender, EventArgs e)
{
  try
  {
      serialPort1.Close();//关闭串口
      button3.Enabled = true;//启用按钮3
      button4.Enabled = false;//禁用按钮4
  }
  catch
  {
      MessageBox.Show("关闭失败.", "错误");//try内的语句有任何一句无法执行的时候,执行
  }
}

​        这里是关于C#操作串口打开和关闭的槽执行代码,我们将两个comboBox的内容分别给串口号和波特率,然后通过open()函数和close()函数进行串口的打开和关闭。由于这部分代码是不安全代码,在执行的时候碰到错误会导致程序的崩溃,所以在这里我使用了try...catch...语句进行处理,当打开失败或者关闭失败的时候,会终止当前执行并弹出提示框作出警告。

​        在端口打开和关闭的时候,“打开”按键和“关闭”按钮也通过他们的Enable属性进行了使能控制。

​        到这里我们的上位机设计就已经结束了,我们将之前做好的Arduino插入到电脑,运行我们写的这个软件,选择对应的端口号和波特率,就可以实现上位机软件控制四个继电器啦。

​        需要完整项目代码的同学可私信古月居微信公众号获得~

​        此次教程为大家阐述了如何基于Arduino和C#实现软硬件搭配,进行工控(其实就是软硬结合的控制)的相关开发。这个教程只是对软硬件协同开发的入门介绍,C#上位机开发方面的相关内容还有很多,再接下来也会逐渐向大家更新内容~