本帖最后由 FPGA课程 于 2024-9-25 17:30 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1概述
ZYNQ有2个TTC定时器,每个TTC具有3个定时计数器,每个定时器都支持中断。TTC可以用于定时,也可以用于产生PWM。
实验目的:
1:了解ZYNQ TTC定时器的功能
2:了解TTC定时器的寄存器
3:使用SDK自带代码测试TTC定时器的定时中断、以及PWM输出。
2系统框图如下系统框图所示,每个TTC具有3个定时计数器模块,每个模块都有自己的预分频器、16bit计数器、事件定时器、中断模块单元。支持的中断类型包括:计数间隔中断、计数匹配中断、计数溢出中断、事件定时器溢出中断。 每种类型都可以单独启用。 以TTC0为例,其全局中断GIC中断号分别为:#42 #43 #44。 TTC的工作模式可以分为一下两种: 间隔模式:计数器在 0 和间隔寄存器的值之间连续递增或递减,计数方向由计数器控制寄存器的 DEC 位决定。当计数器过零时产生一个间隔中断。当计数器值等于匹配寄存器之一时,会产生相应的匹配中断。 溢出模式:计数器在 0 到 0xFFFF 之间连续递增或递减,计数方向由计数器控制寄存器的 DEC 位决定。当计数器过零时产生溢出中断。当计数器值等于匹配寄存器之一时,会产生相应的匹配中断。
当TTC工作于事件模式,事件定时器通过内部的16bit定时计数器(该计数器的时钟就是CPU的系统时钟,并且用户不可见)计数。
3TTC定时器的寄存器
TTC0的基地址为0xF8001000,TTC1的基地址为0xF8002000,每个TTC都有3个定时计数器,这里以TTC0的定时计数器1的寄存器作为说明。以下寄存器命名方法以软件方法命名,比如MACH0寄存器代表了硬件中的MACH1(因为硬件上不存在0的概念,而程序中第一个通常以0开始表示)
CLOCK_CONTROL寄存器XTTCPS_CLK_CNTRL_OFFSET (0x00U) Field Name | Bits | Type | Reset Value | Description | CLOCK_CONTROL | 6:0 | RW | 0x0 | 6bit: 设置外部输入时钟的计数边沿,默认是上升沿,当设置1采用下降沿。 5bit: 时钟源选择,默认是pclk,当设置1选择外部时钟ext_clk 4~1bit: 预分频,分频系数= 2^(N+1) 0bits: 预分频使能,设置1使能预分频 |
CNT_CNTRL寄存器XTTCPS_CLK_CNTRL_OFFSET (0x0CU) Field Name | Bits | Type | Reset Value | Description | CNT_CNTRL | 6:0 | RW | 0x0 | 6bit: 波形极性:该位为高电平时,Match中断时波形输出由高电平变为低电平,溢出或间隔中断时返回高电平; 当低电平时,波形在 Match中断时从低电平变为高电平,并在溢出或间隔中断时返回低电平。 5bit: 输出波形使能,低电平有效。 4bit: 将该位设置为高电平会重置计数器值并重新开始计数; RST 位在重启时自动清除。 3bit: 寄存器匹配模式:当设置匹配时,当计数值与三个匹配寄存器之一匹配时产生中断,并且中断状态寄存器中的相应位被设置。 2bit: 当该位为高时,计数器递减计数。 1bit: 该位为高时,定时器处于间隔模式,计数器定期产生中断; 低电平时,定时器处于溢出模式。 0bit: 禁用计数器:当该位为高时,计数器停止,保持其最后一个值,直到复位、重新启动或再次启用。 |
COUNT_VALUE寄存器XTTCPS_COUNT_VALUE_OFFSET (0x18U) Field Name | Bits | Type | Reset Value | Description | COUNT_VALUE | 15:0 | RO | 0x0 | 当前计数器的值 |
INTERVAL_VAL寄存器XTTCPS_INTERVAL_VAL_OFFSET (0x24U) Field Name | Bits | Type | Reset Value | Description | INTERVAL_VAL | 15:0 | RW | 0x0 | 计数器,递增计数的最大值,或者递减计数的起始值 |
MATCH_0寄存器XTTCPS_MATCH_0_OFFSET (0x30U) Field Name | Bits | Type | Reset Value | Description | MATCH_0 | 15:0 | RW | 0x0 | 比较匹配寄存器0,当计数器的值和比较匹配寄存器的值相等的时候,并且使能了比较匹配模式,就会产生比较匹配中断。每个计数器有3个比较匹配寄存器。 |
MATCH_1寄存器XTTCPS_MATCH_1_OFFSET (0x34U) Field Name | Bits | Type | Reset Value | Description | MATCH_1 | 15:0 | RW | 0x0 | 比较匹配寄存器1,当计数器的值和比较匹配寄存器的值相等的时候,并且使能了比较匹配模式,就会产生比较匹配中断。每个计数器有3个比较匹配寄存器。 |
MATCH_2寄存器XTTCPS_MATCH_2_OFFSET (0x38U) Field Name | Bits | Type | Reset Value | Description | MATCH_2 | 15:0 | RW | 0x0 | 比较匹配寄存器2,当计数器的值和比较匹配寄存器的值相等的时候,并且使能了比较匹配模式,就会产生比较匹配中断。每个计数器有3个比较匹配寄存器。 |
ISR寄存器XTTCPS_ISR_OFFSET (0x54U) Field Name | Bits | Type | Reset Value | Description | ISR | 5:0 | clronrd | 0x0 | 5bit: 事件定时器溢出中断。 4bit: 计数器溢出中断。 3bit: MACH 2寄存器比较匹配中断。 2bit: MACH 1寄存器比较匹配中断。 1bit: MACH 0寄存器比较匹配中断。 0bit: 间隔中断 |
IER寄存器XTTCPS_IER_OFFSET (0x60U) Field Name | Bits | Type | Reset Value | Description | IEN | 5:0 | RW | 0x0 | 中断使能寄存器对应于ISR寄存器的中断位 |
Event_Control_Timer_0寄存器XTTCPS_ Event_Control_Timer_0_VALUE_OFFSET (0x6CU) Field Name | Bits | Type | Reset Value | Description | Event_Control_Timer_0 | 2:0 | RW | 0x0 | 2bit:E_OV 当该位为低时,事件定时器被禁用并在事件定时器寄存器溢出发生时设置为零;当设置为高时,溢出后定时器继续计数。 1bit:E_Lo 当该位为高时,以pclk计数ext_clk 的低电平持续时间; 低电平时,以pclk计数ext_clk 的高电平持续时间。 0bit:E_En 使能定时器,当该位设置1,事件定时器使能。 |
Event_Register_0寄存器XTTCPS_IER_OFFSET (0x60U) Field Name | Bits | Type | Reset Value | Description | Event_Register_0 | 15:0 | RO | 0x0 | 以pclk计数的ext_clk高电平或者低电平的时间。 |
4搭建SOC系统工程详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“01Vitis Soc开发入门”这篇文章。 4.1SOC系统工程
以上设计中,在ZYNQ IP中是设置了TTC0,并且增加了ILA IP用来观察波形 ZYNQ IP中设置TTC0
设置TTC0使用外部50M时钟
由于产生的PWM周期短,所以把采样深度多设置一些,并且使用前面的1M时钟作为Capture 信号
演示程序只输出1路PWM但是依然用ILA检测多少有的PWM输出
4.2编译并导出平台文件以下步骤简写,有不清楚的看第一篇文章。 1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。 2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。 3:添加配套工程路径下uisrc/04_pin/fpga_pin.xdc约束文件 4:生成Bit文件。 5:导出到硬件: FileàExport HardwareàInclude bitstream 6:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
5 搭建Vitis-sdk工程创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 5.1创建SDK Platform工程
右击soc_base编译,编译的时间可能会有点长 5.2创建APP工程本实验使用SDK自带的测试程序测试,并且对测试程序稍作修改。
6程序分析
先看下上面的程序流程图,程序中使用了TTC0的Timer0和Timer1,其中Timer1工作于定时间隔模式,每1000HZ中断一次。Timer0工作于定时间隔和PWM波形模式,每2000hz中断 一次。Timer0每经过500次中断也就是0.5秒更新一次Timer0的比较匹配值。Timer0的PWM周期是2000HZ因此,每间隔0.5ms,Timer0中断一次,并且在中断中更新比较匹配寄存器。 1:TmrInterruptExample函数- static int TmrInterruptExample(void)
- {
- int Status;
- Status = SetupInterruptSystem(INTC_DEVICE_ID, &InterruptController); //设置系统中断
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- Status = SetupTicker();//设置TTC0的Timer1中断
- if (Status != XST_SUCCESS) {
- return Status;
- }
- Status = SetupPWM();//设置TTC0的Timer0中断
- if (Status != XST_SUCCESS) {
- return Status;
- }
- Status = WaitForDutyCycleFull();//设置TTC0的Timer1比较匹配值更新
- if (Status != XST_SUCCESS) {
- return Status;
- }
- /*
- * 由于修改了程序,以下代码不会执行,如果不修改代码以下代码停止定时器工作
- */
- XTtcPs_Stop(&(TtcPsInst[TTC_TICK_DEVICE_ID]));
- XTtcPs_Stop(&(TtcPsInst[TTC_PWM_DEVICE_ID]));
- return XST_SUCCESS;
- }
复制代码
2:SetupTicker函数TTC0的Timer1通过该函数设置定时器的工作模式,时间间隔寄存器参数、中断回调函数等 - int SetupTicker(void)
- {
- int Status;
- TmrCntrSetup *TimerSetup;
- XTtcPs *TtcPsTick;
- /* SettingsTable 参数中定义了定时中断频率、定时间隔寄存器的值、预分频参数、定时器工作模式*/
- TimerSetup = &(SettingsTable[TTC_TICK_DEVICE_ID]);
- /*设置定时器工作于间隔模式、不启用波形输出模式*/
- TimerSetup->Options |= (XTTCPS_OPTION_INTERVAL_MODE |XTTCPS_OPTION_WAVE_DISABLE);
- Status = SetupTimer(TTC_TICK_DEVICE_ID); //设置定时器参数,这个函数下面再看
- if(Status != XST_SUCCESS) {
- return Status;
- }
- TtcPsTick = &(TtcPsInst[TTC_TICK_DEVICE_ID]); //指针重新指向
- /*
- * 设置定时器中断接入系统中断
- */
- Status = XScuGic_Connect(&InterruptController, TTC_TICK_INTR_ID,
- (Xil_ExceptionHandler)XTtcPs_InterruptHandler, (void *)TtcPsTick);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /*
- * 设置定时器全局中断指向的回调函数
- */
- XTtcPs_SetStatusHandler(&(TtcPsInst[TTC_TICK_DEVICE_ID]), &(TtcPsInst[TTC_TICK_DEVICE_ID]),
- (XTtcPs_StatusHandler)TickHandler);
- /*
- *使能定时器
- */
- XScuGic_Enable(&InterruptController, TTC_TICK_INTR_ID);
- /*
- * 使能定时器间隔中断
- */
- XTtcPs_EnableInterrupts(TtcPsTick, XTTCPS_IXR_INTERVAL_MASK);
- /*
- * 启动定时器
- */
- XTtcPs_Start(TtcPsTick);
- return Status;
- }
复制代码
3:SetupPWM函数TTC0的Timer0工作于定时中断和PWM波形输出模式 - int SetupPWM(void)
- {
- int Status;
- TmrCntrSetup *TimerSetup;
- XTtcPs *TtcPsPWM;
- TimerSetup = &(SettingsTable[TTC_PWM_DEVICE_ID]);
- /*
- * 定时间隔模式以及比较匹配波形输出模式
- */
- TimerSetup->Options |= (XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_MATCH_MODE);
- Status = SetupTimer(TTC_PWM_DEVICE_ID); //设置定时器参数,这个函数下面再看
- if(Status != XST_SUCCESS) {
- return Status;
- }
- TtcPsPWM = &(TtcPsInst[TTC_PWM_DEVICE_ID]); //指针重新指向
- /*
- * 设置定时器中断接入系统中断
- */
- Status = XScuGic_Connect(&InterruptController, TTC_PWM_INTR_ID,
- (Xil_ExceptionHandler)XTtcPs_InterruptHandler, (void *)TtcPsPWM);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /*
- * 设置定时器全局中断指向的回调函数
- */
- XTtcPs_SetStatusHandler(&(TtcPsInst[TTC_PWM_DEVICE_ID]), &(TtcPsInst[TTC_PWM_DEVICE_ID]),
- (XTtcPs_StatusHandler) PWMHandler);
- /*
- * Enable the interrupt for the Timer counter
- */
- XScuGic_Enable(&InterruptController, TTC_PWM_INTR_ID);
- /*
- *使能定时器间隔中断
- */
- XTtcPs_EnableInterrupts(TtcPsPWM, XTTCPS_IXR_INTERVAL_MASK);
- /*
- * 启动定时器
- */
- XTtcPs_Start(TtcPsPWM);
- return Status;
- }
复制代码
4:SetupTimer函数该函数设置定时器工作模式,计算合理的预分频参数以及时间间隔参数,至于具体的寄存器设置读者可以继续追踪分析,我们这里不再详细分析。 - int SetupTimer(int DeviceID)
- {
- int Status;
- XTtcPs_Config *Config;
- XTtcPs *Timer;
- TmrCntrSetup *TimerSetup;
- TimerSetup = &SettingsTable[DeviceID];
- Timer = &(TtcPsInst[DeviceID]);
- /*
- * 通过查表方式获取参数信息,这是SDK的通用方式
- */
- Config = XTtcPs_LookupConfig(DeviceID);
- if (NULL == Config) {
- return XST_FAILURE;
- }
- /*
- * 利用参数信息配置定时器的指针,这是SDK的通用方式
- */
- Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);
- if (Status != XST_SUCCESS) {
- return XST_FAILURE;
- }
- /*
- * 设置定时器工作模式
- */
- XTtcPs_SetOptions(Timer, TimerSetup->Options);
- /*
- * 以下函数计算定时器的间隔时间,以及预分频参数
- */
- XTtcPs_CalcIntervalFromFreq(Timer, TimerSetup->OutputHz,
- &(TimerSetup->Interval), &(TimerSetup->Prescaler));
- /*
- * 设置间隔寄存器以及预分频参数
- */
- XTtcPs_SetInterval(Timer, TimerSetup->Interval);
- XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler);
- return XST_SUCCESS;
- }
复制代码
5:WaitForDutyCycleFull函数该函数中通过TTC0的Timer1每中断500次(0.5S)更新一次比较匹配值,TTC0的Timer0会在下次中断中更新最新的比较匹配寄存器的值。 - int WaitForDutyCycleFull(void)
- {
- TmrCntrSetup *TimerSetup;
- u8 DutyCycle; /* The current output duty cycle */
- XTtcPs *TtcPs_PWM; /* Pointer to the instance structure */
- TimerSetup = &(SettingsTable[TTC_PWM_DEVICE_ID]);
- TtcPs_PWM = &(TtcPsInst[TTC_PWM_DEVICE_ID]);
- /*
- * Initialize some variables used by the interrupts and in loops.
- */
- DutyCycle = PWM_DELTA_DUTY;
- PWM_UpdateFlag = TRUE;
- ErrorCount = 0;
- /*
- * Loop until 100% duty cycle in the PWM output.
- */
- while (1) {
- /*
- * If error occurs, disable interrupts, and exit.
- */
- if (0 != ErrorCount) {
- return XST_FAILURE;
- }
- if (PWM_UpdateFlag) {//每次TTC0的Timer1中断500次设置PWM_UpdateFlag=TURE
- MatchValue = (TimerSetup->Interval * DutyCycle) / 100; //更新比较匹配值
- DutyCycle += PWM_DELTA_DUTY; //计算下次需要更新的值
- if(DutyCycle>100) //
- DutyCycle = 0;
- /*
- * 使能中断
- */
- XTtcPs_EnableInterrupts(TtcPs_PWM,
- XTTCPS_IXR_INTERVAL_MASK);
- PWM_UpdateFlag = FALSE;
- }
- }
复制代码
6:TickHandler函数TTC0的timer1每1S中断1000次,每5000次(0.5S)设置PWM_UpdateFlag = TRUE。 - static void TickHandler(void *CallBackRef, u32 StatusEvent)
- {
- if (0 != (XTTCPS_IXR_INTERVAL_MASK & StatusEvent)) {
- TickCount++;
- if (TICKS_PER_CHANGE_PERIOD == TickCount) {
- TickCount = 0;
- PWM_UpdateFlag = TRUE;
- }
- }
- else {
- ErrorCount++;
- }
- }
复制代码
7:PWMHandler函数TTC0的timer0每1S中断2000次,每次中断重新设置比较匹配寄存器。 - static void PWMHandler(void *CallBackRef, u32 StatusEvent)
- {
- XTtcPs *Timer;
- Timer = &(TtcPsInst[TTC_PWM_DEVICE_ID]);
- if (0 != (XTTCPS_IXR_INTERVAL_MASK & StatusEvent)) {
- XTtcPs_SetMatchValue(Timer, 0, MatchValue);
- }
- else {
- /*
- * If it is not Interval event, it is an error.
- */
- ErrorCount++;
- }
- XTtcPs_DisableInterrupts(Timer, XTTCPS_IXR_ALL_MASK);
- }
复制代码
7方案演示
7.1硬件准备
实验需要用到JTAG下载器、USB转串口外设,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON
7.2实验结果本实验可以通过开发板上的LED观察亮度的变化,也可以通过在线逻辑分析仪查看波形
|