目前共有12篇帖子。 內容轉換:不轉換▼
 
點擊 回復
561 11
【程式】串口操作程式
一派護法 十九級
1樓 發表于:2016-7-2 20:48
#include <stdio.h>
#include <Windows.h>

int main(void)
{
    char buffer[100];
    char *p;
    DCB dcb;
    DWORD dwSize;
    HANDLE hPort = CreateFile(TEXT("\\\\.\\COM6"), GENERIC_READ | GENERIC_WRITE, (DWORD)NULL, NULL, OPEN_EXISTING, (DWORD)NULL, NULL);

    if (hPort != INVALID_HANDLE_VALUE)
    {
        ZeroMemory(&dcb, sizeof(dcb)); // 由於dcb結構中還有很多其他成員, 所以使用前必須清零
        dcb.BaudRate = CBR_4800;
        dcb.ByteSize = 8;
        dcb.Parity = NOPARITY;
        dcb.StopBits = ONESTOPBIT;
        if (SetCommState(hPort, &dcb))
            puts("串口打開成功");

        p = "這是一段中文字元串";
        WriteFile(hPort, p, strlen(p), &dwSize, NULL);
        printf("\n發送: %d 字節, 錯誤編號: %d\n", dwSize, GetLastError());
        ReadFile(hPort, buffer, sizeof(buffer), &dwSize, NULL);
        printf("接收: %d 字節, 錯誤編號: %d\n", dwSize, GetLastError());
        if (dwSize > 0)
        {
            buffer[dwSize] = '\0';
            printf("接收到的資料: %s\n", buffer);
        }

        if (CloseHandle(hPort))
            puts("串口已關閉");
    }
    else
        printf("串口打開失敗, 錯誤碼: %d\n", GetLastError());
    return 0;
}
一派護法 十九級
2樓 發表于:2016-7-2 20:51
一派護法 十九級
3樓 發表于:2016-7-2 20:59
COM6無法直接在運行裡面打開:

一派護法 十九級
4樓 發表于:2016-7-2 20:59
就連文件名都不能是com6,即便是有.png的擴展名:

一派護法 十九級
5樓 發表于:2016-7-17 18:50

【注意】

dcb.BaudRate = CBR_9600;

常量是以CBR_开头的,不是以BAUD_开头的

错误的写法:

dcb.BaudRate = BAUD_9600;

一派護法 十九級
6樓 發表于:2016-7-17 18:53

另外,dcb里面还有一个DCBlength成员,必须设置为DCB结构的大小。不过API函数似乎不检查这个成员的值。不过为了兼容性最好写上:

DCB dcb;
ZeroMemory(&dcb, sizeof(dcb));
dcb.DCBlength = sizeof(DCB);
GetCommState(hComm, &dcb); // 获取该串口的默认配置

 

.....

SetCommState(hComm, &dcb);

一派護法 十九級
7樓 發表于:2016-7-17 18:59

【程序2】

#include <iostream>
#include <Windows.h>
#include <strsafe.h>
using namespace std;
#define DEBCD(n) ((((n) & 0xf0) >> 4) * 10 + ((n) & 0x0f))
#ifdef _UNICODE
ostream &operator << (ostream &os, const wchar_t *wstr)
{
 if (os == cout)
  WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), wstr, wcslen(wstr), NULL, NULL);
 return os;
}
#endif
int main(void)
{
 HANDLE hComm = CreateFile(TEXT("\\\\.\\COM10"), GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, NULL, NULL);
 if (hComm == INVALID_HANDLE_VALUE)
 {
  cout << TEXT("打开串口失败") << endl;
  return 0;
 }
 try
 {
  DCB dcb;
  ZeroMemory(&dcb, sizeof(dcb));
  dcb.DCBlength = sizeof(DCB);
  GetCommState(hComm, &dcb);
  dcb.BaudRate = CBR_9600; // 注意是CBR_不是BAUD_
  dcb.ByteSize = 8;
  dcb.fParity = NOPARITY;
  dcb.StopBits = ONESTOPBIT;
  if (!SetCommState(hComm, &dcb))
   throw 0;
  COMMTIMEOUTS tmout;
  ZeroMemory(&tmout, sizeof(tmout));
  tmout.ReadTotalTimeoutConstant = 1000;
  if (!SetCommTimeouts(hComm, &tmout))
   throw 2;
  BYTE buf[100];
  DWORD dwSize;
  buf[0] = 0x05;
  WriteFile(hComm, buf, 1, &dwSize, NULL);
  ReadFile(hComm, buf, _countof(buf), &dwSize, NULL);
  if (buf[0] == 0x20)
  {
   int hour, wday;
   TCHAR h[4];
   LPTSTR list = TEXT("日一二三四五六");
   TCHAR str[100];
   if (buf[3] & 0x80)
   {
    if (buf[3] & 0x20)
     h[0] = 'P';
    else
     h[0] = 'A';
    h[1] = 'M';
    h[2] = ' ';
    h[3] = '\0';
    hour = buf[3] & 0x1f;
    hour = DEBCD(hour);
   }
   else
   {
    h[0] = '\0';
    hour = DEBCD(buf[3]);
   }
   wday = buf[6] & 0x07;
   if (wday == 7)
    wday = 0;
   cout << TEXT("时间为: ") << endl;
   StringCbPrintf(str, sizeof(str), TEXT("20%02d年%02d月%02d日\n%02d:%02d:%02d %s星期%c"), DEBCD(buf[7]), DEBCD(buf[5]), DEBCD(buf[4]), hour, DEBCD(buf[2]), DEBCD(buf[1]), h, list[wday]);
   cout << str << endl;
  }
  else
   throw 1;
 }
 catch (const int iErrId)
 {
  DWORD dwErr = GetLastError();
  switch (iErrId)
  {
  case 0:
   cout << TEXT("设置波特率失败, 错误码: ") << dwErr << endl;
   if (dwErr == ERROR_INVALID_PARAMETER)
    cout << TEXT("参数不正确") << endl;
   break;
  case 1:
   cout << TEXT("读取时间失败") << endl;
   break;
  case 2:
   cout << TEXT("设置超时时间失败") << endl;
  }
 }
 if (hComm != INVALID_HANDLE_VALUE)
  CloseHandle(hComm);
 return 0;
}
【运行结果】

时间为:
2016年07月17日
19:04:25 星期日

一派護法 十九級
8樓 發表于:2016-7-17 22:20

这是每次开机后第一次打开串口时默认的串口配置,可见设备管理器中的串口波特率的设置是形同虚设的,虽然里面设置的是9600,但是默认打开的还是1200,而且每字节的位数默认是7而不是8。

执行SetCommState后,该串口的配置会自动保存,直到关机或注销。所以,如果你在程序A中将串口的波特率设为了9600的话,再在程序B中重新打开串口,无需执行SetCommState函数,波特率仍然是9600,而不是刚开机时的1200。因此有时候即使SetCommState函数执行失败了,后面的串口通讯代码也能正常执行,就是因为可能在运行之前用其他软件正确打开了串口而遗留了正确的串口配置,如果关机重启或注销,问题就会重新出现。

一派護法 十九級
9樓 發表于:2016-7-17 22:22
因此,7楼的代码还有一个很明显的漏洞。程序中只设置了DCB结构体中的DCBlength、BaudRate、ByteSize、fParity和StopBits这五个成员变量。如果有搞破坏的程序恶意设置了DCB中的其他成员变量并保存了,那么再次运行这个程序,串口通讯可能就不能正常进行。
一派護法 十九級
11樓 發表于:2016-7-17 22:47

【获取设备管理器中设置的串口波特率的方法(包括完整的DCB结构体)】
COMMCONFIG cfg;
ZeroMemory(&cfg, sizeof(cfg));

DWORD dwSize = sizeof(cfg);
GetDefaultCommConfig(TEXT("COM10"), &cfg, &dwSize); // 这里不能写成\\\\.\\COM10
cout << "波特率: " << cfg.dcb.BaudRate << endl;

 

一派護法 十九級
12樓 發表于:2016-7-17 22:48
if (!SetCommState(hComm, &cfg.dcb))
    cout << TEXT("设置失败") << endl;
然后在调用SetCommState函数时传入设备管理器里面的dcb就要安全很多。
一派護法 十九級
14樓 發表于:2016-7-18 16:53

【直接填写结构体法】

DCB dcb;
ZeroMemory(&dcb, sizeof(dcb));
dcb.DCBlength = sizeof(DCB);
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.fBinary = TRUE; // 必须是二进制方式传输
if (!SetCommState(hComm, &dcb))
   cout << TEXT("设置失败") << endl;

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:561 回複數:11
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2016-7-18 16:53
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。