一、前言

  APU系统中CPU以串行执行代码的方式完成操作,软件方式很难做到精准计时,因此调用内部定时器硬件完成计时是更好的选择。本文以定时器中断方式控制LED周期性闪烁为例学习私有定时器的使用。同时学习如何将软件程序与硬件比特流文件一起固化到SD卡中,实现上电自动配置与启动自定义系统。

功能定义:通过定时器中断实现与MIO连接的单个LED每200ms变化依次电平,即点亮,200ms后熄灭,200ms后再次点亮,周期往复。

硬件平台:米联客Miz702N

软件工具:VIVADO 2017.4+SDK

二、硬件系统搭建

  私有定时器属于APU内部专用硬件资源,无需在VIVADO中做出配置。由于需要将软硬件系统固化到SD卡中,选择与SD控制器连接的I/O。

根据原理图,SD卡连接在MIO40~47,这也与UG585中的描述一致:

这里直接使用PWM产生呼吸灯效果工程中的硬件系统,可以更直观地观察出定时器控制LED与PWM控制LED互不影响。

  依然是重新产生输出文件、生成HDL Wrapper、RUN Synthesis、Run Implementation、Generate bitstream、export Hardware with bitfile、Launch SDK. 剩下的任务均在SDK中完成。

三、软件设计

  关于私有定时器使用方式,xilinx同样提供了文档和示例程序。

  软件代码中使用的定时器相关函数均来自示例程序。使用私有定时器第一步当然是初始化配置,老套路调用XScuTimer_LookupConfig和XScuTimer_CfgInitialize两个函数。为了保证LED周期性闪烁,必须使能定时器的自动重载,这样每当计数器计数完成后会重新计数。之后最重要的是向定时器装载寄存器写入计数周期数值。实际上私有定时器是一个递减计数器,当从最大值递减到0时刻会产生定时器中断。如和将所要定时的时间长度换算为装载计数器周期数值呢?

  很简单,n=t/T=t*f即可算出装载数值,其中n、t和T分别指所要定时的时间和定时器工作时钟周期。因为定时器工作时钟频率一直是CPU工作时钟的一半,在本系统中即为333MHz。这个n=200*10^(-3)*333*10^6=666*10^5。计数器是N-1~0的计数方式,装载值在n的基础上减1,对应的十六进制数值是0x3F83C3F。

  装载完毕后调用XScuTimer_Start定时器随即开始工作。最后在定时器中断回调函数中对MIO进行反转操作就可以满足功能预期。另外,对之前PWM实现呼吸灯效果的工程做些改善,软件程序如下:

 /*
* main.c
*
* Created on: 2020年2月22日
* Author: s
*/ #include "environment.h" int main()
{
int Status;
u8 i=; freq_step_value = ; Status = gpiops_initialize(&GpioPs,GPIOPS_DEVICE_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} Status = gpio_initialize(&Gpio,GPIO_DEVICE_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} Status = timer_initialize(&TimerInstance,TIMER_DEVICE_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Set the direction for the pin to be output and
* Enable the Output enable for the LED Pin.
*/
gpiops_setOutput(&GpioPs,MIO_OUT_PIN_INDEX); for(i=;i<LOOP_NUM;i++){
gpiops_setOutput(&GpioPs,EMIO_OUT_PIN_BASE_INDEX+i);
} gpio_setDirect(&Gpio, ,GPIO_CHANNEL1); Status = setupIntSystem(&Intc,&Gpio,&TimerInstance,
INTC_GPIO_INTERRUPT_ID,TIMER_IRPT_INTR);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} /*
* Enable Auto reload mode.
*/
XScuTimer_EnableAutoReload(&TimerInstance); /*
* Load the timer counter register.
*/
XScuTimer_LoadTimer(&TimerInstance, TIMER_LOAD_VALUE); /*
* Start the timer counter and then wait for it
* to timeout a number of times.
*/
XScuTimer_Start(&TimerInstance); Status = pwm_led_setFreqStep(freq_step_value);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
} printf("Initialization finish.\n"); while(){ for(i=;i<LOOP_NUM;i++){
if(int_flag == )
{
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);
usleep(*);
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);
}
else
{
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM--i, 0x1);
usleep(*);
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM--i, 0x0);
}
} }
return ;
} int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr,
XScuTimer * TimerInstancePtr,u32 gpio_IntrId,u32 timer_IntrId)
{
int Result;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/ Result = gic_initialize(&Intc,INTC_DEVICE_ID);
if (Result != XST_SUCCESS) {
return XST_FAILURE;
} XScuGic_SetPriorityTriggerType(IntcInstancePtr, gpio_IntrId,
0xA0, 0x3); /*
* Connect the interrupt handler that will be called when an
* interrupt occurs for the device.
*/
Result = XScuGic_Connect(IntcInstancePtr, gpio_IntrId,
(Xil_ExceptionHandler)GpioHandler, gpioInstancePtr);
if (Result != XST_SUCCESS) {
return Result;
} Result = XScuGic_Connect(IntcInstancePtr, timer_IntrId,
(Xil_ExceptionHandler)TimerIntrHandler,
(void *)TimerInstancePtr);
if (Result != XST_SUCCESS) {
return Result;
} /* Enable the interrupt for the GPIO device.*/
XScuGic_Enable(IntcInstancePtr, gpio_IntrId); /*
* Enable the GPIO channel interrupts so that push button can be
* detected and enable interrupts for the GPIO device
*/
XGpio_InterruptEnable(gpioInstancePtr,GPIO_CHANNEL1);
XGpio_InterruptGlobalEnable(gpioInstancePtr); /*
* Enable the interrupt for the device.
*/
XScuGic_Enable(IntcInstancePtr, timer_IntrId);
XScuTimer_EnableInterrupt(TimerInstancePtr); /*
* Initialize the exception table and register the interrupt
* controller handler with the exception table
*/
exception_enable(&Intc); IntrFlag = ; return XST_SUCCESS;
} void GpioHandler(void *CallbackRef)
{
XGpio *GpioPtr = (XGpio *)CallbackRef;
u32 gpio_inputValue; /* Clear the Interrupt */
XGpio_InterruptClear(GpioPtr, GPIO_CHANNEL1);
printf("gpio interrupt.\n"); //IntrFlag = 1;
gpio_inputValue = gpio_readValue(GpioPtr, );
switch(gpio_inputValue)
{
case :
//printf("button up\n");
usleep();
gpio_inputValue = gpio_readValue(GpioPtr, );
if(gpio_inputValue == ){
freq_step_value = freq_step_value < FREQ_STEP_MAX ?
freq_step_value+ : freq_step_value;
printf("%d\n",freq_step_value);
pwm_led_setFreqStep(freq_step_value);
}
break;
case :
//printf("button center\n");
usleep();
gpio_inputValue = gpio_readValue(GpioPtr, );
if(gpio_inputValue == ){
freq_step_value = FREQ_STEP_SET_VALUE;
pwm_led_setFreqStep(freq_step_value);
}
break;
case :
//printf("button left\n");
usleep();
gpio_inputValue = gpio_readValue(GpioPtr, );
if(gpio_inputValue == )
int_flag = ;
break;
case :
//printf("button right\n");
usleep();
gpio_inputValue = gpio_readValue(GpioPtr, );
if(gpio_inputValue == )
int_flag = ;
break;
case :
//print("button down\n");
usleep();
gpio_inputValue = gpio_readValue(GpioPtr, );
if(gpio_inputValue == ){
freq_step_value = freq_step_value > FREQ_STEP_MIN ?
freq_step_value- : freq_step_value;
printf("%d\n",freq_step_value);
pwm_led_setFreqStep(freq_step_value);
}
break;
} } void TimerIntrHandler(void *CallBackRef)
{
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef; XScuTimer_ClearInterruptStatus(TimerInstancePtr); gpiops_outputValue(&GpioPs, MIO_OUT_PIN_INDEX, sys_led_out);
sys_led_out = sys_led_out == 0x0 ? 0x1 : 0x0;
}

main.c

 /*
* timer.h
*
* Created on: 2020年3月5日
* Author: s
*/ #ifndef SRC_TIMER_H_
#define SRC_TIMER_H_ #include "xscutimer.h" #define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR //333*n(ms)*10^3-1 = 333*5*1000-1 = 1664999 0x1967E7
#define TIMER_LOAD_VALUE 0x3F83C3F int timer_initialize(XScuTimer * TimerInstancePtr,u16 TimerDeviceId); #endif /* SRC_TIMER_H_ */

timer.h

 /*
* timer.c
*
* Created on: 2020年3月5日
* Author: s
*/ #include "timer.h" int timer_initialize(XScuTimer * TimerInstancePtr,u16 TimerDeviceId)
{
XScuTimer_Config *ConfigPtr;
/*
* Initialize the Scu Private Timer driver.
*/
ConfigPtr = XScuTimer_LookupConfig(TimerDeviceId); /*
* This is where the virtual address would be used, this example
* uses physical address.
*/
return XScuTimer_CfgInitialize(TimerInstancePtr, ConfigPtr,
ConfigPtr->BaseAddr);
}

timer.c

  相比原来的程序,在GpioHandler中添加了对freq_step_value最值的限制以及按键消抖延时。

四、程序固化

  本工程固化程序时要使用FAT文件系统,更改板级支持包设置,勾选xilffs库并重新生成BSP。

  在固化程序之前,了解CPU的启动过程是非常必要的,这部分概念主要来自UG585文档。在上电复位后,硬件逻辑会根据启动模式引脚的高低电平选择启动方式。

  硬件一些初始化操作后执行CPU内部一个ROM中的代码来启动整个系统,这个ROM的名字叫BootROM。BootROM中的程序是第一个被CPU执行的代码,其主要任务是配置系统,并从boot device中拷贝系统镜像到OCM,配置DDR操作。

  Boot device可以是Quad-SPI,NAND,NOR或者SD卡。Boot device中存储的是boot image,其由BootROM Header和FSBL以及User Code组成,当然也可包括用于配置PL的bitstream和软件OS。软件boot分为三个阶段:

  其中FSBL起到组织作用,将PS部分软件生成的ELF文件和PL部分硬件bit文件组合在一起。该文件利用xilinx的FSBL示例工程生成,用户无需关注内部实现细节。

  创建工程后会自动编译并生成ELF文件。点击工具栏xilinx -> create boot image。按照如下顺序添加三个文件:fsdb.elf --> bit --> <user_code>.elf

  create image后会生成对应的bin文件,也就是之前阐述的启动镜像。

  我们将BOOT.bin文件拷贝到空的SD卡中,利用拨码开关配置Boot Mode MIO Strapping Pins从SD卡启动。上电后等待一段时间MIO连接的LED灯周期性闪烁,PWM呼吸灯频率与流水灯方向根据按键变换,系统正常工作,完成了定时器中断应用和程序固化。

ZYNQ入门实例——定时器中断与程序固化的更多相关文章

  1. ZYNQ入门实例——三种GPIO应用、中断系统及软硬件交叉触发调试

    一.前言 Xlinx的ZYNQ系列SOC集成了APU.各种专用外设资源和传统的FPGA逻辑,为ARM+FPGA的应用提供助力,降低功耗和硬件设计难度的同时极大提高两者间传输的带宽.之前在研究生课题中使 ...

  2. 79.ZYNQ内部私有定时器中断

    上篇文章实现了了PS接受来自PL的中断,本片文章将在ZYNQ的纯PS里实现私有定时器中断.每个一秒中断一次,在中断函数里计数加1,通过串口打印输出. *本文所使用的开发板是Miz702(兼容zedbo ...

  3. S02_CH08_ ZYNQ 定时器中断实验

    S02_CH08_ ZYNQ 定时器中断实验 上一章实现了PS接受来自PL的中断,本章将在ZYNQ的纯PS里实现私有定时器中断.每隔一秒中断一次,在中断函数里计数加1,通过串口打印输出. 8.1中断原 ...

  4. Node.js入门实例程序

    在使用Node.js创建实际“Hello, World!”应用程序之前,让我们看看Node.js的应用程序的部分.Node.js应用程序由以下三个重要组成部分: 导入需要模块: 我们使用require ...

  5. STC10F10XE定时器中断输出10KHz的方波程序

    //咱做硬件的也动手做点测试程序,为了测试新做的电机驱动板,找了个51的板子当10K信号发生器测试IGBT开关延时时间. #include <STC_NEW_8051.H>#include ...

  6. 第十四章 ZYNQ TIMER定时器中断

      上篇文章实现了了PS接受来自PL的中断,本片文章将在ZYNQ的纯PS里实现私有定时器中断.每隔一秒中断一次,在中断函数里计数加1,通过串口打印输出. 本文所使用的开发板是Miz702 PC 开发环 ...

  7. 提升效率(时间准确性),减少时间和资源的消耗——由89C52/89C51的定时器中断引出的一些问题

    尽量用最少的文字描述清楚问题. 事情起因是这样的: 要做遥控小车的平台迁移,STM32开发板无法方便地供电,因此又拿出了尘封的51(STC89C52RC),搭配上最小系统板就可以用排针加杜邦线供电了. ...

  8. 【React】入门实例

    React 可以灵活的应用在各种各样的项目中.你可以用它来创建新的应用程序,你也可以逐步引用而不改变现有的代码库. React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaS ...

  9. TQ2440之定时器中断0——volatile关键字的重要作用

    近日,在学习<ARM处理器裸机开发实战--机制而非策略>一书,在TQ2440开发板上,按照书中实例以及光盘配套程序源代码进行Timer0中断试验,编译成功后烧写到开发板上,没有任何反应,反 ...

随机推荐

  1. z-scores|zα

    6.2 Areas Under the Standard Normal Curve Property 4: Almost all the area under the standard normal ...

  2. 关联规则之FpGrowth算法

    Aprori算法利用频繁集的两个特性,过滤了很多无关的集合,效率提高不少,但是我们发现Apriori算法是一个候选消除算法,每一次消除都需要扫描一次所有数据记录,造成整个算法在面临大数据集时显得无能为 ...

  3. COMET探索系列三【异步通知服务器关闭数据连接实现思路】

    在小编络络 COMET实践笔记一文中注意事项中有这么一段话 使用长连接时, 存在一个很常见的场景:客户端需要关闭页 面,而服务器端还处在读取数据的阻塞状态,客户端需要及时通知服务器端关闭数据连接.服务 ...

  4. iOS动画效果集合、 通过摄像头获取心率、仿淘宝滑动样式、瀑布流、分类切换布局等源码

    iOS精选源码 动画知识运用及常见动画效果收集 较为美观的多级展开列表 MUImageCache -简单轻量的图片缓存方案 iOS 瀑布流之栅格布局 一用就上瘾的JXCategoryView iOS ...

  5. [LC] 19. Remove Nth Node From End of List

    Given a linked list, remove the n-th node from the end of list and return its head. Example: Given l ...

  6. 63)PHP,登录验证

    首先辨析两种状态:   你的用户名和密码通过验证  只能表明你能登录,但是不能保证你登录了. 管理员信息合法和管理员处于的登录状态是两个概念:管理员信息合法证明你的用户名和密码是正确的, 但是管理员信 ...

  7. NIO与IO

    待续... 该文章部分摘自:http://tutorials.jenkov.com/java-nio/index.html 一.I/O简介 I/O(英语:Input/Output),即输入/输出, 指 ...

  8. 学会使用数据讲故事——Excel研究网络研讨会

    编者按:在数据密集型研究的新时代,Excel将成为研究者讲故事的强大工具.在即将举行的Excel研究网络研讨会中,我们将与你探讨如何用新的方式来寻找.查询.分析数据并实现数据可视化.Office 36 ...

  9. cs231n spring 2017 lecture7 Training Neural Networks II

    1. 优化: 1.1 随机梯度下降法(Stochasitc Gradient Decent, SGD)的问题: 1)对于condition number(Hessian矩阵最大和最小的奇异值的比值)很 ...

  10. 数据库连接中断-spring-springBoot

    问题:据库和应用在同一台机,数据库用mysql5.6.20,已经升级druid到最新的1.0.7版本,访问的是localhost的mysql,放一个晚上不访问,第二天访问就报错了,重启服务正常,错误提 ...