Hello!
In this time, we will look how the i.MX93 GPIOs IRQs works, also I will focus on Cortex M33 side with SDK 2_16_0 but also tested on 2_15.
We can see in this other post, how the i.MX8M family works, but for i.MX93 this is a little different because there are a Secure/Non-Secure options and Privilege/Non-Privilege.
According to reference Manual and SDK LED example, we must to set the PCNS and ICNS registers to 0x00 to set in Secure access.
Materials Used:
i.MX93EVK
Jumper cable to connect GPIO2_IO02 with GPIO2_IO03
SDK 2_16_0 from MCUXpresso SDK Builder
Source power for i.MX93EVK
USB C Cable for serial debug
USB C Cable to transfer .bin ro EVK
The Cortex-M33 processor supports Secure and Non-secure security states, Thread and Handler operating modes, and can run in either Thumb or Debug operating states. In addition, the processor can limit or exclude access to some resources by executing code in privileged or unprivileged mode.
Code can execute as privileged or unprivileged. Unprivileged execution limits or excludes access to some resources appropriate to the current security state. Privileged execution has access to all resources available to the security state. Handler mode is always privileged. Thread mode can be privileged or unprivileged.
You can find this information in the ARM documentation.
To resume this post, we will focus just in the necessary registers to configure properly a GPIO as IRQ input.
On this example, we will take the i.MX93EVK board. The GPIO2_IO02 will be configured as an output and the GPIO2_IO03 will be configured as an input with Rising edge IRQ.
On each GPIO2_IO02 Rising edge, the software will detect an IRQ.
At first, we need to configure our IOMUX:
void BOARD_InitPins(void) {
IOMUXC_SetPinMux(IOMUXC_PAD_GPIO_IO02__GPIO2_IO02, 0U);
IOMUXC_SetPinMux(IOMUXC_PAD_GPIO_IO03__GPIO2_IO03, 0U);
IOMUXC_SetPinMux(IOMUXC_PAD_UART2_RXD__LPUART2_RX, 0U);
IOMUXC_SetPinMux(IOMUXC_PAD_UART2_TXD__LPUART2_TX, 0U);
IOMUXC_SetPinConfig(IOMUXC_PAD_GPIO_IO02__GPIO2_IO02,
IOMUXC_PAD_DSE(15U) |
IOMUXC_PAD_FSEL1(2U) |
IOMUXC_PAD_PD_MASK);
IOMUXC_SetPinConfig(IOMUXC_PAD_GPIO_IO03__GPIO2_IO03,
IOMUXC_PAD_PD_MASK);
IOMUXC_SetPinConfig(IOMUXC_PAD_UART2_RXD__LPUART2_RX,
IOMUXC_PAD_PD_MASK);
IOMUXC_SetPinConfig(IOMUXC_PAD_UART2_TXD__LPUART2_TX,
IOMUXC_PAD_DSE(15U));
}
Then, we can start to code. Using as an starting point we can use the SDK/boards/mcimx93evk/driver_examples/rgpio/led_output example.
Our definitions (PIN_OUT_RGPIO and PIN_IN_RGPIO are the same GPIO2 but it is just for good practice):😞
/*******************************************************************************
* Definitions
******************************************************************************/
#define PIN_OUT_RGPIO GPIO2
#define PIN_IN_RGPIO GPIO2
#define PIN_OUT_RGPIO_PIN 2U
#define PIN_IN_RGPIO_PIN 3U
Then, our IRQ handler:
void Reserved73_IRQHandler(void)
{
RGPIO_ClearPinsInterruptFlags(PIN_IN_RGPIO, kRGPIO_InterruptOutput0, 1U << PIN_IN_RGPIO_PIN);
PRINTF("\r\n IRQ.........\r\n");
SDK_ISR_EXIT_BARRIER;
}
Why Reserved73_IRQHandler?
That is the correspondent for GPIO2, you can look this on SDK/devices/MIMX9352/gcc in the file called startup_MIMX9352_cm33.S:
Basically, the interruption will clear the IRQ flag and print a little message.
Now, here we have the complete main function, we will break down the most important points.
int main(void)
{
/* Define the init structure for the output pin*/
rgpio_pin_config_t pin_out_config = {
kRGPIO_DigitalOutput,
0,
};
rgpio_pin_config_t pin_in_config = {
kRGPIO_DigitalInput,
0,
};
/* Board pin, clock, debug console init */
/* clang-format off */
const clock_root_config_t rgpioClkCfg = {
.clockOff = false,
.mux = 0, // 24Mhz Mcore root buswake clock
.div = 1
};
/* clang-format on */
BOARD_InitBootPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
CLOCK_SetRootClock(EXAMPLE_RGPIO_CLOCK_ROOT, &rgpioClkCfg);
CLOCK_EnableClock(EXAMPLE_RGPIO_CLOCK_GATE);
CLOCK_EnableClock(kCLOCK_Gpio2);
/* Set PCNS register value to 0x0 to prepare the RGPIO initialization */
PIN_OUT_RGPIO->PCNS = 0x0;
PIN_IN_RGPIO->ICNS = 0x0;
/* Print a note to terminal. */
PRINTF("\r\n RGPIO Driver example\r\n");
PRINTF("\r\n An IRQ will happen each GPIO2_IO02 Rising edge\r\n");
/* Init output PIN GPIO. */
RGPIO_PinInit(PIN_OUT_RGPIO, PIN_OUT_RGPIO_PIN, &pin_out_config);
/* Init Input with IRQ Pin GPIO*/
RGPIO_SetPinInterruptConfig(PIN_IN_RGPIO, PIN_IN_RGPIO_PIN, kRGPIO_InterruptOutput0, kRGPIO_InterruptRisingEdge);
EnableIRQ(GPIO2_0_IRQn);
RGPIO_PinInit(PIN_IN_RGPIO, PIN_IN_RGPIO_PIN, &pin_in_config);
while (1)
{
SDK_DelayAtLeastUs(1000000U, SystemCoreClock);
RGPIO_PortToggle(PIN_OUT_RGPIO, 1u << PIN_OUT_RGPIO_PIN);
}
}
As we can see, we need set the GPIO2 PCNS register to 0x00:
Pin Control Nonsecure (PCNS)
Configures secure or nonsecure access protection for each pin. You can write to this register only in the Secure-Privilege state if it is not locked (LOCK[PCNS] = 0).
Also the ICNS register to 0x00.
Interrupt Control Nonsecure (ICNS)
Configures secure and nonsecure access protection for each interrupt, or DMA request. You can update this register only in the Secure-Privilege state if it is not locked (LOCK[ICNS] = 0).
Now, we can compile and run the example. On each GPIO2_IO02 Rising edge, the CM33 will detect an IRQ in GPIO2_IO03 (short those pads as showed in the image at first of the post).
I will attach the full .c file.
I hope this information can helps to everyone.
Best regards,
--... ...--
Salas.
記事全体を表示