DSP28335 CPU 定时器深度解析:从原理到实战

在 DSP28335 的开发中,CPU 定时器是实现定时中断、周期任务调度的核心外设之一。本文将从基础概念硬件结构代码解析周期计算实战应用,全面拆解 28335 的 CPU 定时器,帮你彻底搞懂它的使用方法!

一、定时器核心概念扫盲

1. 三个定时器的分工与使用场景

DSP28335 内置 3 个 32 位 CPU 定时器(Timer0/Timer1/Timer2),分工明确:

  • Timer2 :预留给操作系统(如 TI-RTOS、FreeRTOS),作为系统滴答定时器(SysTick),为任务调度提供时间基准(如 1ms 中断),保障任务轮转和延时函数精准执行。
  • Timer0/Timer1 :开放给用户自定义,典型场景:
    • 周期性采样触发(ADC 定时采样);
    • 通信协议定时(UART 超时检测、SPI 同步);
    • 运动控制(电机 PWM 斩波、位置环周期);
    • 故障检测(过流保护延时、看门狗备用)。

2. 32bit 定时器的含义

32bit 定时器指 ** 计数寄存器(TIM) 周期寄存器(PRD)** 为 32 位宽度,计数范围0~2³²-1(0~4294967295)。相比 16 位定时器(仅 0~65535),无需多级分频即可实现更长定时周期(如 150MHz 系统时钟下,无分频最大定时约 28.6 秒,16 位仅 0.437ms)。

3. 计数部分与预分频部分的分工

  • 计数部分 :由 32 位周期寄存器PRDH:PRD和计数寄存器TIMH:TIM组成,采用递减计数:TIM 从 PRD 值递减至 0 时触发中断,自动重载 PRD 重新计数。
  • 预分频部分 :由预订标寄存器TPRH:TPR和预订标计数器PSC组成,对定时器时钟(TIMCLK)分频:PSC 从 TPR 值递减至 0 时,TIM 才减 1,进一步延长定时周期。

4. XH:X 如何表示 32bit?

XH高 16 位寄存器X低 16 位寄存器,拼接为完整 32 位数据:

  • TPRH:TPR:32 位预分频值;
  • TIMH:TIM:32 位计数值;
  • PRDH:PRD:32 位周期值。示例:TPRH=0x0001TPR=0x0000,则 32 位预分频值为0x00010000(65536)。

二、定时器硬件结构与寄存器定义

1. 寄存器集合结构体CPUTIMER_REGS

该结构体直接映射硬件寄存器,定义定时器的核心配置:

cpp 复制代码
// CPU定时器寄存器集合(硬件映射)
struct CPUTIMER_REGS {
   union TIM_GROUP TIM;   // 32位计数寄存器(TIMH:TIM):存储当前计数值,递减计数
   union PRD_GROUP PRD;   // 32位周期寄存器(PRDH:PRD):计数重载值
   union TCR_REG   TCR;   // 控制寄存器:配置启停、中断使能、重载等
   Uint16          rsvd1; // 保留位:硬件对齐/预留
   union TPR_REG   TPR;   // 预分频低位寄存器(低16位)
   union TPRH_REG  TPRH;  // 预分频高位寄存器(高16位,TPRH:TPR组成32位预分频值)
};

2. 辅助变量结构体CPUTIMER_VARS

封装定时器配置与状态,便于上层调用

cpp 复制代码
// 定时器辅助变量(配置/状态封装)
struct CPUTIMER_VARS {
   volatile struct  CPUTIMER_REGS  *RegsAddr; // 指向定时器寄存器基地址
   Uint32    InterruptCount;                 // 中断计数:统计触发次数
   float   CPUFreqInMHz;                     // 系统时钟频率(MHz):用于周期计算
   float   PeriodInUSec;                     // 目标定时周期(微秒)
};

三、定时器初始化代码逐行解析

1. 通用初始化函数InitCpuTimers

该函数将定时器初始化为 "最长周期 + 停止状态",避免上电误触发:

cpp 复制代码
void InitCpuTimers(void)
{
    // ------------------- Timer0初始化(用户自定义) -------------------
    CpuTimer0.RegsAddr = &CpuTimer0Regs; // 绑定Timer0寄存器地址
    CpuTimer0Regs.PRD.all  = 0xFFFFFFFF; // 周期设为最大值(最长定时)
    CpuTimer0Regs.TPR.all  = 0;          // 预分频低16位=0
    CpuTimer0Regs.TPRH.all = 0;          // 预分频高16位=0(无预分频)
    CpuTimer0Regs.TCR.bit.TSS = 1;       // 停止定时器(TSS=1)
    CpuTimer0Regs.TCR.bit.TRB = 1;       // 重载计数寄存器(PRD→TIM)
    CpuTimer0.InterruptCount = 0;        // 重置中断计数

    // ------------------- Timer1/2初始化(预留RTOS) -------------------
    // 注:Timer1/2预留给DSP-BIOS/RTOS,勿随意修改
    CpuTimer1.RegsAddr = &CpuTimer1Regs;
    CpuTimer2.RegsAddr = &CpuTimer2Regs;
    CpuTimer1Regs.PRD.all  = 0xFFFFFFFF;
    CpuTimer2Regs.PRD.all  = 0xFFFFFFFF;
    CpuTimer1Regs.TPR.all  = 0;
    CpuTimer1Regs.TPRH.all = 0;
    CpuTimer2Regs.TPR.all  = 0;
    CpuTimer2Regs.TPRH.all = 0;
    CpuTimer1Regs.TCR.bit.TSS = 1;
    CpuTimer2Regs.TCR.bit.TSS = 1;
    CpuTimer1Regs.TCR.bit.TRB = 1;
    CpuTimer2Regs.TCR.bit.TRB = 1;
    CpuTimer1.InterruptCount = 0;
    CpuTimer2.InterruptCount = 0;
}

2. Timer0 专属初始化函数TIM0_Init

该函数完成 Timer0 的时钟使能、中断配置、参数初始化:

cpp 复制代码
// Timer0初始化:Freq=系统时钟(MHz),Period=定时周期(us)
void TIM0_Init(float Freq, float Period)
{
    EALLOW; // 允许访问受保护寄存器
    SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // 使能Timer0外设时钟
    EDIS;   // 禁止访问受保护寄存器

    // 配置中断向量:Timer0中断服务函数→TINT0向量
    EALLOW;
    PieVectTable.TINT0 = &TIM0_IRQn;
    EDIS;

    // 基础配置(同InitCpuTimers)
    CpuTimer0.RegsAddr = &CpuTimer0Regs;
    CpuTimer0Regs.PRD.all  = 0xFFFFFFFF;
    CpuTimer0Regs.TPR.all  = 0;
    CpuTimer0Regs.TPRH.all = 0;
    CpuTimer0Regs.TCR.bit.TSS = 1;
    CpuTimer0Regs.TCR.bit.TRB = 1;
    CpuTimer0.InterruptCount = 0;

    ConfigCpuTimer(&CpuTimer0, Freq, Period); // 计算并设置实际周期值

    CpuTimer0Regs.TCR.bit.TSS = 0; // 启动定时器(TSS=0)

    // 中断使能配置
    IER |= M_INT1;                  // 使能CPU第1组中断
    PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // 使能PIE第1组第7个中断(Timer0)
    EINT; // 使能全局中断
    ERTM; // 使能实时调试中断
}

四、定时周期计算原理与公式

1. 核心公式定义

符号 含义 单位
X 系统时钟频率 MHz
TDDRH:TDDR 32 位预分频配置值 无(数值)
PRDH:PRD 32 位周期配置值 无(数值)

公式 1(预分频后计数时钟周期):计算 "TIM 减 1" 对应的时间(最小定时单位):

公式 2(总定时周期):计算定时器从计数到中断的总时间:

2. 关键逻辑说明

  • 预分频系数 / 计数次数需+1:定时器从配置值递减至 0 触发动作,实际次数比配置值多 1;
  • ×10⁻⁶:将 MHz 对应的 "微秒级周期" 转换为秒。

五、实战应用:多定时器配置与主循环

电路图如下

1. main 函数中的定时器使用

cpp 复制代码
void main()
{
    int i = 0;

    InitSysCtrl();    // 初始化系统控制(时钟/PLL等)
    InitPieCtrl();    // 初始化PIE中断控制器
    IER = 0x0000;     // 禁用所有CPU中断
    IFR = 0x0000;     // 清除中断标志
    InitPieVectTable();// 初始化PIE向量表

    LED_Init();       // 初始化LED硬件

    // 配置三个定时器:不同周期
    TIM0_Init(150, 500000);  // Timer0:500ms中断
    TIM1_Init(150, 1000000); // Timer1:1s中断
    TIM2_Init(150, 1500000); // Timer2:1.5s中断

    // 主循环
    while(1)
    {
        i++;
        if(i % 2000 == 0)
        {
            LED14_TOGGLE; // 每2000次循环切换LED14(约200ms)
        }
        DELAY_US(100);    // 软件延时100us
    }
}

2. 逻辑说明

  • 三个定时器分别以 500ms、1s、1.5s 周期触发中断,可在各自中断服务函数中实现不同任务(如 LED 翻转、数据采样);
  • 主循环通过DELAY_US(100)和计数器实现 LED 的 200ms 周期翻转,与定时器中断形成 "软件延时 + 硬件定时" 的双层任务调度。

六、总结与拓展

DSP28335 的 CPU 定时器凭借 32 位宽和灵活的预分频设计,能满足从微秒级到秒级的定时需求。核心要点:

  1. 明确定时器分工:Timer0/1 用户自定义,Timer2 预留 RTOS;
  2. 掌握 32 位寄存器拼接逻辑(XH:X);
  3. 熟练运用定时周期公式,根据系统时钟和目标周期反推寄存器配置;
  4. 中断配置需注意 PIE 向量表映射和中断使能层级(CPU 级→PIE 级)。
相关推荐
黑客思维者1 小时前
嵌入式设备软件数字签名系统:从设计到落地实操指南
嵌入式硬件·数字签名
小李做物联网1 小时前
6.7基于单片机stm32物联网嵌入式项目程序开发之人脸健康检测系统
stm32·单片机·嵌入式硬件·物联网·计算机外设
一枝小雨1 小时前
9 更进一步的 bootloader 架构设计
stm32·单片机·嵌入式·软件架构·ota·bootloader·aes加密
Jerry丶Li1 小时前
三十七、STM32的SPI
stm32·单片机·嵌入式硬件
一支闲人2 小时前
NRF24L01无线通信模块的快速上手
stm32·单片机·嵌入式硬件·nrf24l01无线通信模块
qq_401700412 小时前
运算放大器电路二、实例分析
单片机·嵌入式硬件
杰出的胡兵2 小时前
MCU中PFD(Power Fail Detect)和PVD(Programmable Voltage Detector)的区别?
单片机·嵌入式硬件
沐欣工作室_lvyiyi2 小时前
基于单片机的校园路灯智能控制系统(论文+源码)
单片机·嵌入式硬件·毕业设计·校园路灯
纳米软件2 小时前
电源芯片的欠压锁定如何测试,需要哪些设备?-纳米软件
自动化测试·单片机·嵌入式硬件