Windows下串口编程与单片机串口设备通信(win32

Windows下串口编程与单片机串口设备通信(win32

一、前言

串行通信接口,通常简称为“串口”,是一种数据传输方式,其中信息以连续的比特流形式发送,每个比特在不同的时间点被传输。这与并行通信形成对比,在并行通信中,多个比特同时通过多个线路传输。串口通信因其简单的硬件需求和广泛的应用场景而受到青睐,尤其是在远程通信、设备控制、数据采集等领域。

image-20240715144416067image-20240715144518138串口通信在现代技术中的应用场景极为广泛,从个人电脑连接外设(如鼠标、键盘)到工业自动化系统中的传感器网络,从移动设备的数据同步到实验室设备的控制,都能见到其身影。在嵌入式系统开发中,单片机与PC机或其他设备之间的通信经常采用串口,因为其易于实现且成本低廉。

在Windows环境下使用C语言进行串口编程,主要涉及到对Windows API函数的调用。Windows提供了丰富的API用于串口通信,包括CreateFile、SetupComm、PurgeComm、SetCommState、SetCommTimeouts、ReadFile、WriteFile等,这些函数分别用于打开串口、设置串口参数、读写串口数据以及控制串口的输入输出缓冲区等。

下面示例,展示如何使用C语言和Windows API打开指定的串口并进行通信:

代码语言:javascript复制#include

#include

int main() {

HANDLE hComm;

DCB dcbSerialParams = {0};

COMMTIMEOUTS timeouts;

// 打开串口

hComm = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (hComm == INVALID_HANDLE_VALUE) {

printf("无法打开串口。\n");

return -1;

}

// 设置串口参数

dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

GetCommState(hComm, &dcbSerialParams);

dcbSerialParams.BaudRate = CBR_9600; // 设置波特率

dcbSerialParams.ByteSize = 8; // 设置字节大小

dcbSerialParams.StopBits = ONESTOPBIT; // 设置停止位

dcbSerialParams.Parity = NOPARITY; // 设置校验位

SetCommState(hComm, &dcbSerialParams);

// 设置超时时间

timeouts.ReadIntervalTimeout = MAXDWORD;

timeouts.ReadTotalTimeoutMultiplier = 0;

timeouts.ReadTotalTimeoutConstant = 500;

timeouts.WriteTotalTimeoutMultiplier = 0;

timeouts.WriteTotalTimeoutConstant = 500;

SetCommTimeouts(hComm, &timeouts);

// 发送数据

char data[] = "Hello from PC!";

DWORD dwWritten;

WriteFile(hComm, data, strlen(data), &dwWritten, NULL);

// 接收数据

char buffer[256];

DWORD dwRead;

ReadFile(hComm, buffer, sizeof(buffer), &dwRead, NULL);

buffer[dwRead] = '\0'; // 确保字符串以空字符结尾

printf("Received: %s\n", buffer);

// 关闭串口

CloseHandle(hComm);

return 0;

}这段代码展示了如何打开一个串口(例如COM3),设置其通信参数,然后向串口发送数据,并从串口接收数据。通过这样的程序设计,可以实现PC机与单片机或其他串口设备之间的双向通信,为数据交换、设备控制等应用提供基础。

串口通信是连接不同设备之间的一种基本而强大的手段,尤其在嵌入式系统领域。掌握Windows环境下的串口编程,对于从事相关领域的开发者来说至关重要。

二、实操代码2.1 串口编程的函数详解在Windows环境下进行串口编程时,主要依赖于Windows API中的一系列函数。这些函数允许你控制串口的打开、配置、读写操作以及错误处理。下面是几个关键函数的详细说明,包括它们的功能、参数含义和用法:

1. CreateFile功能:打开或创建一个指定的设备或文件。

语法:

代码语言:javascript复制HANDLE CreateFile(

LPCWSTR lpFileName, // 指定文件名或设备名

DWORD dwDesiredAccess, // 请求的访问类型

DWORD dwShareMode, // 共享模式

LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全属性

DWORD dwCreationDisposition, // 创建或打开的处置

DWORD dwFlagsAndAttributes, // 文件属性

HANDLE hTemplateFile // 模板文件句柄

);用法:

通常用于打开串口设备,如CreateFile(TEXT("COM1"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);2. CloseHandle功能:关闭一个已打开的设备或文件句柄。

语法:

代码语言:javascript复制BOOL CloseHandle(

HANDLE hObject // 要关闭的句柄

);用法:

在完成串口操作后调用以释放资源,如CloseHandle(hComm);3. GetCommState功能:获取串口当前的通信状态。

语法:

代码语言:javascript复制BOOL GetCommState(

HANDLE hFile, // 串口句柄

LPDCB lpDCB // 指向DCB结构体的指针

);用法:

用于获取串口的当前配置,如波特率、数据位数等。4. SetCommState功能:设置串口的通信状态。

语法:

代码语言:javascript复制BOOL SetCommState(

HANDLE hFile, // 串口句柄

LPDCB lpDCB // 指向DCB结构体的指针

);用法:

用于设置串口的配置参数,如波特率、数据位、停止位和奇偶校验。5. PurgeComm功能:清除串口的输入输出缓冲区。

语法:

代码语言:javascript复制BOOL PurgeComm(

HANDLE hFile, // 串口句柄

DWORD dwMask // 指定要清除的缓冲区

);用法:

用于清除串口的输入或输出缓冲区,避免数据残留。6. ReadFile功能:从串口读取数据。

语法:

代码语言:javascript复制BOOL ReadFile(

HANDLE hFile, // 串口句柄

LPVOID lpBuffer, // 数据缓冲区

DWORD nNumberOfBytesToRead, // 要读取的字节数

LPDWORD lpNumberOfBytesRead, // 实际读取的字节数

LPOVERLAPPED lpOverlapped // 异步读取时的重叠结构

);用法:

用于从串口读取数据到缓冲区中。7. WriteFile功能:向串口写入数据。

语法:

代码语言:javascript复制BOOL WriteFile(

HANDLE hFile, // 串口句柄

LPCVOID lpBuffer, // 数据缓冲区

DWORD nNumberOfBytesToWrite, // 要写入的字节数

LPDWORD lpNumberOfBytesWritten, // 实际写入的字节数

LPOVERLAPPED lpOverlapped // 异步写入时的重叠结构

);用法:

用于向串口发送数据。8. SetCommTimeouts功能:设置串口的超时值。

语法:

代码语言:javascript复制BOOL SetCommTimeouts(

HANDLE hFile, // 串口句柄

LPCOMMTIMEOUTS lpCommTimeouts // 指向COMMTIMEOUTS结构体的指针

);用法:

用于设置读写操作的超时时间,防止无限期等待。9. GetLastError功能:获取上一次调用失败的错误代码。

语法:

代码语言:javascript复制DWORD GetLastError(void);用法:

当API函数调用失败时,可以调用此函数获取具体的错误代码,帮助诊断问题。以上函数是进行串口编程时最常用的,它们共同提供了串口设备的完整控制能力。在实际编程中,你需要根据具体的应用需求选择合适的函数组合,以实现串口的高效稳定通信。

2.2 扫描当前系统可用串口端口在Windows环境下,使用C语言来枚举所有可用的串口,可以通过调用Windows API函数来实现。

以下代码,会打印出系统上所有可用的串口名称:

代码语言:javascript复制#include

#include

#include

// 定义一个结构体存储串口信息

typedef struct _SERIAL_INFO {

DWORD dwSize;

HANDLE hFile;

DWORD dwDeviceType;

DWORD dwReserved;

DWORD dwProviderSubType;

DWORD dwServiceCharacteristics;

DWORD dwVendorGuidData;

DWORD dwDriverVersion;

DWORD dwDriverDate;

DWORD dwHardwareIndex;

DWORD dwConfigFlags;

DWORD dwNumParameters;

DWORD dwNumProperties;

} SERIAL_INFO;

// 定义一个结构体存储串口属性

typedef struct _SERIAL_PROPERTY_KEY {

DWORD dwPropertyKey;

DWORD dwPropertyType;

DWORD dwReserved;

} SERIAL_PROPERTY_KEY;

int main() {

DWORD dwSize = 0;

DWORD dwRetVal = 0;

HANDLE hComm = NULL;

SERIAL_INFO SerialInfo;

SERIAL_PROPERTY_KEY SerialPropKey;

TCHAR szPortName[MAX_PATH];

DWORD dwBufferSize = 0;

DWORD dwBytesReturned = 0;

DWORD dwError = 0;

// 获取所需的SERIAL_INFO结构体大小

dwRetVal = QueryDosDevice(NULL, NULL, 0);

if (dwRetVal == 0) {

dwSize = GetLastError();

SerialInfo.dwSize = dwSize;

} else {

printf("QueryDosDevice failed with error: %ld\n", GetLastError());

return -1;

}

// 枚举所有的串口

for (int i = 1; i <= 256; i++) {

wsprintf(szPortName, TEXT("COM%d"), i);

dwRetVal = QueryDosDevice(szPortName, NULL, 0);

if (dwRetVal != 0) {

continue; // 如果返回非零,则跳过,表示端口不存在或不可用

}

dwError = GetLastError();

if (dwError != ERROR_INSUFFICIENT_BUFFER) {

continue; // 如果错误不是缓冲区不足,则跳过

}

// 如果是缓冲区不足,则获取正确的缓冲区大小

dwBufferSize = dwError;

if (dwBufferSize > 0) {

SerialInfo.dwSize = dwBufferSize;

dwRetVal = QueryDosDevice(szPortName, (LPTSTR)&SerialInfo, dwBufferSize);

if (dwRetVal != 0) {

// 成功获取串口信息,尝试打开串口

hComm = CreateFile(szPortName,

GENERIC_READ | GENERIC_WRITE,

0, NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

NULL);

if (hComm != INVALID_HANDLE_VALUE) {

// 打印可用的串口号

wprintf(L"Found COM port: %s\n", szPortName);

// 清理资源

CloseHandle(hComm);

}

}

}

}

return 0;

}这个代码片段会遍历从COM1到COM256的所有可能的串口号,尝试打开每一个串口,如果成功打开,则表明该串口是可用的,并将串口号打印出来。

2.3 创建串口程序与单片机进行数据互发通信下面是一个使用C语言在Windows环境下进行串口编程的例子,演示了如何与单片机进行数据互发通信。

创建一个程序,打开串口,设置波特率为115200,然后接收从单片机发送来的数据,将其打印出来,并将同样的数据返回给单片机。

代码语言:javascript复制#include

#include

#include

int main() {

HANDLE hComm;

DCB dcbSerialParams = {0};

COMMTIMEOUTS timeouts;

// 打开串口

hComm = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

if (hComm == INVALID_HANDLE_VALUE) {

printf("无法打开串口。\n");

return -1;

}

// 设置串口参数

dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

if (!GetCommState(hComm, &dcbSerialParams)) {

printf("无法获取串口状态。\n");

CloseHandle(hComm);

return -1;

}

dcbSerialParams.BaudRate = CBR_115200; // 设置波特率为115200

dcbSerialParams.ByteSize = 8; // 设置数据位为8位

dcbSerialParams.StopBits = ONESTOPBIT; // 设置停止位为1位

dcbSerialParams.Parity = NOPARITY; // 设置无校验位

if (!SetCommState(hComm, &dcbSerialParams)) {

printf("无法设置串口参数。\n");

CloseHandle(hComm);

return -1;

}

// 设置超时时间

timeouts.ReadIntervalTimeout = MAXDWORD;

timeouts.ReadTotalTimeoutMultiplier = 0;

timeouts.ReadTotalTimeoutConstant = 500;

timeouts.WriteTotalTimeoutMultiplier = 0;

timeouts.WriteTotalTimeoutConstant = 500;

if (!SetCommTimeouts(hComm, &timeouts)) {

printf("无法设置串口超时时间。\n");

CloseHandle(hComm);

return -1;

}

// 循环读取和回显数据

char buffer[256];

DWORD dwRead, dwWritten;

while (1) {

memset(buffer, 0, sizeof(buffer));

if (!ReadFile(hComm, buffer, sizeof(buffer)-1, &dwRead, NULL)) {

printf("读取数据失败。\n");

break;

}

if (dwRead > 0) {

printf("接收到: %s\n", buffer);

if (!WriteFile(hComm, buffer, dwRead, &dwWritten, NULL)) {

printf("写入数据失败。\n");

break;

}

}

}

// 清理资源

CloseHandle(hComm);

return 0;

}在这个例子中,使用CreateFile函数打开串口,然后通过GetCommState和SetCommState函数设置串口的波特率、数据位、停止位和校验位。接着,使用SetCommTimeouts函数设置读写操作的超时时间,以防在没有数据的情况下无限等待。

接下来,进入一个无限循环,使用ReadFile函数从串口读取数据。如果读取成功,将接收到的数据打印出来,并使用WriteFile函数将同样的数据返回到串口,实现回显功能。

相关推荐

胡歌代言最走心的三个广告 有你喜欢的吗?
Microsoft365个人版

胡歌代言最走心的三个广告 有你喜欢的吗?

📅 10-13 👁️ 6155
方舟生存进化龙怎么吃饲料槽 方舟生存进化怎么喂小龙龙蛋?
365足球打水封号还严重嘛

方舟生存进化龙怎么吃饲料槽 方舟生存进化怎么喂小龙龙蛋?

📅 01-28 👁️ 4003
海外华媒走进文成玉壶镇国际慢城侨韵街区
365足球打水封号还严重嘛

海外华媒走进文成玉壶镇国际慢城侨韵街区

📅 11-02 👁️ 987
什麼是端口443?關於HTTPS端口443的一切都在這裡
365足球打水封号还严重嘛

什麼是端口443?關於HTTPS端口443的一切都在這裡

📅 07-17 👁️ 8725
《魔兽世界》怀旧服烤鲑鱼配方介绍
Microsoft365个人版

《魔兽世界》怀旧服烤鲑鱼配方介绍

📅 07-08 👁️ 6980
树马齿苋养殖方法和注意事项
Microsoft365个人版

树马齿苋养殖方法和注意事项

📅 07-07 👁️ 8828
魔兽世界邮件多久收到(魔兽世界邮件多长时间能收到)
网站日志分析最完整实践
Microsoft365个人版

网站日志分析最完整实践

📅 08-11 👁️ 8698
秾李的拼音
365bet娱乐游戏

秾李的拼音

📅 01-20 👁️ 5301
国际EMS到国外几天到?影响时效因素有哪些?
Microsoft365个人版

国际EMS到国外几天到?影响时效因素有哪些?

📅 10-15 👁️ 5028
小米12S怎么取消上滑解锁
365bet娱乐游戏

小米12S怎么取消上滑解锁

📅 10-26 👁️ 7317
揭秘Windows网络限制解除攻略:轻松实现无障碍网络自由,告别速度瓶颈!