Developing code for MCXN family from Scratch
The MCX family is a newly released MCU family, of course, the SDK package includes almost all the examples of the MCXN modules based on the SDK driver, for example led_blinky which shows the method of toggling GPIO, CTimer interrupt, etc. While the program in this article doesn’t use SDK driver. It focuses on understanding the related module from register level which is friendly to beginners to understand MCX working mechanism.
The doc introduces how to toggle a GPIO, how to have CTimer generate interrupt, and in the CTimer ISR, toggle a LED, while it also describes how to initialize the NVIC so that CTimer can generate ISR by writing the module registers without calling the SDK driver.
Environment:
FRDM-MCXN947 board
MCUXPresso IDE v11.9.0 IDE on Win10 OS
1. Toggle a LED
On the FRDM-MCXN947 board, the PIO0_10(P0_10) of MCXN947 is connected to a LED. With MCUXPresso IDE with v11.9.0, you can add the code to test the LED toggling and CTimer interrupt function.
1.1 Configure the PIO0_10 pin as GPIO0_10 pin
SYSCON->AHBCLKCTRLSET[0]=1<<13;
PORT0->PCR[10]=0x1000;
Before you initialize the PORT0 register to assign the PIO0_10 function, you have to enable the PORT0 gated clock in the SYSCON->AHBCLKCTRL0 reg.
Write the PORT0->PCR[10]=0x1000; to configure the PIO0_10 as GPIO0_10 function.
1.2 Configure the GPIO0_10 pin as GPIO output mode and toggle the GPIO pin.
SYSCON->AHBCLKCTRLSET[0]=1<<19;
GPIO0->PDDR|=1<<10;
GPIO0->PTOR=1<<10;
Before you initialize the GPIO0 registers, you have to enable the GPIO0 gated clock in the SYSCON->AHBCLKCTRL0 reg.
Set the bit 10 of GPIO0->PDDR so that the GPIO0_10 is in GPIO output mode.
Toggle GPIO0_10 pin by writing the bit 10 in GPIO0->PTOR reg
Set a break point in the debugger on the GPIO0->PTOR=1<<10; line,and run step by step, you can see that led toggles.
2. Initialize CTimer
2.1 enable Ctimer gated clock and clock source and divider.
Before you initialize the CTimer register, you have to enable the CTimer gated clock. For example, enable CTimer4 gated clock with the line:
SYSCON->AHBCLKCTRLSET[2]=1<<22;
You have to also select the CTimer clock source and Ctimer clock divider. For example, set the CTimer4 clock source and divider.
SYSCON->CTIMERCLKSEL[4]=0x04;
SYSCON->CTIMERCLKDIV[4]=0x02;
//P0_10 is LED red
void GPIOInit(void)
{
SYSCON->AHBCLKCTRLSET[0]=1<<13;
PORT0->PCR[10]=0x1000;
//enable gated clock of GPIO
SYSCON->AHBCLKCTRLSET[0]=1<<19;
GPIO0->PDDR|=1<<10;
GPIO0->PTOR=1<<10; //set break point here
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
}
2.2 Initialize the CTimer register.
CTIMER4->CTCR=0x00;
CTIMER4->PR=0x00;
//set the CTimet4 mode and enable interrupt for match0
CTIMER4->MCR=0x03;
CTIMER4->PWMC=0x00;
//set the CTimer4 cycle time
CTIMER4->MR[0]=2000000;
CTIMER4->MSR[0]=2000000;
//Start up CTimer4
CTIMER4->TCR=0x01;
2.3 Initialize the NVIC
The IRQ number of CTimer4 is 56, so you have to set the bit 24 of both
NVIC->ISER[1]|=1<<24;
NVIC->ICPR[1]|=1<<24;
and set the priority of
NVIC->IPR[56] with 0x00;
NVIC->IPR[56]=0x00;
//CTimer4 IRQ 56, 56-32=24
void cTimer0Init(void)
{
//CLOCK_SetClkDiv(kCLOCK_DivCtimer4Clk, 1u);
//CLOCK_AttachClk(kFRO_HF_to_CTIMER4);
//enable Ctimer4 gated clock
SYSCON->AHBCLKCTRLSET[2]=1<<22;
SYSCON->PRESETCTRLSET[2]=1<<22;
SYSCON->PRESETCTRLCLR[2]=1<<22;
SYSCON->CTIMERGLOBALSTARTEN|=1<<4;
//select CTimer4 clock source
SYSCON->CTIMERCLKSEL[4]=0x04;
SYSCON->CTIMERCLKDIV[4]=0x02;
//init the CTimer0
CTIMER4->CTCR=0x00;
CTIMER4->PR=0x00;
CTIMER4->MCR=0x03;
CTIMER4->PWMC=0x00;
CTIMER4->MR[0]=2000000;
CTIMER4->MSR[0]=2000000;
//CTIMER0->IR=0x01;
CTIMER4->TCR=0x01;
NVIC->ISER[1]|=1<<24;
NVIC->ICPR[1]|=1<<24;
NVIC->IPR[56]=0x00;
}
2.3 Each interrupt source has unique interrupt service routine in the file startup_mcxn947_cm33_core0.c
For the CTimer4 ISR, it is called:
CTIMER4_IRQHandler
So it is okay to fill the function:
void CTIMER4_IRQHandler(void)
{
//clear flag
CTIMER4->IR|=0x01;
//toggle LED
GPIO0->PTOR=1<<10;
}
In conclusion, the doc gives a snippet which can toggle a LED by writing the register directly. It also give the code to configure CTimer4 so that it can generate interrupt, in the ISR, toggle a LED.
Appendix
This is the entire source code:
/*
* Copyright 2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pin_mux.h"
#include "peripherals.h"
#include "board.h"
/*******************************************************************************
* Definitions
******************************************************************************/
void clockOUTInit(void);
void GPIOInit(void);
void cTimer0Init(void);
void MRTInit(void);
/*******************************************************************************
* Prototypes
******************************************************************************/
/*******************************************************************************
* Variables
******************************************************************************/
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Main function
*/
int main(void)
{
/* Board pin init */
CLOCK_EnableClock(kCLOCK_Gpio0);
BOARD_InitPins();
BOARD_BootClockFRO12M();
//FlexPWMPinInit();
GPIOInit();
cTimer0Init();
//MRTInit();
__asm("nop");
while (1)
{
}
}
//P0_10 is LED red
void GPIOInit(void)
{
SYSCON->AHBCLKCTRLSET[0]=1<<13;
PORT0->PCR[10]=0x1000;
//enable gated clock of GPIO
SYSCON->AHBCLKCTRLSET[0]=1<<19;
GPIO0->PDDR|=1<<10; //set a break point here and run step by step
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
GPIO0->PTOR=1<<10;
}
//CTimer4 IRQ 56, 56-32=24
void cTimer0Init(void)
{
// CLOCK_SetClkDiv(kCLOCK_DivCtimer4Clk, 1u);
// CLOCK_AttachClk(kFRO_HF_to_CTIMER4);
//enable Ctimer4 gated clock
SYSCON->AHBCLKCTRLSET[2]=1<<22;
SYSCON->PRESETCTRLSET[2]=1<<22;
SYSCON->PRESETCTRLCLR[2]=1<<22;
SYSCON->CTIMERGLOBALSTARTEN|=1<<4;
//select CTimer4 clock source
SYSCON->CTIMERCLKSEL[4]=0x04;
SYSCON->CTIMERCLKDIV[4]=0x02;
//init the CTimer0
CTIMER4->CTCR=0x00;
CTIMER4->PR=0x00;
CTIMER4->MCR=0x03;
CTIMER4->PWMC=0x00;
CTIMER4->MR[0]=2000000;
CTIMER4->MSR[0]=2000000;
//CTIMER0->IR=0x01;
CTIMER4->TCR=0x01;
NVIC->ISER[1]|=1<<24;
NVIC->ICPR[1]|=1<<24;
NVIC->IPR[56]=0x00;
}
void CTIMER4_IRQHandler(void)
{
//clear flag
CTIMER4->IR|=0x01;
//toggle LED
GPIO0->PTOR=1<<10;
}
//MRT0 interrupt IRQ is 30
void MRTInit(void)
{
SYSCON->AHBCLKCTRLSET[1]=1<<0;
SYSCON->PRESETCTRLSET[1]=1<<0;
SYSCON->PRESETCTRLCLR[1]=1<<0;
MRT0->CHANNEL[0].INTVAL=6000000;
MRT0->CHANNEL[0].INTVAL|=1<<31;
MRT0->CHANNEL[0].CTRL=0x01;
NVIC->ISER[0]|=1<<30;
NVIC->ICPR[0]|=1<<30;
NVIC->IPR[30]=0x00;
}
void MRT0_IRQHandler(void)
{
if(MRT0->CHANNEL[0].STAT&0x01)
{
MRT0->CHANNEL[0].STAT|=0x01;
}
//toggle LED
GPIO0->PTOR=1<<10;
}