Arduino串口通信
返回上一层
本来是要讲《Arduino与计算机》的,但是后来一想内容太多了。于是我就准备把这部分内容分成《Arduino串口通信》和《Arduino网络通信》两个部分。还有一个重要的原因是我的W5100网络模块正在路上。
在这篇,我们要讲讲Arduino串口通信实际应用相关的内容。说白了就是Arduino和电脑的相互控制,你控制我,我控制你。值得高兴的是,这一篇里不需要其他模块,又可以节约一点了。
串口通信原理等其他东西太多,我就不讲了(其实,要我讲也讲不出来。我只学会两点:在Linux下万事万物皆文件,Windows下就看API。目前,正在学习第三点:一定要约定好。说到这一点就伤心啊,不说了...),我们直接来写代码。
我的测试环境是
Win7 32位(用64位要快些)+XP系统(VM虚拟机)
VC6绿色版(比较小,压缩包只有40M左右吧,我没有用Python的原因是调试起来不方便,有时候Python的版本不一样也很麻烦)
Arduino1.6.11(软件版本)
注意:我的Arduino用的串口是COM3,如果你的串口不是的话,请自行修改代码。
代码见附件。
Arduino自带了一个串口工具,但是,你要操作计算机的话,很不方便。比如说,我在Arduino上按下按钮,运行”记事本”程序打开C:\1.txt,这就要我们自己写代码了。当然,我们会把代码写完,并测试成功。由于时间关系,我写的代码功能和UI上都不尽人意,不过作为教学的一个例来讲,还好吧。现在,就让我们来开始串口编程之行。
一、Arduino向计算机发送的数据(命令行程序)。
先来个例子看看:
首先在Arduino中,写一段简单的测试程序,不断向串口发送"Hello World!"
代码:
void setup()
{
Serial.begin(9600);//设置波特率为9600
}
void loop()
{
Serial.println("Hello World!");
delay(500);
}
由于我用的是Win7系统,所以用VC6写了一段接收程序。
如果大家用的是Python,编写串口程序可能要方便一点,特别是Linux下。
新建一个.CPP文件,用VC6打开,把如下代码拷入去。当然,串口不一样就要改一下串。
代码:
#include <stdio.h>
#include <windows.h>
class MyCom
{
public:
int Open()
{
hCom=CreateFile("COM3",//COM3口
GENERIC_READ|GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
0, //同步方式
NULL);
if(hCom==(HANDLE)-1)
{
printf("打开COM失败!\r\n");
return 0;
}
else
{
printf("打开COM成功!\r\n");
}
return 1;
}
void Set()
{
SetupComm(hCom,1024,1024); //输入缓冲区和输出缓冲区的大小都是1024
COMMTIMEOUTS TimeOuts;
//设定读超时
TimeOuts.ReadIntervalTimeout=1000;
TimeOuts.ReadTotalTimeoutMultiplier=500;
TimeOuts.ReadTotalTimeoutConstant=5000;
//设定写超时
TimeOuts.WriteTotalTimeoutMultiplier=500;
TimeOuts.WriteTotalTimeoutConstant=2000;
SetCommTimeouts(hCom,&TimeOuts); //设置超时
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600; //波特率为9600
dcb.ByteSize=8; //每个字节有8位
dcb.Parity=NOPARITY; //无奇偶校验位
dcb.StopBits=TWOSTOPBITS; //两个停止位
SetCommState(hCom,&dcb);
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
}
int Read()
{
char str[100]={0};
DWORD wCount;//读取的字节数
BOOL bReadStat;
bReadStat=ReadFile(hCom,str,100,&wCount,NULL);
if(!bReadStat)
{
printf("读串口失败!\r\n");
return 0;
}
else
{
printf("串口内容:");
printf("%s\r\n",str);
memset(str,0,100);
return 1;
}
}
protected:
private:
HANDLE hCom; //全局变量,串口句柄
};
int main()
{
MyCom com;
com.Open();//(1) 打开串口,获取串口资源句柄
com.Set();//(2)串口设置
for (int i=0;i<10;i++)
{
com.Read();//串口读取操作
Sleep(200);
}
getchar();
getchar();
return 0;
}
![](upload/attach/201609/70743_qnwi6ca0qrgadu7.png)
由于我写的比较简单,一次要读取串数据100个字节,所以出现了这种情况。
二、计算机向Arduino发送的数据(命令行程序)。
Arduino程序:
int nLED_Green=13;//绿灯
String comdata = "";//字符串
void setup()
{
Serial.begin(9600);
pinMode(nLED_Green,OUTPUT);//设置数字13口为输出接口
}
void loop()
{
while (Serial.available() > 0)
{
comdata += char(Serial.read());
delay(2);
}
if (comdata.length() > 0 && comdata=="on")
{
Serial.println("turn on LED");
digitalWrite(nLED_Green,HIGH);//点亮灯。
comdata = "";
}
else if(comdata.length() > 0 && comdata=="off")
{
Serial.println("turn off LED");
digitalWrite(nLED_Green,LOW);//熄灭灯。
comdata = "";
}
}
编写VC程序。我写的串口程序是同步方式写的,别的程序(包括Arduino)不能同时操作该串口。没有用异步方式,是因为同步方式要简单一点。还有一个更简单的方法,就是调用CSerialPort这个第三方的类,这个类用的人很多,应该不错吧。先不着急,等会写MFC程序的时候再用。
代码:
#include <stdio.h>
#include <windows.h>
class MyCom
{
public:
MyCom()
{
hCom=NULL;
}
~MyCom()
{
if (hCom!=NULL)
{
CloseHandle(hCom);
}
}
//(1) 打开串口,获取串口资源句柄
int Open()
{
hCom=CreateFile("COM3",//COM3口
GENERIC_READ|GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
0, //同步方式
NULL);
if(hCom==(HANDLE)-1)
{
printf("打开COM失败!\r\n");
return 0;
}
else
{
printf("打开COM成功!\r\n");
}
return 1;
}
//(2)串口设置
void Set()
{
SetupComm(hCom,1024,1024); //输入缓冲区和输出缓冲区的大小都是1024
COMMTIMEOUTS TimeOuts;
//设定读超时
TimeOuts.ReadIntervalTimeout=1000;
TimeOuts.ReadTotalTimeoutMultiplier=500;
TimeOuts.ReadTotalTimeoutConstant=5000;
//设定写超时
TimeOuts.WriteTotalTimeoutMultiplier=500;
TimeOuts.WriteTotalTimeoutConstant=2000;
SetCommTimeouts(hCom,&TimeOuts); //设置超时
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600; //波特率为9600
dcb.ByteSize=8; //每个字节有8位
dcb.Parity=NOPARITY; //无奇偶校验位
dcb.StopBits=TWOSTOPBITS; //两个停止位
SetCommState(hCom,&dcb);
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
}
//串口读取操作
int Read()
{
char str[100]={0};
DWORD wCount;//读取的字节数
BOOL bReadStat;
bReadStat=ReadFile(hCom,str,100,&wCount,NULL);
if(!bReadStat)
{
printf("读串口失败!\r\n");
return 0;
}
else
{
printf("串口内容:");
printf("%s\r\n",str);
memset(str,0,100);
return 1;
}
}
int Write(char* lpOutBuffer)
{
DWORD dwBytesWrite=strlen(lpOutBuffer)+1;
COMSTAT ComStat;
DWORD dwErrorFlags;
BOOL bWriteStat;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);
if(!bWriteStat)
{
//AfxMessageBox("写串口失败!");
printf("串口写入失败!\r\n");
return 0;
}
PurgeComm(hCom, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
return 1;
}
protected:
private:
HANDLE hCom; //全局变量,串口句柄
};
int main()
{
MyCom com;
int n = com.Open();
if (n==0)
{
return 0;
}
com.Set();
char szstr[20]="off";
com.Write(szstr);
getchar();
return 0;
}
这个程序写得很简陋。使用方法是先用Arduino串口输入”on”,退出Arduino程序。运行VC编译好的程序,就可以看到LED熄灭了。
这是一个最简单的串口通信的例子了。
三、计算机向Arduino发送的数据(MFC)。
即然,能读串口,又可以写串口数据了。那么我们就可以升级一下了,把VC的控件台程序升级到MFC对话框程序。使用的是第三方的串口类。如果你需要这个类,请看我的附件。
测试了一下效果,还可以
![](upload/attach/201609/70743_wcfgb3enzribjc5.png)
主要代码如下:
void CContrlLEDDlg::OnOK() //开灯
{
m_SerialPort.WriteToPort("on");
m_edt1 = "turn on";
UpdateData(false);
}
void CContrlLEDDlg::OnOk2() //关灯
{
m_SerialPort.WriteToPort("off");
m_edt1 = "turn off";
UpdateData(false);
}
void CContrlLEDDlg::OnButton1() //打开串口
{
if(m_SerialPort.InitPort(this,3,9600,'N',8,1,EV_RXFLAG|EV_RXCHAR,512))//3==COM3
{
m_SerialPort.StartMonitoring();//启动串口通信检测线程函数
m_bPortOpen = TRUE;
}
else
{
AfxMessageBox("没有发现此串口或被占用");
m_bPortOpen = FALSE;
}
GetDlgItem(IDOK)->EnableWindow(m_bPortOpen);
GetDlgItem(IDOK2)->EnableWindow(m_bPortOpen);
}
Arduino程序没有变,还可以用:
int nLED_Green=13;//绿灯
String comdata = "";//字符串
void setup()
{
Serial.begin(9600);
pinMode(nLED_Green,OUTPUT);//设置数字13口为输出接口
}
void loop()
{
while (Serial.available() > 0)
{
comdata += char(Serial.read());
delay(2);
}
if (comdata.length() > 0 && comdata=="on")
{
//Serial.println("turn on LED");
digitalWrite(nLED_Green,HIGH);//点亮灯。
comdata = "";
}
else if(comdata.length() > 0 && comdata=="off")
{
//Serial.println("turn off LED");
digitalWrite(nLED_Green,LOW);//熄灭灯。
comdata = "";
}
}
四、Arduino向计算机发送的数据运行程序(MFC)。
Arduino程序:
const int buttonPin = 12; // 12口作为按钮的接口
const int ledPin = 13; // 13口,LED
int buttonState = 0; // 按钮状态标志用的变量,初始化为0
void setup()
{
Serial.begin(9600);
pinMode(ledPin, OUTPUT);//LED为输出
pinMode(buttonPin, INPUT);//按钮是输入
}
void loop()
{
delay(200);
buttonState = digitalRead(buttonPin);//读取按钮的状态
if (buttonState == HIGH)
{
Serial.println("open");//发送命令
delay(600);
}
}
MFC主要程序代码(新建了一个线程来处理,也可以定时器来读取串口内容):
int ThreadFunc()
{
HANDLE hCom;
int nON=1;
hCom=CreateFile(TEXT("COM3"),//COM3口
GENERIC_READ|GENERIC_WRITE, //允许读和写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
0, //同步方式
NULL);
if(hCom==(HANDLE)-1)
{
//printf("打开COM失败!\n");
return FALSE;
}
else
{
//printf("COM打开成功!\n");
}
SetupComm(hCom,1024,1024); //输入缓冲区和输出缓冲区的大小都是1024
COMMTIMEOUTS TimeOuts;
//设定读超时
TimeOuts.ReadIntervalTimeout=1000;
TimeOuts.ReadTotalTimeoutMultiplier=500;
TimeOuts.ReadTotalTimeoutConstant=5000;
//设定写超时
TimeOuts.WriteTotalTimeoutMultiplier=500;
TimeOuts.WriteTotalTimeoutConstant=2000;
SetCommTimeouts(hCom,&TimeOuts); //设置超时
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=9600; //波特率为9600
dcb.ByteSize=8; //每个字节有8位
dcb.Parity=NOPARITY; //无奇偶校验位
dcb.StopBits=ONE5STOPBITS; //两个停止位
SetCommState(hCom,&dcb);
DWORD wCount;//读取的字节数
BOOL bReadStat;
while(1)
{
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR); //清空缓冲区
char str[5]={0};
bReadStat=ReadFile(hCom,str,4,&wCount,NULL);
if(!bReadStat)
{
return FALSE;
}
str[4]='\0';
if(strcmp(str,"open")==0 && nON == 1)
{
ShellExecute(NULL,"open","C:\\1.txt",NULL,NULL,SW_NORMAL);
myputs("程序已运行");
nON = 0;
}
memset(str,0,5);
Sleep(500);
}
}
![](upload/attach/201609/70743_yt657zy0lukhl2j.png)
最后,你可以把我写的程序整合一下,就可以变成双向控制了。
参考资料:
http://blog.csdn.net/zw0558/article/details/4465814
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课