UART 通用异步收发器
手册 UG585
UART控制器,实现串口收发功能。
支持宽范围可编程的波特率和 I/O的信号格式(串口通信数据位宽一般8位,也支持配置使用6位,7位。常规是使用一个字节8bit)
全双工:指有两个引脚 TX,RX。可以在同一时间内进行收发
异步:不依赖时钟,只有两根数据线
UART的操作是通过配置控制器和模式寄存器使用。要配置波特率或者要发送数据都是靠操作寄存器实现的
UART的结构有独立的 接收RX 和 发送TX 数据的路径,每个路径包含一个64字节的fifo ,这个控制器可以连续的或非连续的将数据写到RX FIFO中,包括一个模式的切换,相当于给RXD和TXD的引脚进行配置
FIFO的中断状态位支持轮询或中断的方式。轮询的方式下不停的读取其寄存器,可以判断当前有没有接收数据。 中断的方式:配置一个接收的阈值,当达到这个阈值之后就产生中断,使用中断的方式能更好的执行代码,CPU能执行其他的功能,只有当接收到串口的数据之后,才打断进程,接收串口的数据。
软件的读写都是通过RX 和TX 的数据寄存器
停止位可以配置,一般使用一位停止位
校验:奇校验,偶校验。校验位SPACE固定为0,校验位MARK固定为1,也可以没有校验。平时不使用校验,选无校验
UART整天框图介绍
CPU实现对UART接口控制器配置:APB SLAVE PORT(高级外设总线,是AMBA总线的一部分【这是ARM的总线架构了】),是一个从机的接口。
还需要提供CPU时钟,和UART的参考时钟
控制和状态寄存器:控制主要是对UART控制器进行配置。状态主要是读取他的状态寄存器来获取当前他的状态,比方说当前 发送FIFO或 接收FIFO 的状态 是空还是满等等。
支持中断的输出:因为有两个串口外设,中断号有两个为 UART0 59,UART1 82
与外部的连接通过MIO或是EMIO。当连接到PL引脚的时候,是一个支持相当于调制解调器的模式,这些串口流的引脚不支持连接到PS端。PS端只能连RX TX
UART控制器是一个APB SLAVE 接口,CPU可以通过PS 的AXI 互连实现对这个接口的配置和读取。比方说串口默认的模式可能不符合我们的预期,现在需要配置串口,就可以通过这个接口实现对他的配置(通过配置控制寄存器,其他的寄存器比如模式寄存器,中断寄存器等等)
要发数据的时候,将数据通过APB的接口将数据写到TX FIFO当中。如何把数据写到TX FIFO中:通过操作TX FIFO的寄存器,只需向寄存器里写入数据,数据就会被写到TX FIFO。当TX FIFO非空之后,就会将数据写到发送器,相当于实现并串的转换,将并行的数据串行发送出去
MODE SWITCH:串行的数据是发到TXD 还是再环回过来。正常使用就是正常的模式:即将串行的数据连接到TXD引脚,选择连接MIO/EMIO的引脚将数据发送出去。这既是发送数据的一个通道
接收数据即是反过程,正常模式下将串行数据连接到接收器当中,在接收器中实现串并转换,将并行的数据送到RXFIFO当中。CPU要读这个数据,通过读取状态寄存器,当前FIFO是否为空,非空就说明FIFO中有了数据,已经接收到数据。CPU就可以通过APB接口将FIFO中的数据读出,通过读接收FIFO的寄存器就可以将其数据读出
UART中断接口连接中断控制器
UART参考时钟,可以选择是否进行8分频,产生波特率
流数据的引脚只能连接到EMIO
不需要了解它的时序,只需要了解它 的寄存器,通过操作这些寄存器实现串口收发的功能
两个状态寄存器
可以读取中断状态寄存器的状态并产生中断。
状态寄存器只能被读取,获取他的状态
STICKY(粘性)某一位被置位之后,它会一直保持这个位,直到我们的软件把它清除。只有朝这个位写1才能清除这个位。和中断掩码寄存器是一个按位与的操作:只有掩码寄存器的某一位为高电平1,中断状态寄存器的状态也是某一位为1,与的结果就都是为1了,只有都是为1的时候才能产生中断然后送到中断控制器。相当于掩码的功能,控制中断状态寄存器的哪些位来去产生中断,如果中断状态寄存器都为1,掩码状态寄存器都为0就不会产生中断。同一位都为1才能产生中断。
中断掩码寄存器是只读的寄存器(READ ONLY),可以把它读出来,通过它是高电平使能状态还是低电平掩码状态去判断中断状态寄存器的哪些位有没有开启中断
它是只读的寄存器,如何改变里面的值?通过下面两个寄存器进行配置的。中断使能寄存器,中断除能寄存器。 使能1 除能0 配置为1 使能0 除能1 配置为0 使能,除能的值相等 配置为0
这4个寄存器每一位所表示的含义
FIFO的中断操作
编程指南
不知道具体是哪一位,打开后面的UART寄存器列表
发送和接收都是TX_RX_FIFO0寄存器,但是对应的是两个不同的FIFO
加不加&是由输入的函数类型决定的,输入的是指针类型的变量,加&才能与输入类型一致
这样子不会报错,这相当于是用函数结构体指针的输入的方式来使用。
设置接收FIFO的触发阈值
高电平触发是针对GPIO外设的,对于UART外设可以不去做这个触发类型的配置
配置UART的中断
对于串口来说,支持很多的中断:FIFO空中断,接收FIFO阈值中断,FIFO满中断。。。中断究竟来自哪里就需要我们去读中断状态寄存器的位,来确认到底因为哪个中断状态产生的中断
只获取一个字节
获取多个字节,最后一个参数是字节数
中断触发后中断状态位需要清除,通过写寄存器操作进行赋值,
/* * main.c * * Created on: 2022年2月22日 * Author: lht */ /* * main.c * * Created on: 2022年2月20日 * Author: lht */ #include "stdio.h" #include "xscugic.h" #include "xuartps.h" #include "xparameters.h" #include "xil_exception.h" #define UART_DEVICE_ID XPAR_PS7_UART_1_DEVICE_ID #define gic_id XPAR_PS7_SCUGIC_0_DEVICE_ID #define uart_intr_id XPAR_XUARTPS_1_INTR void init_uart(); void init_intr_uart(); void uart_intr_handler(); XUartPs_Config *Config; XUartPs UartPs; XScuGic scugic; unsigned char recvbuf[2]; unsigned char sendbuf[2]; int main(){ init_uart(&UartPs); init_intr_uart(&scugic,&UartPs); while(1); return 0; } void init_uart(XUartPs* UartPs){ XUartPs_Config *Config; Config = XUartPs_LookupConfig(UART_DEVICE_ID); XUartPs_CfgInitialize(UartPs, Config, Config->BaseAddress); //SetupInterruptSystem(IntcInstPtr, UartInstPtr, UartIntrId); XUartPs_SetOperMode(UartPs, XUARTPS_OPER_MODE_NORMAL); XUartPs_SetBaudRate(UartPs,115200); XUartPs_SetFifoThreshold(UartPs,2); } void init_intr_uart(XScuGic *scugic,XUartPs *UartPs){ XScuGic_Config *intc_cfg; intc_cfg = XScuGic_LookupConfig(gic_id); XScuGic_CfgInitialize(scugic,intc_cfg,intc_cfg->CpuBaseAddress); Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, scugic); Xil_ExceptionEnable(); XScuGic_Connect(scugic,uart_intr_id,(Xil_ExceptionHandler)uart_intr_handler,UartPs); XUartPs_SetInterruptMask(UartPs,XUARTPS_IXR_RXOVR); XScuGic_Enable(scugic,uart_intr_id); } void uart_intr_handler(XUartPs *CallBackRef){ XUartPs *uart_instance_ptr =(XUartPs *)CallBackRef; u32 isrstatus; u32 rec_data=0; isrstatus = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_IMR_OFFSET); isrstatus &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_ISR_OFFSET); if(isrstatus & XUARTPS_IXR_RXOVR){ rec_data=XUartPs_Recv(&UartPs,recvbuf, 2); //rec_data =XUartPs_RecvByte(uart_instance_ptr->Config.BaseAddress); XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress,XUARTPS_ISR_OFFSET,XUARTPS_IXR_RXOVR); } //XUartPs_Send(&UartPs,recvbuf,2); XUartPs_SendByte(uart_instance_ptr->Config.BaseAddress,rec_data); //printf("rec_data=%d\n",rec_data); }
#include "stdio.h" #include "xparameters.h" #include "xscugic.h" #include "xuartps.h" #include "xil_exception.h" #include "xil_printf.h" #define uart_id XPAR_PS7_UART_1_DEVICE_ID #define baud_rate XUARTPS_DFT_BAUDRATE #define normal_mode XUARTPS_OPER_MODE_NORMAL #define gic_id XPAR_PS7_SCUGIC_0_DEVICE_ID #define uart1_intr XPAR_PS7_UART_1_INTR unsigned char recvbuf[32]; XUartPs uart; XUartPs_Config *uart_cfg; XScuGic scugic; XScuGic_Config * gic_cfg; void init_uart(); void init_intr(); void uart1handler(void *CallBackRef, u32 Event,u32 EventData); int main(){ printf("hi\n\r"); init_intr(); init_uart(); while(1){} return 0; } void init_uart(){ // XUartPs uart; // XUartPs_Config *uart_cfg; u32 intrmask=0; uart_cfg=XUartPs_LookupConfig(uart_id); XUartPs_CfgInitialize(&uart,uart_cfg,uart_cfg->BaseAddress); XUartPs_SetBaudRate(&uart,baud_rate); XUartPs_SetHandler(&uart,(XUartPs_Handler)uart1handler,&uart); intrmask=XUARTPS_IXR_TOUT; XUartPs_SetInterruptMask(&uart,intrmask); XUartPs_SetOperMode(&uart,normal_mode); XUartPs_SetRecvTimeout(&uart,8); XUartPs_Recv(&uart,recvbuf,32);// } void init_intr(){ // XScuGic scugic; // XScuGic_Config * gic_cfg; gic_cfg=XScuGic_LookupConfig(gic_id); XScuGic_CfgInitialize(&scugic,gic_cfg,gic_cfg->CpuBaseAddress); Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&scugic); Xil_ExceptionEnable(); XScuGic_Connect(&scugic,uart1_intr,(Xil_ExceptionHandler)XUartPs_InterruptHandler,&uart); XScuGic_Enable(&scugic,uart1_intr); } void uart1handler(void *CallBackRef, u32 Event,u32 EventData){ u32 recvcnt; if(Event==XUARTPS_EVENT_RECV_TOUT){ recvcnt=EventData; if(recvcnt==8 && recvbuf[0]==0x55 && recvbuf[1]==0x55){ printf("recved frame1\n\r"); } } }