主题 : linux串口非标准波特率怎么实现 复制链接 | 浏览器收藏 | 打印
级别: 新手上路
UID: 4264
精华: 0
发帖: 43
金钱: 215 两
威望: 43 点
综合积分: 86 分
注册时间: 2009-03-07
最后登录: 2016-05-24
楼主  发表于: 2012-07-05 09:11

 linux串口非标准波特率怎么实现

#include <termios.h>
    #include <sys/ioctl.h>
    #include <Linux/serial.h>
    struct serial_t {  
        int     fd;  
        char    *device;/*/dev/ttyS0,...*/  
        int     baud;  
        int     databit;/*5,6,7,8*/  
        char    parity;/*O,E,N*/  
        int    stopbit;/*1,2*/  
        int    startbit;/*1*/  
        struct termios    options;  
    };


int serial_set_speci_baud(struct serial_t *tty,int baud)  
    {  
        struct serial_struct ss,ss_set;  
        cfsetispeed(&tty->options,B38400);  
        cfsetospeed(&tty->options,B38400);  
        tcflush(tty->fd,TCIFLUSH);/*handle unrecevie char*/  
        tcsetattr(tty->fd,TCSANOW,&tty->options);  
        if((ioctl(tty->fd,TIOCGSERIAL,&ss))<0){  
            dprintk("BAUD: error to get the serial_struct info:%s\n",strerror(errno));  
            return -1;  
        }  
        ss.flags = ASYNC_SPD_CUST;  
        ss.custom_divisor = ss.baud_base / baud;  
        if((ioctl(tty->fd,TIOCSSERIAL,&ss))<0){  
            dprintk("BAUD: error to set serial_struct:%s\n",strerror(errno));  
            return -2;  
        }  
        ioctl(tty->fd,TIOCGSERIAL,&ss_set);  
        dprintk("BAUD: success set baud to %d,custom_divisor=%d,baud_base=%d\n",  
                baud,ss_set.custom_divisor,ss_set.baud_base);  
        return 0;  
    }


static struct serial_t __seri_conf[] = {  
        [0] = {//connect with b board, ttyS0  
            .device = "/dev/ttyS0",  
            .baud = 28800,  
            .databit = 8,  
            .parity = 'N',  
            .stopbit = 1,  
        },  
    };

在网上查的这样的代码,但是程序下载到6410中运行到if((ioctl(tty->fd,TIOCSSERIAL,&ss))<0){  
            dprintk("BAUD: error to set serial_struct:%s\n",strerror(errno));  
            return -2;  就退出来了。
请教各位大虾指点一下
级别: 新手上路
UID: 4264
精华: 0
发帖: 43
金钱: 215 两
威望: 43 点
综合积分: 86 分
注册时间: 2009-03-07
最后登录: 2016-05-24
1楼  发表于: 2012-07-05 09:27
没有人弄过这方面的吗?
级别: 新手上路
UID: 4264
精华: 0
发帖: 43
金钱: 215 两
威望: 43 点
综合积分: 86 分
注册时间: 2009-03-07
最后登录: 2016-05-24
2楼  发表于: 2012-07-05 09:29
我的意思是我要实现串口波特率为5787bps,有什么办法啊?
级别: 新手上路
UID: 4264
精华: 0
发帖: 43
金钱: 215 两
威望: 43 点
综合积分: 86 分
注册时间: 2009-03-07
最后登录: 2016-05-24
3楼  发表于: 2012-07-05 11:40
弄好了,有同样问题的请参考这个。






最近在开发板上准备使用非标准波特率的串口处理通讯,但linux下的串口设置总是报错,于是打起更改驱动的主意,开源世界就是自由自在,更改驱动是个艰辛的过程,整的内核经常down掉,但最后终于有些进展,达到目标。

作者Kozmers  kozmers@yahoo.com http://www.kozmers.com

配置:

ARM9 : S3C2440

系统 : linux-3.0.4



1. 设置非标准波特率的应用程序

需要使用ioctl()

#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/serial.h>
#include <sys/ioctl.h>
#include <termios.h>

#include <string.h>
#include <errno.h>



set_baud(int fd, int baud)
{
        int   status;
        struct termios   Opt;
        struct serial_struct Serial;

        tcgetattr(fd, &Opt);      
        tcflush(fd, TCIOFLUSH);
printf("\ncfsetispeed(&Opt,B38400)\n");

        cfsetispeed(&Opt, B38400);
        cfsetospeed(&Opt, B38400);
        tcflush(fd,TCIOFLUSH);      
        status = tcsetattr(fd, TCSANOW, &Opt);
        if  (status != 0)
        {
                perror("tcsetattr fd1");
                return;
        }

        if((ioctl(fd,TIOCGSERIAL,&Serial))<0)
        {
                printf("Fail to get Serial!\n");
                return;
        }
        printf("TIOCGSERIAL: OK.\n");
Serial.flags = ASYNC_SPD_CUST;
        Serial.custom_divisor=Serial.baud_base/baud;
        printf("divisor is %x\n",Serial.custom_divisor);
        if((ioctl(fd,TIOCSSERIAL,&Serial))<0)
        {
                printf("Fail to set Serial\n");
                return;
        }

        printf("TIOCSSERIAL: OK.\n");

        ioctl(fd,TIOCGSERIAL,&Serial);
   printf("\nBAUD: success set baud to %d,custom_divisor=%d,baud_base=%d\n",baud
,Serial.custom_divisor,Serial.baud_base);
}

int
main(int argc, char **argv)
{
      int nonstd_baud = 11000;

  
    if(argc !=2)
    {
        printf("Usage: elf /dev/ttyS0");
        return (-1);
    }
  
  
    int fd = open(argv[1], O_RDWR | O_NOCTTY ); // | O_NDELAY);
    if(-1 == fd)
    {
        perror("Open");
        return -1;
    }
  
        set_baud(fd, 10400);
  
    close(fd);
    return 0;
}

kozmers kozmers@yahoo.com http://www.kozmers.com

注意这里需要先设置波特率到38400,然后设置flag为custom,最后用ioctl函数设定serial struct结构给驱动。

2. 那么驱动中是如何接受这个ioctl呢。

kozmers kozmers@yahoo.com http://www.kozmers.com

S3C2440的UART驱动在内核中是drivers/tty/serial/s3c2440.c

而它尽实现了设备注册等等相关的方法和时钟设定方法。

处理串口初始化和设定波特率的真正代码在drivers/tty/serial/samsung.c中,每个函数都以s3c24xx开头,被具体的s3c2440.c中调用。而samsung.c中也未实现ioctl,这部分是被serialc_core.c中统一完成的。

我们知道设定串口波特率的真正方式是向寄存器中设定时钟和分频。

那么就要确定好初始化时候的时钟选择了什么,如何根据目标波特率得到合理的分频值。

分别为port->uartclk和custom_divisor。

原有的驱动在init_port函数中仅对port->uartclk=1。经测试无法完成后续自定义目标。

kozmers kozmers@yahoo.com http://www.kozmers.com

于是做此更改:

drivers/tty/serial/samsang.c

kozmers kozmers@yahoo.com http://www.kozmers.com



init_port{

port->uartclk        = clk_get_rate(clk_get(&platdev->dev, "pclk")); // 使用pclk作为uartclk时钟。
// port->uartclk = 1; // 将原来的注释掉。
...
}

kozmers kozmers@yahoo.com http://www.kozmers.com

但完成后编译好并调试,使用应用程序后内核就挂掉了,原因是17号空指针,定义在set_termios函数中的serial_set_source方法。

经查找发现判断38400波特率后绕开了clksrc的赋值,clksrc仍然为NULL,下边还把它传递给port设定,当然不可。于是将set_source包含在非38400的else里,如下:

kozmers kozmers@yahoo.com http://www.kozmers.com

set_termios{
...

if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
  quot = port->custom_divisor;
else{ // kozmers modify
  quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);


if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
  dbg("selecting clock %p\n", clk);

  s3c24xx_serial_setsource(port, clksrc);

...

}
}
kozmers kozmers@yahoo.com http://www.kozmers.com



这次再编译,在开发板上运行就OK了。非标准波特率使用成功。

总结:在内核中更改一个文件drivers/tty/serial/samsung.c

1.将init_port中的port->uartclk = clk_get_rate(clk_get(&platdev->dev, "pclk"));

2.将set_termios中的if判断的else包含内容扩宽,更改为:

if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
      quot = port->custom_divisor;
else{ // kozmers modify
      quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);


      if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
           dbg("selecting clock %p\n", clk);

      s3c24xx_serial_setsource(port, clksrc);

...

}
kozmers kozmers@yahoo.com http://www.kozmers.com