linux下串口的使用

本文详细介绍了在Linux系统中如何使用串口,包括打开、设置波特率、配置串口信息、读写数据以及注册和配置485串口。通过示例代码展示了如何操作设备文件来控制串口,并提到了在特定情况下处理串口通信问题的方法。

 

 

1、串口定义

       串行接口简称串口,也称串行通信接口(通常指COM接口),是采用串行通信方式的扩展接口。

2、Linux下的使用

Linux下操作、控制串口是通过操作设备文件进行的,可在/dev目录下看到串口设备文件,如ttyS0ttyS1等。在应该程序中操作串口可进行以下步骤:

  1. 打开串口

int comfd;

comfd = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NONBLOCK);

      if (comfd <0 )  

      {                 

             perror("Can't Open Serial Port");

             return -1;      

      }   

      else {

            return fd;

  }

  1. 设置串口波特率

set_baud(comfd , 9600); //设置波特率为9600

set_baud()函数定义如下:

int baud_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,

                    B38400, B19200, B9600, B4800, B2400, B1200, B300, };

int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,

                                 19200, 9600, 4800, 2400, 1200, 300, };

void set_baud(int fd, int baud){

      int  i;

      int  status;

      struct termios  Opt;

      tcgetattr(fd, &Opt);

      for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {

             if (baud== name_arr[i]) {   

                    tcflush(fd, TCIOFLUSH);   

                    cfsetispeed(&Opt, baud_arr[i]);

                    cfsetospeed(&Opt, baud_arr[i]); 

                    status = tcsetattr(fd, TCSANOW, &Opt);

                    if (status != 0) {      

                           perror("tcsetattr fd");

                           return;   

                    }  

                    tcflush(fd,TCIOFLUSH); 

             }

      }

}

  1. 设置串口信息

struct termios tio;

if ( tcgetattr( fd,&options) != 0) {

perror("SetupSerial 1");   

       return 0;

}

set_Parity(&tio,8,1,'N'); //设置数据位为8,停止位为1,无校验

set_Flag(&tio,10,0,0);//设置VTIME时间为10,VMIN大小为0,无流控

set_Parity函数如下:

void set_Parity(struct termios *tio,uint32_t databit,uint32_t stopbit,char parity)

{

       tio->c_cflag &= ~CSIZE;

       switch (databit) /*设置数据位数*/

       {

       case 7:

          tio->c_cflag |= CS7;

          break;

       case 8:

          tio->c_cflag |= CS8;

          break;

       default:

          fprintf(stderr,"Unsupported data size%d\n",databit);

          return;

       }

       switch (parity)

       {

       case 'n':

       case 'N':

          tio->c_cflag &= ~PARENB;   /* Clear parity enable */

          tio->c_iflag &= ~INPCK;     /* Enable parity checking */

          break;

       case 'o':

       case 'O':

          tio->c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/

          tio->c_iflag |= INPCK;             /* Disnable parity checking */

          break;

       case 'e':

       case 'E':

          tio->c_cflag |= PARENB;     /* Enable parity */

          tio->c_cflag &= ~PARODD;   /* 转换为偶效验*/

          tio->c_iflag |= INPCK;       /* Disnable parity checking */

          break;

       case 's':

       case 'S': /*as no parity*/

          tio->c_cflag &= ~PARENB;

          tio->c_cflag &= ~CSTOPB;

          break;

       default:

          fprintf(stderr,"Unsupported parity\n");

          return;

          }

       /* 设置停止位*/  

       switch (stopbit)

       {

       case 1:

          tio->c_cflag &= ~CSTOPB;

          break;

       case 2:

          tio->c_cflag |= CSTOPB;

          break;

       default:

          fprintf(stderr,"Unsupported stop bits\n");

          return;

       }

}

set_Flag()函数如下:

void set_Flag(struct termios *tio,uint32_t timeout,uint32_t bufout,uint32_t fcon)

{

       switch (fcon) /*设置数据流控*/

       {

       case 0:

          tio->c_cflag&=~CRTSCTS;

          break;

       case 1:

          tio->c_cflag |= CRTSCTS;

          break;

       default:

          fprintf(stderr,"Unsupported FlowControl\n");

          return;

       } 

       tio->c_lflag=0;

       tio->c_cflag|=CREAD|CLOCAL;

       tio->c_iflag|=IGNPAR;

       tio->c_cc[VTIME]=timeout;

       tio->c_cc[VMIN]=bufout;

}

注:函数中的VTIME指定了等待的时间,VMIN指定了读取字符的最小数量。它们不同组合地取值会得到不同的结果,分别如下:

(1)当VTIME>0,VMIN>0时。read调用将保持阻塞直到读取到第一个字符,读到了第一个字符之后开始计时,此后若时间到了VTIME或者时间未到但已读够了VMIN个字符则会返回;若在时间未到之前又读到了一个字符(但此时读到的总数仍不够VMIN)则计时重新开始。

(2)当VTIME>0,VMIN=0时。read调用读到数据则立即返回,否则将为每个字符最多等待VTIME时间。

(3) 当VTIME=0,VMIN>0时。read调用一直阻塞,直到读到VMIN个字符后立即返回。

(4)若在open或fcntl设置了O_NDELALY或O_NONBLOCK标志,read调用不会阻塞而是立即返回,那么VTIME和VMIN就没有意义,效果等同于与把VTIME和VMIN都设为了0。

  1. 使用串口读写数据:

    char iobuf[100];

read(comfd, iobuf, 50);//读50个子节数据

write(comfd, iobuf, 50);//写50个子节数据

  1. 关闭串口

close(comfd);

3、注册多个串口

以内核版本为Linux-2.6.36AT91SAM9260平台为例,内核默认注册了3个串口,一个终端调试串口ttyS0,两个232串口ttyS1ttyS2,现在再注册ttyS3ttyS4这两个串口,在内核源码/linux-2.6.36/arch/arm/mach-at91/board-sam9260ek.c中的__init ek_map_io()函数中添加以下红色字体:

       static void __init ek_map_io(void)

       {

              /* Initialize processor: 18.432 MHz crystal */

              at91sam9260_initialize(18432000);

              /* DGBU on ttyS0. (Rx & Tx only) */

              at91_register_uart(0, 0, 0);

              /* USART0 on ttyS1. (Rx, Tx, CTS, RTS, DTR, DSR, DCD, RI) */

              at91_register_uart(AT91SAM9260_ID_US0,1,ATMEL_UART_CTS| ATMEL_UART_RTS | ATMEL_UART_DTR | ATMEL_UART_DSR | ATMEL_UART_DCD | ATMEL_UART_RI);

              /* USART1 on ttyS2. (Rx, Tx, RTS, CTS) */

              at91_register_uart(AT91SAM9260_ID_US1,2,ATMEL_UART_CTS| ATMEL_UART_RTS);

              /* USART2 on ttyS3. (Rx, Tx) */

              at91_register_uart(AT91SAM9260_ID_US2, 3, 0);

              /* USART3 on ttyS4. (Rx, Tx, RTS) */

              at91_register_uart(AT91SAM9260_ID_US3, 4, ATMEL_UART_RTS);

              /* set serial console to ttyS0 (ie, DBGU) */

              at91_set_serial_console(0);

       }

    重新编译内核,下载到设备上,进入系统后,在/dev目录下会看到新生成的ttyS3ttyS4这两个设备节点,在应用程序中便可使用这两个串口。

4、设置485串口

   以设置ttyS4485串口为例,在内核源码/linux-2.6.36/drivers/serial/atmel_serial.catmel_set_termios()函数中添加以下红色字体:

       static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,

                                  struct ktermios *old)

       {

              unsigned long flags;

              unsigned int mode, imr, quot, baud;

              /* Get current mode register */

              mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL

                                          | ATMEL_US_NBSTOP | ATMEL_US_PAR);

              if (port->line==4){

                     mode = UART_GET_MR(port) & ~(ATMEL_US_USMODE);

                     mode |= ATMEL_US_USMODE_RS485;

              }

              baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);

              quot = uart_get_divisor(port, baud);

       .........................................................................

       .........................................................................

       }    

    在使用485串口传输数据中,测试发现有乱码的出现,需要把485串口DMA模式去掉,在/linux-2.6.36/arch/arm/mach-at91/at91sam9260_devices.c文件中把atmel_uart_data uart3_data结构中use_dma_txuse_dma_rx的值由1改为0,别外添加以下红色字体代码、在发送数据时增加延时处理,如以下代码所示:

static struct atmel_uart_data uart3_data = {

       .use_dma_tx   = 0,

       .use_dma_rx  = 0,

//20120301add

       .rs485            = {

                            .flags             = SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND|SER_RS485_RTS_BEFORE_SEND,

                            .delay_rts_before_send  =5,

                            .delay_rts_after_send    = 5,

       },

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值