当前位置:首页 » 操作系统 » linuxuart

linuxuart

发布时间: 2022-10-22 13:22:22

❶ 如何实现在linux系统上直接控制串口RTS信号电

嵌入式开发板到手后一般都会开放串口,对串口的操作可以说是最基本的操作,那么拿到开发板后怎样操作串口呢?怎样利用串口向外发送数据呢?

我做的是一个通过串口来发送一串字符,实现NBIot的传输,系统是linux系统下的,发送的数据需要是16进制的数据。以下是c语言开发串口的相关代码记录。
1.找到串口的设备号
在linux系统下,所有的设备都是文件,所以要先找到串口这个设备文件,之后就可以对这个串口进行操作。
#define UART_DEV "/dev/ttyAMA2"
2.打开串口并初始化串口

串口的打开只需要一个open函数就可以打开,下面有一个初始化的函数,先将串口打开,如果打开成功,会返回一个设备描述符。

❷ linux 内核 uart driver 只有fifo满才向用户buf传递数据吗

对于串口驱动的移植准备自己分析一下源代码的,但是发现自己好多地方都只知道一 些皮毛,不明白其中的道理,所以我上网搜的时候发现有好多人写了很多很好的文章了,下面我转载的这篇就非常不错,一个困恼我好久的问题是驱动代码中只是注 册了platform驱动,而platform设备注册在哪里?这个问题困恼我好久,源代码中一直没找到,下面文章就解决了这个问题。当然文章中详细了讲 述了很多细节的知识。
原文地址 http://chxxxyg.blog.163.com/blog/static/150281193201082044140894/

(1)串口移植
S3C2440共有3个串口,在SMDK2440平台上串口0和串口1都作为普通串口使用,串口2工作在红外收发模式。TQ2440开发板将它们都作为普通串口,目前所需要的只有串口0,作为控制终端,所以此处不作修改。
在文件 linux/arch/arm/plat-s3c24xx/devs.c中定义了三个串口的硬件资源。
static struct resource s3c2410_uart0_resource[] = {
………………………………
};
static struct resource s3c2410_uart1_resource[] = {
………………………………
};
static struct resource s3c2410_uart2_resource[] = {
………………………………
};
在文件linux/arch/arm/plat-samsung/dev-uart.c中定义了每个串口对应的平台设备。
static struct platform_device s3c24xx_uart_device0 = {
.id = 0,
};
static struct platform_device s3c24xx_uart_device1 = {
.id = 1,
};
static struct platform_device s3c24xx_uart_device2 = {
.id = 2,
};
在文件linux/arch/arm/mach-s3c2440/mach-smdk2440.c中有串口一些寄存器的初始化配置。
static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = {
[0] = {
…………………………
},
[1] = {
…………………………
},
/* IR port */
[2] = {
…………………………
}
};
在文件linux/arch/arm/mach-s3c2440/mach-smdk2440.c中将调用函数
s3c24xx_init_uarts()最终将上面的硬件资源,初始化配置,平台设备整合到一起。
在文件 linux/arch/arm/plat-s3c/init.c中有
static int __init s3c_arch_init(void)
{
………………………………
ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
return ret;
}
这个函数将串口所对应的平台设备添加到了内核。
(2)串口设备驱动原理浅析
我认为任何设备在linux中的实现就“两条线”。一是设备模型的建立,二是读写数据流。串口驱动也是这样。
串口设备模型建立:
串口设备驱动的核心结构体在文件linux/drivers/serial/samsuing.c中如下
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
.nr = CONFIG_SERIAL_SAMSUNG_UARTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
串口驱动的注册
static int __init s3c24xx_serial_modinit(void)
{
………………………………
ret = uart_register_driver(&s3c24xx_uart_drv);
………………………………
}
串口驱动其实是一个典型的tty驱动
int uart_register_driver(struct uart_driver *drv)
{
………………………………
//每一个端口对应一个state
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
………………………………
normal = alloc_tty_driver(drv->nr); //分配该串口驱动对应的tty_driver
………………………………
drv->tty_driver = normal; //让drv->tty_driver字段指向这个tty_driver
………………………………
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
………………………………
//设置该tty驱动对应的操作函数集tty_operations (linux/drivers/char/core.c)
tty_set_operations(normal, &uart_ops);
………………………………
retval = tty_register_driver(normal); //将tty驱动注册到内核
………………………………
}
其实tty驱动的本质是一个字符设备,在文件 linux/drivers/char/tty_io.c中
int tty_register_driver(struct tty_driver *driver)
{
………………………………
cdev_init(&driver->cdev, &tty_fops);
driver->cdev.owner = driver->owner;
error = cdev_add(&driver->cdev, dev, driver->num);
………………………………
}
它所关联的操作函数集tty_fops在文件linux/drivers/char/tty_io.c中实现
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
………………………………
.open = tty_open,
………………………………
};
到此串口的驱动作为tty_driver被注册到了内核。前面提到串口的每一个端口都是作为平台设备被添加到内核的。那么这些平台设备就对应着有它们的平台设备驱动。在文件linux/drivers/serial/s3c2440.c中有:
static struct platform_driver s3c2440_serial_driver = {
.probe = s3c2440_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
.name = "s3c2440-uart",
.owner = THIS_MODULE,
},
};
当其驱动与设备匹配时就会调用他的探测函数
static int s3c2440_serial_probe(struct platform_device *dev)
{
return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
}
每一个端口都有一个描述它的结构体s3c24xx_uart_port 在 文件linux/drivers/serial/samsuing.c
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0, //该端口的中断号
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops, //该端口的操作函数集
.flags = UPF_BOOT_AUTOCONF,
.line = 0, //端口编号
}
},
………………………………

上面探测函数的具体工作是函数s3c24xx_serial_probe()来完成的
int s3c24xx_serial_probe(struct platform_device *dev,
struct s3c24xx_uart_info *info)
{
………………………………
//根据平台设备提供的硬件资源等信息初始化端口描述结构体中的一些字段
ret = s3c24xx_serial_init_port(ourport, info, dev);
//前面注册了串口驱动,这里便要注册串口设备
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
………………………………
}
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
………………………………
//前面说串口驱动是tty_driver,这里可以看到串口设备其实是tty_dev
tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);
………………………………
}
串口数据流分析:
在串口设备模型建立中提到了三个操作函数集,uart_ops ,tty_fops,s3c24xx_serial_ops数据的流动便是这些操作函数间的调用,这些调用关系如下:

在对一个设备进行其他操作之前必须先打开它,linux/drivers/char/tty_io.c
static const struct file_operations tty_fops = {
………………………………
.open = tty_open,
………………………………
};
static int tty_open(struct inode *inode, struct file *filp)
{
………………………………
dev_t device = inode->i_rdev;
………………………………
driver = get_tty_driver(device, &index); //根据端口设备号获取它的索引号
………………………………
if (tty) {
………………………………
} else
tty = tty_init_dev(driver, index, 0); //创建一个tty_struct 并初始化
………………………………
}
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok)
{
………………………………
tty = alloc_tty_struct(); //分配一个tty_struct结构
//一些字段的初始化,
initialize_tty_struct(tty, driver, idx);
//完成的主要工作是driver->ttys[idx] = tty;
retval = tty_driver_install_tty(driver, tty);
………………………………
/*
下面函数主要做的就是调用线路规程的打开函数ld->ops->open(tty)。
在这个打开函数中分配了一个重要的数据缓存
tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
*/
retval = tty_ldisc_setup(tty, tty->link);
}
void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx)
{
………………………………
//获取线路规程操作函数集tty_ldisc_N_TTY,并做这样的工作tty->ldisc = ld;
tty_ldisc_init(tty);
………………………………
/*
下面函数的主要工作是INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
初始化一个延时tty->buf.work 并关联一个处理函数flush_to_ldisc(),这个函数将在
数据读取的时候用到。
*/
tty_buffer_init(tty);
………………………………
tty->driver = driver;
tty->ops = driver->ops; //这里的ops就是struct tty_operations uart_ops
tty->index = idx; //idx就是该tty_struct对应端口的索引号
tty_line_name(driver, idx, tty->name);
}
端口设备打开之后就可以进行读写操作了,这里只讨论数据的读取,在文件 linux/drivers/char/tty_io.c中,
static const struct file_operations tty_fops = {
………………………………
.read = tty_read,
………………………………
};
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
………………………………
ld = tty_ldisc_ref_wait(tty); //获取线路规程结构体
if (ld->ops->read) //调用线路规程操作函数集中的n_tty_read()函数
i = (ld->ops->read)(tty, file, buf, count);
else
………………………………
}
在linux/drivers/char/N_tty.c中:
struct tty_ldisc_ops tty_ldisc_N_TTY = {
………………………………
.open = n_tty_open,
………………………………
.read = n_tty_read,
………………………………
};
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
………………………………
while (nr) {
………………………………
if (tty->icanon && !L_EXTPROC(tty)) {
//如果设置了tty->icanon 就从缓存tty->read_buf[]中逐个数据读取,并判断读出的每一个数//据的正确性或是其他数据类型等。
eol = test_and_clear_bit(tty->read_tail,tty->read_flags);
c = tty->read_buf[tty->read_tail];
………………………………
} else {
………………………………
//如果没有设置tty->icanon就从缓存tty->read_buf[]中批量读取数据,之所以要进行两次读
//取是因为缓存tty->read_buf[]是个环形缓存
uncopied = _from_read_buf(tty, &b, &nr);
uncopied += _from_read_buf(tty, &b, &nr);
………………………………
}
}
………………………………
}
用户空间是从缓存tty->read_buf[]中读取数据读的,那么缓存tty->read_buf[]中的数据有是从那里来的呢?分析如下:
回到文件 linux/drivers/serial/samsuing.c中,串口数据接收中断处理函数实现如下:
这是串口最原始的数据流入的地方
static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
………………………………
while (max_count-- > 0) {
………………………………
ch = rd_regb(port, S3C2410_URXH); //从数据接收缓存中读取一个数据
………………………………
flag = TTY_NORMAL; //普通数据,还可能是其他数据类型在此不做讨论
………………………………
/*
下面函数做的最主要工作是这样
struct tty_buffer *tb = tty->buf.tail;
tb->flag_buf_ptr[tb->used] = flag;
tb->char_buf_ptr[tb->used++] = ch;
将读取的数据和该数据对应标志插入 tty->buf。
*/
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
}
tty_flip_buffer_push(tty); //将读取到的max_count个数据向上层传递。
out:
return IRQ_HANDLED;
}
void tty_flip_buffer_push(struct tty_struct *tty)
{
………………………………
if (tty->low_latency)
flush_to_ldisc(&tty->buf.work.work);
else
schele_delayed_work(&tty->buf.work, 1);
//这里这个延时work在上面串口设备打开中提到过,该work的处理函数也是flush_to_ldisc。
}
static void flush_to_ldisc(struct work_struct *work)
{
………………………………
while ((head = tty->buf.head) != NULL) {
………………………………
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
………………………………
//刚才在串口接收中断处理函数中,将接收到的数据和数据标志存到tty->buf中,现在将
//这些数据和标志用char_buf 和flag_buf指向进一步向上传递。
disc->ops->receive_buf(tty, char_buf,flag_buf, count);
spin_lock_irqsave(&tty->buf.lock, flags);
}
}
上面调用的函数disc->ops->receive_buf在文件linux/drivers/char/N_tty.c中实现
struct tty_ldisc_ops tty_ldisc_N_TTY = {
………………………………
.receive_buf = n_tty_receive_buf,
………………………………
};
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
………………………………
//现在可以看到缓冲区tty->read_buf 中数据的由来了。
if (tty->real_raw) {
//如果设置了tty->real_raw将上面讲到的些传入数据批量拷贝到tty->read_head中。
//对环形缓存区的数据拷贝需要进行两次,第一次拷贝从当前位置考到缓存的末尾,如果还//有没考完的数据而且缓存区开始出处还有剩余空间,就把没考完的数据考到开始的剩余空
//间中。
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
} else {
for (i = count, p = cp, f = fp; i; i--, p++) {
//如果没有设置tty->real_raw,就根据传入数据标志分类获取数据。
………………………………
}
………………………………
}
………………………………
}
到此,数据读取的整个过程就结束了。可以看出数据读取可以分为两个阶段,一个阶段是上层函数从环形缓存区tty->read_buf 读取数据,第二阶段是底层函数将接收的数据考到环形缓存区tty->read_buf 中。

❸ linux启动后怎么打开uart

一般都在UBOOT里修改,也可以在内核中进行修改。 初始化LCD后可以和QT或者其它图形工具做一个开机进度条。

❹ linux 怎么禁用uart驱动

驱动软件你可以去官网下载安装,如果觉得麻烦,你可以尝试使用第三方驱动管理软件,像驱动人生就可以。

❺ linux下uart的文件节点是怎样创建的


(1)试验目的:掌握通过文件系统操作UART设备的方法.

(2)在linux中,所有设备都是以文件的形式被打开并进行读/写操作的,本试验中使用POSIX兼容的文件操作接口函数对底层设备进行操作.其中,POSIX是Portable Operating System Interface for UNIX的首字母缩写,是一套IEEE和ISO标准.

❻ linux如何查看哪个串口是真实串口,哪个串口

1.使用ls -l ttyS*命令显示如下
crw-rw----. 1 root dialout 4, 64 5月 17 02:24 /dev/ttyS0
crw-rw----. 1 root dialout 4, 65 5月 17 02:24 /dev/ttyS1
crw-rw----. 1 root dialout 4, 66 5月 17 02:24 /dev/ttyS2
crw-rw----. 1 root dialout 4, 67 5月 17 02:24 /dev/ttyS3
但你不知到哪个是真实的串口,虽然一般都是ttyS0,但也不敢妄然确定。
2.使用cat /proc/tty/driver/serial
serinfo:1.0 driver revision:
0: uart:16550A port:000003F8 irq:4 tx:0 rx:0
1: uart:unknown port:000002F8 irq:3
2: uart:unknown port:000003E8 irq:4
3: uart:unknown port:000002E8 irq:3
我们发现串口0的uart值时16550A,tx值为0,rx值也为0,因此我们断定本机只有一个串口,是串口0,即ttyS0
3.也可以用dmesg | grep ttyS*,但这个不是很好用,当然你可以自己使用正则法则取找到。
注意:还应查看是否有USB转串口,这个就很简单了:ls ttyUSB*,全部搞定。

❼ Linux怎么把串口设置

简单的运行 dmesg 命令
$ dmesg | grep tty
输出:
[ 37.531286] serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[ 37.531841] 00:0b: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
[ 37.532138] 0000:04:00.3: ttyS1 at I/O 0x1020 (irq = 18) is a 16550A

setserial 命令
setserial 是一个程序用于设定并/或报告某个串口关联的配置信息。该信息包括串口用到的I/O 端口和中断号,以及Break键是否应被解释为Secure Attention Key 等等。 仅仅是输出如下的命令:
$ setserial -g /dev/ttyS[0123]
输出:
/dev/ttyS0, UART: 16550A, Port: 0x03f8, IRQ: 4
/dev/ttyS1, UART: 16550A, Port: 0x1020, IRQ: 18
/dev/ttyS2, UART: unknown, Port: 0x03e8, IRQ: 4
/dev/ttyS3, UART: unknown, Port: 0x02e8, IRQ: 3

带-g选项的setserial帮助找到你的Linux板子上的物理串口。
Linux 串口控制台程序
一旦串口被确定了,你就能使用许多的工具来配置Linux板子:
minicom- 用于控制modem和连接到mp 设备的最好的串口通信程序。
wvidial or other GUI dial up networking program - 一个内建智能PPP 拨号器。
getty / agetty - agetty 打开一个 tty 端口, 提示登录名称并调用 /bin/login 命令。
grub / lilo configuration - 配置串口为系统控制台。

❽ linux内核中怎么通过uart输出

1.通用串行数据总线,用于异步传输,就是电脑串口之类的 2.硬件层一样,驱动层架构不同,因为操作系统给的接口不同 3.除了必须的模块加载卸载函数外,还有read函数,write函数,ioctl函数,最重要的就是这几个,以方便你以文件方式调用文件读写API

❾ arm linux问题:我板子的uart1串口是调试串口,我看大多都是uart0是调试口,我怎么改linux的源码

你用的是什么板子啊?

这个要改uboot的。

比如,如果用的是s3c2410,改一下include/configs/smdk2410.h

将第63行,#defineCONFIG_SERIAL11 /*weuseSERIAL1onSMDK2410*/

的CONFIG_SERIAL2,就行了。

可以看一下cpu/arm920t/s3c24x0/serial.c就能理解了。

#ifdefCONFIG_SERIAL1

#defineUART_NR S3C24X0_UART0

#elifdefined(CONFIG_SERIAL2)

#ifdefined(CONFIG_TRAB)

#error"TRABsupportsonlyCONFIG_SERIAL1"

#endif

#defineUART_NR S3C24X0_UART1

#elifdefined(CONFIG_SERIAL3)

#ifdefined(CONFIG_TRAB)

##error"TRABsupportsonlyCONFIG_SERIAL1"

#endif

#defineUART_NR S3C24X0_UART2

#else

#error"Bad:youdidn'tconfigureserial..."

#endif

❿ linux vivado怎么都识别不了uart

首先第一步,需要把想要观测的信号标记出来,即mark_debug,有两种mark_debug的方法,我用verilog写了一个简单的流水灯程序,只有几行代码,如下:
mole main(
input clk,
input rst,
output reg [7:0] led
);

(*mark_debug = "true"*)reg [23:0] counter;

always @(posedge clk) begin
if(rst) begin
counter <= 0;
led <= 8'b00000001;
end
else counter <= counter + 1;
if (counter == 24'hffffff)
led <= {led[6:0],led[7]};
end

endmole
例如,要观察counter信号的波形,那么在第7行定义reg型信号counter时,前面加上(*mark_debug=“true”*),这样就把counter信号标记了出来。如果用vhdl语言实现的话,这句话用该这样写:
signal counter : std_logic_vector (23 downto 0);
attribute mark_debug: string;
attribute mark_debug of counter : signal is "true";
另外添加xdc约束文件,内容如下:
set_property PACKAGE_PIN Y9 [get_ports clk]
set_property PACKAGE_PIN T18 [get_ports rst]

set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS18 [get_ports rst]

set_property PACKAGE_PIN T22 [get_ports {led[0]}]
set_property PACKAGE_PIN T21 [get_ports {led[1]}]
set_property PACKAGE_PIN U22 [get_ports {led[2]}]
set_property PACKAGE_PIN U21 [get_ports {led[3]}]
set_property PACKAGE_PIN V22 [get_ports {led[4]}]
set_property PACKAGE_PIN W22 [get_ports {led[5]}]
set_property PACKAGE_PIN U19 [get_ports {led[6]}]
set_property PACKAGE_PIN U14 [get_ports {led[7]}]

set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[7]}]
之后run synthesis综合,之后open synthesized design,在左上角选择debug layout,在debug窗口中netlist看到counter信号前面有一个绿色的小蜘蛛,表示counter信号被标记出来了。
这其实是一种比较繁琐的方法,更为方便的方法是,直接综合工程,在之后打开综合设计,在netlist中直接选中想要查看的信号,右键选择mark debug,即可将信号标记出来。
但是采用第一种方式的好处是,如果工程比较复杂的话,一些信号可能会被综合优化掉,加上模块层层实例化,在netlist中可能找不到要观测的信号,这时在代码里面mark_debug,依旧可以将该信号引出来。

接着第二步就是插入调试内核了,在Vivado界面下方,找到Unassigned Debug Nets,右键选择 set up debug,在接下来的对话框中列出了counter信号的lk domain是CLK_IBUG_BUFG,其trig和data项都打了对勾,表示counter信号既作为触发信号也作为数据信号。

热点内容
军用压缩水 发布:2025-05-14 15:27:19 浏览:25
win7c盘加密 发布:2025-05-14 15:04:49 浏览:511
dm码编程 发布:2025-05-14 15:03:56 浏览:405
apache加密 发布:2025-05-14 14:49:13 浏览:970
安卓什么软件苹果不能用 发布:2025-05-14 14:49:03 浏览:772
jsoupjava 发布:2025-05-14 14:38:00 浏览:888
影豹选哪个配置最好 发布:2025-05-14 14:28:50 浏览:256
定期预算法的 发布:2025-05-14 14:24:08 浏览:895
interbase数据库 发布:2025-05-14 13:49:50 浏览:691
微商海报源码 发布:2025-05-14 13:49:42 浏览:347