wuyage

Kinetis KL43 BareMetal SC 代码学习之一

2
阅读(4404)

Kinetis KL17/27 128K/256K Flash 器件,KL33,KL43 所有器件的官方评估板都是FRDM_KL43Z或者TWR_KL43Z48M,其官方主页为:http://www.nxp.com/zh-Hans/products/software-and-tools/hardware-development-tools/freedom-development-boards/freedom-development-platform-for-kinetis-kl43-kl33-kl27-kl17-and-kl13-mcus:FRDM-KL43Z?http://www.nxp.com/zh-Hans/products/software-and-tools/hardware-development-tools/tower-development-boards/mcu-and-processor-modules/kinetis-modules/kinetis-kl43-kl33-kl27-kl17-48-mhz-mcus-tower-system-module:TWR-KL43Z48M?uc=true&lang_cd=zh-Hans

当使用以上芯片时,可以用这两个板子评估,软件代码可以使用本文要介绍的裸板SC 代码,

代码包见附件:

FRDM-KL43Z-SC-BAREMETAL.rar

TWR-KL43Z48M-SC-BAREMETAL.rar

该代码库里面有IAR和Keil的工程(没有KDS的工程)

FRDM_KL43 里的例程较少,TWR_KL43 里则有很多例程。不过两者的底层驱动文件都是一样的。

TWR.jpg

下面让我们先看个简单的GPIO例子吧,这里FRDM_KL43Z为例,开发环境Keil MDK。

打开 \frdm-kl43z48m-sc-baremetal\build\keil\frdm_projects\frdm_led_test 中的工程,先编译一下,咦,怎么一上来编译就报错了。

QQ1.jpg

遇到此状况,莫要着急,原因是工程中CMSIS下的两个文件的路径不对,

QQ2.jpg

在文件上右键options for File...

3.jpg

然后更改Path 路径,每个电脑的Keil安装路径可能不同,这里设置为实际的安装路径位置即可。

QQ3.jpg

然后再重新编译,就不会报错了。

此工程实现的功能,两个LED小灯不断的闪烁。

下面让我们仔细来分析一下这个代码:

我们直接main函数开始看起(main函数之前做了什么,感兴趣的读者可以自行研究),

int main (void) { SIM_Init (SIM_MODULE_CONFIG_ALL_PERIPH_ON); (1) MCG_LITE_Init (MCG_LITE_HIRC_48MHZ); (2) // LED pins configuration PORT_Init (PORTD, PORT_MODULE_ALT1_MODE, PIN_5, 0, NULL); (3) GPIO_Init (GPIOD, GPIO_PIN_OUTPUT, PIN_5); (4) GPIO_Set (GPIOD, PIN_5); (5) PORT_Init (PORTE, PORT_MODULE_ALT1_MODE, PIN_31, 0, NULL); (6) GPIO_Init (GPIOE, GPIO_PIN_OUTPUT, PIN_31); (7) GPIO_Clr (GPIOE, PIN_31); (8) // PIT initialization - CH0 used for LED blinking PIT_Init (PIT0, CH0, PIT_CH_TIMER_EN_CONFIG, 3000000, 1, pit_callback); (9) __enable_irq(); (10) while (1) { } }

(1) SIM_Init() 函数的功能是初始化MCU的SIM模块,

void SIM_Init (tSIM sim) { SIM_SOPT1CFG= sim.SOPT1CFG; if (!(sim.SOPT1&SIM_SOPT1_USBREGEN_MASK)) SIM_SOPT1CFG |= SIM_SOPT1CFG_URWE_MASK; SIM_SOPT1 = sim.SOPT1; SIM_SOPT2 = sim.SOPT2; SIM_SOPT4 = sim.SOPT4; SIM_SOPT5 = sim.SOPT5; SIM_SOPT7 = sim.SOPT7; SIM_SCGC4 = sim.SCGC4; SIM_SCGC5 = sim.SCGC5; SIM_SCGC6 = sim.SCGC6; SIM_SCGC7 = sim.SCGC7; SIM_CLKDIV1 = sim.CLKDIV1; }

参数是一个结构体:

typedef struct { uint32 SOPT1, SOPT1CFG, SOPT2, SOPT4, SOPT5, SOPT7, SCGC4, SCGC5, SCGC6, SCGC7, CLKDIV1; } tSIM;

这个结构体其实对SIM 模块做了一个抽象,它包含了SIM中的一些关键的寄存器。

SIM0.jpg

程序中的实参是SIM_MODULE_CONFIG_ALL_PERIPH_ON,它的定义如下,它对结构体的各个寄存器进行了赋值。

#define SIM_MODULE_CONFIG_ALL_PERIPH_ON /* all clocks on */ \ (tSIM){ \ /* SOPT1 */ SET(SIM_SOPT1_USBREGEN_MASK)|CLR(SIM_SOPT1_USBSSTBY_MASK)| \ CLR(SIM_SOPT1_USBVSTBY_MASK)|SET(SIM_SOPT1_OSC32KSEL(0x00))| \ SET(SIM_SOPT1_OSC32KOUT(0x00)), \ /* SOPT1CFG*/ CLR(SIM_SOPT1CFG_USSWE_MASK)|CLR(SIM_SOPT1CFG_UVSWE_MASK)| \ CLR(SIM_SOPT1CFG_URWE_MASK), \ /* SOPT2 */ SET(SIM_SOPT2_LPUART1SRC(0x01))| \ SET(SIM_SOPT2_LPUART0SRC(0x01))|SET(SIM_SOPT2_TPMSRC(0x01))| \ SET(SIM_SOPT2_FLEXIOSRC(0x01))|CLR(SIM_SOPT2_USBSRC_MASK)| \ SET(SIM_SOPT2_CLKOUTSEL(0x02))|CLR(SIM_SOPT2_RTCCLKOUTSEL_MASK), \ /* SOPT4 */ CLR(SIM_SOPT4_TPM2CLKSEL_MASK)|CLR(SIM_SOPT4_TPM1CLKSEL_MASK)| \ CLR(SIM_SOPT4_TPM0CLKSEL_MASK)|CLR(SIM_SOPT4_TPM2CH0SRC_MASK)| \ SET(SIM_SOPT4_TPM1CH0SRC(0x0)), \ /* SOPT5 */ CLR(SIM_SOPT5_UART2ODE_MASK)|CLR(SIM_SOPT5_LPUART1ODE_MASK)| \ CLR(SIM_SOPT5_LPUART0ODE_MASK)| \ CLR(SIM_SOPT5_LPUART1RXSRC_MASK)|SET(SIM_SOPT5_LPUART1TXSRC(0x00))|\ CLR(SIM_SOPT5_LPUART0RXSRC_MASK)|SET(SIM_SOPT5_LPUART0TXSRC(0x00)),\ /* SOPT7 */ CLR(SIM_SOPT7_ADC0ALTTRGEN_MASK)|CLR(SIM_SOPT7_ADC0PRETRGSEL_MASK)|\ SET(SIM_SOPT7_ADC0TRGSEL(0x00)), \ /* SCGC4 */ SET(SIM_SCGC4_SPI1_MASK)|SET(SIM_SCGC4_SPI0_MASK)| \ SET(SIM_SCGC4_CMP_MASK)|SET(SIM_SCGC4_USBFS_MASK)| \ SET(SIM_SCGC4_UART0_MASK)|SET(SIM_SCGC4_VREF_MASK)| \ SET(SIM_SCGC4_I2C1_MASK)|SET(SIM_SCGC4_I2C0_MASK), \ /* SCGC5 */ SET(SIM_SCGC5_LPUART0_MASK)|SET(SIM_SCGC5_LPUART1_MASK)| \ SET(SIM_SCGC5_PORTE_MASK)|SET(SIM_SCGC5_PORTD_MASK)| \ SET(SIM_SCGC5_PORTC_MASK)|SET(SIM_SCGC5_PORTB_MASK)| \ SET(SIM_SCGC5_PORTA_MASK)|SET(SIM_SCGC5_FLEXIO_MASK)| \ SET(SIM_SCGC5_LPTMR_MASK)|SET(SIM_SCGC5_SLCD_MASK), \ /* SCGC6 */ SET(SIM_SCGC6_DAC0_MASK)| \ SET(SIM_SCGC6_RTC_MASK)|SET(SIM_SCGC6_ADC0_MASK)| \ SET(SIM_SCGC6_TPM2_MASK)|SET(SIM_SCGC6_TPM1_MASK)| \ SET(SIM_SCGC6_TPM0_MASK)|SET(SIM_SCGC6_PIT_MASK)| \ SET(SIM_SCGC6_I2S_MASK)|SET(SIM_SCGC6_DMAMUX_MASK)| \ SET(SIM_SCGC6_FTF_MASK), \ /* SCGC7 */ SET(SIM_SCGC7_DMA_MASK), \ /* CLKDIV1 */ SET(SIM_CLKDIV1_OUTDIV1(DIV_01))|SET(SIM_CLKDIV1_OUTDIV4(DIV_02)),\ }

这个函数主要就是打开各个模块的Clock Gate,对于需要选择不同时钟源的模块,选择其时钟源等,如果一个个看的话,太费劲了。我个人觉得可以不用这个函数,具体使用哪些模块,自己手动配置即可。


(2)MCG_Lite_Init 函数,这个函数很重要,是用来做时钟配置的。这个函数具体实现细节这里不具体说明了,实际使用过程中也不需要修改此函数。

void MCG_LITE_Init (tMCG_LITE mcglite) { // switch to HIRC mode when moving between LIRC2 and LIRC8 modes if (((MCG_S&MCG_S_CLKST(0x1)) && (mcglite.C1&MCG_C1_CLKS(0x1))) && ((MCG_C2&MCG_C2_IRCS_MASK) != (mcglite.C2&MCG_C2_IRCS_MASK))) { MCG_MC |= MCG_MC_HIRCEN_MASK; MCG_C1 = MCG_C1_CLKS(0x0); WAIT_FOR_STATUS_FLAG (MCG_C1, CLKS, CLKST); } // set LIRC first divider MCG_SC = mcglite.SC; // configure external clock if selected (if not let the previous configuration) // or switch between 2MHz and 8Mhz LIRC MCG_C2 = (MCG_C2&0x3C)|mcglite.C2; // if oscilator selected as ext. clock source wait for initialization cycles done if (MCG_C2 & MCG_C2_EREFS0_MASK) { WAIT_FOR_STATUS_FLAG (MCG_C2, EREFS0, OSCINIT0); } // Enable HIRC if required // Note: do not disable HIRC yet if currently in HIRC mode MCG_MC |= (mcglite.MC&MCG_MC_HIRCEN_MASK); // select clock source MCG_C1 = mcglite.C1; // Write all correct required data MCG_C2 = mcglite.C2; // wait for proper clock source selection (valid for all modes EXTCLK, HIRC, LIRC) WAIT_FOR_STATUS_FLAG (MCG_C1, CLKS, CLKST); // disable HIRC if required and select second LIRC divider MCG_MC = mcglite.MC; }

重点看一下其参数:

typedef struct { uint8 C1, C2, SC, MC; } tMCG_LITE;

这个结构体里包含MCG_C1,MCG_C2,MCG_SC,MCG_MC 这四个寄存器,它们用来配置不同的时钟模式。Kinetis KL43的时钟框图如下,我在图中标注了对应位置涉及到的寄存器,看程序时可以对照这个图去理解。

Untitled.png

使用内部48M的配置如下:

#define MCG_LITE_HIRC_48MHZ \ (tMCG_LITE){ \ /* C1 */ SET(MCG_C1_CLKS(0x00))|CLR(MCG_C1_IRCLKEN_MASK)| \ CLR(MCG_C1_IREFSTEN_MASK), \ /* C2 */ SET(MCG_C2_RANGE0(0x00))|CLR(MCG_C2_HGO0_MASK)|CLR(MCG_C2_EREFS0_MASK)|\ CLR(MCG_C2_IRCS_MASK), \ /* SC */ SET(MCG_SC_FCRDIV(0x00)), \ /* MC */ SET(MCG_MC_HIRCEN_MASK)|SET(MCG_MC_LIRC_DIV2(0x00)), \ }

在mcg_lite.h头文件中还有很多其他时钟模式的配置。


(3),(4),(5)这三条语句是用来做GPIO配置的,它的作用是把PTD5 管脚配置为输出功能,输出高电平。

Kinetis 配置GPIO,实际上涉及到到两个模块,一个是PORT,一个是GPIO。

(3)是PORT初始化,它用来配置Pin 的复用功能,中断功能,上下拉功能等,它其实调用的PORTD_Init函数:

#define PORT_Init(port,cfg,mask,ip,callback) port##_Init(cfg,mask,ip,callback)
void PORTD_Init (tPORT port, uint32 pin_mask, uint8 ip, PORT_CALLBACK pCallback) { register uint16 i; if (pCallback != NULL) { pCallbackPORTD = pCallback; NVIC_SetIsr(INT_PORTCD,ip);} for (i=0; i<32; i++) if(pin_mask & (uint32)(1l << i)) { PORTD_BASE_PTR->PCR[i] = port.PCR; } PORTD_ISFR &= ~pin_mask; }

PORT_Init(port,cfg,mask,ip,callback)

port:取值为PORTA,PORTB,PORTC,PORTD或者PORTE

cfg:是一个结构体,它表示的是PORTx_PCR寄存器

typedef struct { uint32 PCR; } tPORT;

本程序中的实参是:

/**************************************************************************/ /*! * @brief Configures pin to Alternative 1 (GPIO) mode with high drive strength. *****************************************************************************/ #define PORT_MODULE_ALT1_MODE /* ALT1 push-pull mode high strength */ \ (tPORT){ \ /* PCR */ CLR(PORT_PCR_ISF_MASK)|SET(PORT_PCR_IRQC(0))| \ SET(PORT_PCR_MUX(1))|SET(PORT_PCR_DSE_MASK)| \ CLR(PORT_PCR_PFE_MASK)|CLR(PORT_PCR_SRE_MASK)| \ CLR(PORT_PCR_PE_MASK)|CLR(PORT_PCR_PS_MASK), \ }

mask:表示具体哪一个引脚,可选值如下

#define PIN_0 (uint32)(1 << 0) #define PIN_1 (uint32)(1 << 1) #define PIN_2 (uint32)(1 << 2) #define PIN_3 (uint32)(1 << 3) #define PIN_4 (uint32)(1 << 4) #define PIN_5 (uint32)(1 << 5) #define PIN_6 (uint32)(1 << 6) #define PIN_7 (uint32)(1 << 7) #define PIN_8 (uint32)(1 << 8) #define PIN_9 (uint32)(1 << 9) #define PIN_10 (uint32)(1 << 10) #define PIN_11 (uint32)(1 << 11) #define PIN_12 (uint32)(1 << 12) #define PIN_13 (uint32)(1 << 13) #define PIN_14 (uint32)(1 << 14) #define PIN_15 (uint32)(1 << 15) #define PIN_16 (uint32)(1 << 16) #define PIN_17 (uint32)(1 << 17) #define PIN_18 (uint32)(1 << 18) #define PIN_19 (uint32)(1 << 19) #define PIN_20 (uint32)(1 << 20) #define PIN_21 (uint32)(1 << 21) #define PIN_22 (uint32)(1 << 22) #define PIN_23 (uint32)(1 << 23) #define PIN_24 (uint32)(1 << 24) #define PIN_25 (uint32)(1 << 25) #define PIN_26 (uint32)(1 << 26) #define PIN_27 (uint32)(1 << 27) #define PIN_28 (uint32)(1 << 28) #define PIN_29 (uint32)(1 << 29) #define PIN_30 (uint32)(1 << 30) #define PIN_31 (uint32)(1 << 31)

ip:中断优先级

callback:中断回调函数

(4)是GPIO初始化

/***************************************************************************//*! * @brief GPIO initialization function. * @param gpio - GPIOA|GPIOB|GPIOC|GPIOD|GPIOE * @param cfg - GPIO configuration structure passed by value: * GPIO_PIN_OUTPUT, * GPIO_PIN_INPUT. * @param mask - PIN_0..PIN_32 * @return none * @note Implemented as function call. ******************************************************************************/ #define GPIO_Init(gpio,cfg,mask) gpio##_Init(cfg,mask)

它用来初始化GPIO是输入还是输出功能。

(5)用来设置输出高电平

/***************************************************************************//*! * @brief Macro set port pins. * @param port - GPIOA|GPIOB|GPIOC|GPIOD|GPIOE * @param mask - PIN_0..PIN_32 * @note Implemented as inlined macro. ******************************************************************************/ #define GPIO_Set(port,mask) port##_PSOR=(mask); /* set bits on GPIO port */

还有两个函数用来设置输出低电平和翻转GPIO

/***************************************************************************//*! * @brief Macro clear port pins. * @param port - GPIOA|GPIOB|GPIOC|GPIOD|GPIOE * @param mask - PIN_0..PIN_32 * @note Implemented as inlined macro. ******************************************************************************/ #define GPIO_Clr(port,mask) port##_PCOR=(mask); /* clear bits on GPIO port */

/***************************************************************************//*!

* @brief Macro toglle port pins.

* @param port - GPIOA|GPIOB|GPIOC|GPIOD|GPIOE

* @param mask - PIN_0..PIN_32

* @note Implemented as inlined macro.

******************************************************************************/

#define GPIO_Tgl(port,mask) port##_PTOR=(mask); /* toggle bits on GPIO port */


(6),(7),(8)的功能和上述(3)(4)(5)一样。

(9)是PIT定时器初始化

/***************************************************************************//*! * @brief PIT initialization function. * @details This function initializes selected timer channel of the * Periodic Interrupt Timer (PIT) block. * @param module PIT0 * @param ch CH0,CH1 * @param cfg Use one of following configuration structures * @ref pit_config * @param value @ref uint32 load register value e * @param ip Interrupt priority 0..3 * @param callback Pointer to @ref PIT_CALLBACK function * @note Implemented as function call. ******************************************************************************/ #define PIT_Init(module,ch,cfg,value,ip,callback) module##_##ch##_Init(cfg,value,ip,callback) /*! @} End of pit_macro */

第三个参数cfg,类型为:

typedef struct { uint32 TCTRL; } tPIT_CH;

它用来描述PIT_TCTRL结构体,它用来设置是否需要使能中断,使能与否timer 等。

PIT.jpg

这里传入的参数是:

/**************************************************************************/ /*! * @brief Timer channel enabled and timer runs after initialization. * Interrupt enabled on the peripheral level. *****************************************************************************/ #define PIT_CH_TIMER_EN_CONFIG \ (tPIT_CH){ \ /* TCTRL */ CLR(PIT_TCTRL_CHN_MASK)|SET(PIT_TCTRL_TIE_MASK)| \ SET(PIT_TCTRL_TEN_MASK), \ } 它其实调用的是以下函数:
void PIT0_CH0_Init (tPIT_CH ch, uint32 value, uint8 ip, PIT_CALLBACK pCallback) { if (pCallback != NULL) { pCallbackPIT = pCallback; NVIC_SetIsr(INT_PIT,ip); } else { pCallbackPIT = NULL; NVIC_ClrIsr(INT_PIT); } PIT_LDVAL0 = value; PIT_TFLG0 = PIT_TFLG_TIF_MASK; /* clear interrupt flag */ PIT_TCTRL0 = ch.TCTRL; PIT_MCR &= ~0x0002; /* clear MDIS bit,Module enbale */ }

PIT 的频率为:bus clock/value

这里为24000000/3000000=8 Hz, 周期为125ms


(10) 是打开全局中断,与它对应的关闭全局中断的函数是__disable_irq。

关于中断问题,多说几句:

本代码中,在上述PIT0_CH0_Init 函数中调用了NVIC_SetIsr(src,ipr) 函数,它实现的功能是使能外设中断并设置它的优先级,其具体实现如下:

/***************************************************************************//*! * @brief Macro enables interrupt request and sets its priority. * @details This macro enables interrupt request and sets its priority. * @param src Select interrupt request: * INT_DMA0,INT_DMA1,INT_DMA2,INT_DMA3,INT_MCM,INT_FTFA,INT_LVD_LVW, * INT_LLW,INT_I2C0,INT_I2C1,INT_SPI0,INT_SPI1,INT_UART0, * INT_UART1,INT_UART2,INT_ADC0,INT_CMP0,INT_TPM0,INT_TPM1, * INT_TPM2,INT_RTC,INT_RTC_Seconds,INT_PIT,INT_Reserved39, * INT_USB0,INT_DAC0,INT_TSI0,INT_MCG,INT_LPTimer,INT_Reserved45, * INT_PORTA,INT_PORTD * @param ipr Interrupt priority 0..3; lower means higher priority * @note Implemented as inlined macro. ******************************************************************************/ #define NVIC_SetIsr(src,ipr){ \ NVIC_ICPR_REG(NVIC_BASE_PTR) |= \ (uint32)(1 << IRQ(src)); \ NVIC_ISER_REG(NVIC_BASE_PTR) |= \ (uint32)(1 << IRQ(src)); \ NVIC_IP_REG(NVIC_BASE_PTR,IPR_INDEX(src)) |= \ (uint32)((uint32)(ipr)<
       

关于此函数的第一个参数有两点需要注意:

1) 它只能使能外设中断并设置优先级,它无法配置ARM Core中断优先级,

2) 直接使用Vector 号就可以,不需要再减16了,因为#define IRQ(x) ((x)-16) 做了这个工作。

/** Interrupt Number Definitions */ typedef enum { INT_Initial_Stack_Pointer = 0, /**< Initial stack pointer */ INT_Initial_Program_Counter = 1, /**< Initial program counter */ INT_NMI = 2, /**< Non-maskable interrupt */ INT_Hard_Fault = 3, /**< Hard fault exception */ INT_Reserved4 = 4, /**< Reserved interrupt 4 */ INT_Reserved5 = 5, /**< Reserved interrupt 5 */ INT_Reserved6 = 6, /**< Reserved interrupt 6 */ INT_Reserved7 = 7, /**< Reserved interrupt 7 */ INT_Reserved8 = 8, /**< Reserved interrupt 8 */ INT_Reserved9 = 9, /**< Reserved interrupt 9 */ INT_Reserved10 = 10, /**< Reserved interrupt 10 */ INT_SVCall = 11, /**< A supervisor call exception */ INT_Reserved12 = 12, /**< Reserved interrupt 12 */ INT_Reserved13 = 13, /**< Reserved interrupt 13 */ INT_PendableSrvReq = 14, /**< PendSV exception - request for system level service */ INT_SysTick = 15, /**< SysTick interrupt */ INT_DMA0 = 16, /**< DMA channel 0 transfer complete/error interrupt */ INT_DMA1 = 17, /**< DMA channel 1 transfer complete/error interrupt */ INT_DMA2 = 18, /**< DMA channel 2 transfer complete/error interrupt */ INT_DMA3 = 19, /**< DMA channel 3 transfer complete/error interrupt */ INT_Reserved20 = 20, /**< Reserved interrupt 20 */ INT_FTFA = 21, /**< FTFA command complete/read collision interrupt */ INT_LVD_LVW = 22, /**< Low Voltage Detect, Low Voltage Warning */ INT_LLW = 23, /**< Low Leakage Wakeup */ INT_I2C0 = 24, /**< I2C0 interrupt */ INT_I2C1 = 25, /**< I2C0 interrupt 25 */ INT_SPI0 = 26, /**< SPI0 interrupt */ INT_SPI1 = 27, /**< SPI1 interrupt */ INT_UART0 = 28, /**< UART0 status/error interrupt */ INT_UART1 = 29, /**< UART1 status/error interrupt */ INT_UART2 = 30, /**< UART2 status/error interrupt */ INT_ADC0 = 31, /**< ADC0 interrupt */ INT_CMP0 = 32, /**< CMP0 interrupt */ INT_TPM0 = 33, /**< TPM0 fault, overflow and channels interrupt */ INT_TPM1 = 34, /**< TPM1 fault, overflow and channels interrupt */ INT_TPM2 = 35, /**< TPM2 fault, overflow and channels interrupt */ INT_RTC = 36, /**< RTC interrupt */ INT_RTC_Seconds = 37, /**< RTC seconds interrupt */ INT_PIT = 38, /**< PIT timer interrupt */ INT_I2S = 39, /**< I2S transmit interrupt */ INT_USB0 = 40, /**< USB0 interrupt */ INT_DAC0 = 41, /**< DAC0 interrupt */ INT_TSI0 = 42, /**< TSI0 interrupt */ INT_MCG = 43, /**< MCG interrupt */ INT_LPTimer = 44, /**< LPTimer interrupt */ INT_LCD = 45, /**< Segment LCD Interrupt */ INT_PORTA = 46, /**< Port A interrupt */ INT_PORTD = 47 /**< Port D interrupt */ } IRQInterruptIndex;

事实上更常使用的是CMSIS 标准函数:

__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)

__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)

__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

/** \brief Enable External Interrupt The function enables a device-specific interrupt in the NVIC interrupt controller. \param [in] IRQn External interrupt number. Value cannot be negative. */ __STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) { NVIC->ISER[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); }
/** \brief Disable External Interrupt The function disables a device-specific interrupt in the NVIC interrupt controller. \param [in] IRQn External interrupt number. Value cannot be negative. */ __STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) { NVIC->ICER[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); }
/** \brief Set Interrupt Priority The function sets the priority of an interrupt. \note The priority cannot be set for every core interrupt. \param [in] IRQn Interrupt number. \param [in] priority Priority to set. */ __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) { if(IRQn < 0) { SCB->SHP[_SHP_IDX(IRQn)] = (SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) | (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); } else { NVIC->IP[_IP_IDX(IRQn)] = (NVIC->IP[_IP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) | (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); } }


注意调用这些函数时:参数是VECTOR号减去16,比如NVIC_EnableIRQ(INT_PIT-16)

另外说明两点:

1)NVIC_EnableIRQ 的参数一定不能是负数

2)NVIC_SetPriority 函数接可以设置外设优先级,也可以设置内核中断优先级,但有三个内核中断优先级是固定不可以更改的:

复位(-3),NMI(-2),Hard Fault(-1)。也就是说除了这三个内核中断不可以更改优先级,其他所有中断的优先级都是可以动态设置的。

222221.jpg

KL43芯片复位时:我们可以看到内核中断都是默认使能的,外设中断都没有使能。另外除了三个固定的中断优先级(这里没有列出来ID为1的复位中断),其他中断优先级都是默认的0,总共有4种优先级,分别为0,1,2,3 数字越小,表示优先级越高。

NVIC.jpg


下面再分析下PIT中断来了,在程序中是如何处理的:

PIT的中断入口函数是:

/****************************************************************************** * interrupt functions definitions * ******************************************************************************/ void pit_isr (void) { /* CH0 interrupt */ if (PIT_TFLG0 == PIT_TFLG_TIF_MASK) { PIT_TFLG0 = PIT_TFLG_TIF_MASK; /* clear interrupt flag */ if (pCallbackPIT != (PIT_CALLBACK)NULL) pCallbackPIT (PIT0CH0_CALLBACK); } /* CH1 interrupt */ if (PIT_TFLG1 == PIT_TFLG_TIF_MASK) { PIT_TFLG1 = PIT_TFLG_TIF_MASK; /* clear interrupt flag */ if (pCallbackPIT != (PIT_CALLBACK)NULL) pCallbackPIT (PIT0CH1_CALLBACK); } }

在appconfig.h中

extern void pit_isr(void); ///< pit isr prototype (defined in pit.c) #undef VECTOR_038 #define VECTOR_038 pit_isr ///< pit_isr interrupt vector re-definition

PIT Channel0 和Channel1 共用一个中断函数。中断处理很简单,清标志位和调用回调函数。

在应用程序中只要实现回调函数即可。






Baidu
map