首页 > 编程知识 正文

UART中断,zynq mpsoc

时间:2023-05-06 20:27:13 阅读:112187 作者:1846

学习内容本文首先介绍了ZYNQ定时器的相关内容,并学习了利用ZYNQ芯片定时器进行操作测试。

开发环境vivado 18.3SDK、PYNQ-Z2开发板。

摘要ZYNQ有两个Cortex-A9处理器,每个Cortex-A9处理器都有专用的32位计时器和32位看门狗计时器。 两个处理器共享一个全局64位计时器。 这些计时器总是以1/2的CPU频率计数。 (CPU_3x2x )。

在系统级别,有两个24位看门狗计时器和两个16位三计时器/计数器。 看门狗计时器的时钟频率为CPU频率(CPU_1x )的1/4或1/6,并且时钟可以是来自MIO端子或PL的外部信号。 两个三定时器/计数器的时钟频率为CPU频率(CPU_1x )的1/4或1/6,可用于计算来自MIO引脚或PL的脉冲宽度。

系统框图下图为ZYNQ内部计时器的系统框图。

在图中,计时器部分包含系统看门狗、CPU内部的看门狗计时器、CPU专用计时器、全局计时器和三计时器。 其中的所有计时器都可以触发中断控制器对中断控制的操作,看门狗计时器可以(无论是系统的还是CPU内部的)进行系统的复位操作。

CPU专用计时器和看门狗计时器对于CPU专用计时器和看门狗计时器都具有以下功能:

32位计数器是8位预分频器,其在达到零时产生中断,其中,可设置中断周期的一个或自动重载模式计数器的可设置起始值计时器和看门狗复位信号被发送到PS复位子系统下图显示了CPU专用计时器和看门狗计时器寄存器的表。

全局计时器(GT )全局计时器是具有自动增量功能的64位增量计数器。 全局计时器与专用计时器在相同的地址空间中映射到内存。全局计时器仅在复位时以安全状态访问。所有Cortex-A9处理器都可以访问全局计时器。 每个Cortex-A9处理器都有一个64位比较器,用于在全局计时器达到比较器值时声明专用中断。 全局计时器总是以CPU频率(CPU_3x2x )的1/2计数。

全局计时器(GT )寄存器菜单如下图所示。

在系统看门狗计时器(SWDT )中,除了两个CPU的专用看门狗计时器之外,还在发生了PS PLL锁定错误等系统故障的情况下,看门狗生成复位信号并重新启动程序,在系统中与CPU专用看门狗计时器不同,SWDT时钟可以来自外部设备或PL端,并向PL端口或外部设备输出复位信号。

特征SWDT的主要功能包括:

内部24位计数器。 可选时钟输入,时钟信号为1 .内部PS总线时钟(CPU_1x ); 2 .内部时钟(来自PL ); 3 .外部时钟(来自MIO )。 当超时时,可以进行系统中断(PS )和系统复位(PS、PL、MIO )。 可编程超时时间:1.超时范围32、760~68、719、476、736时钟周期(100 MHz下可设定范围为330s~687.2s )。 超时时可编程输出信号的持续时间:系统中断脉冲(4、8、16或32个时钟周期(CPU_1x时钟) )。 看门狗计时器寄存器的菜单如下。

编程指南系统看门狗计时器的有效顺序如下。

3358 www.Sina.com/:确保swdt (swdt.mode [ wden ]=0)无效,并且时钟源设置为选定的运行状态,然后继续此步骤。 如果在更改时钟输入源时启用SWDT,则会出现不可预测的行为。 当时钟输入源更改为非执行时钟时,APB访问锁定。 3358 www.Sina.com/: swdt.control [ ckey ]字段必须为0x248,才能写入此寄存器。 设定使用slcr.WDT_CLK_SEL [SEL]位选择时钟输入源输出脉冲长度(零模式寄存器)。 要写入此寄存器,swdt.MODE [ZKEY]字段必须为0xABC。 确保IRQLN满足指定的最小值。设置超时时间(计数器控制寄存器)三计时器(TTC ) TTC包含三个独立的计时器/计数器。 PS端有两个TTC模块,共有六个计时器/计数器。 可以使用NIC 301 _ addr _ region _ ctrl _ registers.security _ APB [ TTC1_ APB ]寄存器位将TTC1控制器设置为安全模式或非安全模式。 TTC控制器的三个计时器具有相同的安全状态。

每个特征的三定时器/计数器具有以下特征:

三个独立的16位预分频器和16位上行/下行计数器。 可选时钟输入,1 .内部PS总线时钟(CPU_1x ); 2 .内部时钟(来自PL ); 3 .外部时钟(来自MIO )。

每个计数器都有中断。

发生溢出中断、定时中断或计数中断、可编程的初始值。

可以生成从MIO到PL的波形输出,例如PWM。 三定时器/计数器寄存器的菜单如下。

p> 计数器编程启用顺序 选择时钟输入源,设置预分频值。(slcr.MIO_MUX_SEL寄存器,TTC时钟控制)在继续执行此操作之前,请确保已禁用TTC(ttc.Counter_Control_x [DIS] = 1)。设置间隔值(间隔寄存器)。 此步仅适用于间隔模式。(可选不配置)设置匹配值(匹配寄存器)。 如果要启用匹配,可以配置此操作。(可选不配置)**使能中断(中断使能寄存器)。**如果要启用中断,可以配置此操作。(可选不配置)启用/禁用波形输出,启用/禁用匹配,设置计数方向,设置模式,使能计数器(TTC计数器控制寄存器)。 此步骤完成后将启动计数器。 计数器编程停止顺序 读回计数器控制寄存器的值。将DIS位设置为1,同时保留其他位。写回计数器控制寄存器。 计数器编程重启顺序 读回计数器控制寄存器的值。将RST位设置为1,同时保留其他位。写回计数器控制寄存器。 事件计时器启用序列 选择外部脉冲源(slcr.MIO_MUX_SEL寄存器)。选定外部待测脉冲。设置溢出处理,选择外部脉冲电平,启用事件计时器(事件控制计时器寄存器)。 此步骤开始测量外部所选电平(高或低)的宽度脉冲。使能中断(中断使能寄存器)。 如果要启用中断,可以配置此操作。(可选不配置)读取测量的宽度(事件寄存器)。 中断清除和应答序列 读取中断寄存器:读取时清除中断寄存器中的所有位。 系统框图

本次工程系统框图如下图所示。

调用UART、TIMER、GPIO资源进行开发设计,UART串口引脚直接连接到MIO,GPIO引脚连接到PL端的EMIO引脚,由ARM核进行配置定时器中断操作,实现定时中断进行流水灯操作。

硬件平台搭建

新建工程,创建 block design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的资源。

硬件系统构建完成如下:


然后我们进行generate output product 然后生成HDL封装。这里用到了UART和GPIO,所以需要进行管脚分配。这里使用的是PYNQZ2开发板,该板卡可直接引用以下约束文件:

#LEDsset_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[1] }]; #IO_L6P_T0_34 Sch=led[1]set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[2] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=led[2]set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { EMIO_LED_tri_io[3] }]; #IO_L23P_T3_35 Sch=led[3]

完成约束后,进行综合和布局布线,生成bit流,然后点击导出硬件资源(包含bit流文件),接着launch SDK。

SDK软件部分

打开SDK后,新建application project。
在system.mss中可以打开相关参考文档辅助设计。

可以选择timer中断的例程进行参考设计,导入uart_intr_example例程模板,

在main.c中输入以下代码:

#include <stdio.h>#include "xparameters.h"#include "xscutimer.h"#include "xscugic.h"#include "xgpiops.h"#include "xil_exception.h"#include "xil_printf.h"//声明定义#define GPIO_ID XPAR_PS7_GPIO_0_DEVICE_ID#define TIMER_ID XPAR_PS7_SCUTIMER_0_DEVICE_ID#define INTR_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID#define TIMER_IRPT_INTRXPAR_SCUTIMER_INTR#define LOAD_VALUE 0X9AF8D9F//0.5s流水 系统时钟650M 定时器时钟325M 周期3ns#define LED0 54#define LED1 55#define LED2 56#define LED3 57//声明示例结构体XGpioPs GpioPs;XScuTimer Timer;XScuGic ScuGic;//函数定义void Emio_init();void Timer_init();void Timer_intr_init(XScuGic *intr, XScuTimer *time);void Timer_IntrHandler(void *CallBackRef);int main(){//EMIO初始化Emio_init();//初始化定时器Timer_init();//初始化中断Timer_intr_init(&ScuGic,&Timer);//启动定时器XScuTimer_Start(&Timer);while(1);return 0;}void Emio_init(){XGpioPs_Config *XGpioPs_Cfg;XGpioPs_Cfg = XGpioPs_LookupConfig(GPIO_ID);XGpioPs_CfgInitialize(&GpioPs,XGpioPs_Cfg,XGpioPs_Cfg->BaseAddr);XGpioPs_SetDirectionPin(&GpioPs,LED0,0x01);XGpioPs_SetOutputEnablePin(&GpioPs,LED0,0x01);XGpioPs_SetDirectionPin(&GpioPs,LED1,0x01);XGpioPs_SetOutputEnablePin(&GpioPs,LED1,0x01);XGpioPs_SetDirectionPin(&GpioPs,LED2,0x01);XGpioPs_SetOutputEnablePin(&GpioPs,LED2,0x01);XGpioPs_SetDirectionPin(&GpioPs,LED3,0x01);XGpioPs_SetOutputEnablePin(&GpioPs,LED3,0x01);}void Timer_init(){int Status;XScuTimer_Config *ConfigPtr;//初始化定时器ConfigPtr = XScuTimer_LookupConfig(TIMER_ID);XScuTimer_CfgInitialize(&Timer, ConfigPtr,ConfigPtr->BaseAddr);//定时器自检测Status = XScuTimer_SelfTest(&Timer);if (Status != XST_SUCCESS) {printf("timer selftest failed!n");}printf("timer selftest success!n");//装载初值XScuTimer_LoadTimer(&Timer, LOAD_VALUE);//使能自动装载模式XScuTimer_EnableAutoReload(&Timer);}void Timer_intr_init(XScuGic *intr, XScuTimer *time){XScuGic_Config *IntcConfig;//初始化定时器中断IntcConfig = XScuGic_LookupConfig(INTR_DEVICE_ID);XScuGic_CfgInitialize(intr,IntcConfig,IntcConfig->CpuBaseAddress);//初始化中断异常函数Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,intr);Xil_ExceptionEnable();//设置中断服务函数XScuGic_Connect(intr, TIMER_IRPT_INTR,(Xil_ExceptionHandler)Timer_IntrHandler,(void *)time);//使能中断控制器XScuGic_Enable(intr, TIMER_IRPT_INTR);//使能定时器XScuTimer_EnableInterrupt(time);}//中断处理函数void Timer_IntrHandler(void *CallBackRef){static char led_status=0x01;XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;if(XScuTimer_IsExpired(TimerInstancePtr)){led_status = led_status<<1;if(led_status == 0X10)led_status =0x01;XGpioPs_WritePin(&GpioPs,LED0,led_status);XGpioPs_WritePin(&GpioPs,LED1,led_status>>1);XGpioPs_WritePin(&GpioPs,LED2,led_status>>2);XGpioPs_WritePin(&GpioPs,LED3,led_status>>3);XScuTimer_ClearInterruptStatus(TimerInstancePtr);}} 部分代码讲解

在主函数中引用了Emio_init();、Timer_init();、Timer_intr_init(&ScuGic,&Timer);
分别完成初始化GPIO操作,初始化定时器,初始化中断定时器等操作。最后用XScuTimer_Start(&Timer);启动定时器。

对于定时器初始化设置主要要对计数模式进行设置,对初始值进行装载配置
也就是调用下面的函数:

//装载初值XScuTimer_LoadTimer(&Timer, LOAD_VALUE);//使能自动装载模式XScuTimer_EnableAutoReload(&Timer);

这里LOAD_VALUE表示需要计时或者计数的次数,因为这里的代码实现的是led的0.5s定时中断,系统CPU时钟为650M,所以定时器的时钟频率为325M,也就是周期为3.07ns,计算0.5s下的计数次数得到这里需要装载的初值。(也即为0X9AF8D9F)

Reference UG585正点原子ZYNQ开发视频

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。