张家口市网站建设网站建设费经营范围

张小明 2026/1/9 15:10:34
张家口市网站建设,网站建设费经营范围,网页游戏怎么开发,人力资源外包收费报价表一、UART是什么#xff1f;UART#xff08;通用异步收发传输器#xff09;是一种异步串行通信接口#xff0c;常用于嵌入式系统和计算机之间传输数据。 它结构简单、稳定可靠#xff0c;是嵌入式开发中最重要的调试和外接模块的接口之一。串口的主要用途#xff1a;打印调…一、UART是什么UART通用异步收发传输器是一种异步串行通信接口常用于嵌入式系统和计算机之间传输数据。 它结构简单、稳定可靠是嵌入式开发中最重要的调试和外接模块的接口之一。串口的主要用途打印调试信息如通过串口助手查看程序输出。连接外设模块GPS、蓝牙、Wi-Fi、GSM模块等。设备间通信两个嵌入式设备之间的数据传输。二、串口的基本连接方式只需要三根线即可通信1、TxD发送数据线2、RxD接收数据线3、GND地线统一电平参考三、串口的通信参数必须一致才能通信1、波特率表示每秒传输的比特数bps常见值9600、19200、115200等。2、起始位每帧数据的开始标志固定为1个比特的低电平。3、数据位实际要传输的数据一般为5~8位如ASCII码7位。4、校验位可选 用于检测数据传输是否正确常见有奇校验或偶校验。5、停止位表示一帧数据的结束通常为1~2位高电平。四、数据传输示例以发送字符‘A’为例‘A’的ASCII值为 0x41二进制为 01000001。 传输过程1、发送方先拉低电平1位时间起始位。2、接收方检测到低电平后开始计时。3、依次发送8位数据从最低位开始。4、最后发送停止位高电平。五、串口电平标准重要UART通信有两种常见的电平标准电平标准逻辑1电压范围逻辑0电压范围特点TTL/CMOS高电平如3.3V/5V低电平如0V常用在芯片内部或短距离通信RS-232-12V ~ -3V3V ~ 12V抗干扰强适合工业、长距离通信注意大多数ARM芯片的串口是TTL电平。如果连接PC的RS-232串口需要使用电平转换芯片如MAX232。现在多数PC没有串口接口常用USB转TTL串口模块如CH340、CP2102通过USB连接。多数ARM芯片有多个串口一般串口0用于调试其他串口用于外接模块。六、TTY/终端/控制台/串口到底是什么这些术语看起来很相似但实际上有不同的含义术语含义简单理解TTYTeletype电传打字机的缩写现在是Linux内核中的一套驱动系统历史遗留的名字现在泛指Linux中处理字符输入输出的子系统Terminal终端指输入输出设备键盘显示器可能是真实的也可能是虚拟的你与计算机交互的“窗口”Console控制台是一种特殊的终端有更高权限能看到内核消息系统管理员的“超级终端”UART串口硬件接口TTY子系统的一部分物理的串行通信接口用于连接外部设备七、设备节点详解/dev目录下的文件Linux中“一切都是文件”设备也被抽象成文件。下面是常见TTY设备节点的区别1、串口设备节点/dev/ttyS0、/dev/ttyS1... 标准串口设备如COM1、COM2/dev/ttySAC0、/dev/ttySAC1... 某些ARM平台特定的串口命名用途连接真实的串口设备如通过USB转串口线连接开发板串口没有虚拟终端的概念一个设备节点就对应一个真实的硬件。实际开发中嵌入式常用串口作为控制台PC常用虚拟终端作为控制台2、虚拟终端设备节点/dev/tty1、/dev/tty2、/dev/tty3... 虚拟终端按CtrlAltF1~F6切换的就是这些特点每个对应一个独立的登录会话一套键盘显示器模拟多个独立终端多个设备节点对应一个真实的硬件即使没有图形界面也能使用多个“命令行窗口”3、特殊设备节点设备节点含义示例/dev/tty0前台终端当前正在显示的虚拟终端切换tty1→tty2/dev/tty0就从tty1变成tty2/dev/tty当前进程的终端程序自身所在的终端不会随前台切换改变在tty3运行的程序无论前台切到哪个终端它的/dev/tty始终是tty3/dev/console控制台接收内核消息的设备由内核启动参数console指定可以是串口consolettyS0或虚拟终端consoletty0八、重要概念澄清1、终端 vs 控制台所有控制台都是终端但并非所有终端都是控制台控制台能看到内核消息如系统启动时的打印可以通过内核参数选择哪个设备作为控制台2、内核参数 console系统启动时可以指定# 让串口ttyS0作为控制台嵌入式常用 consolettyS0,115200 # 让第一个虚拟终端作为控制台PC常用 consoletty0 # 可以指定多个只有最后一个生效 consolettyS0 consoletty0 # tty0生效3、图形界面下的终端在图形界面如Ubuntu桌面打开的终端窗口不是/dev/ttyN而是/dev/pts/N伪终端原理类似但实现方式不同九、实际应用场景场景1嵌入式开发调试开发板通过串口连接电脑内核参数设置 consolettyS0系统启动信息、printk打印都从串口输出开发者通过串口工具如xshell查看和交互场景2系统维护系统图形界面崩溃时按 CtrlAltF2 切换到tty2登录后可以修复系统这里tty2就是你的控制台场景3多用户服务器多个用户通过SSH远程登录每个用户获得一个伪终端/dev/pts/X各自独立工作互不干扰十、TTY驱动框架工作流程1、数据流向图用户空间程序 ↓ read()/write()系统调用 ↓ TTY核心层/dev/ttyS0 ↓ 行规程层编辑、转换、缓冲 ↓ TTY驱动层UART驱动、键盘显示器驱动 ↓ 硬件层串口芯片2、详细流程示例用户输入hello1、用户输入在键盘上依次按下h、e、l、l、o2、TTY驱动层接收键盘扫描码转换为字符3、行规程层回显每个字符到屏幕将字符存入行缓冲区等待回车键4、用户按回车5、行规程层将回车转换为换行\n将整行数据hello\n提交给TTY核心层没输入换行符应用程序就不会接收到数据6、TTY核心层将数据传递给用户空间程序如shell7、用户程序收到hello\n执行相应命令十一、UART子系统的分层结构用户空间程序 ↓ ┌─────────────────────────────────┐ │ 第1层tty_io层 │ ← 系统调用入口管理tty_struct ├─────────────────────────────────┤ │ 第2层行规程层 │ ← 数据处理中心编辑、回显、缓冲 ├─────────────────────────────────┤ │ 第3层串口核心层 │ ← 通用串口逻辑缓冲区管理 ├─────────────────────────────────┤ │ 第4层硬件驱动层 │ ← 具体硬件操作键盘/UART └─────────────────────────────────┘ ↓ 硬件设备第1层tty_io层设备文件接口层位置drivers/tty/tty_io.c作用用户空间与内核的桥梁核心功能1、设备文件操作实现file_operations结构体const struct file_operations tty_fops { .llseek no_llseek, .read tty_read, .write tty_write, .poll tty_poll, .unlocked_ioctl tty_ioctl, .compat_ioctl tty_ioctl, .open tty_open, .release tty_release, .fasync tty_fasync, };2、管理tty_struct每个打开的终端对应一个tty_structstruct tty_struct { struct tty_driver *driver; // 指向tty驱动 struct tty_ldisc *ldisc; // 指向行规程 struct tty_port *port; // 指向端口 struct tty_buffer *buf; // 缓冲区 // ... 其他字段 };3、系统调用入口// 当应用程序调用 read(fd, buf, count) 时 SYSCALL_DEFINE3(read, ...) → vfs_read() → tty_read() // 当应用程序调用 write(fd, buf, count) 时 SYSCALL_DEFINE3(write, ...) → vfs_write() → tty_write()工作流程应用程序open(/dev/ttyS0, O_RDWR) ↓ 系统调用 ↓ tty_open() ↓ 分配tty_struct ↓ 关联行规程N_TTY ↓ 关联串口驱动第2层行规程层数据处理层位置drivers/tty/n_tty.c默认行规程作用数据的智能处理器核心数据结构struct n_tty_data { unsigned char read_buf[N_TTY_BUF_SIZE]; // 读缓冲区4096字节 unsigned char echo_buf[N_TTY_BUF_SIZE]; // 回显缓冲区 size_t read_head, read_tail; // 读写指针 size_t echo_head, echo_tail; // 回显指针 wait_queue_head_t read_wait; // 读等待队列 wait_queue_head_t write_wait; // 写等待队列 unsigned char lnext:1; // 下一个字符字面值 // ... 其他标志位 };关键处理功能1、行编辑退格(Backspace)、清除单词(CtrlW)、清除行(CtrlU)2、回显控制显示输入字符或隐藏如密码输入3、特殊字符处理CtrlC→ SIGINT信号CtrlZ→ SIGTSTP信号Ctrl\→ SIGQUIT信号4、字符转换\r\n↔\n转换第3层串口核心层通用逻辑层位置drivers/tty/serial/serial_core.c作用所有串口共享的通用逻辑核心数据结构struct uart_state { struct tty_port port; // tty端口 struct circ_buf xmit; // 发送环形缓冲区 struct uart_port *uart_port; // 硬件端口指针 struct uart_icount icount; // 统计信息 // ... }; struct uart_port { spinlock_t lock; // 自旋锁 unsigned int iobase; // IO端口基地址 void __iomem *membase; // 内存映射基地址 unsigned int irq; // 中断号 unsigned int uartclk; // 时钟频率 unsigned int fifosize; // FIFO大小 unsigned char x_char; // XON/XOFF字符 struct uart_ops *ops; // 硬件操作函数集 // ... };主要功能1、缓冲区管理环形缓冲区操作2、中断处理框架统一的中断处理入口3、波特率计算将波特率转换为分频值4、流控支持RTS/CTS硬件流控5、状态管理跟踪串口状态打开、关闭、挂起关键函数// 数据流向管理 uart_write() // 写入数据到发送缓冲区 uart_start() // 启动发送 uart_handle_sysrq_char() // 处理SysRq键 uart_insert_char() // 插入接收到的字符 // 配置管理 uart_get_baud_rate() // 计算波特率 uart_update_timeout() // 更新超时设置 uart_set_options() // 设置串口选项第4层硬件驱动层硬件操作层位置drivers/tty/serial/imx.cIMX6ULL作用与具体硬件交互核心操作集static const struct uart_ops imx_uart_ops { .tx_empty imx_tx_empty, // 检查发送是否完成 .set_mctrl imx_set_mctrl, // 设置MODEM控制线 .get_mctrl imx_get_mctrl, // 获取MODEM状态 .stop_tx imx_stop_tx, // 停止发送 .start_tx imx_start_tx, // 开始发送 .stop_rx imx_stop_rx, // 停止接收 .enable_ms imx_enable_ms, // 使能MODEM状态中断 .break_ctl imx_break_ctl, // 控制Break信号 .startup imx_startup, // 启动串口 .shutdown imx_shutdown, // 关闭串口 .set_termios imx_set_termios, // 设置终端属性 .type imx_type, // 返回类型字符串 .request_port imx_request_port, // 请求端口资源 .config_port imx_config_port, // 配置端口 .verify_port imx_verify_port, // 验证端口 };十二、行规程Line Discipline详解1、什么是行规程行规程是TTY子系统中的数据处理层负责在用户输入输出时进行智能处理。2、行规程的三种模式模式函数处理典型应用规范模式cooked行缓冲、编辑、回显、信号处理命令行交互非规范模式cbreak即时响应无行缓冲vim编辑器原始模式raw无任何处理直接传递串口通信3、行规程的主要功能a、行编辑功能退格键处理删除前一个字符清除单词CtrlW清除整行CtrlU重新打印CtrlR# 在规范模式下输入时可以使用这些编辑功能 # 输入 hello按退格键删除最后的o变成hellb、回显控制将用户输入的字符显示在屏幕上可以关闭回显输入密码时c、特殊字符转换将回车\r转换为换行\n将 CtrlC 转换为中断信号SIGINTd、行缓冲等待用户按下回车键才提交整行数据允许在提交前进行编辑十三、注册字符设备驱动的两种方法1、字符设备驱动的基本框架确定主设备号可以静态指定或动态分配。定义file_operations结构体并实现其中的操作函数如open、read、write等。将file_operations结构体注册到内核中。创建设备节点可以通过class_create和device_create自动创建。2、两种注册方法对比Linux内核提供了两种字符设备驱动注册方法下面是它们的对比对比项旧方法register_chrdev新方法cdev_xxx系列出现时间早期方法简单直接较新方法更灵活资源使用一次注册256个次设备号可能浪费只注册需要的设备号范围更节省灵活性较低适合简单驱动高适合复杂驱动现代驱动较少使用逐渐淘汰推荐使用更标准代码复杂度简单一个函数搞定稍复杂需要多步骤3、旧方法详解register_chrdeva、工作原理// 一个函数搞定所有事情 int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);一次性注册主设备号 0~255的所有次设备号自动分配如果major0内核自动分配主设备号简单粗暴适合初学者但不够灵活b、优点与缺点优点代码简单容易理解一个函数完成注册缺点资源浪费注册256个次设备号即使只用一个不够灵活无法精确控制设备号范围不推荐使用新驱动开发中已逐渐淘汰4、新方法详解cdev_xxx系列函数a、工作原理三步法新方法将注册过程分解为三个步骤// 第一步注册设备号范围 alloc_chrdev_region(dev_id, first_minor, count, name); // 第二步初始化cdev结构 cdev_init(my_cdev, fops); // 第三步添加cdev到系统 cdev_add(my_cdev, dev_id, count);b、核心概念理解设备号dev_t包含主设备号和次设备号32位整数高12位为主设备号低20位为次设备号dev_t dev_id; // 设备号 MAJOR(dev_id); // 提取主设备号 MINOR(dev_id); // 提取次设备号 MKDEV(major, minor); // 合成设备号c、函数详解int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);动态分配内核自动分配一个可用的主设备号精确控制可以指定起始次设备号和设备数量示例alloc_chrdev_region(dev_id, 0, 3, mydev)分配一个主设备号次设备号0、1、2共3个设备设备名为mydevvoid cdev_init(struct cdev *cdev, const struct file_operations *fops);初始化cdev结构体关联file_operations操作函数int cdev_add(struct cdev *p, dev_t dev, unsigned int count);将cdev添加到系统设备生效可以指定多个设备count15、为什么推荐新方法场景1多个相似设备// 创建一个主设备号5个次设备号 alloc_chrdev_region(dev_id, 0, 5, multi_device); // 对应5个设备文件/dev/multi_device0, /dev/multi_device1...场景2避免设备号冲突旧方法需要手动指定主设备号可能与其他驱动冲突。 新方法自动分配避免冲突。场景3节省资源只注册实际需要的设备号不浪费。场景4与sysfs集成更好方便创建设备节点管理更规范。十四、场景分析应用程序通过串口发送Hello应用程序: write(fd, Hello, 5); ↓ 系统调用进入内核 ↓ TTY层 (tty_io.c) ↓ tty_write() ↓ 行规程处理如需要 ↓ serial_core.c ↓ uart_write() ↓ 数据放入发送缓冲区 ↓ imx.c硬件驱动 ↓ imx_start_tx() ↓ 使能发送中断/直接发送 ↓ 硬件UART控制器 ↓ 通过TxD引脚发出1、用户空间到TTY层// tty_io.c ssize_t tty_write(struct file *file, const char __user *buf, size_t count) { // 1. 获取当前tty结构 // 2. 调用行规程的write函数 // 3. 行规程可能进行数据处理规范模式下 // 4. 调用uart_write }2、串口核心层处理// serial_core.c int uart_write(struct uart_port *port, const unsigned char *buf, int count) { // 1. 将数据放入环形缓冲区 // 2. 调用硬件驱动的start_tx函数 // 3. 返回写入的字节数 }3、硬件驱动层发送// imx.c static void imx_start_tx(struct uart_port *port) { // 1. 检查发送缓冲区是否有数据 // 2. 使能发送中断或直接发送 // 3. 写入UART数据寄存器 } // 发送中断处理函数 static irqreturn_t imx_txint(int irq, void *dev_id) { // 1. 检查发送缓冲区 // 2. 从缓冲区取出下一个字符 // 3. 写入数据寄存器 // 4. 如果缓冲区空禁用发送中断 }十五、UART读写总体流程图读取流程接收数据 硬件UART收到数据 → 产生接收中断 → 硬件驱动读取数据 → 存入串口缓冲区 → 通知行规程 → 行规程处理数据 → 唤醒等待的应用程序 → 应用从行规程缓冲区读取数据 写入流程发送数据 应用程序调用write → TTY层处理 → 行规程处理 → 存入发送缓冲区 → 串口核心层启动发送 → 硬件驱动开始发送 → 中断方式发送剩余数据 → 发送完成1、读取数据Read流程详解a、应用程序发起读取// 用户程序 char buffer[100]; int count read(fd, buffer, sizeof(buffer)); // 从串口读取数据b、TTY层处理tty_io.c// drivers/tty/tty_io.c ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct tty_struct *tty file-private_data; // 1. 获取当前tty结构 // 2. 检查行规程是否可用 // 3. 调用行规程的读取函数 return tty-ldisc-ops-read(tty, file, buf, count); }关键点tty_read是read系统调用的内核实现调用行规程的read函数默认为N_TTY行规程c、行规程处理n_tty.c// drivers/tty/n_tty.c static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char *kbuf, size_t nr) { // 1. 检查是否有足够数据可读 // 2. 如果无数据让进程休眠等待数据到达 // 3. 从行规程缓冲区复制数据到用户空间 // 4. 返回读取的字节数 }d、数据如何到达行规程缓冲区中断处理流程硬件接收数据 → 产生中断 → 中断处理函数读取数据 → 存入串口缓冲区 → 通知行规程 → 行规程处理存入自己的缓冲区 → 唤醒等待进程以IMX6ULL为例中断方式// drivers/tty/serial/imx.c static irqreturn_t imx_rxint(int irq, void *dev_id) { struct imx_port *sport dev_id; unsigned int rx, flg; // 1. 读取状态寄存器 unsigned int status imx_uart_read(sport, USR1); // 2. 读取数据寄存器获取接收到的字符 rx imx_uart_read(sport, URXD0); // 3. 检查错误奇偶校验、帧错误等 flg TTY_NORMAL; if (status USR1_BRCD) flg TTY_BREAK; // 4. 将数据存入tty缓冲区 tty_insert_flip_char(sport-port.state-port, rx, flg); // 5. 通知行规程处理新数据 tty_flip_buffer_push(sport-port.state-port); return IRQ_HANDLED; }2、写入数据Write流程详解a、应用程序发起写入// 用户程序 const char *msg Hello UART!\n; int count write(fd, msg, strlen(msg)); // 向串口写入数据b、TTY层处理tty_io.c// drivers/tty/tty_io.c ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct tty_struct *tty file-private_data; // 1. 获取当前tty结构 // 2. 检查行规程是否可用 // 3. 调用行规程的写入函数 return tty-ldisc-ops-write(tty, file, buf, count); }c、行规程处理n_tty.c// drivers/tty/n_tty.c static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) { // 1. 如果需要进行数据处理如规范模式下的转换 // 2. 调用uart_write将数据传递给串口核心层 // 3. 返回写入的字节数 }行规程处理的内容规范模式特殊字符处理如回车换行转换回显控制是否将输入字符显示到终端信号生成如CtrlC生成SIGINT信号d、串口核心层处理serial_core.c// drivers/tty/serial/serial_core.c static int uart_write(struct uart_port *port, const unsigned char *buf, int count) { struct circ_buf *circ port-state-xmit; int c, ret 0; // 1. 检查发送缓冲区是否有空间 // 2. 将数据放入环形发送缓冲区 while (1) { c CIRC_SPACE_TO_END(circ-head, circ-tail, UART_XMIT_SIZE); if (count c) c count; memcpy(circ-buf circ-head, buf, c); circ-head (circ-head c) (UART_XMIT_SIZE - 1); buf c; count - c; ret c; if (count 0) break; } // 3. 通知硬件驱动开始发送 port-ops-start_tx(port); return ret; }发送缓冲区结构struct uart_state { struct tty_port port; // tty端口 struct circ_buf xmit; // 发送环形缓冲区 struct uart_port *uart_port; // 串口端口 };e、硬件驱动发送数据发送的两种方式中断方式数据放入缓冲区使能发送中断在中断中发送DMA方式配置DMA自动发送数据中断方式以IMX6ULL为例// drivers/tty/serial/imx.c static void imx_start_tx(struct uart_port *port) { struct imx_port *sport (struct imx_port *)port; unsigned int temp; // 1. 检查发送缓冲区是否有数据 if (!uart_circ_empty(sport-port.state-xmit)) { // 2. 使能发送中断 temp imx_uart_read(sport, UCR1); temp | UCR1_TXMPTYEN; imx_uart_write(sport, temp, UCR1); } } // 发送中断处理函数 static irqreturn_t imx_txint(int irq, void *dev_id) { struct imx_port *sport dev_id; struct circ_buf *xmit sport-port.state-xmit; // 1. 检查发送缓冲区是否还有数据 while (!uart_circ_empty(xmit)) { // 2. 从缓冲区取出一个字符 unsigned char c xmit-buf[xmit-tail]; // 3. 写入UART数据寄存器硬件会自动发送 imx_uart_write(sport, c, URTX0); // 4. 更新缓冲区指针 xmit-tail (xmit-tail 1) (UART_XMIT_SIZE - 1); // 5. 更新统计信息 sport-port.icount.tx; // 6. 如果缓冲区空了停止发送中断 if (uart_circ_empty(xmit)) { temp imx_uart_read(sport, UCR1); temp ~UCR1_TXMPTYEN; imx_uart_write(sport, temp, UCR1); break; } } return IRQ_HANDLED; }
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

西安市建设监理协会官方网站传统网站建设架构

LangFlow镜像内置模板库:上百种工作流任你选用 在AI应用开发日益普及的今天,一个现实问题始终困扰着团队:如何让非技术背景的产品经理、业务分析师甚至高校学生,也能快速上手大语言模型(LLM)项目&#xff1…

张小明 2026/1/3 20:42:14 网站建设

开发一个app软件能赚钱吗网站seo优化是什么

在使用电脑系统时经常会出现丢失找不到某些文件的情况,由于很多常用软件都是采用 Microsoft Visual Studio 编写的,所以这类软件的运行需要依赖微软Visual C运行库,比如像 QQ、迅雷、Adobe 软件等等,如果没有安装VC运行库或者安装…

张小明 2026/1/5 0:35:37 网站建设

济南专业网站建设网站建设教程txt

AI漫剧制作工具2025推荐,解锁低成本创意变现新路径据《2025中国数字内容产业白皮书》显示,2025年国内漫剧市场规模预计突破120亿元,同比增长65%,但传统制作模式下,高达70%的创作者受困于剧本改编难、制作成本高、周期长…

张小明 2026/1/6 5:38:43 网站建设

怎么将dw做的网站导出青岛网站推广公司排名

Tina Pro v10.0:电路仿真专家的进阶指南 【免费下载链接】TinaProv10.0中文版README **Tina Pro v10.0 中文版** 是DesignSoft公司力推的一款高效电子设计自动化(EDA)工具,专注于电路仿真领域。它支持包括电路直流分析、瞬态分析、…

张小明 2026/1/6 9:57:48 网站建设