設置 | 登錄 | 註冊

目前共有12篇帖子。

【程式】串口操作程式

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;

 

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。