ZYNQ XCZU27DR FreeRTOS TTC 定时器中断学习笔记 TTCTriple Timer Counter被称为三重定时器/计数器它是 PS 内部的一种硬件外设。一个 TTC 模块内部包含多个相互独立的定时器计数通道。每个通道都可以独立完成周期定时向上或向下计数计数值匹配溢出检测产生中断输出周期波形测量外部事件。所以 TTC 不仅能“定时”也能“计数”。1.TTC的时钟源可以选择三种LPD_LSBUS_CLK、PS_REF_CLK、RPU_CLK2.时钟经过预分频器降低时钟频率3.进入计数器进行计数4.计数器旁边有一个中断模块TTC中断可以由多种触发达到Interval计数值、计数器溢出、匹配寄存器匹配成功、事件定时器溢出。5.Event Timer事件定时器它可以接收来自芯片外部或者PL的信号。比如PL每产生一个脉冲就通过EMIO送进TTCTTC可以测量两个脉冲之间经过多长时间、外部信号频率是多少、外部事件发生了多少次。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; }