ZYNQ入門實例——定時器中斷與程序固化

1、前言app

  APU系統中CPU以串行執行代碼的方式完成操做,軟件方式很難作到精準計時,所以調用內部定時器硬件完成計時是更好的選擇。本文以定時器中斷方式控制LED週期性閃爍爲例學習私有定時器的使用。同時學習如何將軟件程序與硬件比特流文件一塊兒固化到SD卡中,實現上電自動配置與啓動自定義系統。ide

功能定義:經過定時器中斷實現與MIO鏈接的單個LED每200ms變化依次電平,即點亮,200ms後熄滅,200ms後再次點亮,週期往復。函數

硬件平臺:米聯客Miz702N工具

軟件工具:VIVADO 2017.4+SDK學習

2、硬件系統搭建this

  私有定時器屬於APU內部專用硬件資源,無需在VIVADO中作出配置。因爲須要將軟硬件系統固化到SD卡中,選擇與SD控制器鏈接的I/O。spa

根據原理圖,SD卡鏈接在MIO40~47,這也與UG585中的描述一致:設計

這裏直接使用PWM產生呼吸燈效果工程中的硬件系統,能夠更直觀地觀察出定時器控制LED與PWM控制LED互不影響。3d

   依然是從新產生輸出文件、生成HDL Wrapper、RUN Synthesis、Run Implementation、Generate bitstream、export Hardware with bitfile、Launch SDK. 剩下的任務均在SDK中完成。code

3、軟件設計

  關於私有定時器使用方式,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實現呼吸燈效果的工程作些改善,軟件程序以下:

  1 /*
  2  * main.c
  3  *
  4  *  Created on: 2020年2月22日
  5  *      Author: s
  6  */
  7 
  8 
  9 #include "environment.h"
 10 
 11 
 12 
 13 int main()
 14 {
 15     int Status;
 16     u8 i=0;
 17 
 18     freq_step_value = 10;
 19 
 20     Status = gpiops_initialize(&GpioPs,GPIOPS_DEVICE_ID);
 21     if (Status != XST_SUCCESS) {
 22         return XST_FAILURE;
 23     }
 24 
 25     Status = gpio_initialize(&Gpio,GPIO_DEVICE_ID);
 26     if (Status != XST_SUCCESS) {
 27         return XST_FAILURE;
 28     }
 29 
 30     Status = timer_initialize(&TimerInstance,TIMER_DEVICE_ID);
 31     if (Status != XST_SUCCESS) {
 32         return XST_FAILURE;
 33     }
 34     /*
 35      * Set the direction for the pin to be output and
 36     * Enable the Output enable for the LED Pin.
 37      */
 38     gpiops_setOutput(&GpioPs,MIO_OUT_PIN_INDEX);
 39 
 40     for(i=0;i<LOOP_NUM;i++){
 41         gpiops_setOutput(&GpioPs,EMIO_OUT_PIN_BASE_INDEX+i);
 42     }
 43 
 44     gpio_setDirect(&Gpio, 1,GPIO_CHANNEL1);
 45 
 46     Status = setupIntSystem(&Intc,&Gpio,&TimerInstance,
 47             INTC_GPIO_INTERRUPT_ID,TIMER_IRPT_INTR);
 48     if (Status != XST_SUCCESS) {
 49             return XST_FAILURE;
 50         }
 51 
 52     /*
 53     * Enable Auto reload mode.
 54     */
 55     XScuTimer_EnableAutoReload(&TimerInstance);
 56 
 57     /*
 58     * Load the timer counter register.
 59     */
 60     XScuTimer_LoadTimer(&TimerInstance, TIMER_LOAD_VALUE);
 61 
 62     /*
 63      * Start the timer counter and then wait for it
 64     * to timeout a number of times.
 65     */
 66     XScuTimer_Start(&TimerInstance);
 67 
 68 
 69 
 70     Status = pwm_led_setFreqStep(freq_step_value);
 71     if (Status != XST_SUCCESS) {
 72             return XST_FAILURE;
 73         }
 74 
 75     printf("Initialization finish.\n");
 76 
 77     while(1){
 78 
 79         for(i=0;i<LOOP_NUM;i++){
 80             if(int_flag == 0)
 81             {
 82                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);
 83                 usleep(200*1000);
 84                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);
 85             }
 86             else
 87             {
 88                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM-1-i, 0x1);
 89                 usleep(200*1000);
 90                 gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+LOOP_NUM-1-i, 0x0);
 91             }
 92         }
 93 
 94 
 95     }
 96     return 0;
 97 }
 98 
 99 
100 
101 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr,
102         XScuTimer * TimerInstancePtr,u32 gpio_IntrId,u32 timer_IntrId)
103 {
104     int Result;
105     /*
106     * Initialize the interrupt controller driver so that it is ready to
107     * use.
108     */
109 
110     Result = gic_initialize(&Intc,INTC_DEVICE_ID);
111     if (Result != XST_SUCCESS) {
112             return XST_FAILURE;
113         }
114 
115     XScuGic_SetPriorityTriggerType(IntcInstancePtr, gpio_IntrId,
116                         0xA0, 0x3);
117 
118     /*
119     * Connect the interrupt handler that will be called when an
120      * interrupt occurs for the device.
121      */
122     Result = XScuGic_Connect(IntcInstancePtr, gpio_IntrId,
123                 (Xil_ExceptionHandler)GpioHandler, gpioInstancePtr);
124     if (Result != XST_SUCCESS) {
125         return Result;
126     }
127 
128     Result = XScuGic_Connect(IntcInstancePtr, timer_IntrId,
129                     (Xil_ExceptionHandler)TimerIntrHandler,
130                     (void *)TimerInstancePtr);
131     if (Result != XST_SUCCESS) {
132             return Result;
133         }
134 
135     /* Enable the interrupt for the GPIO device.*/
136     XScuGic_Enable(IntcInstancePtr, gpio_IntrId);
137 
138     /*
139      * Enable the GPIO channel interrupts so that push button can be
140     * detected and enable interrupts for the GPIO device
141     */
142     XGpio_InterruptEnable(gpioInstancePtr,GPIO_CHANNEL1);
143     XGpio_InterruptGlobalEnable(gpioInstancePtr);
144 
145     /*
146     * Enable the interrupt for the device.
147     */
148     XScuGic_Enable(IntcInstancePtr, timer_IntrId);
149     XScuTimer_EnableInterrupt(TimerInstancePtr);
150 
151     /*
152     * Initialize the exception table and register the interrupt
153     * controller handler with the exception table
154     */
155     exception_enable(&Intc);
156 
157     IntrFlag = 0;
158 
159     return XST_SUCCESS;
160 }
161 
162 void GpioHandler(void *CallbackRef)
163 {
164     XGpio *GpioPtr = (XGpio *)CallbackRef;
165     u32 gpio_inputValue;
166 
167 
168     /* Clear the Interrupt */
169     XGpio_InterruptClear(GpioPtr, GPIO_CHANNEL1);
170     printf("gpio interrupt.\n");
171 
172     //IntrFlag = 1;
173     gpio_inputValue = gpio_readValue(GpioPtr, 1);
174     switch(gpio_inputValue)
175     {
176     case 30:
177         //printf("button up\n");
178         usleep(5);
179         gpio_inputValue = gpio_readValue(GpioPtr, 1);
180         if(gpio_inputValue == 30){
181             freq_step_value = freq_step_value < FREQ_STEP_MAX ?
182                     freq_step_value+10 : freq_step_value;
183             printf("%d\n",freq_step_value);
184             pwm_led_setFreqStep(freq_step_value);
185         }
186         break;
187     case 29:
188         //printf("button center\n");
189         usleep(5);
190         gpio_inputValue = gpio_readValue(GpioPtr, 1);
191         if(gpio_inputValue == 29){
192             freq_step_value = FREQ_STEP_SET_VALUE;
193             pwm_led_setFreqStep(freq_step_value);
194         }
195         break;
196     case 27:
197         //printf("button left\n");
198         usleep(5);
199         gpio_inputValue = gpio_readValue(GpioPtr, 1);
200         if(gpio_inputValue == 27)
201             int_flag = 0;
202         break;
203     case 23:
204         //printf("button right\n");
205         usleep(5);
206         gpio_inputValue = gpio_readValue(GpioPtr, 1);
207         if(gpio_inputValue == 23)
208             int_flag = 1;
209         break;
210     case 15:
211         //print("button down\n");
212         usleep(5);
213         gpio_inputValue = gpio_readValue(GpioPtr, 1);
214         if(gpio_inputValue == 15){
215             freq_step_value = freq_step_value > FREQ_STEP_MIN ?
216                     freq_step_value-10 : freq_step_value;
217             printf("%d\n",freq_step_value);
218             pwm_led_setFreqStep(freq_step_value);
219         }
220         break;
221     }
222 
223 }
224 
225 void TimerIntrHandler(void *CallBackRef)
226 {
227     XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
228 
229     XScuTimer_ClearInterruptStatus(TimerInstancePtr);
230 
231     gpiops_outputValue(&GpioPs, MIO_OUT_PIN_INDEX, sys_led_out);
232     sys_led_out  = sys_led_out == 0x0 ? 0x1 : 0x0;
233 }
main.c
 1 /*
 2  * timer.h
 3  *
 4  *  Created on: 2020年3月5日
 5  *      Author: s
 6  */
 7 
 8 #ifndef SRC_TIMER_H_
 9 #define SRC_TIMER_H_
10 
11 #include "xscutimer.h"
12 
13 #define TIMER_DEVICE_ID        XPAR_XSCUTIMER_0_DEVICE_ID
14 #define TIMER_IRPT_INTR        XPAR_SCUTIMER_INTR
15 
16 //333*n(ms)*10^3-1 = 333*5*1000-1 = 1664999 0x1967E7
17 #define TIMER_LOAD_VALUE    0x3F83C3F
18 
19 int timer_initialize(XScuTimer * TimerInstancePtr,u16 TimerDeviceId);
20 
21 
22 #endif /* SRC_TIMER_H_ */
timer.h
 1 /*
 2  * timer.c
 3  *
 4  *  Created on: 2020年3月5日
 5  *      Author: s
 6  */
 7 
 8 
 9 #include "timer.h"
10 
11 int timer_initialize(XScuTimer * TimerInstancePtr,u16 TimerDeviceId)
12 {
13     XScuTimer_Config *ConfigPtr;
14     /*
15     * Initialize the Scu Private Timer driver.
16     */
17     ConfigPtr = XScuTimer_LookupConfig(TimerDeviceId);
18 
19     /*
20     * This is where the virtual address would be used, this example
21     * uses physical address.
22     */
23     return XScuTimer_CfgInitialize(TimerInstancePtr, ConfigPtr,
24                     ConfigPtr->BaseAddr);
25 }
timer.c

  相比原來的程序,在GpioHandler中添加了對freq_step_value最值的限制以及按鍵消抖延時。

4、程序固化

  本工程固化程序時要使用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呼吸燈頻率與流水燈方向根據按鍵變換,系統正常工做,完成了定時器中斷應用和程序固化。

相關文章
相關標籤/搜索