【JokerのZYNQ7020】UART

软件环境:vivado 2017.4        硬件平台:XC7Z020

内部结构大概如图所示,PL这边跟上节中断配置的基本一样,其实主要还是在SDK程序这边。

 Vivado 2017.4 Create Block Design后,添加ZYNQ7 Processing system,然后自动连接就行,Generate the output products,Create a HDL wrapper,Generate Bitstream,Export Hardware(注意勾选include biestream),最后launch SDK,加入如下代码。

    #include <stdio.h>
    #include "xadcps.h"
     
    #include "xil_types.h"
    #include "Xscugic.h"
    #include "Xil_exception.h"
    #include "xuartps.h"
     
    //timer info
    #define UART_DEVICE_ID      XPAR_PS7_UART_1_DEVICE_ID
    #define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
    #define UART_IRPT_INTR      XPAR_XUARTPS_1_INTR
     
    static XScuGic Intc; //GIC
    static XUartPs Uart;//uart
     
     
    static void UartIntrHandler(void *CallBackRef)
    {
     
        XUartPs *InstancePtr = (XUartPs *) CallBackRef;
        u32 IsrStatus;
     
        u32 ReceivedCount=0;
     
        u32 CsrRegister;
        /*
         * Read the interrupt ID register to determine which
         * interrupt is active
         */
        IsrStatus = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
                       XUARTPS_IMR_OFFSET);//e0001000+10=regaddr=e0001010
     
        IsrStatus &= XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
                       XUARTPS_ISR_OFFSET);//e0001000+14=regaddr=e0001014
     
        /* Dispatch an appropriate handler. */
        if((IsrStatus & ((u32)XUARTPS_IXR_RXOVR | (u32)XUARTPS_IXR_RXEMPTY |
                (u32)XUARTPS_IXR_RXFULL)) != (u32)0) {
     
        CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,//判断FIFO触发标准位
                        XUARTPS_SR_OFFSET);//e0001000+2c=regaddr=e000102c
     
        while((CsrRegister & XUARTPS_SR_RXEMPTY)== (u32)0){//读取FIFO中所有数据
                XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET,
                           XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
                                                 XUARTPS_FIFO_OFFSET));
     
                ReceivedCount++;//计数
                CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
                                        XUARTPS_SR_OFFSET);
            }
        }
        printf("this time ReceivedCount=%ld\r\n",ReceivedCount);
        XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
            IsrStatus);
    }
     
    void SetupInterruptSystem(XScuGic *GicInstancePtr,
            XUartPs *UartInstancePtr, u16 UartIntrId)
    {
     
     
            XScuGic_Config *IntcConfig; //GIC config
            Xil_ExceptionInit();
            //initialise the GIC
            IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
            XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
                            IntcConfig->CpuBaseAddress);
     
            Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                        (Xil_ExceptionHandler)XScuGic_InterruptHandler,//connect to the hardware
                        GicInstancePtr);
            XScuGic_Connect(GicInstancePtr, UartIntrId,
                            (Xil_InterruptHandler)UartIntrHandler,//set up the timer interrupt
                            (void *)UartInstancePtr);
     
            XScuGic_Enable(GicInstancePtr, UartIntrId);//enable the interrupt for the Timer at GIC
            XUartPs_SetInterruptMask(UartInstancePtr, XUARTPS_IXR_RXOVR/* | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_TNFUL*/ );
            Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //Enable interrupts in the Processor.
        }
     
     
    int main()
    {
         XUartPs_Config *UartConfigPtr;     //uart config
         UartConfigPtr = XUartPs_LookupConfig(UART_DEVICE_ID);
         XUartPs_CfgInitialize(&Uart,UartConfigPtr,UartConfigPtr->BaseAddress);
         SetupInterruptSystem(&Intc,&Uart,UART_IRPT_INTR);
         while(1);
     
         return 0;
    }

接下来对程序中的一些函数做一些解释说明:

XUartPs_Config串口配置结构体,包括设备号、基址、输入式中频率、引脚链接方式。

    typedef struct {
        u16 DeviceId;     /**< Unique ID  of device */
        u32 BaseAddress; /**< Base address of device (IPIF) */
        u32 InputClockHz;/**< Input clock frequency */
        s32 ModemPinsConnected; /** Specifies whether modem pins are connected
                     *  to MIO or FMIO */
    } XUartPs_Config;

XScuGic要配置中断,Gic结构体自然是不必说的啊。

    typedef struct
    {
        XScuGic_Config *Config;  /**< Configuration table entry */
        u32 IsReady;         /**< Device is initialized and ready */
        u32 UnhandledInterrupts; /**< Intc Statistics */
    } XScuGic;

XUartPs结构体,包括配置项、输入时钟频率、初始化状态、波特率、接收发送buffer、中断函数、中断函数参数等等。

    typedef struct {
        XUartPs_Config Config;    /* Configuration data structure */
        u32 InputClockHz;    /* Input clock frequency */
        u32 IsReady;        /* Device is initialized and ready */
        u32 BaudRate;        /* Current baud rate */
     
        XUartPsBuffer SendBuffer;
        XUartPsBuffer ReceiveBuffer;
     
        XUartPs_Handler Handler;
        void *CallBackRef;    /* Callback reference for event handler */
        u32 Platform;
        u8 is_rxbs_error;
    } XUartPs;

程序最开始处宏定义的UART_DEVICE_ID是设备ID、INTC_DEVICE_ID中断的设备ID和UART_IRPT_INTR是串口的中断号。

    #define UART_DEVICE_ID      XPAR_PS7_UART_1_DEVICE_ID
    #define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID
    #define UART_IRPT_INTR      XPAR_XUARTPS_1_INTR

不断的open declaration可以看到#define XPS_UART1_INT_ID        82U至于这个82怎么来的。查ug585。

XUartPs_LookupConfig()可以看到,是串口专用的初始化配置函数。

    ******************************************************************************/
    XUartPs_Config *XUartPs_LookupConfig(u16 DeviceId)
    {
        XUartPs_Config *CfgPtr = NULL;
     
        u32 Index;
     
        for (Index = 0U; Index < (u32)XPAR_XUARTPS_NUM_INSTANCES; Index++) {
            if (XUartPs_ConfigTable[Index].DeviceId == DeviceId) {
                CfgPtr = &XUartPs_ConfigTable[Index];
                break;
            }
        }
     
        return (XUartPs_Config *)CfgPtr;
    }

其中,XUartPs_ConfigTable表直接跟UART1挂钩,设备号、基址、时钟频率、引脚方式全通过这个结构体配置。

    XUartPs_Config XUartPs_ConfigTable[XPAR_XUARTPS_NUM_INSTANCES] =
    {
        {
            XPAR_PS7_UART_1_DEVICE_ID,
            XPAR_PS7_UART_1_BASEADDR,
            XPAR_PS7_UART_1_UART_CLK_FREQ_HZ,
            XPAR_PS7_UART_1_HAS_MODEM
        }
    };

XUartPs_CfgInitialize()是串口相关参数的初始化函数,例如数据位数、停止位数、校验方式、波特率等。

    s32 XUartPs_CfgInitialize(XUartPs *InstancePtr,
                       XUartPs_Config * Config, u32 EffectiveAddr)
    {
        s32 Status;
        u32 ModeRegister;
        u32 BaudRate;
     
        /* Assert validates the input arguments */
        Xil_AssertNonvoid(InstancePtr != NULL);
        Xil_AssertNonvoid(Config != NULL);
     
        /* Setup the driver instance using passed in parameters */
        InstancePtr->Config.BaseAddress = EffectiveAddr;
        InstancePtr->Config.InputClockHz = Config->InputClockHz;
        InstancePtr->Config.ModemPinsConnected = Config->ModemPinsConnected;
     
        /* Initialize other instance data to default values */
        InstancePtr->Handler = XUartPs_StubHandler;
     
        InstancePtr->SendBuffer.NextBytePtr = NULL;
        InstancePtr->SendBuffer.RemainingBytes = 0U;
        InstancePtr->SendBuffer.RequestedBytes = 0U;
     
        InstancePtr->ReceiveBuffer.NextBytePtr = NULL;
        InstancePtr->ReceiveBuffer.RemainingBytes = 0U;
        InstancePtr->ReceiveBuffer.RequestedBytes = 0U;
     
        /* Initialize the platform data */
        InstancePtr->Platform = XGetPlatform_Info();
     
        InstancePtr->is_rxbs_error = 0U;
     
        /* Flag that the driver instance is ready to use */
        InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
     
        /*
         * Set the default baud rate here, can be changed prior to
         * starting the device
         */
        BaudRate = (u32)XUARTPS_DFT_BAUDRATE;
        Status = XUartPs_SetBaudRate(InstancePtr, BaudRate);
        if (Status != (s32)XST_SUCCESS) {
            InstancePtr->IsReady = 0U;
        } else {
     
            /*
             * Set up the default data format: 8 bit data, 1 stop bit, no
             * parity
             */
            ModeRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
                          XUARTPS_MR_OFFSET);
     
            /* Mask off what's already there */
            ModeRegister &= (~((u32)XUARTPS_MR_CHARLEN_MASK |
                             (u32)XUARTPS_MR_STOPMODE_MASK |
                             (u32)XUARTPS_MR_PARITY_MASK));
     
            /* Set the register value to the desired data format */
            ModeRegister |=    ((u32)XUARTPS_MR_CHARLEN_8_BIT |
                             (u32)XUARTPS_MR_STOPMODE_1_BIT |
                             (u32)XUARTPS_MR_PARITY_NONE);
     
            /* Write the mode register out */
            XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_MR_OFFSET,
                       ModeRegister);
     
            /* Set the RX FIFO trigger at 8 data bytes. */
            XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
                       XUARTPS_RXWM_OFFSET, 0x08U);
     
            /* Set the RX timeout to 1, which will be 4 character time */
            XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
                       XUARTPS_RXTOUT_OFFSET, 0x01U);
     
            /* Disable all interrupts, polled mode is the default */
            XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_IDR_OFFSET,
                       XUARTPS_IXR_MASK);
     
            Status = XST_SUCCESS;
        }
        return Status;
    }

下图中,第一个框是默认波特率的设置,系统默认值是115200,第二个红框设置的是8个数据位数、1个停止位数、无校验方式。第三个红框是接收自带FIFO(看最顶上那张图,接收和发送自带了FIFO)多少字节触发接收中断,这三个红框的设置比较关键,一般改串口初始化配置也就改这几项,剩下要改的都在接收中断函数,就是那个Handle里面。

Xil_ExceptionInit()在这实际是个空函数,里面直接return的,据函数前的说明意思是这函数是个通用API,用于在所有支持的arm处理器上初始化异常处理程序,还放在这里主要是为了避免与之前软件版本产生意外的兼容性问题。

XScuGic_LookupConfig()和XScuGic_CfgInitialize()是与通用中断控制器相关的查找设备ID和获取设备配置表基址的函数,TIMER那一章里也有说过,设备的LookupConfig()和CfgInitialize()配设备的,中断的LookupConfig()和CfgInitialize()配中断的。

Xil_ExceptionRegisterHandler()处理特定异常时用的。通用函数。

    void Xil_ExceptionRegisterHandler(u32 Exception_id,
                        Xil_ExceptionHandler Handler,
                        void *Data)
    {
        XExc_VectorTable[Exception_id].Handler = Handler;
        XExc_VectorTable[Exception_id].Data = Data;
    }

XScuGic_Connect()将串口中断的中断函数和中断控制器绑定,当串口产生中断时,找UartIntrHandler()中断处理函数。

    s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
                          Xil_InterruptHandler Handler, void *CallBackRef)
    {
        /*
         * Assert the arguments
         */
        Xil_AssertNonvoid(InstancePtr != NULL);
        Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
        Xil_AssertNonvoid(Handler != NULL);
        Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
     
        /*
         * The Int_Id is used as an index into the table to select the proper
         * handler
         */
        InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
        InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;
     
        return XST_SUCCESS;
    }

XScuGic_Enable()使能串口中断。

    void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
    {
        u32 Mask;
     
        /*
         * Assert the arguments
         */
        Xil_AssertVoid(InstancePtr != NULL);
        Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
        Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
     
        /*
         * The Int_Id is used to create the appropriate mask for the
         * desired bit position. Int_Id currently limited to 0 - 31
         */
        Mask = 0x00000001U << (Int_Id % 32U);
     
        /*
         * Enable the selected interrupt source by setting the
         * corresponding bit in the Enable Set register.
         */
        XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
                            ((Int_Id / 32U) * 4U), Mask);
    }

XUartPs_SetInterruptMask()是设置中断方式的函数,当前设置的XUARTPS_IXR_RXOVR是接收FIFO来触发中断。

    void XUartPs_SetInterruptMask(XUartPs *InstancePtr, u32 Mask)
    {
        u32 TempMask = Mask;
        /* Assert validates the input arguments */
        Xil_AssertVoid(InstancePtr != NULL);
     
        TempMask &= (u32)XUARTPS_IXR_MASK;
     
        /* Write the mask to the IER Register */
        XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
             XUARTPS_IER_OFFSET, TempMask);
     
        /* Write the inverse of the Mask to the IDR register */
        XUartPs_WriteReg(InstancePtr->Config.BaseAddress,
             XUARTPS_IDR_OFFSET, (~TempMask));
     
    }

除此之外可以看到,可设置的其他中断触发方式还有很多,在这里备注如下。

    #define XUARTPS_IXR_RBRK    0x00002000U /**< 接收FIFO中断探测中断(这个啥意思目前不知=。=) */
    #define XUARTPS_IXR_TOVR    0x00001000U /**< 发送FIFO溢出中断 */
    #define XUARTPS_IXR_TNFUL    0x00000800U /**< 发送FIFO快满时中断 */
    #define XUARTPS_IXR_TTRIG    0x00000400U /**< 发送触发中断 */
    #define XUARTPS_IXR_DMS        0x00000200U /**< 应该是外引脚方式改变时触发中断 */
    #define XUARTPS_IXR_TOUT    0x00000100U /**< 超时错误中断 */
    #define XUARTPS_IXR_PARITY     0x00000080U /**< 校验错误中断 */
    #define XUARTPS_IXR_FRAMING    0x00000040U /**< 框架错误中断 */
    #define XUARTPS_IXR_OVER    0x00000020U /**< 来不及处理overrun中断 */
    #define XUARTPS_IXR_TXFULL     0x00000010U /**< 发送FIFO满中断 */
    #define XUARTPS_IXR_TXEMPTY    0x00000008U /**< 发送FIFO空中断 */
    #define XUARTPS_IXR_RXFULL     0x00000004U /**< 接收FIFO满中断 */
    #define XUARTPS_IXR_RXEMPTY    0x00000002U /**< 接收FIFO空中断 */
    #define XUARTPS_IXR_RXOVR      0x00000001U /**< 接收FIFO到达指定字节个数中断 */
    #define XUARTPS_IXR_MASK    0x00003FFFU /**< Valid bit mask应该是个综合设置(这个啥意思目前不知=。=) */

感觉用的比较多的应该还是最后的这几个。

 手册上还有更加直观的图,看一眼就明白。

Xil_ExceptionEnableMask()初始化最后当然是中断的使能函数。

    #define Xil_ExceptionEnableMask(Mask)    \
            mtcpsr(mfcpsr() & ~ ((Mask) & XIL_EXCEPTION_ALL))

初始化到这基本就算结束了,接下来瞅瞅串口中断里都干了点啥。

第一句,看下IMR, 看看你都使能了哪些中断标志,也就是哪些中断可以被触发,可以被系统响应;第二句,看下ISR,当前产生了哪些中断;两个进行与运算,其结果决定了接下来是否进入中断处理和进入哪个中断处理。

 然后判断,如果被触发的中断是接收相关的FIFO到达指定字节数、FIFO空、FIFO满其中一种,就需要继续进行相应处理。

上面那个CsrRegister存的是当前中断标志,看看哪些中断被置起来了,然后在下面的while里比较,看看被置起来的标志是不是XUARTPS_SR_RXEMPTY,这里需要说明一下,==0意味着读取FIFO不是空的,不信看E000102C寄存器。

 所以整个while的意思就是,如果RX的FIFO不是空的,就把接收FIFO中的数拿出来,写到发送寄存器里去,然后接收/发送的数据计数加1,重新再读取中断状态寄存器,看看还有没有数据需要在继续往外搬,以此循环直到所有数据处理完。

这里特别需要注意两点,也是我实验以后得到的结论:

1.用SDK中自带串口终端发数,按send以后,数据末尾自动添加\n,也就是说如果你发送123,实际发出的是123\n,4个字节。

2.如果你在最开始XUartPs_CfgInitialize()中XUARTPS_RXWM_OFFSET设置的是8字节触发,那么当你接收字节数小于8时,这个while是进不去的,只有当接收字节数大于等于8时,才会依次进while(8次),所以对于FIFO虽然只有8个bit宽度,但深度应该是XUARTPS_RXWM_OFFSET设置的最大64(我猜的。。。)。

3.读取数据和写入数据都是通过操作XUARTPS_FIFO_OFFSET这个偏移量的寄存器来完成的。

当所有数据处理完,最后往XUARTPS_ISR_OFFSET写入之前的ISR状态标志,将相应的中断状态写1清0。

最后小声哔哔一下,本来我以为串口中断两三行就写完了,结果没想到这么多东西。。。

所以在最后总结一下UART,在SDK中大致的流程:

通过XUartPs_LookupConfig函数,找到UART的基址------------>

通过XUartPs_CfgInitialize函数,初始化UART配置------------>

通过Xil_ExceptionInit函数,避免与前版本有兼容性问题------------>

通过XScuGic_LookupConfig函数,找到通用中断控制器基址------------>

通过XScuGic_CfgInitialize函数,初始化通用中断控制器------------>

通过Xil_ExceptionRegisterHandler函数,使能异常中断处理------------>

通过XScuGic_Connect函数,绑定UART中断处理函数------------>

通过XScuGic_Enable函数,在通用中断控制器中使能UART中断------------>

通过XUartPs_SetInterruptMask函数,设置UART的中断触发方式------------>

通过Xil_ExceptionEnableMask函数,使能中断。
---------------------
作者:Joker_是小王。
来源:CSDN
原文:https://blog.csdn.net/natty715/article/details/84668564
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值