zoukankan      html  css  js  c++  java
  • 十.定时器EPIT——1EPIT的入门

    今天来讲一个常用的外设:定时器,I.MX6U提供了多种硬件计时器,有些定时器的作用非常强大,我们从最基础的EPIT定时器开始学习。

    EPIT定时器简介

    EPIT(Enhanced Periodic Interrupt Timer)增强型周期中断定时器,主要功能就是用来完成周期性中断定时。对Cortex-M类型的STM32来说,定时器还具备捕获输入、PWM输出等作用,但是Cortex-A7架构的I.MX6UL的EPIT只用来完成周期性中断定时作用。

    EPIT是一个32位定时器,在处理器不用介入的情况下可以提供精准的定时中断,在通过软件设定后EPIT就会开始运行。先来看下《IMX6ULL参考手册.pdf》第24章上的说明

    EPIT主要功能

    1. 可以选择时钟源的32位向下计数器(上图中显示,包括关闭时钟源有4种选项)
    2. 12位分频器(2^12=4095),对应1~4096分频可选择
    3. 计数器在到达比较值相等时会产生中断
    4. 计数器值可以动态变成
    5. 在低功耗模式和调试模式下可以编程

    我们主要关注的是前3点。

     按照上面的图示,EPIT可以用的有3个时钟源:ipg_clk,ipg_clk_32k和ipg_clk_highfreq。其中ipg_clk就是我们在系统时钟树设置时候设置的66MHz的IPG_ROOT_CLK。

    具体时钟源在参考手册上有详细的说明。

    分频器是12位,可以对时钟源进行1~4096分频。

    经过分频的时钟进入EPIT,在EPIT内有3个比较重要的32位寄存器:Counter Register(EPIT_CNR),Load Register(EPIT_LR)和Compare Register(EPIT_CMPR)。在前面说过,EPIT是个向下计数器,可以给他设置一个初始值(加载寄存器),他就会从这个初始值开始向下递减,直到减到0或者比较器里设定的值。这个计数寄存器里就是保存着这个递减的数值。

    EPIT的工作模式

    EPIT有两种工作模式:

    • set-and-forget mode
    • free-running mode

    两种工作模式通过寄存器EPITx_CR的RLD位(bit[3])来控制(x为1或2,I.MX6UL提供2个EPIT),RLD=1时为set-and-forget模式,在此模式下EPIT的计数器从加载计数器EPITx_LR中获取初始值,不能向计数器寄存器写入数据,计数器自减到0后就会从EPITx_LR中重新加载数据到计数器中,循环进行。

    free-running模式是EPITx_CR[RLD]为0,在此模式下计数器计数到0时会从0xFFFFFFFF重新开始计数,不再从EPITx_LR中加载数据。

    EPIT的寄存器

    前面讲过,I.MX6UL提供了2个EPIT定时器,两套计时器的寄存器是一样的,通过EPITx的x值区分。每个定时器由5个寄存器控制。

     

    控制寄存器Control register

    控制寄存器的结构如下

    每个bit的作用也给了相应的说明

     

     其中比较重要的位要着重说明一下:

    CLKSRC(bit[25:24])时钟源,我们使用01对应ipg_clk_root,也就是66MHz。

    OM(bit[23:22]),EPIT对应有GPIO的IO功能,可以根据用途设置。

     可以查一下参考手册,可以通过设置GPIO1_IO25复用为EPIT1的输出

    PRESCALAR(bit[15:4])共12位,EPIT时钟源,分频器可以设置0~4095对应1~4096分频。

    RLD(bit[3])模式设置,可以切换是否从LR里调取初始值

    OCIEN(bit[2])使能中断时钟(Output Compare Interrupt Enable),1时使能中断,当计数器减到指定值时触发中断。

    ENMOD(bit[1])计数器EPIT使能模式,大概意思就是指定初始值,0时初始值等于最近一次关闭EPIT定时器后计数器里的值,1时从LR寄存器里获取初始值。

    EN([bit0]),使能EPIT,1时使能0时禁止。

    寄存器状态Status Register

    EPITx_SR只有一个标志位可以用OCIF(Output Compare Interrupt Flag,bit[0]),

    但是有两种作用,读取bit,当标志位为1时表明通过比较器有中断发生。当比较发生中断以后要手动清除此位。注意写bit的时候是w1c,意思是write 1 clear。就是要写1。

    加载寄存器Load Register

    加载寄存器EPITx_LR,前面已经说过很多回了,保存一个预设的值,如果 EPITx_CR[RLD]=1时,每次循环完毕后都从该寄存器里获取值,然后从这个值倒数。寄存器为32位,设置范围从0~4294967295。

    比较寄存器Compare Register

    EPITx_CMPR比较寄存器保存一个值,当计数器倒数到这个值时循环结束,如果允许产生中断的话就发生中断。32位。

    计数寄存器Counter Register

    EPITx_CNR保存倒计数的值,32位。

    下面的图表说明了中断及输出和Counter,LoadRegister、CompareRegister三者之间的关系

    上面的图中,LR值为4,CMP的值为2,注意观察Interrupt的信号

    要注意的是,在设置完比较值Compare的时候,计数器数到比较值后触发中断,但是counter还是继续往下数的,一直到数到0才从LR里取值。

    EPIT使用

    使用EPIT和其他的功能差不多,因为EPIT属于中断、也是先初始化、定义中断服务函数,注册中断服务函数等。我们利用蜂鸣器来验证其功能(教程上使用的是LED,我觉得LED在main函数里一直保持闪烁可以证明程序运行良好,就不要动了,用另外的外设调用EPIT即可。)

    初始化

    EPIT初始化的步骤如下:

    初始化EPITx_CR寄存器,包括时钟源、分频值、工作模式、计数器初始值来源(来自LR还是上一次停止前最后的值)、使能比较中断,注册中断服务函数,应该包含一下内容

    • 初始化EPITx_LR的值,决定起始值(计数器从多少倒数)
    • 初始化EPITx_CMPR的值,决定终止值(计数器数到多少停止)
    • 后两条通过直接对寄存器赋值就可以实现,第一条要进行位操作
    • 时钟源:bit[25:24],一般都为1,直接通过1<<24设置
    • 分频值:bit[15:4],具体情况具体分析
    • 工作模式:bit[3],建议值为1,从LR寄存器获取初始值,通过1<<3设置
    • 使能中断:bit[2],设置为1,计数器减至CMP寄存器值或0时产生中断,通过1<<2设置
    • 初始值来源:bit[1],建议为1,从LD获取或从0xFFFFFFFF开始,通过1<<1设置。

    最后的bit[0]的使能EPIT应该在中断服务函数注册完成后进行,在讲中断服务函数的时候说过,如果使能EPIT在注册服务函数前执行,基本上一上电的话就进入中断,程序基本上就不知道卡到哪了。

    更换时钟源

    这个在教程里没有讲,但是参考手册里写的比较清楚,虽然我们一般情况是不会更换时钟源的,但是专门强调了更换时钟源的流程

    1. 通过EPITx_CR的EN禁止EPIT(EN=0)
    2. 通过EPITx_OM的EN禁止EPIT输出(OM=00)
    3. 禁止EPIT中断
    4. 设置时钟源
    5. 清除EPITx_SR标志位(bit[1]写1)
    6. 将设置ENMOD=1,使EPIT从LR设定值或者0xFFFFFFFF开始倒数
    7. 使能EPIT(EN=1)
    8. 使能EPIT中断

    按照参考手册上所说,更换EPIT时钟源应该遵循上述流程。

    代码结构成

    这里除了初始化的代码还加上中断服务函数,主要就是说明中断服务函数最后清除SR标志位的用法。

    #include "bsp_epit.h"
    #include "bsp_int.h"
    #include "bsp_beep.h"
    #include "bsp_led.h"
    
    /*
    * @description            :   EPIT定时器初始化
    * @param-flac             :   分频器:0~4095
    * @param-count_start      :   计数器起始值
    * @param-count_sto        :   计数器起始值
    * @return                 :   None
    */
    void epit1_init(unsigned int flac,
                    unsigned int count_start,
                    unsigned int count_stop)
    {
        if(flac>4095){flac=4095;}
    
        EPIT1->CR =0;                                               //EPIT1_CR清零
        EPIT1->CR = (1<<1) |(1<<2) |(1<<3) |(flac<<4)|(1<<24);      //ENMOD=1 OCIEN=1 RLD=1(从LR取值) CLKSRC=1
        EPIT1->LR = count_start;                                    //设定EPIT1_LR寄存器
        EPIT1->CMPR = count_stop;                                   //设定EPIT1_CMPR寄存器
    
        GIC_EnableIRQ(EPIT1_IRQn);                                  //GIC中使能EPIT1
    
        system_register_irqHandler(EPIT1_IRQn,epit1_irqhandler,NULL);//注册中断函数
    
        EPIT1->CR |=1<<0;                                            //使能EPIC1
    }
    
    /*
    * @description            :   EPIT1中断服务函数
    * @param                  :   这里不需要没有实际参数,但是定义函数的时候是按照前面声明函数是定义的,就把直接把样式复制过来了
    * @return                 :   None
    */
    void epit1_irqhandler(unsigned int gcciar,void *param)
    {
        static unsigned char state=OFF;
        state = !state;
        if(EPIT1->SR &(1<<0))
        {
            beep_switch(state);
        }
        EPIT1->SR |=(1<<0);    //清除中断标志位
    }

    代码的注释也是比较清楚的,这里的函数和教程里的有些区别,教程里函数定义了两个形参,一个是分频值一个是LR的值,这里给了3个,加了比较器的值(仔细想想反而没什么用,计算时间反而还麻烦了些)。

    头文件里就是声明了两个函数

    #ifndef __BSP_EPIT_H
    #define __BSP_EPIT_H
    #include "imx6ul.h"
    
    void epit1_init(unsigned int flac,unsigned int count_start,unsigned int count_stop);
    void epit1_irqhandler(unsigned int gcciar,void *param);
    #endif
    bsp_epit.h

    make的时候记得在makefile里添加路径!

    使用定时器

    这里使用定时器是在main.c函数中,导入bsp_epit.h头文件后调用初始化函数就可以了

    epit1_init(0,33000000,0); 

    注意三个参数:第一个0是分频器,使用1分频,对应时钟66MHz,LR的值为33000000,CMPR的值为0,就是计数器数33000000次触发中断,而33000000刚好是66MHz的一般,刚好对应0.5s。整理一下,可以按照下面的公式计算

    Tout = ((frac +1 )* value) / Tclk——Tout为EPIT的溢出时间(s)

                     frac为分频值,

                     Tclk为EPIT的时钟源(Hz)注意这个是Hz

    按照上述流程,我们就可以使用EPIT定时器了。


    PS:这个定时器不知道为什么,我使用了一个GPIO1_IO09做输出,在1分频下,LR值设置为3300时,示波器测量输出为10KHz,占空比50%,但是在高就只能到50KHz了,不知道是不是指令占的周期过长了。有时间用汇编写一个看看能好一些不!

  • 相关阅读:
    dubbo接口测试
    httpclient接口返回结果中文显示问号
    idea创建springboot项目报错Initialization failed for 'https://start.spring.io' Please check URL, network and proxy settings.
    前端框架angular
    测试思考
    sonar的使用
    IDEA中getter方法报红
    dubbo相关
    IDEA无法从mapper方法直接点进xml文件的解决办法
    java笔记-spring boot系列
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/15793094.html
Copyright © 2011-2022 走看看