wuyage

Kinetis KL43 BareMetal SC 代码学习之一

2
阅读(2477) 评论(1)

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)<<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)。也就是说除了这三个内核中断不可以更改优先级,其他所有中断的优先级都是可以动态设置的。

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 共用一个中断函数。中断处理很简单,清标志位和调用回调函数。

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






  1. 好文章,顶起