经典网站欣赏wordpress评论框
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:35
当前位置: 首页 > news >正文
经典网站欣赏,wordpress评论框,网站开发成本估计,安溪网站建设往期内容 本专栏往期内容#xff1a;Uart子系统 UART串口硬件介绍深入理解TTY体系#xff1a;设备节点与驱动程序框架详解Linux串口应用编程#xff1a;从UART到GPS模块及字符设备驱动 解UART 子系统#xff1a;Linux Kernel 4.9.88 中的核心结构体与设计详解IMX 平台UART驱…往期内容 本专栏往期内容Uart子系统 UART串口硬件介绍深入理解TTY体系设备节点与驱动程序框架详解Linux串口应用编程从UART到GPS模块及字符设备驱动 解UART 子系统Linux Kernel 4.9.88 中的核心结构体与设计详解IMX 平台UART驱动情景分析注册篇IMX 平台UART驱动情景分析open篇 IMX 平台UART驱动情景分析read篇–从硬件驱动到行规程的全链路剖析 interrupt子系统专栏 专栏地址interrupt子系统Linux 链式与层级中断控制器讲解原理与驱动开发 – 末片有专栏内容观看顺序 pinctrl和gpio子系统专栏 专栏地址pinctrl和gpio子系统 编写虚拟的GPIO控制器的驱动程序和pinctrl的交互使用 – 末片有专栏内容观看顺序 input子系统专栏 专栏地址input子系统input角度I2C触摸屏驱动分析和编写一个简单的I2C驱动程序 – 末片有专栏内容观看顺序 I2C子系统专栏 专栏地址IIC子系统具体芯片的IIC控制器驱动程序分析i2c-imx.c-CSDN博客 – 末篇有专栏内容观看顺序 总线和设备树专栏 专栏地址总线和设备树设备树与 Linux 内核设备驱动模型的整合-CSDN博客 – 末篇有专栏内容观看顺序 目录 往期内容1.内核代码1.write过程分析1.1 流程1.2 总框图1.3 代码详情分析1.3.1 tty层1.3.2 核心层1.3.3 硬件层 2.硬件相关的发送 1.内核代码
硬件相关 drivers/tty/serial/imx.cimx.c — imx系列的 drivers/tty/serial/stm32-usart.cstm32-usart.c — stm32系列的
串口核心层
drivers/tty/serial/serial_core.cserial_core.c
TTY层:
drivers/tty/tty_io.ctty_io.c
本文深入剖析了 Linux 串口子系统中的数据写入过程重点涵盖 TTY 层、行规程、核心层及硬件驱动层的协作机制。通过对 tty_write、do_tty_write 等关键函数的详细代码解析逐步追踪数据从用户空间到硬件层的传递路径。文章还探讨了数据写入的分块机制、线程安全处理以及中断与 DMA 方式的硬件发送逻辑。 看之前建议看一下之前关于UART驱动相关结构体的介绍文章解UART 子系统Linux Kernel 4.9.88 中的核心结构体与设计详解 对于TTY体系不了解的可以看深入理解TTY体系设备节点与驱动程序框架详解
1.write过程分析
1.1 流程
流程为 APP写 使用行规程来写数据最终存入uart_state-xmit的buffer里 硬件发送怎么发送数据 使用硬件驱动中uart_ops-start_tx开始发送具体的发送方法有2种通过DMA或通过中断 中断方式 方法1直接使能 tx empty中断一开始tx buffer为空在中断里填入数据方法2写部分数据到tx fifo使能中断剩下的数据再中断里继续发送
1.2 总框图 1.3 代码详情分析
1.3.1 tty层
\Linux-4.9.88\drivers\tty\tty_io.c
static 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_compat_ioctl,.open tty_open,.release tty_release,.fasync tty_fasync,
};和read差不多直接锁定file_operations中的write函数也就是tty_write
\Linux-4.9.88\drivers\tty\tty_io.c
/*** tty_write - tty 设备文件的写入方法* file: 指向 tty 文件的指针* buf: 要写入的用户数据缓冲区* count: 要写入的数据字节数* ppos: 未使用的文件偏移指针** 通过行规程line discipline将数据写入 tty 设备。** 锁机制* - 根据需要对行规程加锁。* - 通过 atomic_write_lock 使写入 tty 驱动的操作串行化。* - 每个设备的行规程写入方法不会并行调用写入操作被分块处理。*/static ssize_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{struct tty_struct *tty file_tty(file); // 从文件结构中获取 tty 结构struct tty_ldisc ld; // tty 的行规程结构指针ssize_t ret; // 返回值表示写入的字节数或错误码// 检查 tty 的有效性和可能的错误if (tty_paranoia_check(tty, file_inode(file), tty_write))return -EIO; // 如果检测到错误返回 -EIO// 如果 tty 无效、无写入操作或存在 I/O 错误返回 -EIOif (!tty || !tty-ops-write || tty_io_error(tty))return -EIO;// 临时调试信息检查是否定义了 write_room 方法if (tty-ops-write_room NULL)tty_err(tty, missing write_room method\n);// 获取 tty 的行规程line discipline引用ld tty_ldisc_ref_wait(tty);if (!ld)return hung_up_tty_write(file, buf, count, ppos); // 如果行规程不可用处理挂起情况// 如果行规程未定义写入方法返回 -EIO否则调用写入操作if (!ld-ops-write)ret -EIO;elseret do_tty_write(ld-ops-write, tty, file, buf, count);// 释放行规程引用tty_ldisc_deref(ld);return ret; // 返回写入的字节数或错误码
}其中对行规进行写入操作就是do_tty_write(ld-ops-write, tty, file, buf, count)函数检查行规程的 write 方法是否存在。如果存在则调用 do_tty_write 函数执行写入操作成功时返回写入的字节数否则返回 -EIO。
其中又涉及到了struct tty_ldisc ld-ops-write在read也有提到过就是下下面的n_tty_write函数。
\Linux-4.9.88\drivers\tty\n_tty.c
static struct tty_ldisc_ops n_tty_ops {.magic TTY_LDISC_MAGIC,.name n_tty,.open n_tty_open,.close n_tty_close,.flush_buffer n_tty_flush_buffer,.read n_tty_read,.write n_tty_write,.ioctl n_tty_ioctl,.set_termios n_tty_set_termios,.poll n_tty_poll,.receive_buf n_tty_receive_buf,.write_wakeup n_tty_write_wakeup,.receive_buf2 n_tty_receive_buf2,
};暂时不进入n_tty_write去看其实现先继续进入do_tty_write函数
\Linux-4.9.88\drivers\tty\tty_io.c
/** 将写操作拆分成合适的块大小以避免 拒绝服务denial-of-service类型的攻击*/
static inline ssize_t do_tty_write(ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),struct tty_struct *tty,struct file *file,const char user buf,size_t count)
{ssize_t ret, written 0; // ret 存储返回值written 存储已写入的字节数unsigned int chunk; // 当前写入块的大小// 锁定 tty 以保证线程安全获取写锁ret tty_write_lock(tty, file-f_flags O_NDELAY);if (ret 0)return ret; // 如果获取锁失败返回错误码/** 将写操作分成临时缓冲区。这 简化了低级驱动的操作因为它们* 不再需要处理锁问题和用户模式访问。** 但如果设置了 TTY_NO_WRITE_SPLIT则应使用* 更大的块大小。** 默认块大小为 2kB因为 NTTY* 层对更大块有问题。它会声称能够处理* 更多字符但实际上不能。** FIXME这个限制可能可以去掉但 64K 的块* 仍然可能会失败除非切换到 vmalloc…*/chunk 2048; // 默认块大小为 2KBif (test_bit(TTY_NO_WRITE_SPLIT, tty-flags))chunk 65536; // 如果设置了标志块大小为 64KBif (count chunk)chunk count; // 确保块大小不超过要写入的字节数// write_buf/write_cnt 受 atomic_write_lock 互斥锁保护if (tty-write_cnt chunk) {unsigned char buf_chunk;if (chunk 1024)chunk 1024; // 确保块大小至少为 1KBbuf_chunk kmalloc(chunk, GFP_KERNEL); // 分配临时缓冲区if (!buf_chunk) {ret -ENOMEM; // 如果内存分配失败返回 -ENOMEMgoto out; // 跳转到清理代码}kfree(tty-write_buf); // 释放旧的缓冲区tty-write_cnt chunk; // 更新当前写入块大小tty-write_buf buf_chunk; // 设置新的写入缓冲区}// 开始写入操作for (;;) {size_t size count; // 当前要写入的大小if (size chunk)size chunk; // 确保不超过块大小ret -EFAULT; // 默认错误码为 EFAULTif (copy_from_user(tty-write_buf, buf, size)) // 从用户空间复制数据到缓冲区break; // 如果失败跳出循环ret write(tty, file, tty-write_buf, size); // 调用写入函数if (ret 0) // 如果写入失败或没有写入字节跳出循环break;written ret; // 累加已写入字节数buf ret; // 更新用户缓冲区指针count - ret; // 更新剩余字节数if (!count) // 如果没有剩余字节结束写入break;ret -ERESTARTSYS; // 设置重启错误if (signal_pending(current)) // 如果有信号挂起跳出循环break;cond_resched(); // 允许调度其他任务}if (written) {tty_update_time(file_inode(file)-i_mtime); // 更新文件的最后修改时间ret written; // 设置返回值为已写入字节数}out:tty_write_unlock(tty); // 解锁 ttyreturn ret; // 返回结果
}do_tty_write 函数负责将用户数据写入 tty 设备。为了避免拒绝服务DoS攻击该函数将写操作拆分成适当大小的块。这种分块机制可以防止过大的写入请求消耗过多资源。 其中调用了 ret write(tty, file, tty-write_buf, size)写入操作这个write函数就是传进来的ld-ops-write也就是n_tty_write
\Linux-4.9.88\drivers\tty\n_tty.c
/** n_tty_write - tty 设备的写函数* tty: tty 设备* file: 文件对象* buf: 用户空间缓冲区指针* nr: I/O 的大小** 终端设备的写函数。此函数与其他写调用是串行化的* 但不与 termios 变化、读取和其他事件串行化。* 由于接收代码会回显字符从而调用驱动的写方法* 因此在此处调用的输出处理函数以及回显处理函数中使用 output_lock 来保护列状态和缓冲区剩余空间。** 这段代码必须确保在挂起时绝对不会进入睡眠状态。** 锁定: output_lock 保护列状态和剩余空间* 注意process_output() 函数本身会获取此锁/static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr)
{const unsigned char b buf; // 将用户缓冲区指针赋值给 bDEFINE_WAIT_FUNC(wait, woken_wake_function); // 定义等待队列int c; // 当前字符ssize_t retval 0; // 返回值初始化/ 作业控制检查 – 必须在开始时进行 (POSIX.1 7.1.1.4). /if (L_TOSTOP(tty) file-f_op-write ! redirected_tty_write) {retval tty_check_change(tty); // 检查 tty 状态变化if (retval)return retval; // 如果检查失败返回错误}down_read(tty-termios_rwsem); // 获取读取锁保护 termios 数据结构/ 写出任何尚未回显的字符 */process_echoes(tty); // 处理待回显的字符add_wait_queue(tty-write_wait, wait); // 将当前进程添加到写等待队列while (1) {if (signal_pending(current)) { // 检查当前进程是否有挂起信号retval -ERESTARTSYS; // 设置重启错误break; // 跳出循环}if (tty_hung_up_p(file) || (tty-link !tty-link-count)) {retval -EIO; // 如果 tty 设备挂起或无有效链接返回 I/O 错误break;}if (O_OPOST(tty)) { // 检查是否启用了输出处理while (nr 0) {ssize_t num process_output_block(tty, b, nr); // 处理输出块if (num 0) {if (num -EAGAIN)break; // 如果返回值是 EAGAIN跳出循环retval num; // 记录错误返回值goto break_out; // 跳转到退出处理}b num; // 更新缓冲区指针nr - num; // 减少待写入的字节数if (nr 0)break; // 如果没有剩余字节结束循环c *b; // 获取当前字符if (process_output(c, tty) 0) // 处理当前字符输出break; // 如果处理出错跳出循环b; nr–; // 更新缓冲区指针和剩余字节数}if (tty-ops-flush_chars) // 如果定义了字符冲刷函数tty-ops-flush_chars(tty); // 调用冲刷函数} else {struct n_tty_data *ldata tty-disc_data; // 获取 n_tty 数据while (nr 0) {mutex_lock(ldata-output_lock); // 获取输出锁c tty-ops-write(tty, b, nr); // 调用 tty 写操作mutex_unlock(ldata-output_lock); // 释放输出锁if (c 0) {retval c; // 记录错误返回值goto break_out; // 跳转到退出处理}if (!c) // 如果没有写入字符结束循环break;b c; // 更新缓冲区指针nr - c; // 更新剩余字节数}}if (!nr) // 如果没有剩余字节结束写入break;if (file-f_flags O_NONBLOCK) { // 如果是非阻塞模式retval -EAGAIN; // 设置 EAGAIN 错误break; // 跳出循环}up_read(tty-termios_rwsem); // 释放读取锁wait_woken(wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); // 允许其他任务调度down_read(tty-termios_rwsem); // 重新获取读取锁}
break_out:remove_wait_queue(tty-write_wait, wait); // 从等待队列中移除当前进程if (nr tty-fasync) // 如果有剩余字节并且异步写入被启用set_bit(TTY_DO_WRITE_WAKEUP, tty-flags); // 设置写入唤醒标志up_read(tty-termios_rwsem); // 释放读取锁return (b - buf) ? b - buf : retval; // 返回实际写入的字节数或错误码
}其中c tty-ops-write(tty, b, nr)调用tty写操作此时的tty是tty_struct结构体类型tty-ops是tty_operations结构体类型此时已经对行规进行过写入操作那么可以猜到tty-ops-write(tty, b, nr)肯定就是将数据写入到tty设备。
1.3.2 核心层
可是在tty层我找不到这个函数定义此时就要去核心层了因为对行规的写操作是已经结束了在\Linux-4.9.88\Linux-4.9.88\drivers\tty\serial\serial_core.c搜索可以找到
\Linux-4.9.88\Linux-4.9.88\drivers\tty\serial\serial_core.c
static const struct tty_operations uart_ops {.open uart_open,.close uart_close,.write uart_write,.put_char uart_put_char,.flush_chars uart_flush_chars,.write_room uart_write_room,.chars_in_buffer uart_chars_in_buffer,.flush_buffer uart_flush_buffer,.ioctl uart_ioctl,.throttle uart_throttle,.unthrottle uart_unthrottle,.send_xchar uart_send_xchar,.set_termios uart_set_termios,.set_ldisc uart_set_ldisc,.stop uart_stop,.start uart_start,.hangup uart_hangup,.break_ctl uart_break_ctl,.wait_until_sent uart_wait_until_sent,
#ifdef CONFIG_PROC_FS.proc_fops uart_proc_fops,
#endif.tiocmget uart_tiocmget,.tiocmset uart_tiocmset,.get_icount uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL.poll_init uart_poll_init,.poll_get_char uart_poll_get_char,.poll_put_char uart_poll_put_char,
#endif
};那么就可以知道tty-ops-write调用的就是uart_write函数
static int uart_write(struct tty_struct *tty,const unsigned char *buf, int count)
{struct uart_state *state tty-driver_data; // 从 tty 结构获取 uart_statestruct uart_port *port; // 声明 uart_port 指针struct circ_buf circ; // 声明循环缓冲区指针unsigned long flags; // 用于保存锁定标志int c, ret 0; // c 用于记录当前写入的字节数ret 用于返回总写入字节数/** 这意味着你在端口关闭后调用了此函数。 没有机会进行写入操作。*/if (!state) {WARN_ON(1); // 打印警告信息return -EL3HLT; // 返回错误表示端口已关闭}circ state-xmit; // 获取当前 uart_state 的发送循环缓冲区if (!circ-buf) // 检查缓冲区是否有效return 0; // 如果无效返回 0port uart_port_lock(state, flags); // 锁定 uart_port并获取相关标志while (port) { // 进入循环处理写入操作c CIRC_SPACE_TO_END(circ-head, circ-tail, UART_XMIT_SIZE); // 计算可用的缓冲区空间if (count c) // 如果待写入字节数小于可用空间c count; // 将 c 设置为待写入字节数if (c 0) // 如果没有可写字节break; // 跳出循环memcpy(circ-buf circ-head, buf, c); // 将数据复制到循环缓冲区circ-head (circ-head c) (UART_XMIT_SIZE - 1); // 更新循环缓冲区头指针buf c; // 更新源缓冲区指针count - c; // 更新待写入字节数ret c; // 更新总写入字节数}uart_start(tty); // 启动 UART 传输uart_port_unlock(port, flags); // 解锁 uart_portreturn ret; // 返回成功写入的字节数
}对数据处理完后就调用了__uart_start(tty);启动uart传输在之前对open进行分析过其实是类似的最后肯定是会进入到硬件驱动相关程序调用struct uart_ops XXX中的函数去启动uart传输
\Linux-4.9.88\drivers\tty\serial\serial_core.c
static void __uart_start(struct tty_struct *tty)
{struct uart_state *state tty-driver_data;struct uart_port port state-uart_port;if (port !uart_tx_stopped(port))port-ops-start_tx(port); //这里不就调用到了
}1.3.3 硬件层
port-ops-start_tx(port)可以看出符合猜想了那么就进入相关硬件层看看\Linux-4.9.88\Linux-4.9.88\drivers\tty\serial\imx.c
static const struct uart_ops imx_pops {.tx_empty imx_tx_empty,.set_mctrl imx_set_mctrl,.get_mctrl imx_get_mctrl,.stop_tx imx_stop_tx,.start_tx imx_start_tx, //这个函数.stop_rx imx_stop_rx,.enable_ms imx_enable_ms,.break_ctl imx_break_ctl,.startup imx_startup,.shutdown imx_shutdown,.flush_buffer imx_flush_buffer,.set_termios imx_set_termios,.type imx_type,.config_port imx_config_port,.verify_port imx_verify_port,
#if defined(CONFIG_CONSOLE_POLL).poll_init imx_poll_init,.poll_get_char imx_poll_get_char,.poll_put_char imx_poll_put_char,
#endif
};port-ops-start_tx(port)调用的就是imx_start_tx。
imx_start_tx 函数用于启动 UART 设备的发送操作。根据 RS485 和 DMA 的设置它配置 UART 硬件准备发送数据。
分析到这里就差不多了从tty层到核心层再到硬件层其实很好懂的主要牢记这个顺序就行了。
2.硬件相关的发送
有兴趣的可以继续看看它是怎么实现硬件发送的 \Linux-4.9.88\drivers\tty\serial\imx.c
/** interrupts disabled on entry/
static void imx_start_tx(struct uart_port *port)
{struct imx_port *sport (struct imx_port )port; // 将 uart_port 强制转换为 imx_port 类型unsigned long temp; // 临时变量用于存储寄存器值// 检查 RS485 相关设置if (port-rs485.flags SER_RS485_ENABLED) {temp readl(port-membase UCR2); // 读取 UCR2 寄存器的当前值if (port-rs485.flags SER_RS485_RTS_ON_SEND)imx_port_rts_inactive(sport, temp); // 设置 RTS 为非活动状态elseimx_port_rts_active(sport, temp); // 设置 RTS 为活动状态// 如果不允许在发送期间接收则禁用接收功能if (!(port-rs485.flags SER_RS485_RX_DURING_TX))temp ~UCR2_RXEN; // 清除 RXEN 位以禁用接收writel(temp, port-membase UCR2); // 写回修改后的值到 UCR2 寄存器// 启用发送器和移位器空的中断temp readl(port-membase UCR4); // 读取 UCR4 寄存器temp | UCR4_TCEN; // 设置 TCEN 位以启用空闲中断writel(temp, port-membase UCR4); // 写回修改后的值到 UCR4 寄存器}// 检查 DMA 是否被启用if (!sport-dma_is_enabled) {temp readl(sport-port.membase UCR1); // 读取 UCR1 寄存器writel(temp | UCR1_TXMPTYEN, sport-port.membase UCR1); // 启用 TXMPTYEN 位}if (sport-dma_is_enabled) {if (sport-port.x_char) {/ 如果有 X-char 要发送启用 TX IRQ 并禁用 TX DMA */temp readl(sport-port.membase UCR1); // 读取 UCR1 寄存器temp ~UCR1_TDMAEN; // 清除 TDMAEN 位以禁用 DMAtemp | UCR1_TXMPTYEN; // 设置 TXMPTYEN 位以启用空闲中断writel(temp, sport-port.membase UCR1); // 写回修改后的值到 UCR1 寄存器return; // 发送 X-char 后返回}// 如果发送缓冲区不为空且发送未停止则调度 DMA 任务if (!uart_circ_empty(port-state-xmit) !uart_tx_stopped(port))schedule_work(sport-tsk_dma_tx);return; // 返回}
}启用中断以发送 X-char。如果发送缓冲区不为空且发送未停止则调度 DMA 任务。该函数用于启动 UART 的发送操作。它根据当前的发送状态和配置如 RS-485 的设置和 DMA 是否启用配置 UART 硬件准备好进行数据发送。 通常在发送数据的初始阶段调用例如在 UART 驱动程序准备发送数据之前或者在设置完 UART 的发送相关寄存器后。
那么既然是写那肯定也需要中断就有对应的中断的处理函数
\Linux-4.9.88\drivers\tty\serial\imx.cstatic irqreturn_t imx_txint(int irq, void *dev_id)
{struct imx_port *sport dev_id; // 获取传递的设备指针unsigned long flags;spin_lock_irqsave(sport-port.lock, flags); // 上锁以保护共享资源imx_transmit_buffer(sport); // 调用函数发送数据spin_unlock_irqrestore(sport-port.lock, flags); // 解锁return IRQ_HANDLED; // 返回中断处理完成
}static inline void imx_transmit_buffer(struct imx_port *sport)
{struct circ_buf xmit sport-port.state-xmit; // 获取发送缓冲区unsigned long temp;if (sport-port.x_char) {/ 如果有字符待发送 /writel(sport-port.x_char, sport-port.membase URTX0); // 发送该字符sport-port.icount.tx; // 发送计数加1sport-port.x_char 0; // 清空待发送字符return;}// 如果发送缓冲区为空或发送被停止停止发送if (uart_circ_empty(xmit) || uart_tx_stopped(sport-port)) {imx_stop_tx(sport-port);return;}// 处理 DMA 发送if (sport-dma_is_enabled) {/** 刚刚发送了 X-char确保启用 TX DMA并禁用 TX IRQ。/temp readl(sport-port.membase UCR1); // 读取 UCR1 寄存器temp ~UCR1_TXMPTYEN; // 清除 TXMPTYEN 位if (sport-dma_is_txing) {temp | UCR1_TDMAEN; // 启用 DMA 传输writel(temp, sport-port.membase UCR1); // 写回 UCR1 寄存器} else {writel(temp, sport-port.membase UCR1); // 仅写回schedule_work(sport-tsk_dma_tx); // 调度 DMA 任务}}// 从发送缓冲区发送数据while (!uart_circ_empty(xmit) !(readl(sport-port.membase uts_reg(sport)) UTS_TXFULL)) {/* 发送 xmit-buf[xmit-tail] 到端口 */writel(xmit-buf[xmit-tail], sport-port.membase URTX0); // 发送数据xmit-tail (xmit-tail 1) (UART_XMIT_SIZE - 1); // 更新尾部索引sport-port.icount.tx; // 发送计数加1}// 如果发送缓冲区中待发送字符少于阈值则唤醒写入操作if (uart_circ_chars_pending(xmit) WAKEUP_CHARS)uart_write_wakeup(sport-port);// 如果发送缓冲区为空停止发送if (uart_circ_empty(xmit))imx_stop_tx(sport-port);
}imx_txint该函数是一个中断处理程序它在 UART 发送中断触发时被调用。主要职责是调用 imx_transmit_buffer 函数来处理发送缓冲区的数据。当 UART 控制器准备好发送下一个字符时生成一个中断触发 imx_txint 函数执行。它会确保在发送操作中对共享资源进行适当的锁定。
流程imx_start_tx 负责准备发送操作并可能启动 DMA 或设置相关寄存器而 imx_txint 则在 UART 发送中断到达时执行负责从发送缓冲区中提取数据并进行发送。调用链在某些情况下imx_start_tx 会在数据准备好发送时被调用而 imx_txint 会响应 UART 发送硬件的状态变化如发送缓冲区的空闲从而进行数据发送。
因此这两个函数一起工作以确保 UART 数据发送的顺畅和高效。imx_start_tx 用于设置和启动发送而 imx_txint 则在硬件准备好时进行实际的数据发送。
- 上一篇: 京紫元年网站建设广东省自然资源厅事务中心
- 下一篇: 经典微网站爱客crm系统
相关文章
-
京紫元年网站建设广东省自然资源厅事务中心
京紫元年网站建设广东省自然资源厅事务中心
- 技术栈
- 2026年03月21日
-
京东网站拼图验证怎么做学校网站建设培训心得
京东网站拼图验证怎么做学校网站建设培训心得
- 技术栈
- 2026年03月21日
-
京东网站建设评估学校多语种网站建设方案
京东网站建设评估学校多语种网站建设方案
- 技术栈
- 2026年03月21日
-
经典微网站爱客crm系统
经典微网站爱客crm系统
- 技术栈
- 2026年03月21日
-
经营地址怎么在国税网站做更改什么网站建设最简单
经营地址怎么在国税网站做更改什么网站建设最简单
- 技术栈
- 2026年03月21日
-
经营类网页游戏大全免费建站网站 seo
经营类网页游戏大全免费建站网站 seo
- 技术栈
- 2026年03月21日


