原文链接: https://www.cnblogs.com/moluoqishi/p/12441264.html
一、前言
APU系统中CPU以串行执行代码的方式完成操作,软件方式很难做到精准计时,因此调用内部定时器硬件完成计时是更好的选择。本文以定时器中断方式控制LED周期性闪烁为例学习私有定时器的使用。同时学习如何将软件程序与硬件比特流文件一起固化到SD卡中,实现上电自动配置与启动自定义系统。
功能定义:通过定时器中断实现与MIO连接的单个LED每200ms变化依次电平,即点亮,200ms后熄灭,200ms后再次点亮,周期往复。
硬件平台:米联客Miz702N
软件工具:VIVADO 2017.4+SDK
二、硬件系统搭建
私有定时器属于APU内部专用硬件资源,无需在VIVADO中做出配置。由于需要将软硬件系统固化到SD卡中,选择与SD控制器连接的I/O。



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

很简单,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=0;
freq_step_value = 10;
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=0;i gpiops_setOutput(&GpioPs,EMIO_OUT_PIN_BASE_INDEX+i);
}
gpio_setDirect(&Gpio, 1,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(1){
for(i=0;i if(int_flag == 0)
{
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);
usleep(200*1000);
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);
}
else
{
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM-1-i, 0x1);
usleep(200*1000);
gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM-1-i, 0x0);
}
}
}
return 0;
}
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 = 0;
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, 1);
switch(gpio_inputValue)
{
case 30:
//printf("button up\n");
usleep(5);
gpio_inputValue = gpio_readValue(GpioPtr, 1);
if(gpio_inputValue == 30){
freq_step_value = freq_step_value freq_step_value+10 : freq_step_value;
printf("%d\n",freq_step_value);
pwm_led_setFreqStep(freq_step_value);
}
break;
case 29:
//printf("button center\n");
usleep(5);
gpio_inputValue = gpio_readValue(GpioPtr, 1);
if(gpio_inputValue == 29){
freq_step_value = FREQ_STEP_SET_VALUE;
pwm_led_setFreqStep(freq_step_value);
}
break;
case 27:
//printf("button left\n");
usleep(5);
gpio_inputValue = gpio_readValue(GpioPtr, 1);
if(gpio_inputValue == 27)
int_flag = 0;
break;
case 23:
//printf("button right\n");
usleep(5);
gpio_inputValue = gpio_readValue(GpioPtr, 1);
if(gpio_inputValue == 23)
int_flag = 1;
break;
case 15:
//print("button down\n");
usleep(5);
gpio_inputValue = gpio_readValue(GpioPtr, 1);
if(gpio_inputValue == 15){
freq_step_value = freq_step_value > FREQ_STEP_MIN ?
freq_step_value-10 : 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。


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



