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>

posted on 2022-06-15 21:34  张凌001  阅读(435)  评论(0)    收藏  举报

导航