UART简介
不需要了解时序。
UART是全双工异步收发的,没有时钟。
UART的操作通过配置和模式寄存器控制。
UART由独立的接受和发送路径构成。每个路径包含深度为64字节的FIFO。FIFO中断状态为支持轮询或中断方式。
轮询方式:不停地读取寄存器看有没有数据。
中断方式:只有当接收到串口数据后才打断主进程来接收数据。
数据位宽:6/7/8bit
停止位:1/1.5/2bit
发送FIFO
CPU通过APB接口将数据写入发送FIFO,直到发送模块(Transmitter,执行并转串)将数据从FIFO中读出并送到移位寄存器。CPU通过操纵TxFIFO寄存器来写数据。
写入数据后,TxFIFO的空标志被清零,发送模块检测到非空后,开始从TxFIFO中读出数据,并转为串行。
接收FIFO
接收FIFO存储来源于接收模块(Receiver,执行串转并)的数据,写入RxFIFO。CPU通过APB接口读取RxFIFO寄存器。
阈值触发:当RxFIFO接收到设定的阈值位数的数据后,阈值触发标志位变为1。可用于产生中断。
模式切换
寄存器操作
中断和状态寄存器
有两个状态寄存器:
- 中断状态寄存器可以被读取状态和产生中断。(sticky)
- 状态寄存器仅能读取状态。(dynamic)
中断状态寄存器和掩码寄存器(只读)按位与。仅有都为1的位才能产生中断,送入中断控制器。
通过中断使能寄存器和中断除能寄存器来控制中断掩码寄存器。
收发FIFO寄存器的几种中断:空、满、触发阈值、接近满、溢出。
发送数据
轮询
- 检查TxFIFO是否为空
- 向TxFIFO中写数(64B)
- 等待TxFIFO空后再次写入
中断
- 除能TxFIFO空中断
- 向TxFIFO中写数(64B)
- 检查TxFIFO中还有没有空间可以写数
- 重复2、3
- 使能中断
- 等待TxFIFO为空后返回1
接收数据
轮询
- 等待RxFIFO中数据达到阈值或接收超时
- 从RxFIFO中读数据
- 重复2直到RxFIFO空
- 若接收超时,清除超时位
中断
- 使能中断
- 等待RxFIFO中数据达到阈值或接收超时
- 从RxFIFO中读数据
- 重复2、3
- 清除中断
读写数据操纵的都是TX_RX_FIFO0寄存器,其中包含收发两个FIFO。
实验:串口中断数据环回
/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*
* helloworld.c: simple test application
*
* This application configures UART 16550 to baud rate 9600.
* PS7 UART (Zynq) is not initialized by this application, since
* bootrom/bsp configures it to baud rate 115200
*
* ------------------------------------------------
* | UART TYPE BAUD RATE |
* ------------------------------------------------
* uartns550 9600
* uartlite Configurable only in HW design
* ps7_uart 115200 (configured by bootrom/bsp)
*/
/*
* 20230307运行成功!
*/
#include <stdio.h>
//#include "platform.h"
#include "xil_printf.h"
#include "xuartps.h"
#include "xparameters.h"
#include "xscugic.h"
#include "xuartps_hw.h"
#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_0_INTR
#define INTC XScuGic
#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
//#define TEST_BUFFER_SIZE 100
XUartPs UartPs ;
XUartPs_Config *Config;
INTC InterruptController; // 中断控制器驱动实例
//u8 RecvBuffer[TEST_BUFFER_SIZE];
void Handler();
void uart_init();
void uart_intr_handler();
void uart_intr_init(INTC *IntcInstancePtr,XUartPs *UartInstancePtr,u16 UartIntrId);
int SetupInterruptSystem(INTC *IntcInstancePtr,XUartPs *UartInstancePtr,u16 UartIntrId);
int main()
{
// 串口初始化
uart_init();
xil_printf("Uart initializes successfully!");
// 串口中断初始化(包含:1.对Uart的中断进行配置;2.对PS的中断控制器(Gic)进行配置)
uart_intr_init(&InterruptController, &UartPs, UART_INT_IRQ_ID);
while(1); // 让程序停在这个位置,用来检测中断,只有cpu收到中断指令,才会跳出while(1),进入中断服务子程序
return 0;
}
// 串口初始化函数
void uart_init(){
Config = XUartPs_LookupConfig(UART_DEVICE_ID);
XUartPs_CfgInitialize(&UartPs, Config, Config->BaseAddress);
// 设置串口工作模式
XUartPs_SetOperMode(&UartPs, XUARTPS_OPER_MODE_NORMAL);
// 设置串口波特率(默认就是115200)
XUartPs_SetBaudRate(&UartPs, 115200);
// 设置接收FIFO中断事件触发阈值,这里设置为1Byte,即每接收到1字节的数据就要产生一次中断,在中断函数中处理此中断
XUartPs_SetFifoThreshold(&UartPs, 1);
}
// 串口中断初始化函数
void uart_intr_init(INTC *IntcInstancePtr,XUartPs *UartInstancePtr,u16 UartIntrId){
/*
* 以下为中断控制器Gic配置
*/
XScuGic_Config *IntcConfig;
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
// 关联中断ID和中断处理函数
XScuGic_Connect(IntcInstancePtr, UartIntrId, (Xil_ExceptionHandler) uart_intr_handler, (void *) UartInstancePtr);
// 设置并打开中断异常处理功能
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler,IntcInstancePtr);
Xil_ExceptionEnable();
/*
* 以下为Uart中断配置
*/
// 设置UART中断触发方式,即哪些状态位能够产生中断
u32 IntrMask;
IntrMask = XUARTPS_IXR_RXOVR; // 接收fifo触发阈值中断位
XUartPs_SetInterruptMask(UartInstancePtr, IntrMask);
/*
* 使能UART中断
*/
XScuGic_Enable(IntcInstancePtr, UartIntrId);
}
// 中断处理函数
void uart_intr_handler(XUartPs *UartInstancePtr){
// 读取中断状态寄存器
u32 rec_data = 0;
u32 IsrStatus;
IsrStatus = XUartPs_ReadReg(UartInstancePtr->Config.BaseAddress,
XUARTPS_IMR_OFFSET); // 获取中断掩码寄存器值,此处应该只有最低位为1,对应接收触发阈值中断
IsrStatus &= XUartPs_ReadReg(UartInstancePtr->Config.BaseAddress,
XUARTPS_ISR_OFFSET); // 获取对应的中断状态寄存器的值
// 若满足中断掩码寄存器和中断状态寄存器的与为1,再和接收fifo触发阈值位相与,为高电平,则确实为所需类型中断
/*
* 接收处理
*/
if(IsrStatus & XUARTPS_IXR_RXOVR) {
// 读取出Rxfifo中接收到的数据(仅一个字节)
// rec_data = XUartPs_Recv(UartInstancePtr, RecvBuffer, TEST_BUFFER_SIZE);
rec_data = XUartPs_RecvByte(UartInstancePtr->Config.BaseAddress);
// 清除中断状态寄存器的接收fifo触发阈值位
XUartPs_WriteReg(UartInstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
XUARTPS_IXR_RXOVR);
}
/*
* 发送处理
*/
XUartPs_SendByte(UartInstancePtr->Config.BaseAddress, rec_data);
}