【问题标题】:Linux, failing to read from serial portLinux,无法从串口读取
【发布时间】:2014-08-23 20:31:05
【问题描述】:

我正在尝试通过 RS232 连接。

通讯参数:

*传输速率:1200 波特

*字符编码:8位ASCII

*奇偶校验:无

*停止位:1

命令由两个字节码组成,格式如下

传输格式

代码 + "FFh"

    Hex code

接收格式

代码 + "FFh"

    Hex code

我尝试了各种初始化,但仍然无法从端口读取任何内容,以下代码就是其中之一:

//RS232test.c
//Transmission rate: 1200 Baud
//8 bit, no parity 1 stop bits.
//Transmit format: CODE + "FFh"
//Receive format: CODE + "FFh"

//Last edited: 23/08/2014

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> 
#include <termios.h> 
#include <unistd.h> 
#include <string.h>
#include <sys/ioctl.h>


#define BAUDRATE B1200

#define MULTI "/dev/ttyS0"
//#define MULTI "/dev/ttyUSB0"

int open_port(struct termios *,struct termios *);
int setDtrRts(int, int);
void close_port(int, struct termios *);

int open_port(struct termios *tty, struct termios *tty_old)
//This opens the tty port for linux saves old port setting
//and saves the new ones
{
    int fd; //file descriptor   
    fd = open(MULTI, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror(MULTI);
        printf("failed to open port\n");
        exit(-1);
        }
    //get previous port settings so it can be restored on exit
    tcgetattr(fd,tty_old);
    //get port settings so they can be set
    tcgetattr(fd,tty);
    //Set baud rates to 1200
    cfsetispeed(tty, BAUDRATE);
    cfsetospeed(tty, BAUDRATE);
    //ICANON -choosing canonical input. 
    tty->c_lflag |= (ICANON);
    //tty->c_lflag &= ~(ISIG);
    //unselecting echo
    tty->c_lflag &= ~(ECHO | ECHOE);
    //CLOCAL - setting local mode, CREAD - enabling receiver
    tty->c_cflag |= (CLOCAL | CREAD);
    //close doesn't change signals
    tty->c_cflag &= ~HUPCL;
    //8N1 no parity 1 stop bit
    tty->c_cflag |= CS8;
    //tty->c_cflag &= ~(PARENB | CSTOPB | CSIZE);
    tty->c_cflag &= ~(PARENB | PARODD /**/| CSIZE);
    tty->c_cflag &= ~CSTOPB;
    //Raw output mode
    tty->c_oflag &= ~OPOST;
    //Enable hardware handshaking
    //tty->c_cflag &= ~CRTSCTS;
    //tty->c_iflag &= ~(IXON | IXOFF | IXANY); //*
    //Flushing communication buffer and changing port setting 
    //tcflush(fd, TCIFLUSH);
    //tcsetattr(fd, TCSANOW, tty);
    tcsetattr(fd, TCSAFLUSH, tty);
    setDtrRts(fd,1);
    return fd;
}

int setDtrRts(int fd, int set)
//sets or clears the dtr and rts
//the needs both set during operation
//otherwise it switches off after approx. 20 seconds
{
    int setbits; 
    setbits |= (TIOCM_DTR | TIOCM_RTS);
    if(set)
        return(ioctl(fd, TIOCMBIC, &setbits));
    else
        return(ioctl(fd, TIOCMBIS, &setbits));
}

void close_port(int fd, struct termios *tty_old)
//closing port
{
    //reset to old options
    //tcsetattrb(fd, TCSANOW, tty_old);
    setDtrRts(fd,0);
    close(fd);
}

int main(void)
{
    int fd=0; //system file number
    int buff_size; //no of characters in buffer
    int bytes; //no of bytes in buffer
    int ctr=0; //general counter
    char in_buffer[] = "F3\xFF";
    char out_buffer[255]; //serial character buffer
    //new port setting and a structure to keep the old ones
    struct termios tty,tty_old;
    //checking if root
    if (getuid()){
        printf("You must be root to use this program.\n");
        exit(-4);
    }
    printf("fd = %d\n",fd);
    //opening port for reading and writing
    fd = open_port(&tty,&tty_old);
    printf("fd = %d\n",fd);
    //flushing
    tcflush(fd,TCIOFLUSH);
    //sending command to serial port
    //strcpy(in_buffer, "F3\xFF"); //placing a command in the buffer
    printf("%s",in_buffer);
    if((buff_size = write(fd, in_buffer, strlen(in_buffer))) < 0){
        printf("Error while sending message\nBuffer contents:\t%s\n",in_buffer);
        return -2;
    }
    usleep(50000); //delay for 50ms
    out_buffer[0] = '\0';
    ioctl(fd,FIONREAD,&bytes);
    printf("\nThere are %d bytes in the buffer\n",bytes);
    if(bytes > 0) {
        //reading response from serial port
        if((buff_size = read(fd, out_buffer,sizeof(out_buffer))) < 0){
            printf("Error while reading message\n");
            return -3;
        } 
        //printing the decimal ASCII values of the response
        printf("Multimeter response:\t");
        while(out_buffer[ctr] != '\0')
        {
            printf("%i ",out_buffer[ctr]);
            ctr++;
        }
        printf("\n%s\n",out_buffer);
    } else printf("Buffer Empty\n");
    //wrap things up
    close_port(fd, &tty_old);
    exit(0);
}

程序输出如下:

fd = 0

fd = 3

F3.h

缓冲区中有0个字节

缓冲区空

在以前的帖子中玩弄了一些建议,但没有成功。

【问题讨论】:

    标签: c linux serial-port


    【解决方案1】:

    您描述的协议不是面向行的,因此使用规范输入模式是错误的。您应该使用旧的 raw 模式,定义为:

    tty->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                   | INLCR | IGNCR | ICRNL | IXON);
    tty->c_oflag &= ~OPOST;
    tty->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty->c_cflag &= ~(CSIZE | PARENB);
    tty->c_cflag |= CS8;
    

    在规范模式下,字符仅在收到 eol 后才可供读取以允许行编辑,这不是您想要的。 (参考:man termios)

    【讨论】:

    • 谢谢,我试试看。
    【解决方案2】:

    以下代码是我自己编写的,一直在我的嵌入式系统上运行。

    int wrap_uart_init(char *port, int baudrate, int datab, int parity, int stopb){
        int fd=open_and_init_tty(port);
        if (fd==-1) {
            perror("cannot open device");
            return fd;
        }
        set_baudrate(fd,baudrate);
        set_dataformat(fd,datab,parity,stopb);
        swflow_ctrl(fd);
        return fd;
    }
    

    像这样调用这个函数:

    int fd = wrap_uart_init("/dev/ttyUSB0", 1200, 8, 'N', 1);
    

    超级好用。物理层协议是什么甚至都不重要,只要 Linux 将设备掩码到 tty 文件中即可。

    #define _BSD_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <termios.h>
    #include <errno.h>
    
    static struct termios origin_attrs;
    static struct termios old_attrs;
    
    int open_tty(const char *dev){
        int fd=open(dev, O_RDWR | O_NOCTTY | O_NDELAY | O_SYNC);
        if (fd==-1)
            perror(FILE_OPEN_ERROR);
        if (fcntl(fd, F_SETFL, 0)<0){
            perror("fcntl error!");
            close(fd);
            fd=-1;
        }
        if (!isatty(STDIN_FILENO)){
            perror("standard input error! ");
            close(fd);
            fd=-1;
        }
        return fd;
    }
    
    void close_tty(int fd){
        if  (tcsetattr(fd,TCSANOW,&old_attrs)) { 
            perror(GET_ATTR_ERROR);
        }
        close(fd);
    }
    
    int open_and_init_tty(const char *dev){
        int fd=open_tty(dev);
        if (fd==-1){
            perror(FILE_OPEN_ERROR);
            return -1;
        }
        if  (tcgetattr(fd,&old_attrs)) { 
            perror(GET_ATTR_ERROR);
            return -1;
        }
        struct termios opt;
        int br=B9600;
        cfsetispeed(&opt, br);
        cfsetospeed(&opt, br); 
        opt.c_cflag = (CS8 | CLOCAL | CREAD);
        opt.c_iflag = (IGNPAR | IXON | IXOFF | IXANY);
        opt.c_lflag = 0;
        opt.c_oflag = 0;
        opt.c_cc[VTIME] = 30;
        opt.c_cc[VMIN] = 5;
        tcflush(fd, TCIOFLUSH);
        if (tcsetattr(fd,TCSANOW,&opt) != 0) {
            perror(SET_ATTR_ERROR);   
            return -1;
        }
        return fd;
    }
    
    int set_baudrate(int fd, int baud){
        int br;
        int status;
        struct termios opt;
        if (tcgetattr(fd, &opt)) {
            perror(GET_ATTR_ERROR);
            return -1;
        }
        switch (baud) {
            case 115200:    br=B115200;break;
    //      case 76800:     br=B76800; break;
            case 57600:     br=B57600; break;
            case 38400:     br=B38400; break;
            case 19200:     br=B19200; break;
            case 9600:  br=B9600;  break;
            case 4800:  br=B4800;  break;
            case 2400:  br=B2400;  break;
            case 1200:  br=B1200;  break;
            case 600:   br=B600;   break;
            case 300:   br=B300;   break;
            default:    perror("Wrong Baud rate!");
                    return -1;
        }
        tcflush(fd, TCIOFLUSH);
        cfsetispeed(&opt, br);
        cfsetospeed(&opt, br);   
        status = tcsetattr(fd, TCSANOW, &opt);  
        if (status) {        
            perror(BAUD_RATE_ERROR);  
            return -2;
        }
        tcflush(fd,TCIOFLUSH);   
        return 0;
    }
    
    int set_dataformat(int fd,int databits,int parity,int stopbits){ 
        struct termios options; 
        if  (tcgetattr(fd,&options)) { 
            perror(GET_ATTR_ERROR);     
            return -1;
        }
        options.c_cflag &= ~CSIZE; 
        switch (databits) {
        case 5:
            options.c_cflag |= CS5; 
            break;
        case 6:
            options.c_cflag |= CS6; 
            break;
        case 7: 
            options.c_cflag |= CS7; 
            break;
        case 8: 
            options.c_cflag |= CS8;
            break;   
        default:    
            perror("Unsupported data size");
            return -2;  
        }
        switch (parity) {
        case 'n':       //no parity check
        case 'N':
            options.c_cflag &= ~PARENB;
            options.c_iflag &= ~INPCK;
            break;  
        case 'o':       //odd check
        case 'O':
            options.c_cflag |= (PARODD | PARENB);
            options.c_iflag |= (INPCK | ISTRIP);
            break;  
        case 'e':       //even check
        case 'E':
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_iflag |= (INPCK | ISTRIP);
            break;
        default:   
            perror("Unsupported parity");    
            return -3;  
        }  
        switch (stopbits) {
        case 1:         //1 stop bit
            options.c_cflag &= ~CSTOPB;  
            break;  
        case 2:         //2 stop bits
            options.c_cflag |= CSTOPB;  
            break;
        default:    
            perror("Unsupported stop bits");  
            return -4; 
        } 
        tcflush(fd,TCIFLUSH);
        options.c_cc[VTIME] = 50;
        options.c_cc[VMIN] = 1;
        if (tcsetattr(fd,TCSANOW,&options) != 0) {
            perror(SET_ATTR_ERROR);   
            return -1;
        }
        return 0;
    }
    
    int set_databit(int fd, int databits){
        struct termios options; 
        if  (tcgetattr(fd,&options)) { 
            perror(GET_ATTR_ERROR);     
            return -1;
        }
        options.c_cflag &= ~CSIZE; 
        switch (databits) {
        case 5:
            options.c_cflag |= CS5; 
            break;
        case 6:
            options.c_cflag |= CS6; 
            break;
        case 7: 
            options.c_cflag |= CS7; 
            break;
        case 8: 
            options.c_cflag |= CS8;
            break;   
        default:    
            perror("Unsupported data size");
            return -1;  
        }
        tcflush(fd,TCIFLUSH);
        if (tcsetattr(fd,TCSANOW,&options) != 0) {
            perror(SET_ATTR_ERROR);   
            return -2;
        }
        return 0;
    }
    
    int set_parity(int fd, int parity) {
        struct termios options; 
        if  (tcgetattr(fd,&options)) { 
            perror(GET_ATTR_ERROR);     
            return -1;
        }
        options.c_cflag &= ~CSIZE; 
        switch (parity) {
        case 'n':       //no parity check
        case 'N':
            options.c_cflag &= ~PARENB;
            options.c_iflag &= ~INPCK;
            break;  
        case 'o':       //odd check
        case 'O':
            options.c_cflag |= (PARODD | PARENB);
            options.c_iflag |= (INPCK | ISTRIP);
            break;  
        case 'e':       //even check
        case 'E':
            options.c_cflag |= PARENB;
            options.c_cflag &= ~PARODD;
            options.c_iflag |= (INPCK | ISTRIP);
            break;
        default:   
            perror("Unsupported parity");    
            return -1;  
        }
        tcflush(fd,TCIFLUSH);
        if (tcsetattr(fd,TCSANOW,&options) != 0) {
            perror(SET_ATTR_ERROR);   
            return -2;
        }
        return 0;
    }
    
    int set_stopbit(int fd, int stopbits) {
        struct termios options; 
        if  (tcgetattr(fd,&options)) { 
            perror(GET_ATTR_ERROR);     
            return -1;
        }
        options.c_cflag &= ~CSIZE; 
        switch (stopbits) {
        case 1:         //1 stop bit
            options.c_cflag &= ~CSTOPB;  
            break;  
        case 2:         //2 stop bits
            options.c_cflag |= CSTOPB;  
            break;
        default:    
            perror("Unsupported stop bits");  
            return -1; 
        }
        tcflush(fd,TCIFLUSH);
        if (tcsetattr(fd,TCSANOW,&options) != 0) {
            perror(SET_ATTR_ERROR);   
            return -2;
        }
        return 0;
    }
    
    int hwflow_ctrl(int fd){
        struct termios options;
        if  (tcgetattr(fd,&options)) { 
            perror(GET_ATTR_ERROR);     
            return -1;
        }
        options.c_cflag |= CRTSCTS;
        options.c_iflag &= ~(IXON | IXOFF | IXANY);
        tcflush(fd,TCIFLUSH);
        if (tcsetattr(fd,TCSANOW,&options) != 0) {
            perror(SET_ATTR_ERROR);   
            return -2;
        }
        return 0;
    }
    
    int swflow_ctrl(int fd){
        struct termios options;
        if  (tcgetattr(fd,&options)) { 
            perror(GET_ATTR_ERROR);     
            return -1;
        }
        options.c_cflag &= ~CRTSCTS;
        options.c_iflag |= (IXON | IXOFF | IXANY);
        tcflush(fd,TCIFLUSH);
        if (tcsetattr(fd,TCSANOW,&options) != 0) {
            perror(SET_ATTR_ERROR);   
            return -2;
        }
        return 0;
    }
    
    int store_termios(int fd){
        if  (tcgetattr( fd,&origin_attrs)) { 
            perror(GET_ATTR_ERROR);
            return -1;
        }
        return 0;
    }
    int recover_termios(int fd){
        tcflush(fd, TCIOFLUSH);
        if (tcsetattr(fd,TCSANOW,&origin_attrs)) {
            perror(SET_ATTR_ERROR);
            return -1;
        }
        tcflush(fd, TCIOFLUSH);
        return 0;
    }
    

    【讨论】:

    • 感谢详细的回复和帮助。我将彻底了解您的包装,以便我可以将其用于我的项目。这很有帮助,我希望它能解决我当前和未来的问题。一切顺利,-G
    • 您的 open_and_init_tty() 例程完全破坏了整个先前版本的 termios 值,因此否定了 的正确符合 POSIX 的程序set_baudrate() 。因Serial Programming Guide for POSIX Operating Systems的应用不一致而被否决
    猜你喜欢
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-22
    • 1970-01-01
    相关资源
    最近更新 更多