查看: 6134|回复: 2

RT1052——6.PIT中断与外设时钟配置

[复制链接]
  • TA的每日心情
    慵懒
    2024-2-8 09:39
  • 签到天数: 217 天

    [LV.7]常住居民III

    92

    主题

    1112

    帖子

    29

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    7637

    热心会员

    最后登录
    2024-4-25
    发表于 2018-10-29 21:33:28 | 显示全部楼层 |阅读模式
    首先吐槽一下MDK5.24a,老是闪退,而且调试不好使(可能因为中文路径),一气之下又换回了我的MDK5.22。还是原来的好使啊。
    现在开始今天的正题,PIT这个PIT只有一个模块,但是里面有4个通道(独立计时),今天就以通道0为例。
    首先在工程添加PIT.c,添加PIT.h

    这个 BOARD_BootClockRUN();是用来配置外设时钟的

    QQ截图20181029194717.png

    这里面最常出现的就是CLOCK_SetMux(),CLOCK_SetDiv()这两个函数。

    先看看这两个函数是干啥的

    /*!
    * @brief Set CCM MUX node to certain value.
    *
    * @param mux   Which mux node to set, see \ref clock_mux_t.
    * @param value Clock mux value to set, different mux has different value range.
    */

    static inline void CLOCK_SetMux(clock_mux_t mux, uint32_t value)
    {
        uint32_t busyShift;

        busyShift = CCM_TUPLE_BUSY_SHIFT(mux);
        CCM_TUPLE_REG(CCM, mux) = (CCM_TUPLE_REG(CCM, mux) & (~CCM_TUPLE_MASK(mux))) |
                                  (((uint32_t)((value) << CCM_TUPLE_SHIFT(mux))) & CCM_TUPLE_MASK(mux));

        assert(busyShift <= CCM_NO_BUSY_WAIT);

        /* Clock switch need Handshake? */
        if (CCM_NO_BUSY_WAIT != busyShift)
        {
            /* Wait until CCM internal handshake finish. */
            while (CCM->CDHIPR & (1U << busyShift))
            {
            }
        }
    }


    /*!
    * @brief Set CCM DIV node to certain value.
    *
    * @param divider Which div node to set, see \ref clock_div_t.
    * @param value   Clock div value to set, different divider has different value range.
    */

    static inline void CLOCK_SetDiv(clock_div_t divider, uint32_t value)
    {
        uint32_t busyShift;

        busyShift = CCM_TUPLE_BUSY_SHIFT(divider);
        CCM_TUPLE_REG(CCM, divider) = (CCM_TUPLE_REG(CCM, divider) & (~CCM_TUPLE_MASK(divider))) |
                                  (((uint32_t)((value) << CCM_TUPLE_SHIFT(divider))) & CCM_TUPLE_MASK(divider));

        assert(busyShift <= CCM_NO_BUSY_WAIT);

        /* Clock switch need Handshake? */
        if (CCM_NO_BUSY_WAIT != busyShift)
        {
            /* Wait until CCM internal handshake finish. */
            while (CCM->CDHIPR & (1U << busyShift))
            {
            }
        }
    }


    嗯,看起来很复杂,其实我们也没必要去完全理解每一步的原理,只需要安心调用库就好了,配合下面三张图和函数的解释看
    QQ截图20181029195301.png
    QQ截图20181029195317.png
    QQ截图20181029195719.png

    可以基本猜到CLOCK_SetMux是选择时钟来源的,而CLOCK_SetDiv是设置时钟分频的。以pit的为例

    QQ截图20181029195159.png

    可以找到应该去配置kCLOCK_PerclkMux和kCLOCK_PerclkDiv。
    我们去看一下CSCMR1的对应位说明

    QQ截图20181029200421.png

    那我们就先设置个比较简单的使用OSC时钟,分频为1(OSC时钟来源是外部震荡电路 频率为24MHz,具体以后再讨论)也就是PERCLK_CLK_
    SEL配置为1,PERCLK_PODF配置为0;

    CLOCK_SetMux(kCLOCK_PerclkMux, 1U);
    CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U);

    这样外设时钟就配置好了。然后接着去看PIT_Init函数,void PIT_Init(PIT_Type *base, const pit_config_t *config);可以看到它有两个参数。第一个是选择配置哪个模块(话说PIT就一个模块),第二个是相关配置,我们可以去看一下pit_config_t
    typedef struct _pit_config
    {
        bool enableRunInDebug; /*!< true: Timers run in debug mode; false: Timers stop in debug mode */
    } pit_config_t;

    (emmmmmm就一个。。。)这个是用来选择debug的时候定时器是否开启。我们就写一下

    pit_config_t pitConfig;
    pitConfig.enableRunInDebug = false;
    PIT_Init(PIT, &pitConfig);


    然后就是要设置定时时间和配置中断了,先上代码
        PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, MSEC_TO_COUNT(1000U, CLOCK_GetFreq(kCLOCK_OscClk)));//配置中断事件
        PIT_EnableInterrupts(PIT, kPIT_Chnl_0, kPIT_TimerInterruptEnable);//使能对应模块中断
        EnableIRQ(PIT_IRQn);                                              //使能中断
        PIT_StartTimer(PIT, kPIT_Chnl_0);                            //开始计时


    先看PIT_SetTimerPeriod,他有三个参数,第一个是选择模块,第二个是选择通道,第三个是选择初始计数(PIT是减计数器,减到0重新配置初值)
    在函数说明里给了一句提示
    * @note Users can call the utility macros provided in fsl_common.h to convert to ticks.
    (那当然要去看了)
    /*! @name Timer utilities */
    /* @{ */
    /*! Macro to convert a microsecond period to raw count value */
    #define USEC_TO_COUNT(us, clockFreqInHz) (uint64_t)((uint64_t)us * clockFreqInHz / 1000000U)
    /*! Macro to convert a raw count value to microsecond */
    #define COUNT_TO_USEC(count, clockFreqInHz) (uint64_t)((uint64_t)count * 1000000U / clockFreqInHz)

    /*! Macro to convert a millisecond period to raw count value */
    #define MSEC_TO_COUNT(ms, clockFreqInHz) (uint64_t)((uint64_t)ms * clockFreqInHz / 1000U)
    /*! Macro to convert a raw count value to millisecond */
    #define COUNT_TO_MSEC(count, clockFreqInHz) (uint64_t)((uint64_t)count * 1000U / clockFreqInHz)
    /* @} */

    可以看到提供了定时转计数和计数转定时的宏函数,不过要提供时钟频率(单位Hz)


    接着看一下CLOCK_GetFreq函数
    /*!
    * @brief Gets the clock frequency for a specific clock name.
    *
    * This function checks the current clock configurations and then calculates
    * the clock frequency for a specific clock name defined in clock_name_t.
    *
    * @param clockName Clock names defined in clock_name_t
    * @return Clock frequency value in hertz
    */
    uint32_t CLOCK_GetFreq(clock_name_t name);

    可以看到,只要提供时钟名称,这个函数就能返回频率
    里面可以选择的参数有
    /*! @brief Clock name used to get clock frequency. */
    typedef enum _clock_name
    {
        kCLOCK_CpuClk              = 0x0U,         /*!< CPU clock */
        kCLOCK_AhbClk              = 0x1U,         /*!< AHB clock */
        kCLOCK_SemcClk             = 0x2U,         /*!< SEMC clock */
        kCLOCK_IpgClk              = 0x3U,         /*!< IPG clock */

        kCLOCK_OscClk              = 0x4U,         /*!< OSC clock selected by PMU_LOWPWR_CTRL[OSC_SEL]. */
        kCLOCK_RtcClk              = 0x5U,         /*!< RTC clock. (RTCCLK) */

        kCLOCK_ArmPllClk           = 0x6U,         /*!< ARMPLLCLK. */

        kCLOCK_Usb1PllClk          = 0x7U,         /*!< USB1PLLCLK. */
        kCLOCK_Usb1PllPfd0Clk      = 0x8U,         /*!< USB1PLLPDF0CLK. */
        kCLOCK_Usb1PllPfd1Clk      = 0x9U,         /*!< USB1PLLPFD1CLK. */
        kCLOCK_Usb1PllPfd2Clk      = 0xAU,         /*!< USB1PLLPFD2CLK. */
        kCLOCK_Usb1PllPfd3Clk      = 0xBU,         /*!< USB1PLLPFD3CLK. */

        kCLOCK_Usb2PllClk          = 0xCU,         /*!< USB2PLLCLK. */

        kCLOCK_SysPllClk           = 0xDU,         /*!< SYSPLLCLK. */
        kCLOCK_SysPllPfd0Clk       = 0xEU,         /*!< SYSPLLPDF0CLK. */
        kCLOCK_SysPllPfd1Clk       = 0xFU,         /*!< SYSPLLPFD1CLK. */
        kCLOCK_SysPllPfd2Clk       = 0x10U,        /*!< SYSPLLPFD2CLK. */
        kCLOCK_SysPllPfd3Clk       = 0x11U,        /*!< SYSPLLPFD3CLK. */

        kCLOCK_EnetPll0Clk         = 0x12U,        /*!< Enet PLLCLK ref_enetpll0. */
        kCLOCK_EnetPll1Clk         = 0x13U,        /*!< Enet PLLCLK ref_enetpll1. */

        kCLOCK_AudioPllClk         = 0x14U,        /*!< Audio PLLCLK. */
        kCLOCK_VideoPllClk         = 0x15U,        /*!< Video PLLCLK. */
    } clock_name_t;


    从中间找到我们需要的osc时钟。这样的话,就可以用MSEC_TO_COUNT(1000U, CLOCK_GetFreq(kCLOCK_OscClk))的到定时1s的计数值

    然后开启有关中断

    /*! @brief List of PIT interrupts */
    typedef enum _pit_interrupt_enable
    {
        kPIT_TimerInterruptEnable = PIT_TCTRL_TIE_MASK, /*!< Timer interrupt enable*/
    } pit_interrupt_enable_t;

    (emmmmmmmm,也就一种)

    配置好中断使能,最后就是开启计数,利用PIT_StartTimer。

    编写中断回调函数
    void PIT_IRQHandler(void)
    {
            if(PIT_GetStatusFlags(PIT, kPIT_Chnl_0) == kPIT_TimerFlag)    //判断是不是对应中断
            {
                    PIT_ClearStatusFlags(PIT, kPIT_Chnl_0, kPIT_TimerFlag);//清空中断标志位
                   
                    LPUART_WriteByte(LPUART1, '1');
                    LPUART_WriteByte(LPUART1, '\r');
                    LPUART_WriteByte(LPUART1, '\n');
            }
    }

    也没啥好说的。现象就是先打印 你好世界 然后每隔1S发送一个1

    QQ截图20181029213046.png

    (注意为了便于观察实验结果,我开启了时间戳)
    最后附上工程
      MDK版本:5.22
      pack:NXP.MIMXRT1052_DFP.10.0.1.pack
      下载算法:飞凌嵌入式提供的 MIMXRT_QSPIFLASH.FLM
      启动方式:spi flash启动  
      下载方式:STlink

    4.pit.zip (1.46 MB, 下载次数: 39)
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    1

    帖子

    0

    新手上路

    Rank: 1

    积分
    16
    最后登录
    2023-9-9
    发表于 2023-9-9 12:41:10 | 显示全部楼层
    请问1s延时是怎么确定的?看代码里没有这部分
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2024-2-8 09:39
  • 签到天数: 217 天

    [LV.7]常住居民III

    92

    主题

    1112

    帖子

    29

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    7637

    热心会员

    最后登录
    2024-4-25
     楼主| 发表于 2023-9-10 09:54:15 | 显示全部楼层
    liuhailonga 发表于 2023-9-9 12:41
    请问1s延时是怎么确定的?看代码里没有这部分

    MSEC_TO_COUNT(1000U, CLOCK_GetFreq(kCLOCK_OscClk))
    这里
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-25 13:48 , Processed in 0.125571 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表