【普中DSP28335开发攻略】-- 第 16 章 定时器中断实验

(1)实验平台:普中DSP28335开发板


上一章我们介绍了 F28335 的外部中断, 这一章我们来学习下 F28335 的 CPU定时器。F28335的CPU定时器有3个且均为32位, 分别是Timer0、Timer1、Timer2,其中 Timer2 是为操作系统 DSP/BIOS 保留的, 当未移植操作系统时, 可用来做普通的定时器。 这三个定时器的中断信号分别为 TINT0,TINT1,TINT2, 分别对应于中断向量 INT1, INT13, INT14。 本章以定时器 0 为例进行讲解, 让大家学会 F28335的 CPU 定时器的使用。 本章要实现的功能是: 通过 Timer0 中断控制 D2 指示灯闪烁, 主函数控制 D1 指示灯闪烁。 学习本章可以参考《tms320f28335 数据手册》 -4.2 32-Bit CPU-Timers 0/1/2 章节。 若结合视频学习效果更佳。 本章分为如下几部分内容:

[16.1 定时器介绍](#16.1 定时器介绍)

[16.1.1 定时器工作原理](#16.1.1 定时器工作原理)

[16.1.2 定时器相关寄存器](#16.1.2 定时器相关寄存器)

[16.2 定时器配置步骤](#16.2 定时器配置步骤)

[16.3 硬件设计](#16.3 硬件设计)

[16.4 软件设计](#16.4 软件设计)

[16.4.1 定时器 0 初始化函数](#16.4.1 定时器 0 初始化函数)

[16.4.2 定时器 0 中断服务函数](#16.4.2 定时器 0 中断服务函数)

[16.4.3 主函数](#16.4.3 主函数)

[16.5 实验现象](#16.5 实验现象)

课后作业


16.1 定时器介绍

16.1.1 定时器工作原理

TMS320F28335 的 CPU Time 有三个, 分别为 Timer0, Timer1, Timer2, 其中Timer2 是为操作系统 DSP/BIOS 保留的, 当未移植操作系统时, 可用来做普通的定时器。 这三个定时器的中断信号分别为 TINT0, TINT1, TINT2, 分别对应于中断向量 INT1, INT13, INT14。 定时器的功能如下图所示:

从上图可知, 定时器有一个预分频模块和一个定时/计数模块, 其中预分频模块包括一个 16 位的定时器分频寄存器(TDDRH: TDDR) 和一个 16 位的预定标计数器(PSCH: PSC) ; 定时/计数模块包括一个 32 位的周期寄存器(PRDH: PRD)和一个 32 位的计数寄存器(TIMH:TIM) 。

当系统时钟(SYSCLKOUT) 来一个脉冲, PSCH: PSC 预定标计数器减 1, 当 PSCH: PSC 预定标计数器减到 0 的时候, 预定标计数器产生下溢后向定时器的 32 位计数器 TIMH:TIM 借位, 即 TIMH:TIM 计数器减 1, 同时 PSCH: PSC 可以重载定时器分频寄存器(TDDRH: TDDR) 的值; 当计数寄存器 TIMH: TIM 减到 0 产生下溢的时候, 计数寄存器会重载周期寄存器(PRDH: PRD) 的值, 同时定时器会产生一个中断信号给 CPU。 定时器的中断结构如下图所示:

定时器中断属于 PIE 中断, 中断信号经过 PIE 后, 再进入处理器, 定时器 0的中断属于 PIE 第一组中断中的第 7 个小中断。

16.1.2 定时器相关寄存器

定时器配置和控制寄存器如下图所示:

(1) 定时器控制寄存器 TIMERxTCR

定时器控制寄存器各位功能描述如下所示:

(2) 定时器预定标寄存器

定时器预定标寄存器各位功能描述如下所示:

(3) 定时器计数器

定时计数器各位功能描述如下所示:

(4) 定时器周期寄存器

定时器周期寄存器各位功能描述如下所示:

16.2 定时器配置步骤

接下来我们介绍下如何使用库函数对通用定时器进行配置。 这个也是在编写程序中必须要了解的。 本章以定时器 0 为例, 至于定时器 1、 2 是完全类似。 具体步骤如下: (定时器相关库函数在 DSP2833x_CpuTimers.c 文件中)

(1) 使能定时器时钟

我们知道 F28335 的 CPU 定时器的时钟是由外设时钟控制寄存器 3 所控制,并且在对系统时钟控制寄存器写操作时必须关闭写保护, 使能定时器时钟代码如下:

cpp 复制代码
EALLOW;
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
EDIS;

(2) 初始化定时器参数, 包括制定定时器寄存器地址、 周期寄存器值、 预定标计数器值等。

要使用 F28335 的 CPU 定时器, 还需要对其内部相关寄存器的设置, 具体设置代码如下:

cpp 复制代码
//指向定时器 0 的寄存器地址
CpuTimer0.RegsAddr = &CpuTimer0Regs;
//设置定时器 0 的周期寄存器值
CpuTimer0Regs.PRD.all = 0xFFFFFFFF;
//设置定时器预定标计数器值为 0
CpuTimer0Regs.TPR.all = 0;
CpuTimer0Regs.TPRH.all = 0;
//确保定时器 0 为停止状态
CpuTimer0Regs.TCR.bit.TSS = 1;
//重载使能
CpuTimer0Regs.TCR.bit.TRB = 1;
// Reset interrupt counters:
CpuTimer0.InterruptCount = 0;

(3) 定时器的设置

要使用定时器还需要设置它的定时周期以及设置定时器的控制寄存器等。 这些在 TI 提供的库函数"DSP2833x_CpuTimers.c" 内已经有函数实现, 如下所示:

cpp 复制代码
void ConfigCpuTimer(struct CPUTIMER_VARS *Timer, float Freq, float Period)

该函数有 3 个参数, 第 1 个表示哪个定时器, 第 2 个表示定时器频率, 第 3个表示定时器周期值。 函数具体内部实现代码如下:

cpp 复制代码
void ConfigCpuTimer(struct CPUTIMER_VARS *Timer, float Freq, float Period)
{
    Uint32 temp;
    // Initialize timer period:
    Timer->CPUFreqInMHz = Freq;
    Timer->PeriodInUSec = Period;
    temp = (long) (Freq * Period);
    Timer->RegsAddr->PRD.all = temp;
    // Set pre-scale counter to divide by 1 (SYSCLKOUT):
    Timer->RegsAddr->TPR.all = 0;
    Timer->RegsAddr->TPRH.all = 0;
    // Initialize timer control register:
    Timer->RegsAddr->TCR.bit.TSS = 1; // 1 = Stop timer, 0 = Start/Restart Timer
    Timer->RegsAddr->TCR.bit.TRB = 1; // 1 = reload timer
    Timer->RegsAddr->TCR.bit.SOFT = 0;
    Timer->RegsAddr->TCR.bit.FREE = 0; // Timer Free Run Disabled
    Timer->RegsAddr->TCR.bit.TIE = 1; // 0 = Disable/ 1 = Enable
    Timer Interrupt
    // Reset interrupt counter:
    Timer->InterruptCount = 0;
}

函数内部具体操作我们可以不用了解, 对于初学者来说会使用即可。

(4) 开启定时器中断功能, 并使能定时器

具体实现代码如下:

cpp 复制代码
//设置定时器 0 的中断入口地址为中断向量表的 INT0
EALLOW;
PieVectTable.TINT0 = &TIM0_IRQn;
EDIS;
//开始定时器功能
CpuTimer0Regs.TCR.bit.TSS=0;
//开启 CPU 第一组中断并使能第一组中断的第 7 个小中断, 即定时器 0
IER |= M_INT1;
PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
//使能总中断
EINT;
ERTM;

(5) 编写定时器中断服务函数

最后我们还需要编写一个定时器中断服务函数, 通过中断函数处理定时器产生的相关中断。 定时器中断服务函数名在上一步就已经指定, 即中断服务函数入口地址, 只不过在编写中断服务函数时需要在函数名前面加上一个关键字"interrupt" , 这个在外部中断实验章节已经介绍过。 定时器 0 中断服务函数如下所示:

cpp 复制代码
interrupt void TIM0_IRQn(void)
{

......功能程序

}

在中断执行结束前要清除相应的中断标志位, 以等待下次中断的到来。

16.3 硬件设计

本实验使用到硬件资源如下:

(1) D1、 D2 指示灯

(2) 定时器 0

LED 灯模块电路在前面章节已介绍, 这里就不多说, 至于定时器 0 是 F28335芯片内部资源, 只要通过软件对其配置即可使用。

16.4 软件设计

本章所要实现的功能是: 通过 Timer0 中断控制 D2 指示灯闪烁, 主函数控制 D1 指示灯闪烁提示系统运行状态。 程序框架如下:

(1) 初始化定时器 0, 并使能相应中断

(2) 编写定时器 0 中断函数

(3) 编写主函数

打开"E:\DSP8233x_ProjectExample\DSP2833x_Example\Example10_DSP2833x_ Time0" 工程, 在 APP 文件夹内可以看到又新建了一个 time 文件夹。 在DSP2833x_Libraries 工程组中添加了 DSP2833x_CpuTimers.c 库文件。 定时器相关操作的库函数及寄存器都放在此文件中, 所以使用到定时器就必须加入这个文件, 同时要包含对应的头文件路径, 这个在前面创建工程模板时已完成。 这里我们分析几个重要函数, 其他部分程序大家可以打开工程查看。

16.4.1 定时器 0 初始化函数

要使用定时器 0 中断, 我们必须先对它进行配置。 其初始化代码如下:

cpp 复制代码
//定时器 0 初始化函数
//Freq: CPU 时钟频率(150MHz)
//Period: 定时周期值, 单位 us
void TIM0_Init(float Freq, float Period)
{
    EALLOW;
    SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
    EDIS;
    //设置定时器 0 的中断入口地址为中断向量表的 INT0
    EALLOW;
    PieVectTable.TINT0 = &TIM0_IRQn;
    EDIS;
    //指向定时器 0 的寄存器地址
    CpuTimer0.RegsAddr = &CpuTimer0Regs;
    //设置定时器 0 的周期寄存器值
    CpuTimer0Regs.PRD.all = 0xFFFFFFFF;
    //设置定时器预定标计数器值为 0
    CpuTimer0Regs.TPR.all = 0;
    CpuTimer0Regs.TPRH.all = 0;
    //确保定时器 0 为停止状态
    CpuTimer0Regs.TCR.bit.TSS = 1;
    //重载使能
    CpuTimer0Regs.TCR.bit.TRB = 1;
    // Reset interrupt counters:
    CpuTimer0.InterruptCount = 0;
    ConfigCpuTimer(&CpuTimer0, Freq, Period);
    //开始定时器功能
    CpuTimer0Regs.TCR.bit.TSS=0;
    //开启 CPU 第一组中断并使能第一组中断的第 7 个小中断, 即定时器 0
    IER |= M_INT1;
    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
    //使能总中断
    EINT;
    ERTM;
}

在TIM0_Init()函数中, 有 2 个入口参数, 一个是 CPU 时钟频率, 另一个是定时周期值。 默认系统 CPU 时钟频率是 150MHZ, 根据定时周期计算公式:

所以要定时多少时间, 可以通过定时周期值 Period 参数来设置。 比如要定时 500ms, 则 Freq=150, Period=500000。

函数内首先开启定时器 0 时钟, 并设置定时器 0 相关寄存器及初始化定时器0, 然后设置定时器 0 入口函数, 使能 PIE 中断, 最后开启定时器 0 中断功能和总中断。 这些配置是按照前面介绍的步骤完成, 除了时钟外, 其他顺序可以调换。

其实如果你会配置定时器 0 中断, 那么定时器 1、 2 中断都是类似的。

16.4.2 定时器 0 中断服务函数

初始化定时器 0 后, 定时器 0 中断就已经开启了, 当定时时间到达后会触发一次中断, 这时程序就会进入中断服务函数执行, 所以我们还需要编写对应的定时器 0 中断函数, 具体代码如下:

cpp 复制代码
interrupt void TIM0_IRQn(void)
{
    EALLOW;
    LED2_TOGGLE;
    PieCtrlRegs.PIEACK.bit.ACK1=1;
    EDIS;
}

中断服务函数内容非常简单, 进入中断后控制 LED2 状态翻转, 在中断函数结束前将 PIEACKx 相应的标志清零, 即写 1 清零。 由于对 PIE 等寄存器操作需要关闭写保护, 因此将该语句放在 EALLOW...EDIS 中。

16.4.3 主函数

编写好定时器 0 初始化和中断服务函数后, 接下来就可以编写主函数了, 代码如下:

cpp 复制代码
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP2833x Examples Include File
#include "leds.h"
#include "time.h"
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
    int i=0;
    InitSysCtrl();
    InitPieCtrl();
    IER = 0x0000;
    IFR = 0x0000;
    InitPieVectTable();
    LED_Init();
    TIM0_Init(150,500000);
    while(1)
    {
        i++;
        if(i%2000==0)
        {
            LED1_TOGGLE;
        } 
        DELAY_US(100);
    }
}

主函数实现的功能很简单, 首先初始化系统时钟、 PIE 中断相关寄存器及中断向量表, 本套教程所有有关中断代码都采用这种设置, 后面就不做重复。 再对使用到的硬件端口时钟和 IO 口初始化, 然后调用我们前面编写的 定时器 0 的初始化函数, 最后进入 while 循环语句, 不断让 D1 指示间隔 200ms 闪烁。

有的朋友就会问, 在主函数中怎么没有看到 D2 指示灯的控制呢? 因为我们在调用 TIM0_Init(150,500000);函数时即使能了定时器 0, 并设置定时周期时间是 500ms, 即到达 500ms 后就会进入一次定时器 0 中断, 在定时器 0 中断函数内即可实现 D2 指示灯的控制。

16.5 实验现象

使用仿真器将开发板和电脑连接成功后, 把编写好的程序编译后, 如果没有报错即可将点击仿真调试, 程序即会写入到芯片的 RAM 内, 运行结果是 D2 指示灯间隔 500ms 状态翻转一次, 主函数控制 D1 指示灯间隔 200ms 闪烁提示系统运行状态。 当系统断电后, 程序不会继续执行, 因为我们现在使用的是仿真调试,即程序写入到 RAM 内, 而非 FLASH。

课后作业

(1) 修改定时器 0 初始化函数参数值, 设定 1 秒钟的定时中断, 让 D2 指示灯 1 秒钟状态反转一次, 实现 2 秒钟闪烁一次。

(2) 使用定时器 1 中断控制 D2 指示灯闪烁, 闪烁时间自定义。 (温馨提示: 只需要在初始化函数和中断函数中, 将 定时器 0 修改为定时器 1 即可)

相关推荐
雅欣鱼子酱1 小时前
电流检测的电路设计与选型——分流电阻法
stm32·单片机·嵌入式硬件·芯片·电子元器件·电流检测芯片
DIY机器人工房3 小时前
嵌入式面试题:看你学习了自动控制原理这门课,讲一下欠驱动系统?
stm32·单片机·学习·嵌入式·自动控制原理
从零点4 小时前
STM32通过SPI进行W25Q128(外部flash)读写操作
stm32·单片机·嵌入式硬件
深圳元器猫6 小时前
航顺HK32L08X:国产超低功耗MCU新标杆
单片机·mcu
the sun347 小时前
单片机简单介绍
单片机·嵌入式硬件·51单片机
d111111111d7 小时前
STM32外设学习-I2C通信(代码)--MPU6050--笔记
笔记·stm32·单片机·嵌入式硬件·学习
贝塔实验室8 小时前
Altium Designer 6.0 初学教程-在Altium Designer 中对PCB 进行板层设置及内电层进行分割
嵌入式硬件·fpga开发·编辑器·硬件工程·信息与通信·信号处理·pcb工艺
Jerry丶Li8 小时前
三十二、STM32的USART(串口收发文本数据包)
stm32·单片机·嵌入式硬件
ACP广源盛139246256738 小时前
GSV1201D(2201D)#ACP@DisplayPort 1.2 转 HDMI 1.4 转换器(带嵌入式 MCU)
单片机·嵌入式硬件·电脑·音视频