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"); } } }