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

本文介绍了在Linux环境下,针对S3C2440 ARM9系统,如何通过修改内核驱动来实现非标准波特率的串口通信。作者详细讲述了从应用程序层面使用ioctl()函数设置波特率,到驱动层面如何接收和处理ioctl请求,以及遇到的问题和解决方法,包括更新初始化时的时钟选择和修复set_termios函数中的空指针错误。最终实现了非标准波特率的成功设置。
摘要由CSDN通过智能技术生成

弄好了,有同样问题的请参考这个。

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

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

配置:

ARM9 : S3C2440

系统 : linux-3.0.4

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

需要使用ioctl()

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值