简单的串行通信实用程序
介绍
在嵌入式设计中,需要与 PC 进行通信的通道是很常见的。最简单的方法是串行通信,因为每个微控制器都有 UART 端口来与外界进行串行通信。但在计算机上,这并不总是那么容易。
在这里,我提出了一个非常简单的串行通信库,具有非阻塞读取和不依赖事件的功能,这可能是一个优势,具体取决于我们需要的软件类型。
除此之外,我还提供了一个非常有用的串行端口枚举器,以及一个简单的Timer
类,它允许我们测量间隔并自动执行一些操作。此类timer
不是事件驱动的,因此您必须轮询间隔的状态以了解是否该执行某些操作。
在以后的文章中,我将解释如何使用事件驱动的计时器来真正实现功能的自动化。但就目前而言,这个简单的功能timer
对于我们的应用程序来说已经足够了。
使用代码
在代码中,我使用三个独立的库。一种用于串行通信,一种用于计时,一种用于 PC 中可用端口的摘要。我分别上传每个库以及使用它们的示例项目。
RS232库
该库为 PC 的 COM 端口带来了简单易用的界面。标题提供了这些功能:
#ifndef rs232_INCLUDED#define rs232_INCLUDED#ifdef __cplusplusextern "C" {#endif#include <stdio.h>#include <string.h>#ifdef __linux__#include <termios.h>#include <sys/ioctl.h>#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <limits.h>#else#include <windows.h>#endifint RS232_OpenComport(int, int);int RS232_PollComport(int, unsigned char *, int);int RS232_SendByte(int, unsigned char);int RS232_SendBuf(int, unsigned char *, int);void RS232_CloseComport(int);void RS232_cputs(int, const char *);int RS232_IsDCDEnabled(int);int RS232_IsCTSEnabled(int);int RS232_IsDSREnabled(int);void RS232_enableDTR(int);void RS232_disableDTR(int);void RS232_enableRTS(int);void RS232_disableRTS(int);#ifdef __cplusplus} /* extern "C" */#endif#endif
该库由 Teunis van Beelen 编写,更多信息请参见此处。
Com 端口枚举器
该库提供了一种枚举 PC 中可用串行端口的简单方法。它并不完美,但效果很好。
#ifndef LISTPORTS_H#define LISTPORTS_H#define VERSION_LISTPORTS 0x00020000#ifdef __cplusplusextern "C"{#endif#include <windows.h>typedef struct{ LPCTSTR lpPortName; /* "COM1", etc. */ LPCTSTR lpFriendlyName; /* Suitable to describe the port, as for */ /* instance "Infrared serial port (COM4)" */ LPCTSTR lpTechnology; /* "BIOS","INFRARED","USB", etc. */}LISTPORTS_PORTINFO;typedef BOOL (CALLBACK* LISTPORTS_CALLBACK)(LPVOID lpCallbackValue, LISTPORTS_PORTINFO* lpPortInfo);/* User provided callback funtion that receives the information on each * serial port available. * The strings provided on the LISTPORTS_INFO are not to be referenced after * the callback returns; instead make copies of them for later use. * If the callback returns FALSE, port enumeration is aborted. */BOOL ListPorts(LISTPORTS_CALLBACK lpCallback,LPVOID lpCallbackValue);/* Lists serial ports available on the system, passing the information on * each port on succesive calls to lpCallback. * lpCallbackValue, treated opaquely by ListPorts(), is intended to carry * information internal to the callback routine. * Returns TRUE if succesful, otherwise error code can be retrieved via * GetLastError(). */#ifdef __cplusplus}#endif#elif VERSION_LISTPORTS!=0x00020000#error You have included two LISTPORTS.H with different version numbers#endif
typedef BOOL (CALLBACK* LISTPORTS_CALLBACK)(LPVOID lpCallbackValue, LISTPORTS_PORTINFO* lpPortInfo);
定义。在示例中,我提供:
string comPorts[4]; // Vector containing available portsint port_cnt=0;/* Callback function that list the available ports */static BOOL CALLBACK callback(LPVOID lpCallbackValue,LISTPORTS_PORTINFO* lpPortInfo) { _tprintf( TEXT("\"%s\" \"%s\" \"%s\"\n"), lpPortInfo->lpPortName,lpPortInfo->lpTechnology,lpPortInfo->lpFriendlyName); comPorts[port_cnt] = lpPortInfo->lpPortName; port_cnt++; return TRUE; }
该库由 Joaquín Mª López Muñoz 编写。
这个例子
该示例只是一个控制台应用程序,模拟一个非常简单的终端,该终端发送一个字符或string
到端口广告以显示连接到它的设备的响应。它是使用Code::Blocks
Windows 7 编写的。
#include <iostream>#include "rs232.h"#include "timerclass.h"#include "listports.h"#include <tchar.h>using namespace std; string comPorts[4]; // Vector containing available portsint port_cnt=0;/* Definition of the Callback function that list the available ports */static BOOL CALLBACK callback(LPVOID lpCallbackValue,LISTPORTS_PORTINFO* lpPortInfo) { _tprintf( TEXT("\"%s\" \"%s\" \"%s\"\n"), lpPortInfo->lpPortName,lpPortInfo->lpTechnology,lpPortInfo->lpFriendlyName); comPorts[port_cnt] = lpPortInfo->lpPortName; port_cnt++; return TRUE; }int main(int argc, char* argv[]) { int n; unsigned char buf[4096]; int cport_nr = 4; int bdrate = 9600; //unsigned char outBuffer[128]; string outBuffer; ListPorts(callback,NULL); //Calling to the list ports function cout << "Available ports: " << endl; for(int i=0; i<port_cnt; i++) { cout << (i+1) << ": " << comPorts[i] << endl; } cout << endl << "Choose a port "; cin >> cport_nr; cport_nr--; // according to rs232.h, the port numbers start on 0 for COM1 if(RS232_OpenComport(cport_nr, bdrate)) //Open the port { cout << "Can not open com port " << comPorts[cport_nr]; return(0); } while(1) { //n = RS232_PollComport(cport_nr, buf, 4095); cout << "Type message to send" << endl << "Type 'quit' to exit" << endl; cin >> outBuffer; if(outBuffer == "quit") { RS232_CloseComport(cport_nr); return 0; } RS232_SendBuf(cport_nr, (unsigned char*) outBuffer.c_str(), outBuffer.size()); Sleep(100); n = RS232_PollComport(cport_nr, buf, 4096); if(n>0) { buf[n]=0; cout << "Received: " << buf << endl << endl; } buf[0]=0; } }
包含库之后是端口列表器的回调函数的定义。它显示可用的端口并将其名称存储在string
.
然后在main
函数中,首先调用该ListPort
函数并再次显示可用端口。询问所需的端口并尝试打开它。如果端口打开,程序将进入无限循环,直到用户键入“退出”。
在循环中,程序要求发送消息并将其放在 COM 端口上。
RS232_SendBuf(cport_nr, (unsigned char*) outBuffer.c_str(), outBuffer.size());
等待 100 毫秒并轮询端口以获取响应:
n = RS232_PollComport(cport_nr, buf, 4096);
如果有任何消息,则会显示在屏幕上:
if(n>0) { buf[n]=0; cout << "Received: " << buf << endl << endl; }
在捕获过程中,与充当回声器的 Arduino 板进行通信。