基于单片机C#串口通信.doc
基于C与单片机串口通信的投票器李浩东20093101004 周守悦20093101012一 作品的设计概述我们知道每年每个班都需要班委换届,有很多同学积极参加竞选,然而每一次竞选投票都是大家拿出一张纸,然后再纸上写上自己心目中班委的名字,然后交给监票读票记票,这个过程不仅大大浪费了大家的宝贵时间,还有可能出现漏票等情况,体现不了公平公正公开。本设计是通过按钮给班委竞选人投票,每个候选人都对应一个按钮,投票人如果想投票给某个人可以按下其对应按钮,每按下一次改竞选人的票数就会自动增加1,每个人只能按下一次,电脑显示屏将通过柱形图动态的呈现每个候选人获得票数竞争的情况以及通过框图显示总票数,不仅使得投票结果更加公开公正,而且也大大节省了大家的时间.本设计的创新点是通过柱形图动态显示整个投票过程,而不是直接显示到最后投票结果,更加体现公正公开。二 作品的设计与分析1. 主要功能与分析 主要使用单片机和PC机之间的串口通信,在单片机硬件上设置七个按键,其中四个键是用来给A,B,C,D四个人投票的,这四个按键每按下一次就自动增1,记录这四个按键按下的总次数num1,num2,num3,num4,并把四个数按顺序不断循环通过串口发给PC机,PC机通过串口把这些数据存储下来,并读出来,通过C#编程,把这四个人所获得的总票数在picturebox控件上面的柱形图动态呈现出来,通过time控件,不断更新这个人所获得的票数,让投票人通过柱形图更加形象直观的看出每个被投票人的竞争情况,同时在柱形图下方有着这四个人获得总票数的真实数目。还有一个按键是票数清零,如果这次投票已经完成或者无效可以按下这个按键,此时A,B,C,D四个人的总票数将变成零.还有一个按键作用是停止投票,如果需要停止这次投票可以按下此键,这时候那四个投票的按键将不可用.最后一个按键的作用是继续投票,如需继续投票,可按此键.其系统设计图如下:PC机图像显示清零继续暂停ABCD单片机数据传输2.串口通信规则单片机与PC机为了可以进行通信,必须要遵守一定的通信规则,这个共同的规则就是通信端口的初始化.通信端口的初始化有以下几项必须设置:(1)数据的传输速率传输双方通过传输线的电压改变来交换数据,但传输线的电压改变的速度必须和接收端的接收速度保持一致,RS232通常用于异步传输,即双方并没有一个可参考的同步时钟作为基准.由于没有一个参考时钟,双方所发送的高低电位到底代表几个位就不得而知了,要使得双方的数据读取正常,就要考虑到传输速率波特率,其所代表的意义是每秒钟所能产生的最大电压状态改变率,或者说是每秒钟可以振荡的次数。原始信号经过不同的波特率取样后,所得的结果完全不一样。取样速度只有原来的一半时,信号被跳着取样,数据因此产生错误。因此通信双方获得相同的通信速度是首先要做的事情。(2)数据的发送单位一般串行通信端口所发送的数据是字符类型的,若用来传输文件,则会使用二进制的数据类型。当使用字符类型时,通常使用ASCII码,ASCII码中8个位形成一个字符。以实际的RS232传输来看,由于大多数应用只是发送文字码,因此只要7个位就可以将ASCII码的0127号字符表达出来,所有的可见字符都在这个范围内,所以只要7个数据位就足够了。不同的情况下,会使用到不同的发送单位,但使用多少个位合成一个字节必须先行确定。(3)起始位及停止位由于异步串行通信中并没有使用同步脉冲作为基准,故接收端完全不知道发送端何时将进行数据的发送,而当发送端准备要开始发送数据时,发送端会在所送出的字符前后分别加上高电位的起始位(逻辑0)及低电位的停止位(逻辑1),它们分别是所谓的起始位和停止位。当发送端要开始发送数据时,便将传输在线的电位由低电位提升至高电位,而当发送结束后,再将电位降至低电位.接收端会因起始位的触发(因电压由低电位升至高电位)而开始接收数据,并因停止位的通知(因电压维持在低电位)而确切数据的字符信号已经结束。(4)校验位的检查为了预防错误的产生,因此使用校验位作为检查的机制;校验位是用来检查所发送数据正确性的一种核对码,其中又分成奇校验位和偶校验位两种方式,分别是检查字符码中I的数目是奇数或偶数。以偶校验位为例,A的ASCII码01100001 (二进制),其中1的数目是三个,因此校验位便是1,使1的数目保持偶数。同理,校验位是奇校验位时,A的校验位便是0,使1的数目保持奇数。3. 串口通信协议由于本设计主要从单片机串口发送数据,所以因此有关串口通信的原理的介绍都是围绕单片机讲解的。(1) 80C51串行口的控制寄存器(1。1)特殊功能寄存器SCONSCON 是一个特殊功能寄存器,用以设定串行口的工作方式、接收/发送控制以及设置状态标志,字节地址为98H。SCON寄存器的各位定义如表3-1所示。位76543210字节地址:98HSM0SM1SM2RENTB8RB8TI RISCON 表31 SCON寄存器SM0和SM1为工作方式选择位,可选择四种工作方式,如表3-2所示。SM0SM1方式说明波特率000移位寄存器fosc/1201110位异步收发器(8位数据)可变10211位异步收发器(9位数据)fosc/64或fosc/3211311位异步收发器(9位数据)可变表32 串口通信4种工作方式SM2为多机通信控制位,主要用于方式2和方式3。当接收机的SM2=1时可以利用收到的RB8来控制是否激活RI(RB80时不激活RI,收到的信息丢弃;RB81时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走)。当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能)。通过控制SM2,可以实现多机通信。在方式0时,SM2必须是0.在方式1时,若SM2=1,则只有接收到有效停止位时,RI才置1。REN为允许串行接收位.由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收。TB8用在方式2或方式3中,是发送数据的第九位,可以用软件规定其作用。可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位(在方式0和方式1中,该位未用)。RB8用在方式2或方式3中,是接收到数据的第九位,作为奇偶校验位或地址帧/数据帧的标志位。在方式1时,若SM2=0,则RB8是接收到的停止位。TI,发送中断标志位。在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。RI,接收中断标志位。在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请7.(1。2)特殊功能寄存器PCONPCON的字节地址为87H,它的第7位SMOD是与串口通信波特率的设置有关的选择位。SMOD(PCON。7)为波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍。复位时,SMOD=0.(2)80C51单片机串行口的工作方式(2.1)方式0设置SCON寄存器的SM0、SM10 0时,串行口工作于方式0。此时,串行口为同步移位寄存器的输入输出方式。主要用于扩展并行输入或输出口.数据由RXD(P3。0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出。发送和接收均为8位数据,低位在先,高位在后。波特率固定为fosc/12.其中fosc为时钟频率。(2.2)方式1设置SCON寄存器的SM0、SM10 1时,串行口工作于方式1。方式1是10位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如图所示。其中1位起始位,8位数据位,1位停止位。 用软件置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断.方式一的输入输出图如图38、39所示.图3-8 方式1输入图39 方式1输出(2。3)方式2和方式3设置SCON寄存器的SM0、SM11 0时,串行口工作于方式2,当SM0、SM11 1时,串行口工作于方式3。方式2或方式3为11位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚 。方式2和方式3时起始位1位,数据9位(含1位附加的第9位,发送时为SCON中的TB8,接收时为RB8),停止位1位,一帧数据为11位。方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时器T1的溢出率决定。 方式2和方式3输出:发送开始时,先把起始位0输出到TXD引脚,然后发送移位寄存器的输出位(D0)到TXD引脚.每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD引脚输出.第一次移位时,停止位“1”移入输出移位寄存器的第9位上 ,以后每次移位,左边都移入0。当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1,向CPU请求中断。发送时序图如下图3-10所示.图310 方式2或方式3的发送时序图方式2和方式3输入:接收时,数据从右边移入输入移位寄存器,在起始位0移到最左边时,控制电路进行最后一次移位.当RI=0,且SM2=0(或接收到的第9位数据为1)时,接收到的数据装入接收缓冲器SBUF和RB8(接收数据的第9位),置RI=1,向CPU请求中断。如果条件不满足,则数据丢失,且不置位RI,继续搜索RXD引脚的负跳变。接收时序图如图3-11所示。图3-11 方式2或方式3的接收时序图(3)波特率的计算在串行通信中,收发双方对发送或接收数据的速率要有约定。通过软件可对单片机串行口编程为四种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。串行口的四种工作方式对应三种波特率。由于输入的移位时钟的来源不同,所以,各种方式的波特率计算公式也不相同.方式0的波特率 = fosc/12方式2的波特率 =(2SMOD/64)· fosc 方式1的波特率 =(2SMOD/32)·(T1溢出率)方式3的波特率 =(2SMOD/32)·(T1溢出率)当T1作为波特率发生器时,最典型的用法是使T1工作在自动再装入的8位定时器方式(即方式2,且TCON的TR1=1,以启动定时器).这时溢出率取决于TH1中的计数值。计算公式如3-1所示: T1 溢出率 = fosc /12×256 (TH1) (3-1)在单片机的应用中,常用的晶振频率为:12MHz和11。0592MHz。所以,选用的波特率也相对固定。本设计根据要求选用工作方式1。4.单片机的设计(1) 硬件设计主要有单片机,七个按键,开关,以及一个与PC机相连接的USB串口,其硬件图像如下图:(2) 软件部分开始其单片机C语言程序框图如下:继续不发数据暂停结束发送数据Num1,num2,num3,nun4全为零清零Num1+Num4+Num3+Num2+C?A?D?B?判断是否有按键按下初始化(串口设置)5.C#的程序设计(1) 串口通信的串行端口类 SerialPort (1。1)SerialPort的主要属性属性说明BaudRate获取或设置串行波特率。BytesToRead获取接收缓冲区中数据的字节数。BytesToWrite获取发送缓冲区中数据的字节数。DataBits获取或设置每个字节的标准数据位长度.Encoding获取或设置传输前后文本转换的字节编码。IsOpen获取一个值,该值指示 SerialPort 对象的打开或关闭状态.NewLine获取或设置用于解释 ReadLine 和 WriteLine 方法调用结束的值.Parity获取或设置奇偶校验检查协议.PortName获取或设置通信端口,包括但不限于所有可用的 COM 端口。ReadBufferSize获取或设置 SerialPort 输入缓冲区的大小。ReadTimeout获取或设置读取操作未完成时发生超时之前的毫秒数。ReceivedBytesThreshold获取或设置 DataReceived 事件发生前内部输入缓冲区中的字节数.Site获取或设置 Component 的 ISite。(从 Component 继承。)StopBits获取或设置每个字节的标准停止位数.WriteBufferSize获取或设置串行端口输出缓冲区的大小。WriteTimeout获取或设置写入操作未完成时发生超时之前的毫秒数。(1.2)SerialPort的主要方法方法说明Close关闭端口连接,将 IsOpen 属性设置为 false,并释放内部 Stream 对象。Dispose已重载。 释放 SerialPort 对象使用的非托管资源.Equals 已重载. 确定两个 Object 实例是否相等。 (从 Object 继承.)GetPortNames获取当前计算机的串行端口名称数组。Open打开一个新的串行端口连接。Read已重载。 从 SerialPort 输入缓冲区中读取。ReadByte从 SerialPort 输入缓冲区中同步读取一个字节。ReadChar从 SerialPort 输入缓冲区中同步读取一个字符。ReadExisting在编码的基础上,读取 SerialPort 对象的流和输入缓冲区中所有立即可用的字节.ReadLine一直读取到输入缓冲区中的 NewLine 值。ReadTo一直读取到输入缓冲区中的指定 value 的字符串。ToString 返回包含 Component 的名称的 String(如果有)。不应重写此方法。 (从 Component 继承。)Write已重载. 将数据写入串行端口输出缓冲区。WriteLine将指定的字符串和 NewLine 值写入输出缓冲区。(1。3)SerialPort的主要事件事件说明DataReceived表示将处理 SerialPort 对象的数据接收事件的方法。ErrorReceived表示处理 SerialPort 对象的错误事件的方法.(2) C界面串口设计开始(3) C#程序框图串口初始化不接收数据串口打开?显示图像Time事件触发接收数据三功能显示以及结果(图1)投票结果显示(图2)投票结果动态显示四结论本设计利用了单片机串口通信编程和C#串口通信编程,让单片机和PC机实现了通信。虽然是比较简单的C#与单片机之间的串口通信,但是也大大加深了自己对课堂知识的理解,同时大大提高了自己的动手设计能力。这次设计也让我充分认识到C#强大的功能,但也让我看到很多不足,界面完善不够好,程序编写较为简单,还没用到数据库等功能,这也大大的激发了我对C兴趣.最后,非常感谢唐老师,他让复杂枯燥的C变得生动有趣,上他的课非常有意思,大大提高了我对C#的兴趣,同时的加强了我的设计技能.参考文献1 张明锋PIC单片机入门与实践M北京:北京航空航天大学出版社,20042 黄亮基于AT89C51单片机的串行通信程序设计J。 中国地质大学,单片机实用电子制作 P29-323 金华。 C#网络编程技术教程M。 人民邮电出版社附件C程序: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.Drawing。Drawing2D;using System.IO;using System。IO。Ports;namespace toupiao public partial class Form1 : Form /定义委托类型 public delegate void mydelegate(); int i=0; int a, b, c, d; string n1, n2, n3, n4,num; /byte n = new byte4; /=byte rc = new byte4; public Form1() InitializeComponent(); private void button1_Click(object sender, EventArgs e) if (!sp.IsOpen) /设置串口 sp。PortName = textBox1。Text; /指定要使用的串口号 sp。BaudRate = 9600; /设定波特率 sp.Parity = System。IO.Ports。Parity。None; /设定奇偶验证协议 sp。DataBits = 8; /设定字长 sp。StopBits = System.IO.Ports.StopBits.One; /设定停止位 sp。DtrEnable = true; /数据终端准备就绪 sp。RtsEnable = true; /启用请求发送 sp.ReadTimeout = 1000; /设置数据读取超时为1秒 try sp.Open(); button1。Text = "关闭串口”; catch (Exception ex) MessageBox。Show(ex。Message); else sp。Close(); button1。Text = "打开串口”; private void tuxiang(string num1,string num2,string num3,string num4) try a = Convert.ToInt32(num1); b = Convert。ToInt32(num2); c = Convert.ToInt32(num3); d = Convert。ToInt32(num4); catch int sum = a+b+c+d; float a1 = Convert。ToSingle(Convert.ToSingle(a) * 100 / Convert.ToSingle(sum); float b1 = Convert.ToSingle(Convert。ToSingle(b) 100 / Convert.ToSingle(sum); float c1 = Convert.ToSingle(Convert.ToSingle(c) 100 / Convert。ToSingle(sum)); float d1 = Convert.ToSingle(Convert。ToSingle(d) * 100 / Convert.ToSingle(sum); int width = 300, height = 300; Bitmap map = new Bitmap(width, height); Graphics g = Graphics.FromImage(map); try g。Clear(Color。White); Brush brush1 = new SolidBrush(Color.White); Brush brush2 = new SolidBrush(Color.Black); Brush brush3 = new SolidBrush(Color。Red); Brush brush4 = new SolidBrush(Color.Green); Brush brush5 = new SolidBrush(Color。Orange); Brush brush6 = new SolidBrush(Color。DarkBlue); Font font1 = new Font(”Courier New", 16, FontStyle。Bold); Font font2 = new Font(”Courier New", 8); g。FillRectangle(brush1, 0, 0, width, height); g.DrawString(”投票结果”, font1, brush2, new Point(90, 20); Point p1 = new Point(70, 50); Point p2 = new Point(230, 50); g。DrawLine(new Pen(Color.Black), p1, p2); g.DrawString("A:”, font2, brush2, new Point(30, 80); g。DrawString(”B:”, font2, brush2, new Point(30, 110)); g.DrawString("C:”, font2, brush2, new Point(30, 140)); g.DrawString("D:", font2, brush2, new Point(30, 170)); g.FillRectangle(brush3, 95, 80, a1, 17); g。FillRectangle(brush4, 95, 110, b1, 17); g。FillRectangle(brush5, 95, 140, c1, 17); g.FillRectangle(brush6, 95, 170, d1, 17); g.DrawRectangle(new Pen(Color。Green), 10, 210, 280, 80); g.DrawString(”A:” + a.ToString() + ”票”, font2, brush2, new Point(15, 220); g。DrawString("B:” + b.ToString() + "票", font2, brush2, new Point(150, 220)); g。DrawString(”C:” + c。ToString() + "票", font2, brush2, new Point(15, 260); g。DrawString(”D:” + d。ToString() + ”票", font2, brush2, new Point(150, 260)); pictureBox1.Image = map; catch (Exception ex) MessageBox.Show(ex。Message); private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) /调用异步委托处理数据读取及显示的任务 this.BeginInvoke(new mydelegate(readsp); void readsp() int cd = sp.BytesToRead; byte n = new bytecd; sp.Read(n, 0, cd); num = Encoding.UTF8。GetString(n); i+; if (i 4) i = 1; switch (i) case 1: n1 = num; break; case 2: n2 = num; break; case 3: n3 = num; break; case 4: n4 = num; break; private void timer1_Tick(object sender, EventArgs e) tuxiang(n1, n2, n3, n4); 单片机程序:#include<reg52.h>#define uchar unsigned char#define uint unsigned intuchar num1,num2,num3,num4,temp,t;uchar num4;uint i;void SendByte(uchar sc);void init();uchar keyscan();void delay(uchar c) uchar a,b; for(a=0;a<c;a+) for(b=0;b110;b+); void init() i=0; SCON=0x40; TMOD=0x20; TH1=0xfd; TL1=0xfd; ES=1; TR1=1; EA=1; TI=0;void main() init();while(1) keyscan(); t=numi; SendByte(t); i+; if(i>3) i=0;void SendByte(uchar sc)SBUF=sc;while(!TI);TI=0;delay(200);uchar keyscan()P2=0xfe;temp=P2;temp=temp0xf0;while(temp!=0xf0)delay(10);temp=P2;temp=temp0xf0;while(temp!=0xf0)temp=P3;switch(temp)case 0xee:num1+;break;case 0xde:num2+;break;case 0xbe:num3+;break;case 0x7e:num4+;break;while(temp!=0xf0)temp=P2;temp=temp0xf0; P3=0xfd;temp=P3;temp=temp0xf0;while(temp!=0xf0)delay(10);temp=P2;temp=temp&0xf0;while(temp!=0xf0)temp=P2;switch(temp)case 0xed: num1=0;num2=0;num3=0;num4=0;break;case 0xdd:EA=0;break;case 0xbd:EA=1;break;while(temp!=0xf0)temp=P2;temp=temp&0xf0;num0=num1;num1=num2;num2=num3;num3=num4;return num;