• PIT中断与外设时钟配置


    首先吐槽一下MDK5.24a,老是闪退,而且调试不好使(可能因为中文路径),一气之下又换回了我的MDK5.22。还是原来的好使啊。

    现在开始今天的正题,PIT这个PIT只有一个模块,但是里面有4个通道(独立计时),今天就以通道0为例。
    首先在工程添加PIT.c,添加PIT.h

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

    <ignore_js_op>

    这里面最常出现的就是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))
            {
            }
        }
    }


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

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

    <ignore_js_op>

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

    <ignore_js_op>

    那我们就先设置个比较简单的使用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

    <ignore_js_op>

  • 相关阅读:
    Spring AOP切点表达式用法总结
    各种文档地址记录
    回顾乐信集团工作经历
    Redux的简单使用
    简单介绍软件测试(一)
    jupyter notebook 安装代码提示功能
    解决matplotlib不显示中文的问题
    前端生成二维码并下载(PC端)
    XSS绕过常见方式
    JWT的安全问题
  • 原文地址:https://www.cnblogs.com/zhugeanran/p/16379925.html
Copyright © 2020-2023  润新知