From bf3391656f97311bd869d1de9cd2a7cd651a35e0 Mon Sep 17 00:00:00 2001 From: Adrian Soundy Date: Thu, 28 Nov 2019 10:43:51 +1300 Subject: [PATCH] Nxp Gpio update to use a standard interface and enable interrupts (#1511) * Nxp Gpio update to use a standard interface and enable interrupts * Missing calls to Gpio initialize/uninitialize * Configure and enable interrupt for pins * make IRQHandlers have "C" linkage NOTE: GPIO2_Compined_16_31_IRQHandler is used to SDCard detect - needs refactor * InterruptFlags are for whole 32-bit port, we need only 16 bits * Fix bug in getting pin * Fix usage of xTimerChangePeriodFromISR function * It's not allowed to create timers in ISR -> move to EnableInputPin * Second EnableInput function call caused a change of the pin configuration to the wrong one (in the method SetDriveMode) - fixed --- targets/FreeRTOS/NXP/include/TargetPAL.h | 10 + .../nanoCLR/Windows.Devices.Gpio/cpu_gpio.cpp | 570 +++++++++++++++++- ...ve_Windows_Devices_Gpio_GpioController.cpp | 4 +- ...io_native_Windows_Devices_Gpio_GpioPin.cpp | 413 ++++++------- .../win_dev_gpio_native_target.h | 6 + targets/FreeRTOS/NXP/nanoCLR/targetHAL.cpp | 4 + 6 files changed, 772 insertions(+), 235 deletions(-) create mode 100644 targets/FreeRTOS/NXP/include/TargetPAL.h diff --git a/targets/FreeRTOS/NXP/include/TargetPAL.h b/targets/FreeRTOS/NXP/include/TargetPAL.h new file mode 100644 index 0000000000..477e3254e8 --- /dev/null +++ b/targets/FreeRTOS/NXP/include/TargetPAL.h @@ -0,0 +1,10 @@ +// +// Copyright (c) 2017 The nanoFramework project contributors +// See LICENSE file in the project root for full license information. +// + +#ifndef _TARGETPAL_H_ +#define _TARGETPAL_H_ + + +#endif // _TARGETPAL_H_ diff --git a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/cpu_gpio.cpp b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/cpu_gpio.cpp index 7efc7b7111..d719761258 100644 --- a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/cpu_gpio.cpp +++ b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/cpu_gpio.cpp @@ -5,6 +5,572 @@ // // i.MX RT1060 EVK board -// User Button (GPIO5-00) -// Ardunio interface (GPIO1) +// User Button (GPIO5-00) - Pin number 128 +// Ardunio interface (GPIO1) - Pin number 0 -> 31 + +#include "win_dev_gpio_native_target.h" +#include "nf_rt_events_native.h" + +#include "timers.h" + +#define GPIO_MAX_PINS 160 // 5 Ports * 32 bits ? +#define GPIO_BITS_PORT 16 // 16 bits per gpio port +#define TOTAL_GPIO_PORTS ((GPIO_MAX_PINS + (GPIO_BITS_PORT - 1)) / GPIO_BITS_PORT) + +// Structure to hold information about input pin +struct gpio_input_state +{ + GPIO_PIN pinNumber; // Pin number + TimerHandle_t debounceTimer; // debounce timer for this Pin + GPIO_INTERRUPT_SERVICE_ROUTINE isrPtr; // Ptr to user ISR or null + uint32_t debounceMs; // debounce Millsecs, no debonce=0 + uint8_t mode; // Interrupt mode + void * param; // Param to user isr call + bool expected; // Expected state for debounce handler + bool waitingDebounce; // True if waiting for debounce timer to complete +}; + +// Array of gpio_input_state ptrs for each gpio port +// each port (GPIO1 to GPIO5) has a low & high 16 bit port. +typedef gpio_input_state * statePortArray[GPIO_BITS_PORT]; + +// For each 16 bit gpio port we have a ptr to an array ptrs to gpio_input_state +// These are initialised only when an input gpio bit is enabled keeping memory use to the minimum +static statePortArray * port_array[TOTAL_GPIO_PORTS]; + +// Array of bits for saving reserved state +static uint16_t pinReserved[TOTAL_GPIO_PORTS]; + + +// this is an utility define to get a port number from our "encoded" pin number +// pin 0 -> (GPIO_MAX_PINS - 1) +// i.e Port 0 = Pins 0 to 15, port 1 = pins 16 to 31 etc +#define GetIoPort(pinNumber) (pinNumber/GPIO_BITS_PORT) +#define GetIoBit(pinNumber) (pinNumber%GPIO_BITS_PORT) +#define IsValidGpioPin(pinNumber) (pinNumber < GPIO_MAX_PINS) + + +void Gpio_DebounceHandler(TimerHandle_t xTimer) +{ + gpio_input_state* pGpio = (gpio_input_state*)pvTimerGetTimerID(xTimer); + if (pGpio->isrPtr) + { + bool actual =(GpioPinValue)GPIO_PinRead(GPIO_BASE(pGpio->pinNumber), GPIO_PIN(pGpio->pinNumber)); + if (actual == pGpio->expected) + { + pGpio->isrPtr(pGpio->pinNumber, actual, pGpio->param); + if (pGpio->mode == GPIO_INT_EDGE_BOTH) + { // both edges + pGpio->expected ^= 1; // update expected state + } + } + } + + pGpio->waitingDebounce = false; +} + +void GPIO_Main_IRQHandler( int portIndex, GPIO_Type * portBase ) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + // Get interrupting pins + uint32_t intPins = GPIO_PortGetInterruptFlags(portBase); + + // clear the interrupt status + GPIO_PortClearInterruptFlags(portBase, intPins); + + if (portIndex % 2) + { + // use the upper 16 bits for odd ports + intPins >>= 16; + } + else + { + // use the lower 16 bits for even ports + intPins &= 0xFFFF; + } + + // This port been initialised ? + statePortArray * inputStates = port_array[portIndex]; + if ( inputStates ) + { + uint32_t bitNumber = 0; + + // Handle all pins with pending interrupt + while(intPins) + { + if ( intPins & 0x01 ) + { + // Interupt on pin ? + gpio_input_state * pGpio = (*inputStates)[bitNumber]; + // Do we have gpio_input_state setup for this pin ? + if (pGpio) + { + // Ignore any pin changes during debounce + if (!pGpio->waitingDebounce) + { + // If user ISR available then call it + if (pGpio->isrPtr) + { + // If debounce timer defined then first wait for it to expire + if (pGpio->debounceMs > 0) + { + pGpio->waitingDebounce = true; + + // Start Debounce timer + xTimerChangePeriodFromISR(pGpio->debounceTimer, pdMS_TO_TICKS(pGpio->debounceMs), &xHigherPriorityTaskWoken); + } + else + { + GpioPinValue PinState = (GpioPinValue)GPIO_PinRead(GPIO_BASE(pGpio->pinNumber), GPIO_PIN(pGpio->pinNumber)); + pGpio->isrPtr(pGpio->pinNumber, PinState, pGpio->param); + } + } + } + } // if pin setup in nanoFramework + } // if interrupt + + intPins>>=1; + bitNumber++; + } // while + } + + portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + + // Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping + // exception return operation might vector to incorrect interrupt +#if defined __CORTEX_M && (__CORTEX_M == 4U) + __DSB(); +#endif +} + +extern "C" +{ +// Gpio ISR handler for GPIO port 1 bits 0-15 +void GPIO1_Combined_0_15_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 0, GPIO1 ); +} +// Gpio ISR handler for GPIO port 1 bits 16-31 +void GPIO1_Combined_16_31_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 1, GPIO1 ); +} +// Gpio ISR handler for GPIO port 2 bits 0-15 +void GPIO2_Combined_0_15_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 2, GPIO2 ); +} +// Gpio ISR handler for GPIO port 2 bits 16-31 + +// TODO: this handler is used to sdcard detect +// void GPIO2_Combined_16_31_IRQHandler(void) +// { +// GPIO_Main_IRQHandler( 3, GPIO2 ); +// } +// Gpio ISR handler for GPIO port 3 bits 0-15 +void GPIO3_Combined_0_15_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 4, GPIO3 ); +} +// Gpio ISR handler for GPIO port 3 bits 16-31 +void GPIO3_Combined_16_31_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 5, GPIO3 ); +} +// Gpio ISR handler for GPIO port 4 bits 0-15 +void GPIO4_Combined_0_15_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 6, GPIO4 ); +} +// Gpio ISR handler for GPIO port 4 bits 16-31 +void GPIO4_Combined_16_31_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 7, GPIO4 ); +} +// Gpio ISR handler for GPIO port 5 bits 0-15 +void GPIO5_Combined_0_15_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 8, GPIO5 ); +} +// Gpio ISR handler for GPIO port 5 bits 16-31 +void GPIO5_Combined_16_31_IRQHandler(void) +{ + GPIO_Main_IRQHandler( 9, GPIO5 ); +} +} + +// Get pointer to gpio_input_state for Gpio pin +// return NULL if not found +gpio_input_state * GetInputState(GPIO_PIN pinNumber ) +{ + int port = GetIoPort(pinNumber); + int bit = GetIoBit(pinNumber); + + if ( port_array[port] == NULL) return NULL; + + statePortArray * inputStates = port_array[port]; + return *inputStates[bit]; +} + +// Allocate a new gpio_input_state and add to end of list +// if already exist then just return current ptr +gpio_input_state * AllocateGpioInputState(GPIO_PIN pinNumber) +{ + int port = GetIoPort(pinNumber); + int bit = GetIoBit(pinNumber); + + if ( port_array[port] == NULL) + { + port_array[port] = (statePortArray*)malloc(sizeof(statePortArray)); + if (port_array[port] == NULL ) return NULL; + + memset(port_array[port], 0, sizeof(statePortArray) ); + } + + statePortArray * inputStates = port_array[port]; + + gpio_input_state * pGpio = (*inputStates)[bit]; + if (pGpio == NULL) + { + pGpio = (gpio_input_state *)malloc(sizeof(gpio_input_state)); + memset(pGpio, 0, sizeof(gpio_input_state)); + pGpio->pinNumber = pinNumber; + (*inputStates)[bit] = pGpio; + } + return pGpio; +} + +// Delete gpio_input_state from List and tidy up ( Timer & ISR handler ) +void DeleteInputState(GPIO_PIN pinNumber) +{ + int port = GetIoPort(pinNumber); + int bit = GetIoBit(pinNumber); + + statePortArray* inputStates = port_array[port]; + if ( inputStates == NULL ) return; + + gpio_input_state * pGpio = (*inputStates)[bit]; + if (pGpio) + { + if (pGpio->debounceTimer != 0) + { + xTimerDelete(pGpio->debounceTimer, 100); + } + + // Remove interrupt associatted with pin + gpio_pin_config_t config = {kGPIO_DigitalInput, 0, kGPIO_NoIntmode }; + GPIO_PinInit(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber), &config); + + free(pGpio); + (*inputStates)[bit] = NULL; + } +} + + +bool CPU_GPIO_Initialize() +{ + // All port ptrs are null + memset(port_array, 0, sizeof(port_array)); + + // Make sure all pins are not reserved + memset(pinReserved, 0, sizeof(pinReserved)); + + return true; +} + +bool CPU_GPIO_Uninitialize() +{ + // First remove any active pin states + for(int pinNumber=0; pinNumber < GPIO_MAX_PINS; pinNumber++) + { + DeleteInputState(pinNumber); + } + + // Remove statePortArray if any + for(int port=0; port < TOTAL_GPIO_PORTS; port++) + { + statePortArray* inputStates = port_array[port]; + if ( inputStates != NULL ) + { + free(port_array[port]); // free up inputStates array + port_array[port] = NULL; + } + } + + return true; +} + +// Set/reset reserved state of pin +bool CPU_GPIO_ReservePin(GPIO_PIN pinNumber, bool fReserve) +{ + // Check if valid pin number + if (!IsValidGpioPin(pinNumber)) return false; + + int port = pinNumber >> 4; + int bit = 1 << (pinNumber & 0x0F); + + GLOBAL_LOCK(); + + if (fReserve) + { + if (pinReserved[port] & bit) + { + GLOBAL_UNLOCK(); + return false; // already reserved + } + + pinReserved[port] |= bit; + } + else + { + pinReserved[port] &= ~bit; + } + + GLOBAL_UNLOCK(); + return true; +} + +// Return if Pin is reserved +bool CPU_GPIO_PinIsBusy(GPIO_PIN pinNumber) +{ + // Check if valid pin number + if (!IsValidGpioPin(pinNumber)) return false; + + int port = pinNumber >> 4, sh = pinNumber & 0x0F; + return (pinReserved[port] >> sh) & 1; +} + +// Return maximum number of pins +int32_t CPU_GPIO_GetPinCount() +{ + return GPIO_MAX_PINS; +} + +// Get current state of pin +GpioPinValue CPU_GPIO_GetPinState(GPIO_PIN pinNumber) +{ + return (GpioPinValue)GPIO_PinRead(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber)); +} + +// Set Pin state +void CPU_GPIO_SetPinState(GPIO_PIN pinNumber, GpioPinValue PinState) +{ + GPIO_PinWrite(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber), PinState); +} + +// Toggle pin state +void CPU_GPIO_TogglePinState(GPIO_PIN pinNumber) +{ + GPIO_PortToggle(GPIO_BASE(pinNumber), 0x1u << GPIO_PIN(pinNumber)); +} + +// +// CPU_GPIO_EnableInputPin +// Enable input pin +// +bool CPU_GPIO_EnableInputPin(GPIO_PIN pinNumber, int64_t debounceTimeMilliseconds, GPIO_INTERRUPT_SERVICE_ROUTINE Pin_ISR, void* ISR_Param, GPIO_INT_EDGE IntEdge, GpioPinDriveMode driveMode) +{ + gpio_input_state * pGpio; + + // Check if valid pin number + if (!IsValidGpioPin(pinNumber)) return false; + + // Check Input drive mode + if (driveMode >= (int)GpioPinDriveMode_Output) + return false; + + if (!CPU_GPIO_SetDriveMode(pinNumber, driveMode)) + return false; + + pGpio = AllocateGpioInputState(pinNumber); + + // Map nanoFRamework Interrupt edge to NXP edge + // NONE=0, EDGE_LOW=1, EDGE_HIGH=2, EDGE_BOTH=3, LEVEL_HIGH=4, LEVEL_LOW + const gpio_interrupt_mode_t mapint[6] = { kGPIO_NoIntmode, kGPIO_IntFallingEdge, kGPIO_IntRisingEdge, kGPIO_IntRisingOrFallingEdge, kGPIO_IntHighLevel, kGPIO_IntLowLevel }; + + // enable interupt mode with correct edge + gpio_pin_config_t config = {kGPIO_DigitalInput, 0, mapint[IntEdge] }; + GPIO_PinInit(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber), &config); + + // Enable GPIO pin interrupt + IRQn_Type isrNo = (IRQn_Type)(GPIO1_Combined_0_15_IRQn + GetIoPort(pinNumber)); + NVIC_SetPriority(isrNo, 8U); + EnableIRQ(isrNo); + GPIO_PortEnableInterrupts(GPIO_BASE(pinNumber), 1U << GetIoBit(pinNumber)); + GPIO_PortClearInterruptFlags(GPIO_BASE(pinNumber), 1U << GetIoBit(pinNumber)); + + // Initialise Gpio state structure + pGpio->isrPtr = Pin_ISR; + pGpio->mode = IntEdge; + pGpio->param = (void *)ISR_Param; + pGpio->debounceMs = (uint32_t)(debounceTimeMilliseconds); + + // Set up expected new value for debounce + if ( pGpio->debounceMs > 0) + { + if (pGpio->debounceTimer == 0) + { + // Create timer if it doesn't already exist for this pin + pGpio->debounceTimer = xTimerCreate("debounce", 100, pdFALSE, (void*)pGpio, Gpio_DebounceHandler); + } + switch (IntEdge) + { + case GPIO_INT_NONE: + case GPIO_INT_EDGE_LOW: + case GPIO_INT_LEVEL_LOW: + pGpio->expected = false; + break; + + case GPIO_INT_EDGE_HIGH: + case GPIO_INT_LEVEL_HIGH: + pGpio->expected = true; + break; + + case GPIO_INT_EDGE_BOTH: + pGpio->expected = !CPU_GPIO_GetPinState(pinNumber); // Use NOT current state + break; + } + } + + return true; +} + +// Enable an output pin +// +// pinNumber - Gpio pin number +// InitialState - Initial state of pin +// driveMode - Drive mode and resistors +// return - True if succesful, false invalid pin, pin not putput, invalid drive mode for ouptput // +bool CPU_GPIO_EnableOutputPin(GPIO_PIN pinNumber, GpioPinValue InitialState, GpioPinDriveMode driveMode) +{ + // Check if valid pin number + if (!IsValidGpioPin(pinNumber)) return false; + + // check is output drive mode + if (driveMode < (int)GpioPinDriveMode_Output) return false; + + if (CPU_GPIO_SetDriveMode(pinNumber, driveMode) == false) return false; + + CPU_GPIO_SetPinState(pinNumber, InitialState); + + return true; +} + +void CPU_GPIO_DisablePin(GPIO_PIN pinNumber, GpioPinDriveMode driveMode, uint32_t alternateFunction) +{ + GLOBAL_LOCK(); + + CPU_GPIO_SetDriveMode(pinNumber, driveMode); + + DeleteInputState(pinNumber); + + if (alternateFunction) + { + GPIO_PinMux(GPIO_PORT(pinNumber), GPIO_PIN(pinNumber), alternateFunction); + } + + GLOBAL_UNLOCK(); + + CPU_GPIO_ReservePin(pinNumber, false); +} + + +// Validate pin and set drive mode +// return true if ok +bool CPU_GPIO_SetDriveMode(GPIO_PIN pinNumber, GpioPinDriveMode driveMode) +{ + // Check if valid pin number + if (!IsValidGpioPin(pinNumber)) return false; + + gpio_pin_direction_t direction; + uint32_t pinConfig; + + switch (driveMode) + { + case GpioPinDriveMode_Input: + direction = kGPIO_DigitalInput; + pinConfig = GPIO_IO; + break; + + case GpioPinDriveMode_InputPullDown: + direction = kGPIO_DigitalInput; + pinConfig = GPIO_IN_PULLDOWN; + break; + + case GpioPinDriveMode_InputPullUp: + direction = kGPIO_DigitalInput; + pinConfig = GPIO_IN_PULLUP; + break; + + case GpioPinDriveMode_Output: + direction = kGPIO_DigitalOutput; + pinConfig = GPIO_IO; + break; + + case GpioPinDriveMode_OutputOpenDrain: + direction = kGPIO_DigitalOutput; + pinConfig = GPIO_OUT_OPENDRAIN; + break; + + default: + // all other modes are NOT supported + return false; + } + + gpio_pin_config_t config = {direction, 0, kGPIO_NoIntmode}; + + GPIO_PinMux(GPIO_PORT(pinNumber), GPIO_PIN(pinNumber), 0x5u); + GPIO_PinConfig(GPIO_PORT(pinNumber), GPIO_PIN(pinNumber), pinConfig); + GPIO_PinInit(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber), &config); + + return true; +} + +bool CPU_GPIO_DriveModeSupported(GPIO_PIN pinNumber, GpioPinDriveMode driveMode) +{ + // Check if valid pin number + if (!IsValidGpioPin(pinNumber)) return false; + + bool driveModeSupported = false; + + // check if the requested drive mode is supported + if ((driveMode == GpioPinDriveMode_Input) || + (driveMode == GpioPinDriveMode_InputPullDown) || + (driveMode == GpioPinDriveMode_InputPullUp) || + (driveMode == GpioPinDriveMode_Output) || + (driveMode == GpioPinDriveMode_OutputOpenDrain)) + { + driveModeSupported = true; + } + + return driveModeSupported; +} + +uint32_t CPU_GPIO_GetPinDebounce(GPIO_PIN pinNumber) +{ + // Check if valid pin number + if (IsValidGpioPin(pinNumber)) + { + gpio_input_state * ptr = GetInputState(pinNumber); + if (ptr) + return ptr->debounceMs; + } + + return 0; +} + +bool CPU_GPIO_SetPinDebounce(GPIO_PIN pinNumber, int64_t debounceTimeMilliseconds) +{ + // Check if valid pin number + if (IsValidGpioPin(pinNumber)) + { + gpio_input_state * ptr = GetInputState(pinNumber); + if (ptr) + { + ptr->debounceMs = (uint32_t)(debounceTimeMilliseconds); + return true; + } + } + return false; +} diff --git a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioController.cpp b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioController.cpp index 42519d1765..c8d1340ec8 100644 --- a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioController.cpp +++ b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioController.cpp @@ -12,8 +12,8 @@ HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioController::get_Pin { NANOCLR_HEADER(); { - // return count ot available GPIO pins - stack.SetResult_I4( 130 ); // Fixme: temporary arbitrary value + // Return value to the managed application + stack.SetResult_I4(CPU_GPIO_GetPinCount()); } NANOCLR_NOCLEANUP_NOLABEL(); } diff --git a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioPin.cpp b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioPin.cpp index d5b6ac60ba..4d62915cfb 100644 --- a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioPin.cpp +++ b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_Windows_Devices_Gpio_GpioPin.cpp @@ -4,285 +4,236 @@ // See LICENSE file in the project root for full license information. // - -#include "MIMXRT1062.h" -#include "fsl_gpio.h" -#include "fsl_gpio_ext.h" -#undef MIN -#undef MAX - +#include #include "win_dev_gpio_native_target.h" #include "nf_rt_events_native.h" - /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// -HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::Read___WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack ) +void Gpio_Interupt_ISR(GPIO_PIN pinNumber, bool pinState, void* param ) { - NANOCLR_HEADER(); - { - CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + NATIVE_INTERRUPT_START + + CLR_RT_HeapBlock *pThis = (CLR_RT_HeapBlock *)param; + if (pThis != NULL) + { + // check if object has been disposed + if (pThis[Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue].NumericByRef().u1 == 0) + { + // flag to determine if there are any callbacks registered in managed code + bool callbacksRegistered = (pThis[Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___callbacks].Dereference() != NULL); + if (callbacksRegistered) + { + // if handle registed then post a managed event with the current pin reading + PostManagedEvent(EVENT_GPIO, 0, (uint16_t)pinNumber, (uint32_t)pinState); + } + } + } + + NATIVE_INTERRUPT_END +} - // check if object has been disposed - if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); - } +HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::Read___WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack ) +{ + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - int32_t pinNumber = pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); + } - uint32_t val = GPIO_PinRead(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber)); + GPIO_PIN pinNumber = (GPIO_PIN)pThis[ FIELD___pinNumber ].NumericByRefConst().s4; - stack.SetResult_I4(val); - } - NANOCLR_NOCLEANUP(); + stack.SetResult_I4( CPU_GPIO_GetPinState(pinNumber) ); + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::Toggle___VOID( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - - // check if object has been disposed - if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); - } - - int32_t pinNumber = pThis[ FIELD___pinNumber ].NumericByRefConst().s4; - - GpioPinDriveMode driveMode = (GpioPinDriveMode)pThis[ FIELD___driveMode ].NumericByRefConst().s4; - - // sanity check for drive mode set to output so we don't mess up writing to an input pin - if ((driveMode == GpioPinDriveMode_Output) || - (driveMode == GpioPinDriveMode_OutputOpenDrain) || - (driveMode == GpioPinDriveMode_OutputOpenDrainPullUp) || - (driveMode == GpioPinDriveMode_OutputOpenSource) || - (driveMode == GpioPinDriveMode_OutputOpenSourcePullDown)) - { - GPIO_PortToggle(GPIO_BASE(pinNumber), 0x1u << GPIO_PIN(pinNumber)); - - // // store new state - pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = (GpioPinValue)(GpioPinValue_High ^ (GpioPinValue)pThis[ FIELD___lastOutputValue ].NumericByRef().s4); - } - - } - NANOCLR_NOCLEANUP(); + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + + // check if object has been disposed + if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); + } + + GPIO_PIN pinNumber = (GPIO_PIN)pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + GpioPinDriveMode driveMode = (GpioPinDriveMode)pThis[ FIELD___driveMode ].NumericByRefConst().s4; + + // sanity check for drive mode set to output so we don't mess up writing to an input pin + if (driveMode >= GpioPinDriveMode_Output) + { + // Not all lower level API offer a 'toggle', so need to rely on the last output value field and toggle that one + GpioPinValue newState = (GpioPinValue)(GpioPinValue_High ^ (GpioPinValue)pThis[ FIELD___lastOutputValue ].NumericByRef().s4); + + // ...write back to the GPIO... + CPU_GPIO_SetPinState(pinNumber, newState ); + + // ... and finally store it + pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = newState; + } + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::DisposeNative___VOID( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - } - NANOCLR_NOCLEANUP(); + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + + // set pin to input to save power + // clear interrupts + // releases the pin + GPIO_PIN pinNumber = (GPIO_PIN)pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + + CPU_GPIO_DisablePin(pinNumber, GpioPinDriveMode_Input, 0) ; + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeIsDriveModeSupported___BOOLEAN__WindowsDevicesGpioGpioPinDriveMode( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4; - - bool driveModeSupported = false; - - // check if the requested drive mode is supported - if ((driveMode == GpioPinDriveMode_Input) || - (driveMode == GpioPinDriveMode_InputPullDown) || - (driveMode == GpioPinDriveMode_InputPullUp) || - (driveMode == GpioPinDriveMode_Output) || - (driveMode == GpioPinDriveMode_OutputOpenDrain)) - { - driveModeSupported = true; - } - - // Return value to the managed application - stack.SetResult_Boolean( driveModeSupported ) ; - } - NANOCLR_NOCLEANUP_NOLABEL(); + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + + GPIO_PIN pinNumber = (GPIO_PIN)pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + + GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4; + + // Return value to the managed application + stack.SetResult_Boolean(CPU_GPIO_DriveModeSupported(pinNumber, driveMode)) ; + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetDriveMode___VOID__WindowsDevicesGpioGpioPinDriveMode( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - - if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); - } - - int32_t pinNumber = pThis[ FIELD___pinNumber ].NumericByRefConst().s4; - - // it's better cast this this to the appropriate enum - GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4; - - // check if drive mode is input - bool driveModeIsInput = false; - - if( driveMode == GpioPinDriveMode_Input || - driveMode == GpioPinDriveMode_InputPullDown || - driveMode == GpioPinDriveMode_InputPullUp) - { - driveModeIsInput = true; - } - - // flag to signal that interrupts need to be setup - bool setupInterrupt = false; - - // flag to determine if there are any callbacks registered in managed code - bool callbacksRegistered = (pThis[ FIELD___callbacks ].Dereference() != NULL); - - gpio_pin_direction_t direction; - uint32_t pinConfig; - - switch (driveMode) - { - case GpioPinDriveMode_Input: - direction = kGPIO_DigitalInput; - pinConfig = GPIO_IO; - setupInterrupt = true; - break; - - case GpioPinDriveMode_InputPullDown: - direction = kGPIO_DigitalInput; - pinConfig = GPIO_IN_PULLDOWN; - setupInterrupt = true; - break; - - case GpioPinDriveMode_InputPullUp: - direction = kGPIO_DigitalInput; - pinConfig = GPIO_IN_PULLUP; - setupInterrupt = true; - break; - - case GpioPinDriveMode_Output: - direction = kGPIO_DigitalOutput; - pinConfig = GPIO_IO; - break; - - case GpioPinDriveMode_OutputOpenDrain: - direction = kGPIO_DigitalOutput; - pinConfig = GPIO_OUT_OPENDRAIN; - break; - - default: - // all other modes are NOT supported - NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); - break; - } - - gpio_pin_config_t config = {direction, 0, kGPIO_NoIntmode}; - - GPIO_PinMux(GPIO_PORT(pinNumber), GPIO_PIN(pinNumber), 0x5u); - GPIO_PinConfig(GPIO_PORT(pinNumber), GPIO_PIN(pinNumber), pinConfig); - GPIO_PinInit(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber), &config); - - // if drive mode is output, read the pad to update the managed field _lastOutputValue - if(!driveModeIsInput) - { - uint32_t val = GPIO_PinRead(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber)); - pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = val; - } - - if(callbacksRegistered && setupInterrupt) - { - // there are callbacks registered and... - // the drive mode is input so need to setup the interrupt - - // protect this from GC so that the callback is where it's supposed to - //CLR_RT_ProtectFromGC gc( *pThis ); - - // read pad and store current value to check on debounce callback - } - } - NANOCLR_NOCLEANUP(); + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + bool validPin; + + if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); + } + + GPIO_PIN pinNumber = (GPIO_PIN)pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + GpioPinDriveMode driveMode = (GpioPinDriveMode)stack.Arg1().NumericByRef().s4; + + if (driveMode >= (int)GpioPinDriveMode_Output) + { + validPin = CPU_GPIO_EnableOutputPin(pinNumber, GpioPinValue_Low, driveMode); + } + else + { + int64_t debounceTimeoutMilsec = (CLR_INT64_TEMP_CAST) pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___debounceTimeout ].NumericByRefConst().s8 / TIME_CONVERSION__TO_MILLISECONDS; + + validPin = CPU_GPIO_EnableInputPin(pinNumber, debounceTimeoutMilsec, Gpio_Interupt_ISR, (void*)pThis, GPIO_INT_EDGE_BOTH, driveMode); + } + + if (!validPin) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + // protect this from GC so that the callback is where it's supposed to + CLR_RT_ProtectFromGC gc( *pThis ); + + + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeInit___BOOLEAN__I4( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - // TODO is probably a good idea keep track of the used pins, so we can check that here - // TODO is probably a good idea to check if this pin exists - - // Return value to the managed application - stack.SetResult_Boolean(true ); - } - NANOCLR_NOCLEANUP_NOLABEL(); + NANOCLR_HEADER(); + { + GPIO_PIN pinNumber = (GPIO_PIN)stack.Arg1().NumericByRef().s4; + + // Return value to the managed application + stack.SetResult_Boolean(CPU_GPIO_ReservePin(pinNumber, true)); + } + NANOCLR_NOCLEANUP_NOLABEL(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetDebounceTimeout___VOID( CLR_RT_StackFrame& stack ) { - (void)stack; + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + + GPIO_PIN pinNumber = (GPIO_PIN)stack.Arg1().NumericByRef().s4; - NANOCLR_HEADER(); + int64_t debounceTimeoutMilsec = (CLR_INT64_TEMP_CAST) pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___debounceTimeout ].NumericByRefConst().s8 / TIME_CONVERSION__TO_MILLISECONDS; - // nothing to do here as the debounce timeout is grabbed from the managed object when required + CPU_GPIO_SetPinDebounce( pinNumber, debounceTimeoutMilsec ); - NANOCLR_NOCLEANUP_NOLABEL(); + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::WriteNative___VOID__WindowsDevicesGpioGpioPinValue( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - - // check if object has been disposed - if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); - } - - int32_t pinNumber = pThis[ FIELD___pinNumber ].NumericByRefConst().s4; - - GpioPinDriveMode driveMode = (GpioPinDriveMode)pThis[ FIELD___driveMode ].NumericByRefConst().s4; - - GpioPinValue state = (GpioPinValue)stack.Arg1().NumericByRef().s4; - - // sanity check for drive mode set to output so we don't mess up writing to an input pin - if ((driveMode == GpioPinDriveMode_Output) || - (driveMode == GpioPinDriveMode_OutputOpenDrain) || - (driveMode == GpioPinDriveMode_OutputOpenDrainPullUp) || - (driveMode == GpioPinDriveMode_OutputOpenSource) || - (driveMode == GpioPinDriveMode_OutputOpenSourcePullDown)) - { - GPIO_PinWrite(GPIO_BASE(pinNumber), GPIO_PIN(pinNumber), state); - - // update the managed field _lastOutputValue - pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = state; - } - else - { - NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); - } - } - NANOCLR_NOCLEANUP(); + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); + + // check if object has been disposed + if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); + } + + GPIO_PIN pinNumber = (GPIO_PIN)pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + GpioPinDriveMode driveMode = (GpioPinDriveMode)pThis[ FIELD___driveMode ].NumericByRefConst().s4; + + GpioPinValue state = (GpioPinValue)stack.Arg1().NumericByRef().s4; + + // sanity check for drive mode set to output so we don't mess up writing to an input pin + if ((driveMode >= GpioPinDriveMode_Output) ) + { + CPU_GPIO_SetPinState(pinNumber, state ); + + // store the output value in the field + pThis[ FIELD___lastOutputValue ].NumericByRef().s4 = state; + } + else + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + } + NANOCLR_NOCLEANUP(); } HRESULT Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::NativeSetAlternateFunction___VOID__I4( CLR_RT_StackFrame& stack ) { - NANOCLR_HEADER(); - { - CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - - // check if object has been disposed - if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) - { - NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); - } + NANOCLR_HEADER(); + { + CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); - int32_t pinNumber = pThis[ FIELD___pinNumber ].NumericByRefConst().s4; + // check if object has been disposed + if(pThis[ Library_win_dev_gpio_native_Windows_Devices_Gpio_GpioPin::FIELD___disposedValue ].NumericByRef().u1 != 0) + { + NANOCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); + } - int32_t alternateFunction = stack.Arg1().NumericByRef().s4; + // get pin number and take the port and pad references from that one + int16_t pinNumber = pThis[ FIELD___pinNumber ].NumericByRefConst().s4; - GPIO_PinMux(GPIO_PORT(pinNumber), GPIO_PIN(pinNumber), alternateFunction); + // get alternate function argument + int32_t alternateFunction = stack.Arg1().NumericByRef().s4; - } - NANOCLR_NOCLEANUP(); + CPU_GPIO_DisablePin( pinNumber, GpioPinDriveMode_Input, alternateFunction); + } + NANOCLR_NOCLEANUP(); } diff --git a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_target.h b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_target.h index 991e00d6ac..3f6a77b94e 100644 --- a/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_target.h +++ b/targets/FreeRTOS/NXP/nanoCLR/Windows.Devices.Gpio/win_dev_gpio_native_target.h @@ -10,4 +10,10 @@ #include +#include "MIMXRT1062.h" +#include "fsl_gpio.h" +#include "fsl_gpio_ext.h" +#undef MIN +#undef MAX + #endif //_WIN_DEV_GPIO_NATIVE_TARGET_H_ diff --git a/targets/FreeRTOS/NXP/nanoCLR/targetHAL.cpp b/targets/FreeRTOS/NXP/nanoCLR/targetHAL.cpp index 01c8d6763c..8b6d34d94c 100644 --- a/targets/FreeRTOS/NXP/nanoCLR/targetHAL.cpp +++ b/targets/FreeRTOS/NXP/nanoCLR/targetHAL.cpp @@ -67,6 +67,8 @@ void nanoHAL_Initialize() Events_Initialize(); + CPU_GPIO_Initialize(); + // no PAL events required until now //PalEvent_Initialize(); @@ -101,6 +103,8 @@ void nanoHAL_Uninitialize() BlockStorageList_UnInitializeDevices(); + CPU_GPIO_Uninitialize(); + //PalEvent_Uninitialize(); Events_Uninitialize();