ZYNQ XCZU27DR FreeRTOS TTC 定时器中断学习笔记
2026/7/8 2:26:34 网站建设 项目流程

TTC

Triple Timer Counter被称为三重定时器/计数器

它是 PS 内部的一种硬件外设。一个 TTC 模块内部包含多个相互独立的定时器计数通道。

每个通道都可以独立完成:

  • 周期定时;
  • 向上或向下计数;
  • 计数值匹配;
  • 溢出检测;
  • 产生中断;
  • 输出周期波形;
  • 测量外部事件。

所以 TTC 不仅能“定时”,也能“计数”。

1.TTC的时钟源可以选择三种LPD_LSBUS_CLK、PS_REF_CLK、RPU_CLK

2.时钟经过预分频器降低时钟频率

3.进入计数器进行计数

4.计数器旁边有一个中断模块,TTC中断可以由多种触发:达到Interval计数值、计数器溢出、匹配寄存器匹配成功、事件定时器溢出。

5.Event Timer:事件定时器,它可以接收来自芯片外部或者PL的信号。比如PL每产生一个脉冲,就通过EMIO送进TTC,TTC可以测量:两个脉冲之间经过多长时间、外部信号频率是多少、外部事件发生了多少次。

TTC0和TTC1

在刚开始设计硬件平台的时候选择了TTC0但是没有选择TTC1导致编译报错。排查问题后发现,在Freertos中自己需要一个硬件定时器产生系统Tick,用来完成vTaskDelay() 延时、任务时间片调度、阻塞任务超时等任务,在这里用到的就是TTC0。因此我们需要启用TTC1作为用户定时器来完成任务。

通常采用这种分工:(只需要选择没有被Freertos占用的计数器即可)

FreeRTOS 系统 Tick → 使用 TTC0 中的某个计数器

用户 LED 定时中断 → 使用 TTC1 中未占用的计数器

实验任务

此次实验基于XCZU27DR / Zynq UltraScale+ RFSoC / Cortex-A53 / FreeRTOS 实验目标:使用 TTC 定时器周期产生中断,通过信号量唤醒 LED 任务,实现 LED 电平翻转。

工作流程如下:

main() │ ├─ 初始化 GPIO ├─ 创建二值信号量 TimerSemaphore ├─ 创建 LedTask └─ 启动 FreeRTOS 调度器 │ ▼ LedTask 开始运行 │ ├─ 初始化 TTC ├─ 注册 TTC 中断函数 ├─ 使能 TTC 中断 ├─ 启动 TTC └─ 等待 TimerSemaphore │ ▼ TTC 计数到设定值 │ ▼ 产生中断 │ ▼ CPU 进入 TtcInterruptHandler() │ ├─ 读取中断状态 ├─ 清除中断标志 ├─ 释放 TimerSemaphore └─ 必要时立即切换任务 │ ▼ LedTask 被唤醒 │ └─ 翻转 LED 电平

实验代码

初始化TTC

大致的流程套路基本都是:

找到设备→ 初始化驱动→ 设置工作模式→ 配置周期→ 清中断→ 注册ISR→ 使能中断→ 启动设备
static int TtcInitialize(void) { XTtcPs_Config *ConfigPtr; XInterval IntervalValue; u8 PrescalerValue; int Status; /* * 根据设备ID查找TTC硬件配置。 */ ConfigPtr = XTtcPs_LookupConfig(TIMER_DEVICE_ID); if (ConfigPtr == NULL) { xil_printf("ERROR: TTC configuration not found\r\n"); return XST_FAILURE; } /* * 初始化TTC驱动实例。 */ Status = XTtcPs_CfgInitialize( &TtcInstance, ConfigPtr, ConfigPtr->BaseAddress ); if (Status != XST_SUCCESS) { xil_printf("ERROR: TTC initialization failed\r\n"); return XST_FAILURE; } /* * 配置参数之前先停止定时器。 */ XTtcPs_Stop(&TtcInstance); /* * Interval Mode: * 计数器达到Interval值后产生中断,然后重新开始计数。 * * WAVE_DISABLE: * 关闭TTC波形输出,只使用定时中断功能。 */ Status = XTtcPs_SetOptions( &TtcInstance, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE ); if (Status != XST_SUCCESS) { xil_printf("ERROR: TTC option configuration failed\r\n"); return XST_FAILURE; } /* * 根据TTC真实输入时钟自动计算: * 1. Interval计数值 * 2. 预分频值 * * 这里要求输出频率为5 Hz,即每200 ms一次中断。 */ XTtcPs_CalcIntervalFromFreq( &TtcInstance, TIMER_INTERRUPT_FREQ_HZ, &IntervalValue, &PrescalerValue ); XTtcPs_SetInterval( &TtcInstance, IntervalValue ); XTtcPs_SetPrescaler( &TtcInstance, PrescalerValue ); /* * 清除可能残留的中断状态。 */ XTtcPs_ClearInterruptStatus( &TtcInstance, XTTCPS_IXR_ALL_MASK ); /* * 使能TTC的Interval中断。 */ XTtcPs_EnableInterrupts( &TtcInstance, XTTCPS_IXR_INTERVAL_MASK ); /* * 将TTC中断号和TtcInterruptHandler中断函数关联起来。 * * FreeRTOS已经初始化中断控制器,因此这里不再单独初始化XScuGic。 */ Status = xPortInstallInterruptHandler( TIMER_INTR_ID, (XInterruptHandler)TtcInterruptHandler, &TtcInstance ); if (Status != pdPASS) { xil_printf("ERROR: TTC interrupt handler installation failed\r\n"); return XST_FAILURE; } /* * 在GIC中使能TTC中断。 */ vPortEnableInterrupt(TIMER_INTR_ID); xil_printf("TTC configuration successful\r\n"); xil_printf("Interval value = %u\r\n", (unsigned int)IntervalValue); xil_printf("Prescaler value = %u\r\n", (unsigned int)PrescalerValue); return XST_SUCCESS; }

TTC 中断函数

在中断函数里面要求禁用延时,快进快出。

在这里中断函数做了三件事情:读取中断状态、清除中断标志、通知 LED 任务

static void TtcInterruptHandler(void *CallbackRef) { XTtcPs *TtcPtr; u32 InterruptStatus; BaseType_t HigherPriorityTaskWoken = pdFALSE; TtcPtr = (XTtcPs *)CallbackRef; InterruptStatus = XTtcPs_GetInterruptStatus(TtcPtr); XTtcPs_ClearInterruptStatus( TtcPtr, InterruptStatus ); xSemaphoreGiveFromISR( TimerSemaphore, &HigherPriorityTaskWoken ); portYIELD_FROM_ISR( HigherPriorityTaskWoken ); }

完整代码:

/* * FreeRTOS TTC interrupt LED test * * Platform: * XCZU27DR / Cortex-A53 * * Function: * TTC3 generates an interrupt every 200 ms. * The ISR gives a binary semaphore. * LedTask obtains the semaphore and toggles LED0. */ #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "portmacro.h" #include "xparameters.h" #include "xgpiops.h" #include "xttcps.h" #include "xil_exception.h" #include "xil_printf.h" #include "xstatus.h" /*-----------------------------------------------------------*/ /* GPIO configuration */ /*-----------------------------------------------------------*/ #define LED0_PIN 78U /* EMIO[0] -> AP5 -> LED0 */ #define LED_ON 1U #define LED_OFF 0U #if defined(XPAR_XGPIOPS_0_DEVICE_ID) #define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID #elif defined(XPAR_PSU_GPIO_0_DEVICE_ID) #define GPIO_DEVICE_ID XPAR_PSU_GPIO_0_DEVICE_ID #else #error "Cannot find PS GPIO device ID in xparameters.h" #endif /*-----------------------------------------------------------*/ /* TTC configuration */ /*-----------------------------------------------------------*/ /* * 使用TTC1中的第一个计数器,即XTTCPS_3。 * 不要使用FreeRTOS系统Tick已经占用的TTC计数器。 */ #if defined(XPAR_XTTCPS_3_DEVICE_ID) #define TIMER_DEVICE_ID XPAR_XTTCPS_3_DEVICE_ID #define TIMER_INTR_ID XPAR_XTTCPS_3_INTR #else #error "XPAR_XTTCPS_3_DEVICE_ID is not defined. Enable TTC1 in Vivado and rebuild the platform." #endif /* * 5 Hz = 每200 ms产生一次中断。 * * 每次中断翻转一次LED: * 亮200 ms + 灭200 ms,完整闪烁周期为400 ms。 */ #define TIMER_INTERRUPT_FREQ_HZ 5U /*-----------------------------------------------------------*/ /* FreeRTOS task configuration */ /*-----------------------------------------------------------*/ #define LED_TASK_STACK_SIZE (configMINIMAL_STACK_SIZE * 2U) #define LED_TASK_PRIORITY (tskIDLE_PRIORITY + 1U) /*-----------------------------------------------------------*/ /* Global variables */ /*-----------------------------------------------------------*/ static XGpioPs GpioInstance; static XTtcPs TtcInstance; static SemaphoreHandle_t TimerSemaphore = NULL; static TaskHandle_t LedTaskHandle = NULL; /*-----------------------------------------------------------*/ /* Function declarations */ /*-----------------------------------------------------------*/ static int GpioInitialize(void); static int TtcInitialize(void); static void LedTask(void *pvParameters); static void TtcInterruptHandler(void *CallbackRef); /*-----------------------------------------------------------*/ /* GPIO initialization */ /*-----------------------------------------------------------*/ static int GpioInitialize(void) { XGpioPs_Config *ConfigPtr; int Status; /* * 根据设备ID查找PS GPIO硬件配置信息。 */ ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID); if (ConfigPtr == NULL) { xil_printf("ERROR: GPIO configuration not found\r\n"); return XST_FAILURE; } /* * 初始化PS GPIO驱动实例。 */ Status = XGpioPs_CfgInitialize( &GpioInstance, ConfigPtr, ConfigPtr->BaseAddr ); if (Status != XST_SUCCESS) { xil_printf("ERROR: GPIO initialization failed\r\n"); return XST_FAILURE; } /* * 将EMIO[0]配置为输出。 */ XGpioPs_SetDirectionPin( &GpioInstance, LED0_PIN, 1U ); /* * 使能EMIO[0]输出驱动。 */ XGpioPs_SetOutputEnablePin( &GpioInstance, LED0_PIN, 1U ); /* * 初始状态关闭LED。 */ XGpioPs_WritePin( &GpioInstance, LED0_PIN, LED_OFF ); return XST_SUCCESS; } /*-----------------------------------------------------------*/ /* TTC initialization */ /*-----------------------------------------------------------*/ static int TtcInitialize(void) { XTtcPs_Config *ConfigPtr; XInterval IntervalValue; u8 PrescalerValue; int Status; /* * 根据设备ID查找TTC硬件配置。 */ ConfigPtr = XTtcPs_LookupConfig(TIMER_DEVICE_ID); if (ConfigPtr == NULL) { xil_printf("ERROR: TTC configuration not found\r\n"); return XST_FAILURE; } /* * 初始化TTC驱动实例。 */ Status = XTtcPs_CfgInitialize( &TtcInstance, ConfigPtr, ConfigPtr->BaseAddress ); if (Status != XST_SUCCESS) { xil_printf("ERROR: TTC initialization failed\r\n"); return XST_FAILURE; } /* * 配置参数之前先停止定时器。 */ XTtcPs_Stop(&TtcInstance); /* * Interval Mode: * 计数器达到Interval值后产生中断,然后重新开始计数。 * * WAVE_DISABLE: * 关闭TTC波形输出,只使用定时中断功能。 */ Status = XTtcPs_SetOptions( &TtcInstance, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE ); if (Status != XST_SUCCESS) { xil_printf("ERROR: TTC option configuration failed\r\n"); return XST_FAILURE; } /* * 根据TTC真实输入时钟自动计算: * 1. Interval计数值 * 2. 预分频值 * * 这里要求输出频率为5 Hz,即每200 ms一次中断。 */ XTtcPs_CalcIntervalFromFreq( &TtcInstance, TIMER_INTERRUPT_FREQ_HZ, &IntervalValue, &PrescalerValue ); XTtcPs_SetInterval( &TtcInstance, IntervalValue ); XTtcPs_SetPrescaler( &TtcInstance, PrescalerValue ); /* * 清除可能残留的中断状态。 */ XTtcPs_ClearInterruptStatus( &TtcInstance, XTTCPS_IXR_ALL_MASK ); /* * 使能TTC的Interval中断。 */ XTtcPs_EnableInterrupts( &TtcInstance, XTTCPS_IXR_INTERVAL_MASK ); /* * 将TTC中断号和TtcInterruptHandler中断函数关联起来。 * * FreeRTOS已经初始化中断控制器,因此这里不再单独初始化XScuGic。 */ Status = xPortInstallInterruptHandler( TIMER_INTR_ID, (XInterruptHandler)TtcInterruptHandler, &TtcInstance ); if (Status != pdPASS) { xil_printf("ERROR: TTC interrupt handler installation failed\r\n"); return XST_FAILURE; } /* * 在GIC中使能TTC中断。 */ vPortEnableInterrupt(TIMER_INTR_ID); xil_printf("TTC configuration successful\r\n"); xil_printf("Interval value = %u\r\n", (unsigned int)IntervalValue); xil_printf("Prescaler value = %u\r\n", (unsigned int)PrescalerValue); return XST_SUCCESS; } /*-----------------------------------------------------------*/ /* TTC interrupt handler */ /*-----------------------------------------------------------*/ static void TtcInterruptHandler(void *CallbackRef) { XTtcPs *TtcPtr; u32 InterruptStatus; BaseType_t HigherPriorityTaskWoken = pdFALSE; TtcPtr = (XTtcPs *)CallbackRef; /* * 读取TTC中断状态。 */ InterruptStatus = XTtcPs_GetInterruptStatus(TtcPtr); /* * 清除已经发生的中断标志。 * 如果不清除,CPU可能反复进入同一个中断。 */ XTtcPs_ClearInterruptStatus( TtcPtr, InterruptStatus ); /* * 释放二值信号量,唤醒LED任务。 * * 中断中必须使用带FromISR后缀的API。 */ xSemaphoreGiveFromISR( TimerSemaphore, &HigherPriorityTaskWoken ); /* * 如果唤醒了更高优先级任务, * 在退出中断时立即执行任务切换。 */ portYIELD_FROM_ISR(HigherPriorityTaskWoken); } /*-----------------------------------------------------------*/ /* LED task */ /*-----------------------------------------------------------*/ static void LedTask(void *pvParameters) { u32 LedState = LED_OFF; (void)pvParameters; xil_printf("LedTask started\r\n"); /* * 初始化TTC和中断。 */ if (TtcInitialize() != XST_SUCCESS) { xil_printf("ERROR: TTC setup failed\r\n"); XGpioPs_WritePin( &GpioInstance, LED0_PIN, LED_OFF ); vTaskDelete(NULL); return; } /* * 启动TTC。 * 之后每200 ms产生一次中断。 */ XTtcPs_Start(&TtcInstance); xil_printf("TTC started, LED0 will toggle every 200 ms\r\n"); for (;;) { /* * 信号量为空时,任务进入阻塞状态, * 不会一直占用CPU。 */ if (xSemaphoreTake( TimerSemaphore, portMAX_DELAY ) == pdTRUE) { /* * 翻转LED状态。 */ LedState ^= 1U; XGpioPs_WritePin( &GpioInstance, LED0_PIN, LedState ); } } } /*-----------------------------------------------------------*/ /* Main function */ /*-----------------------------------------------------------*/ int main(void) { BaseType_t Result; xil_printf("\r\n"); xil_printf("========================================\r\n"); xil_printf("FreeRTOS TTC interrupt LED test\r\n"); xil_printf("LED0 GPIO: %u\r\n", LED0_PIN); xil_printf("Timer frequency: %u Hz\r\n", TIMER_INTERRUPT_FREQ_HZ); xil_printf("========================================\r\n"); /* * 初始化GPIO。 */ if (GpioInitialize() != XST_SUCCESS) { xil_printf("ERROR: GPIO setup failed\r\n"); return XST_FAILURE; } xil_printf("GPIO initialization successful\r\n"); /* * 创建二值信号量。 * 创建完成后,初始状态为空。 */ TimerSemaphore = xSemaphoreCreateBinary(); if (TimerSemaphore == NULL) { xil_printf("ERROR: Semaphore creation failed\r\n"); return XST_FAILURE; } /* * 创建LED处理任务。 */ Result = xTaskCreate( LedTask, "LedTask", LED_TASK_STACK_SIZE, NULL, LED_TASK_PRIORITY, &LedTaskHandle ); if (Result != pdPASS) { xil_printf("ERROR: LedTask creation failed\r\n"); vSemaphoreDelete(TimerSemaphore); return XST_FAILURE; } xil_printf("Starting FreeRTOS scheduler\r\n"); /* * 启动FreeRTOS调度器。 */ vTaskStartScheduler(); /* * 正常情况下不会运行到这里。 * 如果运行到这里,通常说明FreeRTOS堆空间不足。 */ xil_printf("ERROR: FreeRTOS scheduler failed to start\r\n"); XGpioPs_WritePin( &GpioInstance, LED0_PIN, LED_OFF ); return XST_FAILURE; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询