首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

嵌入式串口重定向實現:基於Termios的雙向透傳與控制台切換

     編輯:關於C語言

嵌入式串口重定向實現:基於Termios的雙向透傳與控制台切換

在嵌入式Linux系統中,實現串口重定向可將標準輸入輸出(stdin/stdout)重映射到指定UART,實現遠程控制台、日志透傳、調試命令下發。本文解析基於 termios 的串口配置、雙線程雙向透傳、標準流重定向、退出序列檢測的完整實現代碼。

一、串口參數配置 setOption()

封裝 termios 結構體,支持波特率、數據位、校驗位、停止位、模式(規範/非規範)靈活配置:

int setOption(int fd, int nSpeed, int nBits, char mode, char nEvent, int nStop) {
    struct termios newtio;
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag |= CLOCAL | CREAD;  // 忽略調制解調器狀態、使能接收
    newtio.c_cflag &= ~CSIZE;
    // 數據位
    switch(nBits) { case 7: newtio.c_cflag|=CS7; break; case 8: newtio.c_cflag|=CS8; break; }
    // 模式:S=規範/回顯, N/M=非規範/無回顯/原始
    switch(mode) {
        case 'S': newtio.c_lflag|=ICANON|ECHO; newtio.c_cc[VMIN]=0; newtio.c_cc[VTIME]=0; break;
        case 'N': case 'M': newtio.c_lflag&=~(ICANON|ECHO); newtio.c_lflag|=ECHOE;
                           newtio.c_oflag|=ONLCR|OPOST; newtio.c_cc[VMIN]=1; newtio.c_cc[VTIME]=0; break;
    }
    // 校驗位
    switch(nEvent) {
        case 'O': newtio.c_cflag|=PARENB|PARODD; newtio.c_iflag|=INPCK|ISTRIP; break;
        case 'E': newtio.c_cflag|=PARENB; newtio.c_cflag&=~PARODD; newtio.c_iflag|=INPCK|ISTRIP; break;
        case 'N': newtio.c_cflag&=~PARENB; break;
    }
    // 波特率
    speed_t spd = B9600;
    switch(nSpeed) { case 2400:spd=B2400;break; case 4800:spd=B4800;break; case 9600:spd=B9600;break; case 115200:spd=B115200;break; }
    cfsetispeed(&newtio, spd); cfsetospeed(&newtio, spd);
    // 停止位
    if(nStop==1) newtio.c_cflag&=~CSTOPB; else if(nStop==2) newtio.c_cflag|=CSTOPB;
    tcflush(fd, TCIFLUSH);
    return tcsetattr(fd, TCSANOW, &newtio);
}

關鍵點: 非規範模式(VMIN=1, VTIME=0)下 read() 阻塞直到至少1字節,適合實時命令交互。

二、退出清理與標準流還原 exitBmconsole()

void exitBmconsole(int signum) {
    tcflush(console_fd, TCIFLUSH); tcflush(uart3_fd, TCIFLUSH);
    resetConsoleConfig();  // 還原控制台屬性
    dup2(oldstdin, 0);     // stdin 還原
    dup2(oldstdout, 1);    // stdout 還原
    close(uart3_fd); close(console_fd);
    muxClearAll(i2cfd_mux, MUX_SLAVE_ADDR); close(i2cfd_mux);
    char muxName[5]; sprintf(muxName,"MUX%d",gMuxId); setInUseFlag(muxName,0);
    exit(0);
}

保存原始 stdin/stdout 文件描述符(oldstdin/oldstdout),退出時用 dup2 還原,確保不影響父進程/其他終端。

三、UART3 → Console 透傳線程 MuxOutputToCom5()

void *MuxOutputToCom5(void) {
    setOption(uart3_fd, 115200, 8, 'N', 'N', 1);
    char buff[MAX_BUFF_LEN+1];
    while(1) {
        memset(buff,0,sizeof(buff));
        int nread = read(uart3_fd, buff, MAX_BUFF_LEN);
        if(nread > 0) {
            if(buff[0]==10 && buff[1]==10)  // 雙換行符特殊處理
                write(console_fd, &buff[1], nread);
            else
                write(console_fd, buff, nread);
        }
    }
}

雙換行符(0x0A 0x0A)作為特殊協議標記,去除首個換行符再轉發,可能用於幀同步。

四、Console → UART3 透傳 + 退出序列檢測 Com5InputToMux()

void *Com5InputToMux(void) {
    freopen("/usr/std.in","w+",stdin);   // 重定向 stdin 到文件/設備
    freopen("/usr/std.out","w+",stdout); // 重定向 stdout
    setOption(console_fd, 115200, 8, 'M', 'N', 1);  // 非規範模式
    char buff[MAX_BUFF_LEN+1], inputBuff[TEMP_ST_LEN+1];
    int inputNumber=0, recievExitFlag=0;
    while(1) {
        memset(buff,0,sizeof(buff));
        int nread = read(console_fd, buff, MAX_BUFF_LEN);
        if(nread > 0) {
            // 退出序列檢測:\q + 回車
            if(recievExitFlag) {
                if(!(nread==1 && ((inputNumber==1 && buff[0]!=LOWER_Q_ASCII) || (inputNumber==2 && buff[0]!=CR_ASCII)))) {
                    inputNumber=0; recievExitFlag=0; memset(inputBuff,0,sizeof(inputBuff));
                }
            }
            if(nread == 1) {
                if(buff[0] == CR_ASCII) {  // 回車
                    if(inputNumber==2 && inputBuff[0]==QUIT_IDENTIFY_ASCII && inputBuff[1]==LOWER_Q_ASCII)
                        exitBmconsole(0);  // 觸發退出
                    inputNumber=0; recievExitFlag=0; memset(inputBuff,0,sizeof(inputBuff));
                }
                if(inputNumber > MAX_BUFF_LEN) { inputNumber=0; memset(inputBuff,0,sizeof(inputBuff)); }
                if(buff[0] == QUIT_IDENTIFY_ASCII) recievExitFlag=1;  // 檢測到 \q
                if(recievExitFlag) inputBuff[inputNumber++] = buff[0];
            }
            if(recievExitFlag != 1) write(uart3_fd, buff, nread);  // 非退出序列字符透傳
        }
    }
}

退出協議: 輸入 \q (0x5C 0x71) 後跟回車 (0x0D) 觸發安全退出。狀態機 recievExitFlag + inputBuff 實現序列檢測,防止誤觸發。

五、架構總結與注意事項

  1. 雙線程雙向管道:MuxOutputToCom5 (UART→Console) + Com5InputToMux (Console→UART) 並行運行。
  2. 標準流劫持:freopen 將 stdin/stdout 重定向,使 printf/scanf 直接走串口。
  3. Termios 非規範模式:逐字節即時讀取,無需回車確認,適合交互式命令行。
  4. 資源管理:退出時還原標準流、關閉文件描述符、清理 MUX 狀態,避免資源洩漏。
  5. 協議設計:雙換行符同步、退出序列狀態機,體現嵌入式通訊的健壯性考慮。

此代碼可直接移植到基於STM32/ARM Linux的網關、DTU、工控機項目,實現遠程串口調試、固件升級、AT命令透傳等功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved