1 实验任务
本实验使用中断方式实现UART串口数据的连续发送。
2 系统框图
参见6.1。
3 硬件设计
参见6.1。
4 软件设计
4.1 注意事项
- 系统上电、程序下载后,此时TX FIFO虽然为空,但并不会触发空中断;空中断的触发前提是FIFO中有数据且被读空,即FIFO中的最后一个数据被读出(This event is triggered whenever the final word is removed from the transmit FIFO);
- 程序设计思路:
- (1)在程序下载后,假设上一次数据已发送完毕,SendComplete置1
- (2)将每一轮的首次数据发送放在while循环中,这样TxBuffer中的数据才能一轮一轮的循环发送起来(刚开始把首次数据发送放在while循环外边,在第一轮的最后一次数据发送完毕并触发空中断后,SendComplete置1,然后就没有然后了…)
- 对比XUartPs_Send函数和XUartPs_Recv函数发现:XUartPs_Send函数仅禁用与发送相关的中断,而XUartPs_Recv函数则禁用所有中断,原因是(以下内容来自DeepSeek)
- (1)接收的情况:如果数据到达RX FIFO,但未被及时读取,可能导致RX FIFO满;如果RX FIFO满了,此时又有新的数据到达,则新到达的数据会被丢弃;所以,要优先保证接收数据的过程不被打断(禁用所有中断),及时取走
- (2)发送的情况:只要TX FIFO未满,就可以继续写入数据;如果TX FIFO满了,硬件会自动停止发送,不会导致数据损坏
4.2 函数详解
- XUartPs_Send函数:
- (1)函数功能:发送数据到TX FIFO
- (2)函数实现:
- 1)禁用TX FIFO空中断和TX FIFO满中断
- 2)设置缓冲区参数,包括请求发送的字节数、剩余待发送的字节数和指向下一个要发送的字节的指针
- 3)调用XUartPs_SendBuffer函数,该函数完成数据发送功能并返回实际发送的字节数
- 4)接收XUartPs_SendBuffer函数的返回值并返回
- XUartPs_SendBuffer函数:
- (1)函数功能:以轮询或中断驱动模式将缓冲区数据发送到TX FIFO
- (2)函数实现:
- 1)发送数据到TX FIFO,如果TX FIFO未满且缓冲区中还有数据未发送,则将数据写入TX FIFO(If the TX FIFO is full, send nothing. Otherwise put bytes into the TX FIFO unil it is full, or all of the data has been put into the FIFO)
- 2)更新缓冲区指针和剩余字节数
- 3)使能TX FIFO空中断:注意,如果接收中断已启用,则启用TX FIFO空中断(这也是为何在UartInit函数中使能看似无关的XUARTPS_IXR_RXOVR中断的原因)
- 疑问1:为什么在启用接收中断的前提下才能启用TX FIFO空中断
- 猜测原因是:让发送和接收工作在相同的模式(轮询或中断)下
- 疑问2:为什么仅启用了TX FIFO空中断,TX FIFO满中断在XUartPs_Send也被禁用了,为何未被重新启用(以下内容来自DeepSeek)
- XUartPs_SendBuffer函数已处理TX FIFO满的情况:在while循环中,只有当TX FIFO未满时才会写入数据,一旦TX FIFO满则停止写入;此时,不需要TXFULL中断通知,因为XUartPs_SendBuffer函数已主动检查并处理了TX FIFO满的状态;
- TXEMPTY中断用于继续发送:启用TXEMPTY中断的目的是在TX FIFO有空闲空间时(即发送了一部分数据后),触发中断自动填充剩余数据
- 疑问1:为什么在启用接收中断的前提下才能启用TX FIFO空中断
- 4)返回实际发送的字节数
4.3 工程源码
/************************** Include Files ***********************************/
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"
#include "stdio.h"
#include "sleep.h"
/************************** Constant Definitions ****************************/
#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INTR_ID XPAR_XUARTPS_1_INTR
#define BUFFER_SIZE 256 // 发送缓冲区大小
#define FIFO_TRIGGER_LEVEL 32 // FIFO触发阈值
#define RECV_TIMEOUT 4 // 接收超时时间(单位:波特率时钟周期)
#define DEBUG 0
/************************** Function Prototypes *****************************/
s32 UartPsInit(XUartPs *UartPsInstPtr, XUartPsFormat* UartFormatPtr);
s32 SetupInterruptSystem(XScuGic *IntcInstPtr, XUartPs *UartPsInstPtr);
void UartIntrHandler(void *CallBackRef);
/************************** Variable Definitions ****************************/
XUartPs UartInst;
XScuGic IntcInst;
u8 TxBuffer[BUFFER_SIZE] = { 0 }; // 接收缓冲区
int RxDataLength = 0; // 接收到的数据长度
XUartPsFormat UartFormat = {
XUARTPS_DFT_BAUDRATE, // 115200
XUARTPS_FORMAT_8_BITS,
XUARTPS_FORMAT_NO_PARITY,
XUARTPS_FORMAT_1_STOP_BIT
};
// 发送状态
u32 TotalBytesSent; // 已发送的字节数
int SendComplete; // 发送完成标志
/************************** Function Implementation *************************/
int main()
{
//
s32 Status;
u32 BytesSent;
//
for (int i = 0; i < BUFFER_SIZE; i++) {
TxBuffer[i] = (u8)i; // 填充从 0 开始的递增数
}
// 初始化UART
Status = UartPsInit(&UartInst, &UartFormat);
if (Status == XST_FAILURE) {
printf("Error : uart initialization failed.\n");
return XST_FAILURE;
}
// 设置中断系统
Status = SetupInterruptSystem(&IntcInst, &UartInst);
if (Status == XST_FAILURE) {
printf("Error : setup interrupt system failed.\n");
return XST_FAILURE;
}
// 主循环
SendComplete = 1;
while(1)
{
if (SendComplete == 1) {
//
TotalBytesSent = 0;
SendComplete = 0;
sleep(3);
// 启动第一次发送
BytesSent = XUartPs_Send(&UartInst, TxBuffer, BUFFER_SIZE);
TotalBytesSent += BytesSent;
}
}
//
return XST_SUCCESS;
}
/****************************************************************************/
s32 UartPsInit(XUartPs *UartInstPtr, XUartPsFormat* UartFormatPtr)
{
//
s32 Status;
XUartPs_Config *UartConfigPtr;
// 查找UART配置
UartConfigPtr = XUartPs_LookupConfig(UART_DEVICE_ID);
if(NULL == UartConfigPtr)
{
return XST_FAILURE;
}
// 初始化UART
Status = XUartPs_CfgInitialize(UartInstPtr, UartConfigPtr, UartConfigPtr->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// 设置UART数据格式
XUartPs_SetDataFormat(UartInstPtr, UartFormatPtr);
// 设置UART操作模式
XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_NORMAL);
// 设置接收FIFO触发阈值
XUartPs_SetFifoThreshold(UartInstPtr, FIFO_TRIGGER_LEVEL);
// 设置接收超时
XUartPs_SetRecvTimeout(UartInstPtr, RECV_TIMEOUT);
// 设置中断掩码,使能发送FIFO空中断
XUartPs_SetInterruptMask(UartInstPtr, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TXEMPTY);
//
return XST_SUCCESS;
}
/****************************************************************************/
s32 SetupInterruptSystem(XScuGic *IntcInstPtr, XUartPs *UartInstPtr)
{
//
s32 Status;
XScuGic_Config *IntcConfigPtr;
// 初始化中断控制器GIC
IntcConfigPtr = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfigPtr)
{
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstPtr, IntcConfigPtr, IntcConfigPtr->CpuBaseAddress);
if (Status != XST_SUCCESS)
{
return XST_FAILURE;
}
// 注册异常处理程序
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, IntcInstPtr);
Xil_ExceptionEnable();
// 连接UART中断处理程序
XScuGic_Connect(IntcInstPtr, UART_INTR_ID, (Xil_InterruptHandler)UartIntrHandler, (void *)UartInstPtr);
// 使能UART中断
XScuGic_Enable(IntcInstPtr, UART_INTR_ID);
//
return XST_SUCCESS;
}
/****************************************************************************/
void UartIntrHandler(void *CallBackRef)
{
//
XUartPs* UartInstPtr = (XUartPs*)CallBackRef;
u32 IsrStatus;
// 读取中断状态
IsrStatus = XUartPs_ReadReg(UartInstPtr->Config.BaseAddress, XUARTPS_IMR_OFFSET);
IsrStatus &= XUartPs_ReadReg(UartInstPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET);
// 处理发送FIFO空中断
if ((IsrStatus & (u32)XUARTPS_IXR_TXEMPTY) != (u32)0) {
//
XUartPs_WriteReg(UartInstPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TXEMPTY);
//
if (TotalBytesSent < BUFFER_SIZE) {
// 继续发送剩余的数据
u32 BytesSent = XUartPs_Send(UartInstPtr, &TxBuffer[TotalBytesSent], BUFFER_SIZE - TotalBytesSent);
TotalBytesSent += BytesSent;
}
else {
// 所有数据已发送完毕
SendComplete = 1;
//
#if DEBUG
printf("UartPs sent data successfully in interrupt mode!\n");
#endif
}
}
//
return;
}