您的位置:首页 > 文旅 > 旅游 > 沭阳网站建设多少钱_横琴高新区最新_网站及搜索引擎优化建议_成都达洱狐网络科技有限公司

沭阳网站建设多少钱_横琴高新区最新_网站及搜索引擎优化建议_成都达洱狐网络科技有限公司

2024/12/28 9:36:13 来源:https://blog.csdn.net/caiji0169/article/details/144104129  浏览:    关键词:沭阳网站建设多少钱_横琴高新区最新_网站及搜索引擎优化建议_成都达洱狐网络科技有限公司
沭阳网站建设多少钱_横琴高新区最新_网站及搜索引擎优化建议_成都达洱狐网络科技有限公司

往期内容

本专栏往期内容:Uart子系统

  1. UART串口硬件介绍
  2. 深入理解TTY体系:设备节点与驱动程序框架详解
  3. Linux串口应用编程:从UART到GPS模块及字符设备驱动
  4. 解UART 子系统:Linux Kernel 4.9.88 中的核心结构体与设计详解
  5. IMX 平台UART驱动情景分析:注册篇
  6. IMX 平台UART驱动情景分析:open篇
  7. IMX 平台UART驱动情景分析:read篇–从硬件驱动到行规程的全链路剖析
  8. IMX 平台UART驱动情景分析:write篇–从 TTY 层到硬件驱动的写操作流程解析

interrupt子系统专栏:

  1. 专栏地址:interrupt子系统
  2. Linux 链式与层级中断控制器讲解:原理与驱动开发
    – 末片,有专栏内容观看顺序

pinctrl和gpio子系统专栏:

  1. 专栏地址:pinctrl和gpio子系统

  2. 编写虚拟的GPIO控制器的驱动程序:和pinctrl的交互使用

    – 末片,有专栏内容观看顺序

input子系统专栏:

  1. 专栏地址:input子系统
  2. input角度:I2C触摸屏驱动分析和编写一个简单的I2C驱动程序
    – 末片,有专栏内容观看顺序

I2C子系统专栏:

  1. 专栏地址:IIC子系统
  2. 具体芯片的IIC控制器驱动程序分析:i2c-imx.c-CSDN博客
    – 末篇,有专栏内容观看顺序

总线和设备树专栏:

  1. 专栏地址:总线和设备树
  2. 设备树与 Linux 内核设备驱动模型的整合-CSDN博客
    – 末篇,有专栏内容观看顺序

img

目录

  • 往期内容
  • 1.UART驱动调试方法
    • 1.1 怎么得到UART硬件上收发的数据
      • 1.1.1 接收到的原始数据(收)
      • 1.1.2 发送出去的数据(发)
    • 1.2 proc文件
      • 1.2.1 /proc/interrupts
      • 1.2.2 /proc/tty/drivers
      • 1.2.3 /proc/tty/driver(非常有用)
      • 1.2.4 /proc/tty/ldiscs
    • 1.3 sys文件
  • 2.编写虚拟UART驱动程序
    • 2.1 要做的事
    • 2.2 虚拟的UART
    • 2.3 编程
      • 2.3.1 代码说明
      • 2.3.2 /proc文件
      • 2.3.3. 触发中断
    • 2.4 调试

1.UART驱动调试方法

img

1.1 怎么得到UART硬件上收发的数据

1.1.1 接收到的原始数据(收)

可以在接收中断函数里把它打印出来,这些数据也会存入UART对应的tty_port的buffer里:

img

imx_rxint// 读取硬件状态// 得到数据// 在对应的uart_port中更新统计信息, 比如sport->port.icount.rx++;------添加打印---------// 把数据存入tty_port里的tty_buffertty_insert_flip_char(port, rx, flg)------添加打印,确保是否接收到数据---------// 通知行规程来处理tty_flip_buffer_push(port);tty_schedule_flip(port);queue_work(system_unbound_wq, &buf->work); // 使用工作队列来处理// 对应flush_to_ldisc函数

1.1.2 发送出去的数据(发)

所有要发送出去的串口数据,都会通过uart_write函数发送,所有可以在uart_write中把它们打印出来:

imgimg

1.2 proc文件

1.2.1 /proc/interrupts

查看中断次数。

img

1.2.2 /proc/tty/drivers

img

1.2.3 /proc/tty/driver(非常有用)

img

1.2.4 /proc/tty/ldiscs

img

1.3 sys文件

drivers\tty\serial\serial_core.c中,有如下代码:

img

这写代码会在/sys目录中创建串口的对应文件,查看这些文件可以得到串口的很多参数。

怎么找到这些文件?在开发板上执行:

cd /sys
find -name uartclk  // 就可以找到这些文件所在目录

2.编写虚拟UART驱动程序

2.1 要做的事

img

  • 注册一个uart_driver:它里面有名字、主次设备号等

  • 对于每一个port,调用uart_add_one_port,里面的核心是uart_ops,提供了硬件操作函数

    • uart_add_one_port由platform_driver的probe函数调用

    • 所以:

      • 编写设备树节点
      • 注册platform_driver

2.2 虚拟的UART

img为了做实验,还要创建一个虚拟文件:/proc/virt_uart_buf

  • 要发数据给虚拟串口时,执行:echo “xxx” > /proc/virt_uart_buf
  • 要读取虚拟串口的数据时,执行:cat /proc/virt_uart_buf

2.3 编程

📎virtual_uart.c

📎serial_send_recv.c – 测试程序

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderobj-m   += virtual_uart.o
/{virtual_uart: virtual_uart_100ask {compatible = "100ask,virtual_uart";interrupt-parent = <&intc>;interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;};};

无非就是实现uart_driver、uart_ops、file_operations virt_uart_buf_fops、/proc/virt_uart_buf,采用plataform_driver

2.3.1 代码说明

1. 基本结构和宏定义

  • BUF_LEN 1024:定义了环形缓冲区的长度,1024字节。
  • NEXT_PLACE(i):计算缓冲区的下一个位置,这里通过位与操作实现循环数组(环形缓冲区)。

2. 环形缓冲区相关

代码定义了两个环形缓冲区:

  • txbuf:发送缓冲区,用于存储要发送的数据。
  • rxbuf:接收缓冲区,用于存储接收的数据。

并定义了如下指针和变量:

  • tx_buf_r, tx_buf_w:发送缓冲区的读写位置。
  • rx_buf_w:接收缓冲区的写位置。

环形缓冲区的相关操作:

  • is_txbuf_empty():判断发送缓冲区是否为空。
  • is_txbuf_full():判断发送缓冲区是否已满。
  • txbuf_put():向发送缓冲区放入一个字节。
  • txbuf_get():从发送缓冲区取出一个字节。
  • txbuf_count():计算缓冲区中的有效数据字节数。

3. UART驱动结构体

  • uart_driver virt_uart_drv:表示一个UART驱动结构体,其中包括驱动名称、设备名称、设备数量等信息。
struct uart_driver virt_uart_drv = {.owner = THIS_MODULE,.driver_name = "VIRT_UART",.dev_name = "ttyVIRT",  //最后设备节点的名字:/dev/ttyVIRTx.major = 0, // 动态分配主设备号.minor = 0, // 动态分配次设备号.nr = 1,    // UART设备数量为1
};
  • uart_port virt_port:表示虚拟串口硬件信息(包含硬件资源配置),如I/O地址、IRQ、FIFO大小、操作集等。

4. proc文件系统的创建

  • proc_create():创建一个proc文件,virt_uart_buf用来与用户空间交互。
uart_proc_file = proc_create("virt_uart_buf", 0, NULL, &virt_uart_buf_fops);

通过 /proc/virt_uart_buf 文件,可以读写虚拟UART的缓冲区。virt_uart_buf_fops是文件操作集,定义了read和write方法。

5. 文件操作函数

  • virt_uart_buf_read():从虚拟UART的发送缓冲区读取数据,拷贝给用户空间的缓冲区。
ssize_t virt_uart_buf_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) {int cnt = txbuf_count();int i;unsigned char val;cnt = (cnt > size) ? size : cnt; // 限制读取字节数for (i = 0; i < cnt; i++) {txbuf_get(&val); // 从环形缓冲区获取数据copy_to_user(buf + i, &val, 1); // 复制数据到用户空间}return cnt;
}
  • virt_uart_buf_write():从用户空间接收数据,存入接收缓冲区,并模拟触发RX中断。
static ssize_t virt_uart_buf_write(struct file *file, const char __user *buf, size_t size, loff_t *off) {copy_from_user(rxbuf, buf, size); // 从用户空间拷贝数据到接收缓冲区rx_buf_w = size; // 更新接收缓冲区写指针irq_set_irqchip_state(virt_port->irq, IRQCHIP_STATE_PENDING, 1); // 模拟RX中断return size;
}

6. UART操作函数

这些函数定义了UART操作,如启动、停止传输等:

  • virt_tx_empty():判断发送缓冲区是否为空,这里总是返回1,因为数据在缓冲区瞬间发送完毕。
  • virt_start_tx():开始发送数据。它从UART内部的环形缓冲区读取数据并写入txbuf,表示发送操作。
  • virt_set_termios():配置UART波特率、停止位等参数,这里未实现。
  • virt_startup():启动UART设备,这里返回0,表示不需要额外初始化。
  • virt_set_mctrl()virt_get_mctrl():控制UART调制解调器状态,暂未实现。
  • virt_shutdown():关闭UART设备。
  • virt_type():返回虚拟UART类型的字符串。

7. 中断处理函数

  • virt_uart_rxint():虚拟的RX中断处理函数,处理接收的数据,将接收到的数据放入TTY层。
static irqreturn_t virt_uart_rxint(int irq, void *dev_id) {struct uart_port *port = dev_id;struct tty_port *tport = &port->state->port;unsigned long flags;int i;spin_lock_irqsave(&port->lock, flags);for (i = 0; i < rx_buf_w; i++) {port->icount.rx++; // 增加接收计数tty_insert_flip_char(tport, rxbuf[i], TTY_NORMAL); // 插入TTY缓冲区 / put data to ldisc}rx_buf_w = 0;spin_unlock_irqrestore(&port->lock, flags);tty_flip_buffer_push(tport); // 推送到用户空间return IRQ_HANDLED;
}

8. 平台设备驱动

  • virtual_uart_probe():平台设备的探测函数,用于初始化UART设备并请求中断。这个函数负责:
static int virtual_uart_probe(struct platform_device *pdev) {rxirq = platform_get_irq(pdev, 0); // 获取中断号virt_port = devm_kzalloc(&pdev->dev, sizeof(*virt_port), GFP_KERNEL); // 分配port结构体virt_port->irq = rxirq; // 设置中断号ret = devm_request_irq(&pdev->dev, rxirq, virt_uart_rxint, 0, dev_name(&pdev->dev), virt_port); // 注册中断return uart_add_one_port(&virt_uart_drv, virt_port); // 添加一个UART端口
}
  1. 创建proc文件。
  2. 从设备树中获取中断号并注册中断处理函数。
  3. 分配并初始化uart_port结构体,注册UART设备。
  • virtual_uart_remove():用于清理和移除UART设备,包括删除proc文件和反注册UART端口。
static int virtual_uart_remove(struct platform_device *pdev) {uart_remove_one_port(&virt_uart_drv, virt_port);proc_remove(uart_proc_file);return 0;
}

9. 设备树匹配

  • of_device_id virtual_uart_of_match[]:定义设备树匹配表,用于匹配“100ask,virtual_uart”兼容字符串。

10. 平台驱动结构体

  • platform_driver virtual_uart_driver:定义平台驱动结构体,其中包含proberemove函数,以及设备名称和设备树匹配表。

11. 模块初始化与退出

  • virtual_uart_init():模块初始化函数,注册UART驱动并注册平台驱动。
  • virtual_uart_exit():模块退出函数,反注册平台驱动和UART驱动。

调用关系总结:

  • 模块加载时,module_init()调用virtual_uart_init(),注册UART驱动并调用platform_driver_register()注册平台驱动。
  • virtual_uart_probe()会被调用,分配和初始化uart_port,注册中断处理函数并将UART端口注册到系统中。
  • 中断处理函数virt_uart_rxint()会在接收中断时被调用,处理接收的数据。
  • 用户可以通过/proc/virt_uart_buf文件读取和写入虚拟UART缓冲区,触发相关操作。

2.3.2 /proc文件

参考/proc/cmdline,怎么找到它对应的驱动?在Linux内核源码下执行以下命令搜索:

grep "cmdline" * -nr | grep proc

得到:

fs/proc/cmdline.c:26:   proc_create("cmdline", 0, NULL, &cmdline_proc_fops);

2.3.3. 触发中断

使用如下函数:

int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,bool val);

怎么找到它的?在中断子系统中,我们知道往GIC寄存器GICD_ISPENDRn写入某一位就可以触发中断。内核代码中怎么访问这些寄存器?
drivers\irqchip\irq-gic.c中可以看到irq_chip中的"irq_set_irqchip_state"被用来设置中断状态:

static struct irq_chip gic_chip = {.irq_mask		= gic_mask_irq,.irq_unmask		= gic_unmask_irq,.irq_eoi		= gic_eoi_irq,.irq_set_type		= gic_set_type,.irq_get_irqchip_state	= gic_irq_get_irqchip_state,.irq_set_irqchip_state	= gic_irq_set_irqchip_state, /* 2. 继续搜"irq_set_irqchip_state" */.flags			= IRQCHIP_SET_TYPE_MASKED |IRQCHIP_SKIP_SET_WAKE |IRQCHIP_MASK_ON_SUSPEND,
};static int gic_irq_set_irqchip_state(struct irq_data *d,enum irqchip_irq_state which, bool val)
{u32 reg;switch (which) {case IRQCHIP_STATE_PENDING:reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR; /* 1. 找到寄存器 */break;case IRQCHIP_STATE_ACTIVE:reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR;break;case IRQCHIP_STATE_MASKED:reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET;break;default:return -EINVAL;}gic_poke_irq(d, reg);return 0;
}

继续搜"irq_set_irqchip_state",在drivers\irqchip\irq-gic.c中可以看到:

int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,bool val)
{......
}EXPORT_SYMBOL_GPL(irq_set_irqchip_state);

以后就可与使用如下代码触发某个中断:

irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, 1);

2.4 调试

装载驱动程序后,可以知道其设备节点是:/dev/ttyVIRT0
运行测试程序后,出现了input/output error之类的错误,如何去调试查看呢?
>>>strace -o log.txt ./serial send recv /dev/ttyVIRT0
该命令会将输出信息保存到log.txt中,方便我们去查看

img

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com