首页
社区
课程
招聘
[原创]Arduino串口通信
2016-9-24 22:10 11822

[原创]Arduino串口通信

2016-9-24 22:10
11822
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;
}

由于我写的比较简单,一次要读取串数据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对话框程序。使用的是第三方的串口类。如果你需要这个类,请看我的附件。
测试了一下效果,还可以

主要代码如下:
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);
    }
}

最后,你可以把我写的程序整合一下,就可以变成双向控制了。

参考资料:
http://blog.csdn.net/zw0558/article/details/4465814

[培训]科锐软件逆向50期预科班报名即将截止,速来!!! 50期正式班报名火爆招生中!!!

上传的附件:
收藏
免费 3
打赏
分享
最新回复 (4)
雪    币: 8865
活跃值: (2379)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 10 2016-9-24 23:58
2
0
good job!
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 2016-9-26 18:15
3
0
老v也会关注这个?
雪    币: 8865
活跃值: (2379)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 10 2016-9-26 19:28
4
0
话说 ardunio,树莓,xX啊
我去年玩了半年智能XX和XX
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 2016-9-27 11:18
5
0
做产品么?我弄过ardunio和stm32
游客
登录 | 注册 方可回帖
返回