Kinetis KL43 BareMetal SC 代码学习之一
2赞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 代码,
代码包见附件:
该代码库里面有IAR和Keil的工程(没有KDS的工程)
FRDM_KL43 里的例程较少,TWR_KL43 里则有很多例程。不过两者的底层驱动文件都是一样的。


下面让我们先看个简单的GPIO例子吧,这里FRDM_KL43Z为例,开发环境Keil MDK。
打开 \frdm-kl43z48m-sc-baremetal\build\keil\frdm_projects\frdm_led_test 中的工程,先编译一下,咦,怎么一上来编译就报错了。

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

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

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

然后再重新编译,就不会报错了。
此工程实现的功能,两个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中的一些关键的寄存器。

程序中的实参是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的时钟框图如下,我在图中标注了对应位置涉及到的寄存器,看程序时可以对照这个图去理解。

使用内部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 等。

这里传入的参数是:
/**************************************************************************/ /*!
* @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)<<IPR_SHIFT(src)); \
}关于此函数的第一个参数有两点需要注意:
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)。也就是说除了这三个内核中断不可以更改优先级,其他所有中断的优先级都是可以动态设置的。

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

下面再分析下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 共用一个中断函数。中断处理很简单,清标志位和调用回调函数。
在应用程序中只要实现回调函数即可。
