ZYNQ学习笔记2-ZYNQ的UART控制器4
简介本节接上一篇继续进行UART库函数分析。主函数分析16.XUartPs_SetInterruptMask函数概述该函数是 UART 驱动中用于设置中断掩码的核心接口它的主要作用是根据传入的掩码值一次性使能或禁用UART 控制器的多个中断源。输入参数包括指向 XUartPs实例的指针 InstancePtr以及一个 32 位掩码值Mask——该掩码中为1的位表示使能对应的中断为0的位表示禁用对应的中断无返回值。该函数通过同时操作两个寄存器IER 中断使能寄存器和 IDR 中断禁用寄存器来实现“一次性写入”的效果避免了传统驱动中需要分别调用“使能”和“禁用”函数的繁琐。语句分析函数首先通过 Xil_AssertVoid 断言检查实例指针是否非空确保驱动实例已正确初始化。接着将传入的 Mask 与 XUARTPS_IXR_MASK中断掩码全集通常为 0xFFFFFFFF 或所有有效中断位的组合按位相与将结果存入局部变量 TempMask。这一步的作用是过滤无效的掩码位确保只有硬件支持的中断位被写入寄存器防止意外置位未定义或保留的位导致异常行为。随后函数向IER中断使能寄存器写入 TempMask向IDR中断禁用寄存器写入 ~TempMask即 TempMask 的按位取反。由于 UART 硬件的中断控制逻辑设计为IER中写 1使能对应中断IDR中写 1 禁用对应中断且两者是独立的寄存器因此同时向两者写入互补的值就实现了“只使能 TempMask 中为 1 的位禁用其余所有位”的效果。这种设计确保了在调用该函数后UART 的中断状态与传入的 Mask 完全一致不会受之前寄存器状态的影响。逐句注释/****************************************************************************/ /** * * 此函数设置中断掩码。 * * param InstancePtr 指向 XUartPs 实例的指针 * param Mask 包含要启用或禁用的中断位。 * 1 使能中断0 禁用中断。 * * return 无 * * note 无 * *****************************************************************************/ void XUartPs_SetInterruptMask(XUartPs *InstancePtr, u32 Mask) { u32 TempMask Mask; /* 定义临时掩码变量并初始化为传入值 */ /* 断言验证输入参数的有效性 */ Xil_AssertVoid(InstancePtr ! NULL); /* 确保实例指针非空 */ /* 将掩码与硬件支持的中断全集按位相与过滤无效位 */ TempMask (u32)XUARTPS_IXR_MASK; /* 此操作可防止意外写入未定义或保留的位即硬件不支持的中断位 */ /* 将掩码写入 IER中断使能寄存器 */ XUartPs_WriteReg(InstancePtr-Config.BaseAddress, XUARTPS_IER_OFFSET, TempMask); /* IER 中写 1 使能对应的中断位 */ /* 将掩码的取反值写入 IDR中断禁用寄存器 */ XUartPs_WriteReg(InstancePtr-Config.BaseAddress, XUARTPS_IDR_OFFSET, (~TempMask)); /* IDR 中写 1 禁用对应的中断位 */ /* 通过同时写入 IER 和 IDR确保了最终中断状态与传入的 Mask 完全一致 */ }17.XUartPs_SetHandler函数概述该函数是 UART 驱动中用于注册用户回调函数的核心接口。它的主要作用是将上层应用程序提供的回调函数指针FuncPtr和回调引用参数CallBackRef保存到 UART驱动实例中使得当 UART 发生需要通知上层的事件如数据接收完成、发送完成、错误发生等时驱动能够调用该回调函数并传入 CallBackRef 作为参数。输入参数包括指向 XUartPs实例的指针 InstancePtr、回调函数指针FuncPtr类型为 XUartPs_Handler即三参数函数指针 void (*)(void*, u32, u32)以及回调引用CallBackRef通常传入 UART 实例自身的指针无返回值。语句分析函数首先通过 Xil_AssertVoid 宏对输入参数进行有效性断言确保 InstancePtr 非空、FuncPtr 非空并且驱动实例已初始化就绪IsReady XIL_COMPONENT_IS_READY。特别值得注意的是注释明确指出不会对 CallBackRef 进行断言检查因为驱动层无法判断什么样的引用值是“合法”的——它只是一个需要原样传递的指针具体含义由上层应用决定。随后函数将传入的 FuncPtr 强制转换为 XUartPs_Handler 类型后存入 InstancePtr-Handler 字段并将 CallBackRef 存入 InstancePtr-CallBackRef 字段。整个操作非常简单直接——就是“保存指针”——但它在中断处理链路中处于核心位置当中断分发函数如ReceiveDataHandler需要通知应用层时会执行 InstancePtr-Handler(InstancePtr-CallBackRef, Event, EventData)从而将控制权交还给应用程序。该函数的设计使得驱动层完全独立于应用层业务逻辑实现了清晰的解耦。逐句注释/****************************************************************************/ /** * * 此函数设置当发生需要应用程序关注的事件中断时将被调用的处理函数。 * * param InstancePtr 指向 XUartPs 实例的指针 * param FuncPtr 指向回调函数的指针 * param CallBackRef 上层回调引用在调用回调函数时被传回 * * return 无 * * note * * 不会对 CallBackRef 进行断言检查因为驱动不知道它应该是什么值也不应该知道 * *****************************************************************************/ void XUartPs_SetHandler(XUartPs *InstancePtr, XUartPs_Handler FuncPtr, void *CallBackRef) { /* * 断言验证输入参数的有效性 * CallBackRef 不进行校验因为无法判断什么值是有效的 */ Xil_AssertVoid(InstancePtr ! NULL); /* 确保实例指针非空 */ Xil_AssertVoid(FuncPtr ! NULL); /* 确保回调函数指针非空传入 NULL 会导致后续崩溃 */ Xil_AssertVoid(InstancePtr-IsReady XIL_COMPONENT_IS_READY); /* 确保驱动实例已初始化就绪 */ /* 保存回调函数指针 */ InstancePtr-Handler (XUartPs_Handler)FuncPtr; /* 将用户提供的函数指针存入实例的 Handler 字段供中断处理时调用 */ /* 保存回调引用参数 */ InstancePtr-CallBackRef CallBackRef; /* 将用户提供的回调引用存入实例的 CallBackRef 字段在调用回调时原样传回 */ }中断函数分析1.XUartPs_IsReceiveData宏定义宏概述该宏定义是 UART 驱动中用于快速判断接收 FIFO 中是否有有效数据的核心工具。它的主要作用是通过读取 UART 状态寄存器SR中的RXEMPTY接收 FIFO 空标志位判断当前 FIFO 中是否存有尚未被读取的数据字节。输入参数是 BaseAddressUART 控制器的基地址为 32 位无符号整数返回值为 u32 类型——返回 1逻辑真表示 FIFO 中有数据可读返回 0逻辑假表示 FIFO 为空。它被设计为宏而非函数以避免函数调用开销适用于频繁调用的中断上下文。逻辑分析宏首先通过 Xil_In32 读取 UART 状态寄存器SR的 32 位值并将该值与 XUARTPS_SR_RXEMPTY进行按位与运算从而提取出RXEMPTY标志位的状态。接着宏将提取的结果与 XUARTPS_SR_RXEMPTY 本身进行比较若两者相等即该位为 1表示 FIFO 当前为空比较结果为逻辑真1若两者不等即该位为 0表示 FIFO 中有数据比较结果为逻辑假0。最后宏在表达式最前面使用逻辑非运算符 ! 对比较结果取反因此最终返回值为当 FIFO非空时返回 1真当 FIFO为空时返回 0假。这种“读取状态 → 提取标志 → 与掩码比较 → 取反”的四步操作简洁地将硬件状态位转换为了直观的“是否有数据”的布尔答案。逐行注释/****************************************************************************/ /** * 判断接收器和/或 FIFO 中是否有接收数据。 * * param BaseAddress 包含设备基地址。 * * return 若有接收数据则返回 TRUE非 0否则返回 FALSE0。 * * note C 语言风格签名 * u32 XUartPs_IsReceiveData(u32 BaseAddress) * ******************************************************************************/ #define XUartPs_IsReceiveData(BaseAddress) \ /* 读取状态寄存器SR提取 RXEMPTY 位FIFO 空标志*/ \ !((Xil_In32((BaseAddress) XUARTPS_SR_OFFSET) \ (u32)XUARTPS_SR_RXEMPTY) (u32)XUARTPS_SR_RXEMPTY) /* * 完整逻辑解析 * 1. Xil_In32(...) 读取 SR 寄存器 * 2. XUARTPS_SR_RXEMPTY 提取空标志位其他位清零 * 3. XUARTPS_SR_RXEMPTY 判断该位是否为 1FIFO 是否为空 * - 若为 1则比较结果为真FIFO 空 * - 若为 0则比较结果为假FIFO 非空 * 4. !逻辑非 取反结果 * - 若 FIFO 空比较真→ 取反为 0无数据 * - 若 FIFO 非空比较假→ 取反为 1有数据 * 最终返回值直接指示“是否有数据可读”。 */2.XUartPs_RecvByte函数概述该函数是 UART 驱动中用于同步轮询接收单字节数据的最基础底层接口。它的主要作用是一直阻塞CPU 执行直到硬件接收 FIFO 中至少有一个有效数据字节然后立即将该字节从 FIFO 读出并返回给调用者输入参数是 UART 控制器的基地址BaseAddress返回值是接收到的 8 位数据字节u8 类型。该函数是纯硬件层面的直接操作不依赖任何软件缓冲区或中断机制适用于简单轮询场景或作为中断处理中手动读取数据的核心工具。整体分析函数首先在栈上声明一个 32 位局部变量 RecievedByte 用于暂存从 FIFO 寄存器读出的原始数据随后进入一个 while 循环循环条件是 !XUartPs_IsReceiveData(BaseAddress)即通过之前分析的宏读取状态寄存器的 RXEMPTY 位判断 FIFO是否为空若 FIFO 为空则循环体执行空语句 ; 持续等待直到 FIFO 中有数据才会退出循环退出循环后立即调用 XUartPs_ReadReg(BaseAddress, XUARTPS_FIFO_OFFSET) 从硬件 FIFO 寄存器中读出32 位数据由于 UART 数据宽度为 8 位实际有效值在低 8 位最后将 RecievedByte 强制转换为u8类型并返回给调用者。整个函数通过“忙等待”实现了数据的即时读取但需要注意该函数在中断上下文中调用时会长时间占用 CPU可能影响系统实时性。逐行注释/****************************************************************************/ /** * * 此函数从设备接收一个字节。它在轮询模式下工作并会阻塞直到接收到一个字节。 * * param BaseAddress 包含设备的基地址。 * * return 接收到的数据字节。 * * note 无 * *****************************************************************************/ u8 XUartPs_RecvByte(u32 BaseAddress) { u32 RecievedByte; /* 定义 32 位变量用于暂存从 FIFO 读出的原始数据 */ /* 等待直到有数据可用即 FIFO 非空 */ while (!XUartPs_IsReceiveData(BaseAddress)) { ; /* 空循环体持续轮询 FIFO 空标志直到硬件收到数据 */ } /* 从 FIFO 寄存器中读取一个 32 位数据 */ RecievedByte XUartPs_ReadReg(BaseAddress, XUARTPS_FIFO_OFFSET); /* UART FIFO 数据宽度为 8 位有效数据位于读回值的低 8 位 */ /* 返回接收到的字节强制转换为 8 位无符号整型 */ return (u8)RecievedByte; }3.XUartPs_SendByte函数概述该函数是 UART 驱动中用于同步轮询发送单字节数据的最基础底层接口。它的主要作用是一直阻塞 CPU 执行直到硬件发送 FIFO 中有足够的空间容纳待发送数据然后将传入的数据字节写入 FIFO 寄存器后立即返回输入参数包括 UART 控制器的基地址BaseAddress和待发送的 8 位数据字节Data无返回值。该函数与 XUartPs_RecvByte 形成完整的收发对称是纯硬件级别的直接操作不依赖任何软件缓冲区或中断机制。逻辑分析函数首先进入一个 while 循环循环条件是 XUartPs_IsTransmitFull(BaseAddress)——这是一个检测发送 FIFO 是否已满的宏内部通过读取状态寄存器SR的TXFULL位来判断。若 FIFO 已满循环执行空语句 ; 持续等待直到硬件将 FIFO 中的数据移入移位寄存器并开始串行发送释放出空位。当 FIFO 有可用空间时循环退出函数调用 XUartPs_WriteReg 将传入的 Data 字节强制转换为 32 位写入发送 FIFO 寄存器XUARTPS_FIFO_OFFSET写入完成后函数立即返回。需要注意的是该函数只保证数据被成功写入 FIFO并不保证数据已经通过串行线路完全发送完毕——实际发送由硬件移位寄存器在后台完成FIFO 释放空位后即可写入下一个字节从而实现高效的数据流水线发送。逐行注释/****************************************************************************/ /** * * 此函数使用设备发送一个字节。该函数在轮询模式下工作并会阻塞直到数据已被放入 TX FIFO 寄存器。 * * param BaseAddress 包含设备的基地址。 * param Data 包含要发送的字节。 * * return 无 * * note 无 * *****************************************************************************/ void XUartPs_SendByte(u32 BaseAddress, u8 Data) { /* 等待直到 TX FIFO 中有可用空间即 FIFO 未满 */ while (XUartPs_IsTransmitFull(BaseAddress)) { ; /* 空循环体持续轮询 TX FIFO 满标志直到硬件腾出空位 */ } /* 将字节写入 TX FIFO */ XUartPs_WriteReg(BaseAddress, XUARTPS_FIFO_OFFSET, (u32)Data); /* 写入后函数立即返回硬件自动将 FIFO 中的数据移入移位寄存器进行串行发送 */ }