串口开发小小心得

原创 2007年10月15日 18:00:00

本人新入行,写的也只是很肤浅的自己的一点点理解。欢迎批评指正,qq15065328.emailzxiki@163.commsnzxiki@hotmail.com。如果这些经验能对您有所帮助,我将非常荣幸。欢迎转载(可能抬举自己了),但请把以上文字一并保留。谢谢!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

项目(先这么称呼吧)简介,简单的来说就是计算机通过串口分别控制4个二极管的亮度。

上位机部分:只说说我用到的一些函数,和遇到的问题。基本的比如什么是异步通讯啦之类的就不说了,随便网上一搜能找一箩筐!

下边是一些函数:

函数名  作用

1.CreateFile 打开串口

2.GetCommState  检测串口设置

3.SetCommState 设置串口

BuilderCommDCB 用字符串中的值来填充设备控制块

GetCommTimeouts 检测通信超时设置

SetCommTimeouts 设置通信超时参数

SetCommMask 设定被监控事件

WaitCommEvent 等待被监控事件发生

WaitForMultipleObjects 等待多个被监测对象的结果

4.WriteFile 发送数据

ReadFile  接收数据

GetOverlappedResult 返回最后重叠(异步)操作结果

5.PurgeComm 清空串口缓冲区,退出所有相关操作

6.ClearCommError 更新串口状态结构体,并清除所有串口硬件错误

7.CloseHandle  关闭串行口

红颜色标出来的是我用到的一些函数,因为是最简单的应用,所以用到的函数也都是最最简单的一些,都是非常容易理解的。只是通过计算机向51发送指令所以连ReadFile()都没用到。省了很多事。。。。。。

1.CreateFile()

功能:打开串口设备

函数原型

HANDLE CreateFile

LPCTSTR lpFileName, // 串口名称字符串;如: "COM1" "COM2"

DWORD dwDesiredAccess, // 设置读写属性(访问模式 );一般为 GENERIC_READ|GENERIC_WRITE,

DWORD dwShareMode, // 共享模式;"必须" 0, 即不能共享

LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性;一般为NULL

DWORD dwCreationDistribution, // 创建方式,串口设置必须设置此值; 在这里"必须" OPEN_EXISTING

DWORD dwFlagsAndAttributes, // 文件属性和标志;在这里我们设置成FILE_FLAG_OVERLAPPED ,实现异步I/O关于Overlapped I/O模型,自己上网搜了去理解吧,我自己也说不清楚,懵懂的很。

HANDLE hTemplateFile // 临时文件的句柄,通常为NULL 

;

说明:

如果调用成功,那么该函数返回文件的句柄,如果调用失败,则函数返回INVALID_HANDLE_VALUE

 

2.GetCommState()

功能:获得串口状态

BOOL GetCommState

HANDLE hFile, // handle of communications device

LPDCB lpDCB // address of device-control block structure

;

 

3.SetCommState()

功能:设置串口状态 

BOOL SetCommState

HANDLE hFile, // handle of communications device

LPDCB lpDCB // address of device-control block structure

;

说明:

在打开通信设备句柄后,常常需要对串行口进行一些初始化工作。这需要通过一个DCB结构来进行。DCB结构包含了诸如波特率、每个字符的数据位数、奇偶校验和停止位数等信息。在查询或配置置串行口的属性时,都要用DCB结构来作为缓冲区。

调用GetCommState函数可以获得串口的配置,该函数把当前配置填充到一个DCB结构中。一般在用CreateFile打开串行口后,可以调用GetCommState函数来获取串行口的初始配置。要修改串行口的配置,应该先修改DCB结构,然后再调用SetCommState函数用指定的DCB结构来设置串行口

For example:

DCB dcb;

memset(&dec,0,sizeof(dcb));

if(!GetCommState(HComm,&dcb))//获取当前DCB配置

 return FALSE;

dcb.BaudRate = CBR_9600;//修改数据传输率。。。。这里还有很多参数可以修改,比如,停止位,有无校验等等,具体参见dcb结构。

............

if(SetCommState(hComm,&dcb))//设置新参数

......    //错误处理

4.WriteFile()

功能:来将资料写入Serial port.

函数原型:

BOOL WriteFile

HANDLE hFile, // handle to file to write to

LPCVOID lpBuffer, // 写如字符串的首地址

DWORD nNumberOfBytesToWrite, // 要写如字符的个数

LPDWORD lpNumberOfBytesWritten, // 实际写入字节数,为一个int型指针

LPOVERLAPPED lpOverlapped // i/o重构结构,我讲不清楚,sorry

;

 

说明:

   ReadFile函数只要在串行口输入缓冲区中读入指定数量的字符,就算完成操作。

WriteFile函数不但要把指定数量的字符拷入到输出缓冲中,而且要等这些字符从串行口送出去后才算完成操作。

ReadFileWriteFile返回FALSE时,不一定就是操作失败,线程应该调用GetLastError函数分析返回的结果。例如,在重叠操作时如果操作还未完成函数就返回,那么函数就返回FALSE,而且GetLastError函数返回ERROR_IO_PENDING。如果GetLastError函数返回ERROR_IO_PENDING,则说明重叠操作还为完成,线程可以等待操作完成。

有两种等待办法:一种办法是用象WaitForSingleObject这样的等待函数来等待OVERLAPPED结构的hEvent成员,可以规定等待的时间,在等待函数返回后,调用GetOverlappedResult

另一种办法是调用GetOverlappedResult函数等待,如果指定该函数的bWait参数为TRUE

那么该函数将等待OVERLAPPED结构的hEvent 事件。GetOverlappedResult可以返回一个OVERLAPPED结构来报告包括实际传输字节在内的重叠操作结果。

如果规定了读/写操作的超时,那么当超过规定时间后,hEvent成员会变成有信号的。因此,在超时发生后,WaitForSingleObjectGetOverlappedResult都会结束等待。WaitForSingleObjectdwMilliseconds参数会规定一个等待超时,该函数实际等待的时间是两个超时的最小值。注意GetOverlappedResult不能设置等待的时限,因此如果hEvent成员无信号,则该函数将一直等待下去

 

5.PurgeComm()

功能:终止目前正在进行的读或写的动作

函数原型:

BOOL PurgeComm

HANDLE hFile, // handle of communications resource

DWORD dwFlags // action to perform

;

参数说明:

HANDLE hFile,//串口名称字符串

dwFlags 共有四种 flags:

  PURGE_TXABORT: 终止目前正在进行的(背景)写入动作

  PURGE_RXABORT: 终正目前正在进行的(背景)读取动作

  PURGE_TXCLEAR: flush 写入的 buffer

  PURGE_TXCLEAR: flush 读取的 buffer

调用PurgeComm函数可以终止正在进行的读写操作,该函数还会清除输入或输出缓冲区中的内容。

 

6.ClearCommError()

功能: 从字面上的意思看来, 它是用来清除错误情况用的, 但是实际上它还可以拿来取得目前通讯设备的一些信息.

函数原型:

BOOL ClearCommError

HANDLE hFile, // handle to communications device

LPDWORD lpErrors, // pointer to variable to receive error codes

LPCOMSTAT lpStat // pointer to buffer for communications status

;

说明:

 在调用ReadFileWriteFile之前,线程应该调用ClearCommError函数清除错误标志。

该函数负责报告指定的错误和设备的当前状态。

7CloseHandle();

功能:关闭串口

BOOL CloseHandleHANDLE hObject // handle to object to close

 

 

下面是我在bcb里的一些具体实现:

 

 

1.       打开串口

void __fastcall TForm1::Button1Click(TObject *Sender)

{

 

 

        hCom=CreateFile( "COM2", //文件名

                         GENERIC_READ|GENERIC_WRITE,//访问模式允许读写

                         0, //此项必须是0

                         0,//无安全参数

                         OPEN_EXISTING,//创建方式

                         FILE_FLAG_OVERLAPPED,//异步工作方式

                         0);

        if (hCom==INVALID_HANDLE_VALUE)

        {

                ShowMessage("Can not open the port !");

                CloseHandle(hCom);

                hCom = 0;

                return;

        }

        else ShowMessage("COM2 open success!");

        Button1->Enabled=false;

        CheckBox1->Enabled=true;

        CheckBox2->Enabled=true;

        CheckBox3->Enabled=true;

        CheckBox4->Enabled=true;

        CheckBox5->Enabled=true;

 

        if(!GetCommState(hCom,&dcb)) //获得串口设置并用它填充dcb结构体

                ShowMessage("GetCommState failed");

 

        if (!SetupComm(hCom,1024,1024)) //设置输入输出缓冲区大小

                ShowMessage("SetupComm failed");

 

        dcb.BaudRate=9600;

        dcb.fParity=0;

        dcb.Parity=NOPARITY;

        dcb.StopBits=ONESTOPBIT;

        dcb.ByteSize=8;

 

 

        if(!SetCommState(hCom,&dcb)) //重新配置串口

              ShowMessage("SetCommState failed");

 

        SetCommMask(hCom,EV_TXEMPTY); //EV_RXCHAR|

        memset(&os,0,sizeof(OVERLAPPED));

        os.hEvent=CreateEvent(NULL,true,false,NULL);

 

}

2.       发送一个字节

void sendchar(char ch)

{  DWORD BytesSent=0;

   OverWrite.Offset=0;

   OverWrite.OffsetHigh=0;

 

   BOOL bResult;

  bResult=WriteFile(hCom,

            &ch,

            1,

            &BytesSent,

            &OverWrite);

  if(bResult) return;

  DWORD dwError=GetLastError();

  if(dwError!=ERROR_IO_PENDING)

  {

    ShowMessage("写字符错误,请确认串口以打开!");

    return;

  }

  if(!GetOverlappedResult(hCom,&OverWrite,&BytesSent,TRUE))

  {

     ShowMessage("写字符错误,请确认串口以打开!");

     return;

  }

  }      

在与51通讯的过程中,一次有计算机发送两个数据给单片机,第一个是控制二极管亮度的数据,第二个是选择控制哪个二极管的数据。为了简单考虑,每次发送过程调用两次send函数。下面的是发送子程序:

void send(char channel,int Vdata)

{     PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);

      sendchar(char(Vdata));

      PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);

      sendchar(channel);

      Sleep(10);

 }

上位机的程序就这么样了。。。。。

 

单片机源码:这是根据上位机的程序来写的。

#include <reg51.h>

 

 

char datax=0;//作为判断接受的数据为控制信号的标志使用

char receive;

sbit a=P3^2;

sbit b=P3^3;

sbit c=P3^4;

sbit d=P3^5;

sbit e=P3^7;

 

void initial(void);

void P3con(char x);

void delay(short ms);

 

void main(void)

{

int i; 

for(i=0;i<5;i++)

  {

    e=0;d=0;c=0;b=0;a=0;P1=0;

    delay(2000);

    a=1;

    b=1;

    c=1;

    d=1;

    e=1;

    P1=0xFF;

    delay(2000);

   

  }

  initial();//串口初始化

  while(1);

}

 

 

void initial(void)      //初始化子程序

{

  IP=0x10;//串口中断优先级高

  IE=0x90;//开中断

  TMOD=0x20;//定时器1 工作模式2

  TH1=253;TL1=253;//设置定时器1初值,波特率为9.6k/s

  SCON=0x50;//串行模式1 8bit异步传输

  PCON=0x7f&PCON;//SMOD=0

  TR1=1;//计时器开

}

 

 

void Sinterrupt(void) interrupt 4 using 3 //串口中断响应程序;

{

  if(TI) TI=0;

  else if(RI)

  {   

     RI=0;

     receive=SBUF;

     delay(10);

     SBUF=receive;

     //delay(10);

     //while(!TI);

     //TI=0;

    if(datax==0)                

      {

        P1=receive;                

        datax++;

        return;

      }

    if(datax==1)                 

     {

        P3con(receive);

        datax=0; 

        return;

     }

  }

}

 

 

void P3con(char x)   //dac0809的片选信号

{

  switch(x)

 {

  case '1': a=1;delay(2);a=0;break;

  case '2': b=1;delay(2);a=0;break;

  case '3': c=1;delay(2);a=0;break;

  case '4': d=1;delay(2);a=0;break;

  case '5': e=1;delay(2);a=0;break;

  default : break;

  }

}

 

 

void delay(int ms)

{

  int i,j;

  for (i=0;i<ms;i++)

    for (j=0;j<120;j++)

    {}

} 

 

这里就不多说了,基本懂得51c的都能看懂。

 

 

仿真:用的是proteus,串口调试助手,虚拟串口这三个软件。

这是虚拟串口的软件。

这个,串口调试助手

 

具体怎么用,一摸索就知道了,要说明的是仿真是串口调试助手的“串口”选择“COM3”protues中串口要选择“COM4”,其他比如波特率之类的设置就不废话了。

 

 

 

 

 

 

 

 

 

 

 

下面这个是仿真的截图

 

另外注意的是,51rxdrs232rxdtxdtxd,这里,我十分不明白。。。。

就这些啦,哈哈,第一次写点东西,想想以后回来自己看看肯定会笑话自己的,不过,没什么,不这样哪有进步~虽然很稚嫩,但是是自己的~要指导的请联系我,感激不尽!

 

串口通信与并口通信的浅理解

在说到通信的时候,一般都会提到窜口通信和并口通信的问题,以前一直都是听别人说,我一点也不明白,毕竟这个概念还是偏硬件了点,但是人还是要往前进的,今天多google了几下,总结了下理解方式,算是一次学习...
  • playboyanta123
  • playboyanta123
  • 2012-12-26 10:06:17
  • 5280

串口的标志位 USART_FLAG_TXE 和USART_FLAG_TC的理解

xuande 发表于 2015-9-11 07:41回复【4楼】wolfcan:---------------------------------TXE是指“弹仓”空;TC是“枪膛”空。这个形象。也就...
  • qingzhuyuxian
  • qingzhuyuxian
  • 2018-03-04 15:14:42
  • 20

简单理解SOCKET

对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问: 什么是TCP/IP、UDP? Socket在哪里呢? Socket是什么呢? 你...
  • Sayesan
  • Sayesan
  • 2017-10-30 17:37:13
  • 104

C# AE开发心得

  • 2012年03月30日 15:28
  • 13.05MB
  • 下载

网页制作小感悟

两个月的时间依然让我成长了一丢丢。。。。。。。。。。 开始学习时,使用dreameveaer,,后来慢慢发现原来不止这一个软件可以编写网页,,,,其他的例如eclipse。。。。PHPstorm,,...
  • strivenoend
  • strivenoend
  • 2017-04-24 16:40:24
  • 776

VC++串口通信

Win 32系统把文件的概念进行了扩展。无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。该函数的声明为: HANDLE Create...
  • wqsailor12
  • wqsailor12
  • 2013-01-05 10:50:08
  • 6829

SPI IIC UART通信协议简单说明

通信可以形象的比喻成两个人讲话:1、你说的别人得能听懂:双方约定信号的协议。2、你的语速别人得能接受:双方满足时序要求。 一、IIC协议: 2C串行总线一般有两根信号线,一根是双向的数据线...
  • QQ576494799
  • QQ576494799
  • 2016-11-17 11:01:52
  • 578

关于Qt开发经验总结

  • 2011年11月30日 10:36
  • 41KB
  • 下载

小小画图板的开发 java

  • 2011年03月03日 08:51
  • 53KB
  • 下载

在CB6下基于api函数编写串口通信程序简介

1-在C++ Builder 6.0下基于api函数编写串口通信程序简介: 在dos/win95/win98的年代,操作系统对串口是不保护的,也就是说将串口的的资源完全 开放给用户,用户可以用直接操作...
  • henhen2002
  • henhen2002
  • 2009-08-26 07:45:00
  • 1012
收藏助手
不良信息举报
您举报文章:串口开发小小心得
举报原因:
原因补充:

(最多只允许输入30个字)