需求

运行在windows平台下的C语言的命令行程序,实现每分钟向串口COM3端口发送时间字符串,格式为4位年+2位月+2位日+1位星期(从1-7)+2位小时(24小时制)+2位分钟+2位秒

代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

// 根据日期计算星期,参考 https://stackoverflow.com/questions/6054016/c-program-to-find-day-of-week-given-date
int get_weekday(int year, int month, int day) {
    int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    year -= month < 3;
    return (year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;
}

// 格式化时间字符串,参考 https://stackoverflow.com/questions/5141960/get-the-current-time-in-c
void format_time(char *output) {
    time_t rawtime;
    struct tm *timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    int year = timeinfo->tm_year + 1900;
    int month = timeinfo->tm_mon + 1;
    int day = timeinfo->tm_mday;
    int weekday = get_weekday(year, month, day);
    int hour = timeinfo->tm_hour;
    int minute = timeinfo->tm_min;
    int second = timeinfo->tm_sec;
    sprintf(output, "%04d%02d%02d%d%02d%02d%02d", year, month, day, weekday, hour, minute, second);
}

// 打开串口,参考 https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/configuration-of-com-ports
HANDLE open_serial_port() {
    HANDLE hSerial = CreateFile("\\\\.\\COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if (hSerial == INVALID_HANDLE_VALUE) {
        printf("Error opening serial port.\n");
        exit(1);
    }
    return hSerial;
}

// 设置串口参数,参考 https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-serialport
void set_serial_port(HANDLE hSerial) {
    DCB dcbSerialParams = {0};
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(hSerial, &dcbSerialParams)) {
        printf("Error getting serial port state.\n");
        exit(1);
    }
    dcbSerialParams.BaudRate = CBR_9600;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;
    if (!SetCommState(hSerial, &dcbSerialParams)) {
        printf("Error setting serial port state.\n");
        exit(1);
    }
}

// 向串口发送数据,参考 https://support.microsoft.com/en-us/topic/howto-specify-serial-ports-larger-than-com9-db9078a5-b7b6-bf00-240f-f749ebfd913e
void send_data(HANDLE hSerial, char *data) {
    DWORD dwBytesWritten = 0;
    if (!WriteFile(hSerial, data, strlen(data), &dwBytesWritten, NULL)) {
        printf("Error writing to serial port.\n");
        exit(1);
    }
}

// 关闭串口
void close_serial_port(HANDLE hSerial) {
    CloseHandle(hSerial);
}

// 主函数
int main() {
    HANDLE hSerial = open_serial_port();
    set_serial_port(hSerial);
    char time_str[16];
    while (1) {
        format_time(time_str);
        send_data(hSerial, time_str);
        printf("Sent: %s\n", time_str);
        Sleep(60000); // 暂停一分钟
    }
    close_serial_port(hSerial);
    return 0;
}

原理

  • 包含了一些头文件,比如stdio.h, stdlib.h, time.h和windows.h,这些文件提供了一些标准的函数和类型,比如printf, time_t, HANDLE等。
  • 然后,定义一个函数get_weekday,它根据给定的年月日,计算出星期几,从1到7分别代表周一到周日。它使用了一个公式,参考了一个网页的回答。
  • 接着,定义一个函数format_time,它获取当前的时间,然后格式化成一个字符串,格式为4位年+2位月+2位日+1位星期(从1-7)+2位小时(24小时制)+2位分钟+2位秒。它使用了time函数,localtime函数和sprintf函数,参考了另一个网页的回答。
  • 然后,定义一个函数open_serial_port,它打开串口COM3,并返回一个句柄,用于后续的操作。它使用了CreateFile函数,参考了微软的文档。
  • 接着,定义一个函数set_serial_port,它设置串口的参数,比如波特率,数据位,停止位和校验位。它使用了GetCommState函数和SetCommState函数,参考了另一个微软的文档。
  • 然后,定义一个函数send_data,它向串口发送数据,也就是时间字符串。它使用了WriteFile函数,参考了另一个微软的文档。
  • 接着,定义一个函数close_serial_port,它关闭串口。它使用了CloseHandle函数。
  • 最后,定义一个主函数,它首先打开串口,然后设置串口,然后进入一个无限循环,每次循环中,它格式化当前时间,然后发送到串口,然后打印到屏幕,然后暂停一分钟。当循环结束时,它关闭串口,然后返回0。
Last modification:February 16, 2023
If you think my article is useful to you, please feel free to appreciate