--- bsps/arm/stm32f4/adc/adc.c | 655 ++++++++++++++++++ bsps/arm/stm32f4/gpio/gpio.c | 542 +++++++++++++++ bsps/arm/stm32f4/include/bsp.h | 4 - bsps/arm/stm32f4/include/bsp/stm32f4_adc.h | 209 ++++++ bsps/arm/stm32f4/include/bsp/stm32f4_gpio.h | 273 ++++++++ bsps/arm/stm32f4/include/bsp/stm32f4_hal.h | 17 + bsps/arm/stm32f4/include/bsp/stm32f4_periph.h | 50 ++ bsps/arm/stm32f4/start/bspstart.c | 7 +- bsps/arm/stm32f4/start/bspstarthook.c | 8 + bsps/arm/stm32f4/start/periph.c | 58 ++ spec/build/bsps/arm/stm32f4/grp.yml | 6 +- spec/build/bsps/arm/stm32f4/obj.yml | 5 + spec/build/bsps/arm/stm32f4/optengpio.yml | 16 + .../build/bsps/arm/stm32f4/optnumgpioctrl.yml | 16 + 14 files changed, 1856 insertions(+), 10 deletions(-) create mode 100644 bsps/arm/stm32f4/adc/adc.c create mode 100644 bsps/arm/stm32f4/gpio/gpio.c create mode 100644 bsps/arm/stm32f4/include/bsp/stm32f4_adc.h create mode 100644 bsps/arm/stm32f4/include/bsp/stm32f4_gpio.h create mode 100644 bsps/arm/stm32f4/include/bsp/stm32f4_hal.h create mode 100644 bsps/arm/stm32f4/include/bsp/stm32f4_periph.h create mode 100644 bsps/arm/stm32f4/start/periph.c create mode 100644 spec/build/bsps/arm/stm32f4/optengpio.yml create mode 100644 spec/build/bsps/arm/stm32f4/optnumgpioctrl.yml
diff --git a/bsps/arm/stm32f4/adc/adc.c b/bsps/arm/stm32f4/adc/adc.c new file mode 100644 index 0000000000..21baa9ee2c --- /dev/null +++ b/bsps/arm/stm32f4/adc/adc.c @@ -0,0 +1,655 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <bsp/stm32f4_adc.h> +#include <bsp/stm32f4_gpio.h> +#include <stdlib.h> + +#if defined(ADC3) +#define NUM_ADC 3 +#elif defined(ADC2) +#define NUM_ADC 2 +#else +#define NUM_ADC 1 +#endif + +/************** Interrupt manager *****************/ +typedef struct { + void *arg; + stm32f4_gpio *gpio; +} stm32f4_interrupt_arg; + +typedef struct { + stm32f4_interrupt_arg arg; + rtems_gpio_isr isr; +} stm32f4_interrupt; + +void adc_irq_handler(void *arg); + +static stm32f4_interrupt isr_table[NUM_ADC]; + +/** + * This tells if there is already an ISR registered at each + * table index (element set to true). + */ +static bool isr_registered[NUM_ADC] = {0}; + +typedef struct { + uint32_t adc_value; + rtems_adc_status status; +} stm32f4_adc_data; +static stm32f4_adc_data adc_data[NUM_ADC] = {0}; + +static rtems_status_code stm32f4_adc_select_channel( + rtems_gpio *base +); + +/***************/ +#ifdef ADC1 +static ADC_TypeDef_Protected _ADC1_protected = { ADC1, false }; +ADC_TypeDef_Protected *const ADC1_protected = &_ADC1_protected; +#endif +#ifdef ADC2 +static ADC_TypeDef_Protected _ADC2_protected = { ADC2, false }; +ADC_TypeDef_Protected *const ADC2_protected = &_ADC2_protected; +#endif +#ifdef ADC3 +static ADC_TypeDef_Protected _ADC3_protected = { ADC3, false }; +ADC_TypeDef_Protected *const ADC3_protected = &_ADC3_protected; +#endif + +/* Helpers */ +#define STM32F4_GET_ADC_NUMBER(ADCx) \ + ( (uintptr_t) ( ADCx ) == (uintptr_t) ADC1 ? 1 : \ + (uintptr_t) ( ADCx ) == (uintptr_t) ADC2 ? 2 : \ + (uintptr_t) ( ADCx ) == (uintptr_t) ADC3 ? 3 : \ + 0 ) +#define STM32F4_GET_ADCx_FROM_NUMBER(num) (\ + ( num ) == 1 ? ADC1 : \ + ( num ) == 2 && NUM_ADC >= 2 ? ADC2 : \ + ( num ) == 3 && NUM_ADC == 3 ? ADC3 : \ + NULL) +#define STM32F4_GET_ADCx_PROTECTED_FROM_NUMBER(num) (\ + ( num ) == 1 ? ADC1_protected : \ + ( num ) == 2 && NUM_ADC >= 2 ? ADC2_protected : \ + ( num ) == 3 && NUM_ADC == 3 ? ADC3_protected : \ + NULL) + +// TODO: other variants +ADC_TypeDef *stm32f4_get_ADCx( + GPIO_TypeDef *gpio +) +{ + switch ((uintptr_t) gpio) { +#if defined(STM32F405xx) || defined(STM32F407xx) + case (uintptr_t) GPIOA: + case (uintptr_t) GPIOB: + case (uintptr_t) GPIOC: + return ADC1; + case (uintptr_t) GPIOF: + return ADC3; +#endif /* defined(STM32F405xx) || defined(STM32F407xx) */ + default: + return NULL; + } +} + +// TODO: other variants +ADC_TypeDef_Protected *stm32f4_get_ADCx_protected( + GPIO_TypeDef *gpio +) +{ + switch ((uintptr_t) gpio) { +#if defined(STM32F405xx) || defined(STM32F407xx) + case (uintptr_t) GPIOA: + case (uintptr_t) GPIOB: + case (uintptr_t) GPIOC: + return ADC1_protected; + case (uintptr_t) GPIOF: + return ADC3_protected; +#endif /* defined(STM32F405xx) || defined(STM32F407xx) */ + default: + return NULL; + } +} + +// TODO: other variants +rtems_status_code stm32f4_get_LL_ADC_CHANNEL( + stm32f4_gpio *gpio, + uint32_t *channel +) +{ + uintptr_t port = (uintptr_t) gpio->port; +#if defined(STM32F405xx) || defined(STM32F407xx) + if (port == (uintptr_t) GPIOA) { + switch (gpio->pin) { + case 0: + *channel = LL_ADC_CHANNEL_0; + break; + case 1: + *channel = LL_ADC_CHANNEL_1; + break; + case 2: + *channel = LL_ADC_CHANNEL_2; + break; + case 3: + *channel = LL_ADC_CHANNEL_3; + break; + case 4: + *channel = LL_ADC_CHANNEL_4; + break; + case 5: + *channel = LL_ADC_CHANNEL_5; + break; + case 6: + *channel = LL_ADC_CHANNEL_6; + break; + case 7: + *channel = LL_ADC_CHANNEL_7; + break; + default: + return RTEMS_UNSATISFIED; + } + } + else if (port == (uintptr_t) GPIOB) { + switch (gpio->pin) { + case 0: + *channel = LL_ADC_CHANNEL_8; + break; + case 1: + *channel = LL_ADC_CHANNEL_9; + break; + default: + return RTEMS_UNSATISFIED; + } + } + else if (port == (uintptr_t) GPIOC) { + switch (gpio->pin) { + case 0: + *channel = LL_ADC_CHANNEL_10; + break; + case 1: + *channel = LL_ADC_CHANNEL_11; + break; + case 2: + *channel = LL_ADC_CHANNEL_12; + break; + case 3: + *channel = LL_ADC_CHANNEL_13; + break; + case 4: + *channel = LL_ADC_CHANNEL_14; + break; + case 5: + *channel = LL_ADC_CHANNEL_15; + break; + default: + return RTEMS_UNSATISFIED; + } + } + else if (port == (uintptr_t) GPIOF) { + switch (gpio->pin) { + case 3: + *channel = LL_ADC_CHANNEL_9; + break; + case 4: + *channel = LL_ADC_CHANNEL_14; + break; + case 5: + *channel = LL_ADC_CHANNEL_15; + break; + case 6: + *channel = LL_ADC_CHANNEL_4; + break; + case 7: + *channel = LL_ADC_CHANNEL_5; + break; + case 8: + *channel = LL_ADC_CHANNEL_6; + break; + case 9: + *channel = LL_ADC_CHANNEL_7; + break; + case 10: + *channel = LL_ADC_CHANNEL_8; + break; + default: + return RTEMS_UNSATISFIED; + } + } + else + return RTEMS_UNSATISFIED; +#endif /* defined(STM32F405xx) || defined(STM32F407xx) */ + return RTEMS_SUCCESSFUL; +} + +bool stm32f4_is_adc_pin( + stm32f4_gpio *gpio +) +{ + uint32_t tmp; + return stm32f4_get_LL_ADC_CHANNEL(gpio, &tmp) == RTEMS_SUCCESSFUL; +} + +static const rtems_adc_api stm32f4_adc_base_api = + RTEMS_ADC_BUILD_API( + stm32f4_adc_init, + stm32f4_adc_read_raw, + stm32f4_adc_start_read_raw_nb, + stm32f4_adc_read_raw_nb, + stm32f4_adc_set_resolution, + stm32f4_adc_set_alignment, + stm32f4_adc_configure_interrupt, + stm32f4_adc_remove_interrupt, + stm32f4_adc_enable_interrupt, + stm32f4_adc_disable_interrupt + ); + +rtems_periph_api *stm32f4_adc_get( + rtems_gpio *pin +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(pin); + uint32_t channel; + // if the pin does not have ADC functionality + if (stm32f4_get_LL_ADC_CHANNEL(gpio, &channel) != RTEMS_SUCCESSFUL) { + return NULL; + } + + // First allocate space for stm32f4_adc object + stm32f4_adc *api = malloc(sizeof(stm32f4_adc)); + if (api == NULL) { + return NULL; + } + api->base_api = stm32f4_adc_base_api; + + // Then allocate space for stm32f4_adc_config object + api->adc_config = malloc(sizeof(stm32f4_adc_config)); + if (api->adc_config == NULL) { + return NULL; + } + api->adc_config->ADCx = stm32f4_get_ADCx_protected(gpio->port); + api->adc_config->channel = channel; + api->adc_config->resolution = STM32F4_ADC_DEFAULT_RESOLUTION; + api->adc_config->alignment = STM32F4_ADC_DEFAULT_ALIGNMENT; + api->adc_config->locked = false; + return (rtems_periph_api *) api; +} + +rtems_status_code stm32f4_adc_destroy( + rtems_gpio *pin +) +{ + if (pin->api != NULL) { + free(((stm32f4_adc *) pin->api)->adc_config); + free(pin->api); + return RTEMS_SUCCESSFUL; + } + return RTEMS_UNSATISFIED; +} + +static rtems_status_code stm32f4_adc_select_channel( + rtems_gpio *base +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + if (!STM32F4_IS_LOCKED(adc_config) && + !STM32F4_IS_LOCKED(adc_config->ADCx)) { + STM32F4_LOCK(adc_config); + STM32F4_LOCK(adc_config->ADCx); + + LL_ADC_Disable(adc_config->ADCx->ADCx); + + LL_ADC_SetResolution(adc_config->ADCx->ADCx, adc_config->resolution); + LL_ADC_SetDataAlignment(adc_config->ADCx->ADCx, adc_config->alignment); + LL_ADC_REG_SetSequencerRanks( + adc_config->ADCx->ADCx, + LL_ADC_REG_RANK_1, + adc_config->channel + ); + LL_ADC_SetChannelSamplingTime( + adc_config->ADCx->ADCx, + adc_config->channel, + STM32F4_ADC_DEFAULT_SAMPLINGTIME + ); + + LL_ADC_Enable(adc_config->ADCx->ADCx); + /* Delay for ADC stabilization time */ + /* Compute number of CPU cycles to wait for */ + volatile uint32_t counter = (ADC_STAB_DELAY_US * (SystemCoreClock / 1000000U)); + while(counter != 0U) + { + counter--; + } + + STM32F4_UNLOCK(adc_config); + STM32F4_UNLOCK(adc_config->ADCx); + return RTEMS_SUCCESSFUL; + } + return RTEMS_UNSATISFIED; +} + +void stm32f4_adc_start( + void +) +{ + // Install ISR for non-blocking read + rtems_status_code sc = rtems_interrupt_handler_install( + ADC_IRQn, + NULL, + RTEMS_INTERRUPT_SHARED, + adc_irq_handler, + NULL + ); + while (sc != RTEMS_SUCCESSFUL); +} +RTEMS_SYSINIT_ITEM( + stm32f4_adc_start, + RTEMS_SYSINIT_DEVICE_DRIVERS, + RTEMS_SYSINIT_ORDER_LAST +); + +void stm32f4_adc_init( + rtems_gpio *base +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + if (!LL_ADC_IsEnabled(adc_config->ADCx->ADCx)) { + // Enable clock + switch ((uintptr_t) adc_config->ADCx->ADCx) { +#ifdef ADC1 + case (uintptr_t) ADC1: + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1); + break; +#endif +#ifdef ADC2 + case (uintptr_t) ADC2: + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC2); + break; +#endif +#ifdef ADC3 + case (uintptr_t) ADC3: + LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC3); + break; +#endif + default: + return; + } + + // ADC common setup + LL_ADC_SetSequencersScanMode(adc_config->ADCx->ADCx, LL_ADC_SEQ_SCAN_DISABLE); + LL_ADC_REG_SetTriggerSource(adc_config->ADCx->ADCx, LL_ADC_REG_TRIG_SOFTWARE); + LL_ADC_REG_SetSequencerLength(adc_config->ADCx->ADCx, LL_ADC_REG_SEQ_SCAN_DISABLE); + LL_ADC_REG_SetSequencerDiscont(adc_config->ADCx->ADCx, LL_ADC_REG_SEQ_DISCONT_DISABLE); + LL_ADC_REG_SetContinuousMode(adc_config->ADCx->ADCx, LL_ADC_REG_CONV_SINGLE); + LL_ADC_REG_SetDMATransfer(adc_config->ADCx->ADCx, LL_ADC_REG_DMA_TRANSFER_NONE); + + LL_ADC_REG_SetFlagEndOfConversion(adc_config->ADCx->ADCx, LL_ADC_REG_FLAG_EOC_UNITARY_CONV); + + LL_ADC_SetCommonClock(__LL_ADC_COMMON_INSTANCE(adc_config->ADCx->ADCx), LL_ADC_CLOCK_SYNC_PCLK_DIV2); + LL_ADC_SetMultimode(__LL_ADC_COMMON_INSTANCE(adc_config->ADCx->ADCx), LL_ADC_MULTI_INDEPENDENT); + + LL_ADC_Enable(adc_config->ADCx->ADCx); + /* Delay for ADC stabilization time */ + /* Compute number of CPU cycles to wait for */ + volatile uint32_t counter = (ADC_STAB_DELAY_US * (SystemCoreClock / 1000000U)); + while(counter != 0U) + { + counter--; + } + } +} + +rtems_status_code stm32f4_adc_read_raw( + rtems_gpio *base, + uint32_t *result, + uint32_t timeout +) +{ + uint32_t tickstart = 0U; + rtems_status_code sc = stm32f4_adc_select_channel(base); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + STM32F4_LOCK(adc_config->ADCx); + STM32F4_LOCK(adc_config); + LL_ADC_REG_StartConversionSWStart(adc_config->ADCx->ADCx); + while (!LL_ADC_IsActiveFlag_EOCS(adc_config->ADCx->ADCx)) { + if (timeout != RTEMS_ADC_NO_TIMEOUT) { + if (timeout == 0U || ((HAL_GetTick() - tickstart) > timeout)) { + if (!LL_ADC_IsActiveFlag_EOCS(adc_config->ADCx->ADCx)) { + return RTEMS_TIMEOUT; + } + } + } + } + *result = LL_ADC_REG_ReadConversionData32(adc_config->ADCx->ADCx); + STM32F4_UNLOCK(adc_config->ADCx); + STM32F4_UNLOCK(adc_config); + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_adc_start_read_raw_nb( + rtems_gpio *base +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + unsigned int adc_idx = STM32F4_GET_ADC_NUMBER(adc_config->ADCx->ADCx) - 1; + if (adc_data[adc_idx].status == RTEMS_ADC_NOT_STARTED) { + // start conversion here + rtems_status_code sc = stm32f4_adc_select_channel(base); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + STM32F4_LOCK(adc_config); + STM32F4_LOCK(adc_config->ADCx); + LL_ADC_EnableIT_EOCS(adc_config->ADCx->ADCx); + LL_ADC_REG_StartConversionSWStart(adc_config->ADCx->ADCx); + adc_data[adc_idx].status = RTEMS_ADC_NOT_READY; + + return RTEMS_SUCCESSFUL; + } + return RTEMS_UNSATISFIED; +} + +rtems_adc_status stm32f4_adc_read_raw_nb( + rtems_gpio *base, + uint32_t *result +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + unsigned int adc_idx = STM32F4_GET_ADC_NUMBER(adc_config->ADCx->ADCx) - 1; + rtems_adc_status ret = adc_data[adc_idx].status; + if (ret == RTEMS_ADC_READY) { + *result = adc_data[adc_idx].adc_value; + adc_data[adc_idx].status = RTEMS_ADC_NOT_STARTED; + + STM32F4_UNLOCK(adc_config->ADCx); + STM32F4_UNLOCK(adc_config); + } + return ret; +} + +rtems_status_code stm32f4_adc_set_resolution( + rtems_gpio *base, + unsigned int bits +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + if (!STM32F4_IS_LOCKED(adc_config)) { + STM32F4_LOCK(adc_config); + switch (bits) { + case 12: + adc_config->resolution = LL_ADC_RESOLUTION_12B; + break; + case 10: + adc_config->resolution = LL_ADC_RESOLUTION_10B; + break; + case 8: + adc_config->resolution = LL_ADC_RESOLUTION_8B; + break; + case 6: + adc_config->resolution = LL_ADC_RESOLUTION_6B; + break; + default: + return RTEMS_UNSATISFIED; + } + STM32F4_UNLOCK(adc_config); + return RTEMS_SUCCESSFUL; + } + return RTEMS_RESOURCE_IN_USE; +} + +rtems_status_code stm32f4_adc_set_alignment( + rtems_gpio *base, + rtems_adc_align align +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + if (!STM32F4_IS_LOCKED(adc_config)) { + STM32F4_LOCK(adc_config); + switch (align) { + case RTEMS_ADC_ALIGN_LEFT: + adc_config->alignment = LL_ADC_DATA_ALIGN_LEFT; + break; + case RTEMS_ADC_ALIGN_RIGHT: + adc_config->alignment = LL_ADC_DATA_ALIGN_RIGHT; + break; + default: + return RTEMS_UNSATISFIED; + } + STM32F4_UNLOCK(adc_config); + return RTEMS_SUCCESSFUL; + } + return RTEMS_RESOURCE_IN_USE; +} + +rtems_status_code stm32f4_adc_configure_interrupt( + rtems_gpio *base, + rtems_adc_isr isr, + void *arg +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + unsigned int adc_idx = STM32F4_GET_ADC_NUMBER(adc_config->ADCx->ADCx) - 1; + if (!isr_registered[adc_idx]) { + isr_table[adc_idx] = (stm32f4_interrupt){ + .arg = { + .arg = arg, + .gpio = gpio + }, + .isr = isr + }; + isr_registered[adc_idx] = true; + return rtems_interrupt_handler_install( + ADC_IRQn, + NULL, + RTEMS_INTERRUPT_SHARED, + adc_irq_handler, + &isr_table[adc_idx].arg + ); + } + return RTEMS_UNSATISFIED; +} + +rtems_status_code stm32f4_adc_remove_interrupt( + rtems_gpio *base +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + unsigned int adc_idx = STM32F4_GET_ADC_NUMBER(adc_config->ADCx->ADCx) - 1; + rtems_status_code sc = rtems_interrupt_handler_remove( + ADC_IRQn, + adc_irq_handler, + &isr_table[adc_idx].arg + ); + if (sc == RTEMS_SUCCESSFUL) { + isr_registered[adc_idx] = false; + } + return sc; +} + +rtems_status_code stm32f4_adc_enable_interrupt( + rtems_gpio *base +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + if (!STM32F4_IS_LOCKED(adc_config->ADCx)) { + if (isr_registered[STM32F4_GET_ADC_NUMBER(adc_config->ADCx->ADCx) - 1]) { + LL_ADC_EnableIT_EOCS(adc_config->ADCx->ADCx); + return RTEMS_SUCCESSFUL; + } + return RTEMS_UNSATISFIED; + } + return RTEMS_RESOURCE_IN_USE; +} + +rtems_status_code stm32f4_adc_disable_interrupt( + rtems_gpio *base +) +{ + stm32f4_adc_config *adc_config = ((stm32f4_adc *) base->api)->adc_config; + if (!STM32F4_IS_LOCKED(adc_config->ADCx)) { + if (isr_registered[STM32F4_GET_ADC_NUMBER(adc_config->ADCx->ADCx) - 1]) { + LL_ADC_DisableIT_EOCS(adc_config->ADCx->ADCx); + return RTEMS_SUCCESSFUL; + } + return RTEMS_UNSATISFIED; + } + return RTEMS_RESOURCE_IN_USE; +} + +void adc_irq_handler(void *arg) { + rtems_interrupt_level level; + rtems_interrupt_disable( level ); + unsigned int i; + for (i = 0; i < NUM_ADC; ++i) { + ADC_TypeDef *adcx = STM32F4_GET_ADCx_FROM_NUMBER(i+1); + if (LL_ADC_IsActiveFlag_EOCS(adcx)) { + // if the current IRQ has no ISR registered, + // it means the IRQ happens from a non-blocking read + if (!isr_registered[i]) { + if (adc_data[i].status == RTEMS_ADC_NOT_READY) { + LL_ADC_DisableIT_EOCS(adcx); + adc_data[i].adc_value = LL_ADC_REG_ReadConversionData32(adcx); + adc_data[i].status = RTEMS_ADC_READY; + } + } + // if there is an ISR registered, call it + else { + LL_ADC_ClearFlag_EOCS(adcx); + stm32f4_interrupt_arg *stm32_arg = (stm32f4_interrupt_arg *) arg; + (*isr_table[i].isr)(stm32_arg->arg); + } + break; + } + } + rtems_interrupt_enable( level ); +} + diff --git a/bsps/arm/stm32f4/gpio/gpio.c b/bsps/arm/stm32f4/gpio/gpio.c new file mode 100644 index 0000000000..0a54862ad2 --- /dev/null +++ b/bsps/arm/stm32f4/gpio/gpio.c @@ -0,0 +1,542 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <bsp.h> +#include <rtems.h> +#include <stdlib.h> +#include <bsp/stm32f4_gpio.h> +#include <bsp/stm32f4_periph.h> + +/*********** GPIO API ***************/ +static rtems_status_code stm32f4_gpio_get( + uint32_t interm_pin, + rtems_gpio **out +); + +static rtems_status_code stm32f4_gpio_destroy( + rtems_gpio *base +); + +/*********************************************************/ + +/** + * @brief STM32F4 GPIO handlers + */ +static const rtems_gpio_handlers stm32f4_gpio_handlers = { + .set_pin_mode = stm32f4_gpio_set_pin_mode, + .set_pull = stm32f4_gpio_set_pull, + .configure_interrupt = stm32f4_gpio_configure_interrupt, + .remove_interrupt = stm32f4_gpio_remove_interrupt, + .enable_interrupt = stm32f4_gpio_enable_interrupt, + .disable_interrupt = stm32f4_gpio_disable_interrupt, + .read = stm32f4_gpio_read, + .write = stm32f4_gpio_write, + .toggle = stm32f4_gpio_toggle +}; + +static GPIO_TypeDef * const GPIOx[] = { +#ifdef GPIOA_BASE + GPIOA +#endif /* GPIOA_BASE */ +#ifdef GPIOB_BASE + , GPIOB +#endif /* GPIOB_BASE */ +#ifdef GPIOC_BASE + , GPIOC +#endif /* GPIOC_BASE */ +#ifdef GPIOD_BASE + , GPIOD +#endif /* GPIOD_BASE */ +#ifdef GPIOE_BASE + , GPIOE +#endif /* GPIOE_BASE */ +#ifdef GPIOF_BASE + , GPIOF +#endif /* GPIOF_BASE */ +#ifdef GPIOG_BASE + , GPIOG +#endif /* GPIOG_BASE */ +#ifdef GPIOH_BASE + , GPIOH +#endif /* GPIOH_BASE */ +#ifdef GPIOI_BASE + , GPIOI +#endif /* GPIOI_BASE */ +#ifdef GPIOJ_BASE + , GPIOJ +#endif /* GPIOJ_BASE */ +#ifdef GPIOK_BASE + , GPIOK +#endif /* GPIOK_BASE */ +}; + +static unsigned int const EXTIx_IRQn[] = { + EXTI0_IRQn, + EXTI1_IRQn, + EXTI2_IRQn, + EXTI3_IRQn, + EXTI4_IRQn, + EXTI9_5_IRQn, + EXTI9_5_IRQn, + EXTI9_5_IRQn, + EXTI9_5_IRQn, + EXTI9_5_IRQn, + EXTI15_10_IRQn, + EXTI15_10_IRQn, + EXTI15_10_IRQn, + EXTI15_10_IRQn, + EXTI15_10_IRQn, + EXTI15_10_IRQn +}; + +/** + * @brief Converts intermediate pin number to port pointer. + * + * Intermediate pin number is a way of numerically labeling + * pins. Pins are labeled incrementally across all ports. + * Pins 0-15 from port A are 0-15. Pins 0-15 from port B are + * 16-31. And so on. + * + * @param interm_pin is the intermediate pin number + */ +#define STM32F4_GET_PORT(interm_pin) (GPIOx[ ( interm_pin ) / 16 ]) + +/** + * @brief Converts intermediate pin number to 0-15. + * + * Intermediate pin number is a way of numerically labeling + * pins. Pins are labeled incrementally across all ports. + * Pins 0-15 from port A are 0-15. Pins 0-15 from port B are + * 16-31. And so on. + * + * @param interm_pin is the intermediate pin number + */ +#define STM32F4_GET_PIN_0_15(interm_pin) (( interm_pin ) % 16) + +/** + * @brief Converts pin number from 0-15 to HAL pin mask. + * @param pin is the pin number from 0-15 + */ +#define STM32F4_GET_HAL_GPIO_PIN(pin) ((uint16_t) (1 << ( pin ))) + +/** + * @brief Get EXTI Line from pin number 0-15 + * @param pin is the pin number from 0-15 + */ +#define STM32F4_GET_LL_EXTI_LINE(pin) (0x1UL << ( pin )) + +/** + * @brief Get EXTI IRQ number from pin 0-15 + * @param pin is the pin number from 0-15 + */ +#define STM32F4_GET_EXTI_IRQn(pin) (EXTIx_IRQn[( pin )]) + +/************** Interrupt manager *****************/ +typedef struct { + void *arg; + stm32f4_gpio *gpio; +} stm32f4_interrupt_arg; + +typedef struct { + stm32f4_interrupt_arg arg; + rtems_gpio_isr isr; +} stm32f4_interrupt; + +static stm32f4_interrupt isr_table[16]; +static bool isr_registered[16] = {0}; + +void exti_handler(void *arg); + + +/********** STM32F4 GPIO API functions ************/ + +rtems_status_code bsp_gpio_register_controllers( + void +) +{ + return rtems_gpio_register( + stm32f4_gpio_get, + stm32f4_gpio_destroy, + stm32f4_periph_get_api, + stm32f4_periph_remove_api, + sizeof(GPIOx)/sizeof(GPIOx[0])*16 + ); +} + +rtems_status_code stm32f4_gpio_get( + uint32_t interm_pin, + rtems_gpio **out +) +{ + stm32f4_gpio *tmp = malloc(sizeof(stm32f4_gpio)); + if (tmp == NULL) { + return RTEMS_NO_MEMORY; + } + tmp->base = RTEMS_GPIO_BUILD_BASE(&stm32f4_gpio_handlers); + tmp->pin = STM32F4_GET_PIN_0_15(interm_pin); + tmp->port = STM32F4_GET_PORT(interm_pin); + + // initialize the pin + stm32f4_gpio_init((rtems_gpio *) tmp); + + *out = (rtems_gpio *) tmp; + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_destroy( + rtems_gpio *base +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + free(gpio); + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_set_pin_mode( + rtems_gpio *base, + rtems_gpio_pin_mode mode +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + uint32_t pin_mask = STM32F4_GET_HAL_GPIO_PIN(gpio->pin); + + uint32_t stm32f4_mode, stm32f4_output_type; + switch (mode) { + case RTEMS_GPIO_PINMODE_OUTPUT_PP: + stm32f4_mode = LL_GPIO_MODE_OUTPUT; + stm32f4_output_type = LL_GPIO_OUTPUT_PUSHPULL; + break; + case RTEMS_GPIO_PINMODE_OUTPUT_OD: + stm32f4_mode = LL_GPIO_MODE_OUTPUT; + stm32f4_output_type = LL_GPIO_OUTPUT_OPENDRAIN; + break; + case RTEMS_GPIO_PINMODE_INPUT: + stm32f4_mode = LL_GPIO_MODE_INPUT; + break; + case RTEMS_GPIO_PINMODE_ANALOG: + stm32f4_mode = LL_GPIO_MODE_ANALOG; + break; + case RTEMS_GPIO_PINMODE_BSP_SPECIFIC: + stm32f4_mode = LL_GPIO_MODE_ALTERNATE; + break; + default: + /* illegal argument */ + return RTEMS_UNSATISFIED; + } + LL_GPIO_SetPinMode(gpio->port, pin_mask, stm32f4_mode); + if (stm32f4_mode == LL_GPIO_MODE_OUTPUT) { + LL_GPIO_SetPinOutputType(gpio->port, pin_mask, stm32f4_output_type); + } + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_set_pull( + rtems_gpio *base, + rtems_gpio_pull pull +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + uint32_t pin_mask = STM32F4_GET_HAL_GPIO_PIN(gpio->pin); + uint32_t stm32f4_pull; + + switch (pull) { + case RTEMS_GPIO_NOPULL: + stm32f4_pull = LL_GPIO_PULL_NO; + break; + case RTEMS_GPIO_PULLUP: + stm32f4_pull = LL_GPIO_PULL_UP; + break; + case RTEMS_GPIO_PULLDOWN: + stm32f4_pull = LL_GPIO_PULL_DOWN; + break; + default: + /* Illegal argument */ + return RTEMS_UNSATISFIED; + } + LL_GPIO_SetPinPull(gpio->port, pin_mask, stm32f4_pull); + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_configure_interrupt( + rtems_gpio *base, + rtems_gpio_isr isr, + void *arg, + rtems_gpio_interrupt_trig trig, + rtems_gpio_pull pull +) +{ + // configure pin + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + uint32_t pin_mask = STM32F4_GET_HAL_GPIO_PIN(gpio->pin); + GPIO_InitTypeDef hal_conf; + + switch (trig) { + case RTEMS_GPIO_INT_TRIG_NONE: + return RTEMS_SUCCESSFUL; + case RTEMS_GPIO_INT_TRIG_FALLING: + hal_conf.Mode = GPIO_MODE_IT_FALLING; + break; + case RTEMS_GPIO_INT_TRIG_RISING: + hal_conf.Mode = GPIO_MODE_IT_RISING; + break; + case RTEMS_GPIO_INT_TRIG_BOTH_EDGES: + hal_conf.Mode = GPIO_MODE_IT_RISING_FALLING; + break; + default: + /* Invalid argument */ + return RTEMS_UNSATISFIED; + } + switch (pull) { + case RTEMS_GPIO_NOPULL: + hal_conf.Pull = GPIO_NOPULL; + break; + case RTEMS_GPIO_PULLUP: + hal_conf.Pull = GPIO_PULLUP; + break; + case RTEMS_GPIO_PULLDOWN: + hal_conf.Pull = GPIO_PULLDOWN; + break; + default: + /* Illegal argument */ + return RTEMS_UNSATISFIED; + } + hal_conf.Pin = pin_mask; + HAL_GPIO_Init(gpio->port, &hal_conf); + + // RTEMS interrupt config + if (isr_registered[gpio->pin]) { + return RTEMS_UNSATISFIED; + } + isr_table[gpio->pin] = (stm32f4_interrupt){ + .arg = { + .arg = arg, + .gpio = gpio + }, + .isr = isr + }; + isr_registered[gpio->pin] = true; + rtems_option opt = gpio->pin < 5 ? + RTEMS_INTERRUPT_UNIQUE : + RTEMS_INTERRUPT_SHARED; + rtems_status_code sc = rtems_interrupt_handler_install( + STM32F4_GET_EXTI_IRQn(gpio->pin), + NULL, + opt, + exti_handler, + &isr_table[gpio->pin].arg + ); + + return sc; +} + +rtems_status_code stm32f4_gpio_remove_interrupt( + rtems_gpio *base +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + if (isr_registered[gpio->pin]) { + rtems_status_code sc = rtems_interrupt_handler_remove( + STM32F4_GET_EXTI_IRQn(gpio->pin), + exti_handler, + &isr_table[gpio->pin].arg + ); + if (sc == RTEMS_SUCCESSFUL) { + isr_registered[gpio->pin] = false; + } + return sc; + } + return RTEMS_UNSATISFIED; +} + +rtems_status_code stm32f4_gpio_enable_interrupt( + rtems_gpio *base +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + LL_EXTI_EnableIT_0_31(STM32F4_GET_LL_EXTI_LINE(gpio->pin)); + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_disable_interrupt( + rtems_gpio *base +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + LL_EXTI_DisableIT_0_31(STM32F4_GET_LL_EXTI_LINE(gpio->pin)); + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_write( + rtems_gpio *base, + rtems_gpio_pin_state value +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + + if (value) + LL_GPIO_SetOutputPin(gpio->port, STM32F4_GET_HAL_GPIO_PIN(gpio->pin)); + else + LL_GPIO_ResetOutputPin(gpio->port, STM32F4_GET_HAL_GPIO_PIN(gpio->pin)); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_read( + rtems_gpio *base, + rtems_gpio_pin_state *value +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + + *value = LL_GPIO_IsInputPinSet( + gpio->port, + STM32F4_GET_HAL_GPIO_PIN(gpio->pin) + ); + return RTEMS_SUCCESSFUL; +} + +rtems_status_code stm32f4_gpio_toggle( + rtems_gpio *base +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + + LL_GPIO_TogglePin( + gpio->port, + STM32F4_GET_HAL_GPIO_PIN(gpio->pin) + ); + return RTEMS_SUCCESSFUL; +} + +void exti_handler(void *arg) { + stm32f4_interrupt_arg *stm32_arg = (stm32f4_interrupt_arg *) arg; + uint32_t pin_mask = STM32F4_GET_HAL_GPIO_PIN(stm32_arg->gpio->pin); + if(__HAL_GPIO_EXTI_GET_IT(pin_mask) != RESET) + { + __HAL_GPIO_EXTI_CLEAR_IT(pin_mask); + (*isr_table[stm32_arg->gpio->pin].isr)(stm32_arg->arg); + } +} + +/************ STM32F4 Other specific GPIO functions ************/ +rtems_status_code stm32f4_gpio_init(rtems_gpio *base) { + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + + switch ((uintptr_t) gpio->port) { +#ifdef GPIOA_BASE + case (uintptr_t) GPIOA: + __HAL_RCC_GPIOA_CLK_ENABLE(); + break; +#endif /* GPIOA_BASE */ +#ifdef GPIOB_BASE + case (uintptr_t) GPIOB: + __HAL_RCC_GPIOB_CLK_ENABLE(); + break; +#endif /* GPIOB_BASE */ +#ifdef GPIOC_BASE + case (uintptr_t) GPIOC: + __HAL_RCC_GPIOC_CLK_ENABLE(); + break; +#endif /* GPIOC_BASE */ +#ifdef GPIOD_BASE + case (uintptr_t) GPIOD: + __HAL_RCC_GPIOD_CLK_ENABLE(); + break; +#endif /* GPIOD_BASE */ +#ifdef GPIOE_BASE + case (uintptr_t) GPIOE: + __HAL_RCC_GPIOE_CLK_ENABLE(); + break; +#endif /* GPIOE_BASE */ +#ifdef GPIOF_BASE + case (uintptr_t) GPIOF: + __HAL_RCC_GPIOF_CLK_ENABLE(); + break; +#endif /* GPIOF_BASE */ +#ifdef GPIOG_BASE + case (uintptr_t) GPIOG: + __HAL_RCC_GPIOG_CLK_ENABLE(); + break; +#endif /* GPIOG_BASE */ +#ifdef GPIOH_BASE + case (uintptr_t) GPIOH: + __HAL_RCC_GPIOH_CLK_ENABLE(); + break; +#endif /* GPIOH_BASE */ +#ifdef GPIOI_BASE + case (uintptr_t) GPIOI: + __HAL_RCC_GPIOI_CLK_ENABLE(); + break; +#endif /* GPIOI_BASE */ +#ifdef GPIOJ_BASE + case (uintptr_t) GPIOJ: + __HAL_RCC_GPIOJ_CLK_ENABLE(); + break; +#endif /* GPIOJ_BASE */ +#ifdef GPIOK_BASE + case (uintptr_t) GPIOK: + __HAL_RCC_GPIOK_CLK_ENABLE(); + break; +#endif /* GPIOK_BASE */ + default: + return RTEMS_UNSATISFIED; + } + return RTEMS_SUCCESSFUL; +} + +void stm32f4_gpio_lock_pin( + rtems_gpio *base +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + LL_GPIO_LockPin( + gpio->port, + STM32F4_GET_HAL_GPIO_PIN(gpio->pin) + ); +} + +void stm32f4_gpio_set_af( + rtems_gpio *base, + uint32_t alternate +) +{ + stm32f4_gpio *gpio = stm32f4_get_gpio_from_base(base); + if (gpio->pin < 8) + LL_GPIO_SetAFPin_0_7( + gpio->port, + STM32F4_GET_HAL_GPIO_PIN(gpio->pin), + alternate + ); + else + LL_GPIO_SetAFPin_8_15( + gpio->port, + STM32F4_GET_HAL_GPIO_PIN(gpio->pin), + alternate + ); +} + diff --git a/bsps/arm/stm32f4/include/bsp.h b/bsps/arm/stm32f4/include/bsp.h index 42a74e109d..968545cd5a 100644 --- a/bsps/arm/stm32f4/include/bsp.h +++ b/bsps/arm/stm32f4/include/bsp.h @@ -42,10 +42,6 @@ extern "C" { #define BSP_ARMV7M_SYSTICK_FREQUENCY STM32F4_HCLK -#ifdef __rtems__ -void Error_Handler(void); -#endif /* __rtems__ */ - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bsps/arm/stm32f4/include/bsp/stm32f4_adc.h b/bsps/arm/stm32f4/include/bsp/stm32f4_adc.h new file mode 100644 index 0000000000..973fadf828 --- /dev/null +++ b/bsps/arm/stm32f4/include/bsp/stm32f4_adc.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_ARM_STM32F4_BSP_ADC +#define LIBBSP_ARM_STM32F4_BSP_ADC + +#include <rtems/sysinit.h> +#include <bsp/adc.h> +#include <stm32f4xx_ll_bus.h> +#include <stm32f4xx_ll_adc.h> +#include <bsp/stm32f4_gpio.h> + +#define STM32F4_ADC_DEFAULT_RESOLUTION LL_ADC_RESOLUTION_10B +#define STM32F4_ADC_DEFAULT_ALIGNMENT LL_ADC_DATA_ALIGN_RIGHT +#define STM32F4_ADC_DEFAULT_SAMPLINGTIME LL_ADC_SAMPLINGTIME_3CYCLES + +/** + * Macros for simple locking of shared objects. + * Structs must have a bool member named "locked" + */ +#define STM32F4_LOCK(obj) \ + do { \ + ( obj )->locked = true; \ + } while (0) + +#define STM32F4_UNLOCK(obj) \ + do { \ + ( obj )->locked = false; \ + } while (0) + +#define STM32F4_IS_LOCKED(obj) \ + (( obj )->locked) + +/** + * @brief Wrapper of ADC_TypeDef with a simple lock. + */ +typedef struct { + ADC_TypeDef *ADCx; + bool locked; +} ADC_TypeDef_Protected; + +/** + * @brief Structure containing ADC configuration for an + * ADC pin. + */ +typedef struct { + /** + * @brief Locks ADC configuration change on this pin + * if set to true. + */ + bool locked; + /** + * @brief STM32F4-defined ADCx. + */ + ADC_TypeDef_Protected *ADCx; + /** + * @brief ADC channel of the pin. + * This can be LL_ADC_CHANNEL_n defined by STM32F4 LL + * driver, where 0 <= n <= 18. + */ + uint32_t channel; + /** + * @brief Resolution of the ADC pin. + * This can be one of the following values: + * @ref LL_ADC_RESOLUTION_12B + * @ref LL_ADC_RESOLUTION_10B + * @ref LL_ADC_RESOLUTION_8B + * @ref LL_ADC_RESOLUTION_6B + */ + uint32_t resolution; + /** + * @brief Data alignment of the ADC pin. + * This can be one of the following values: + * @ref LL_ADC_DATA_ALIGN_RIGHT + * @ref LL_ADC_DATA_ALIGN_LEFT + */ + uint32_t alignment; +} stm32f4_adc_config; + +/** + * @brief STM32F4 wrapper of peripherals API. + */ +typedef struct { + rtems_adc_api base_api; + stm32f4_adc_config *adc_config; +} stm32f4_adc; + +const rtems_adc_handlers *stm32f4_get_adc_handlers( + void +); + +/** + * @brief Get the HAL-defined ADCx pointer. + * @note This function should not be called. Use + * @see stm32f4_get_ADCx_protected() instead. + */ +ADC_TypeDef *stm32f4_get_ADCx( + GPIO_TypeDef *gpiox +); + +ADC_TypeDef_Protected *stm32f4_get_ADCx_protected( + GPIO_TypeDef *gpiox +); + +rtems_status_code stm32f4_get_LL_ADC_CHANNEL( + stm32f4_gpio *gpio, + uint32_t *channel +); + +bool stm32f4_is_adc_pin( + stm32f4_gpio *gpio +); + +rtems_periph_api *stm32f4_adc_get( + rtems_gpio *gpio +); + +rtems_status_code stm32f4_adc_destroy( + rtems_gpio *gpio +); + +/***/ +/** + * @brief Perform initialization for STM32F4 ADC manager + * + * This function should be called in bspstarthook under + * if ADC is enabled. + */ +extern void stm32f4_adc_start( + void +); + +/** + * @brief Performs initialization for an ADC pin + * @note This function needs to be registered with the + * peripherals API. + */ +void stm32f4_adc_init( + rtems_gpio *base +); + +rtems_status_code stm32f4_adc_read_raw( + rtems_gpio *base, + uint32_t *result, + uint32_t timeout +); + +rtems_status_code stm32f4_adc_start_read_raw_nb( + rtems_gpio *base +); + +rtems_adc_status stm32f4_adc_read_raw_nb( + rtems_gpio *base, + uint32_t *result +); + +rtems_status_code stm32f4_adc_set_resolution( + rtems_gpio *base, + unsigned int bits +); + +rtems_status_code stm32f4_adc_set_alignment( + rtems_gpio *base, + rtems_adc_align align +); + +rtems_status_code stm32f4_adc_configure_interrupt( + rtems_gpio *base, + rtems_adc_isr isr, + void *arg +); + +rtems_status_code stm32f4_adc_remove_interrupt( + rtems_gpio *base +); + +rtems_status_code stm32f4_adc_enable_interrupt( + rtems_gpio *base +); + +rtems_status_code stm32f4_adc_disable_interrupt( + rtems_gpio *base +); + +#endif /* LIBBSP_ARM_STM32F4_BSP_ADC */ diff --git a/bsps/arm/stm32f4/include/bsp/stm32f4_gpio.h b/bsps/arm/stm32f4/include/bsp/stm32f4_gpio.h new file mode 100644 index 0000000000..6bd6baea86 --- /dev/null +++ b/bsps/arm/stm32f4/include/bsp/stm32f4_gpio.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_ARM_STM32F4_BSP_GPIO +#define LIBBSP_ARM_STM32F4_BSP_GPIO + +#include <stdbool.h> +#include <stm32f4xx.h> +#include <stm32f4xx_ll_gpio.h> +#include <stm32f4xx_ll_exti.h> +#include <bsp/gpio2.h> + +/*********** Helpers *****************/ +/** + * @brief Macro to get stm32f4_gpio object from a base rtems_gpio + * object. + * + * This is a wrapper of RTEMS_CONTAINER_OF macro + * + * @param base The pointer to a rtems_gpio object + * @retval The pointer to the stm32f4_gpio object owning + * the specified rtems_gpio object + */ +#define stm32f4_get_gpio_from_base(_base) \ + RTEMS_CONTAINER_OF(_base, stm32f4_gpio, base) + +/** + * @brief STM32F4 BSP GPIO structure + * + */ +typedef struct { + /** + * @brief This member is a rtems_gpio object. + */ + rtems_gpio base; + /** + *@brief This member is the pin number from 0 to 15. + */ + uint32_t pin; + /** + * @brief This member is HAL GPIOx pointer. + */ + GPIO_TypeDef *port; +} stm32f4_gpio; + +/** + * @name STM32F4 GPIO functions + * + * @{ + */ + +/** + * @name GPIO API implementation of STM32F4 BSP + * + * @{ + */ + + +/** + * @brief Sets the pin mode. + * + * @param[in] base The pointer to the GPIO object. + * @param mode is a value of @see rtems_gpio_pin_mode + * + * @retval RTEMS_SUCCESSFUL if the pin mode is valid. + * @retval RTEMS_UNSATISFIED if the pin mode is + * invalid. + */ +extern rtems_status_code stm32f4_gpio_set_pin_mode( + rtems_gpio *base, + rtems_gpio_pin_mode mode +); + +/** + * @brief Sets pull resistor mode. + * + * @param[in] base The pointer to the GPIO object. + * @param pull is a value of @see rtems_gpio_pull + * + * @retval RTEMS_SUCCESSFUL if the pull resistor + * mode is valid. + * @retval RTEMS_UNSATISFIED if the pull resistor + * mode is invalid. + */ +extern rtems_status_code stm32f4_gpio_set_pull( + rtems_gpio *base, + rtems_gpio_pull pull +); + +/** + * @brief Configures user-defined ISR for an EXTI. + * + * This function is used to register a custom ISR + * with a GPIO pin. This API supports up to 1 ISR + * for each EXTI line (total 16 ISRs) at a time. + * + * @note If there is already an ISR registered with + * a line, it needs to be removed to be able + * to register a new one. + * @note This function does not enable interrupt. + * use @see rtems_gpio_enable_interrupt(). + * + * @param[in] base The pointer to the GPIO object. + * + * @retval RTEMS_SUCCESSFUL if interrupt + * configuration is successful. + * @retval RTEMS_UNSATISFIED if trigger mode/pull + * resistor mode is invalid or an ISR + * has already been registered for this + * EXTI line. + * @retval @see rtems_interrupt_handler_install() + */ +extern rtems_status_code stm32f4_gpio_configure_interrupt( + rtems_gpio *base, + rtems_gpio_isr isr, + void *arg, + rtems_gpio_interrupt_trig trig, + rtems_gpio_pull pull +); + +/** + * @brief Removes the registered ISR. + * + * @note This function does not disable interrupt. + * @ref rtems_gpio_disable_interrupt() + * + * @param[in] base The pointer to the GPIO object. + * + * @retval RTEMS_SUCCESSFUL if ISR removed successfully. + * @retval RTEMS_UNSATISFIED if no ISR registered for + * selected line. + * @retval @see rtems_interrupt_handler_remove() + */ +extern rtems_status_code stm32f4_gpio_remove_interrupt( + rtems_gpio *base +); + +/** + * @brief Enables EXTI for the line connected to this + * pin. + * + * @param[in] base The pointer to the GPIO object. + * + * @retval RTEMS_SUCCESSFUL + */ +extern rtems_status_code stm32f4_gpio_enable_interrupt( + rtems_gpio *base +); + +/** + * @brief Disables EXTI for the line connected to + * this pin. + * + * @param[in] base The pointer to the GPIO object. + * + * @retval RTEMS_SUCCESSFUL + */ +extern rtems_status_code stm32f4_gpio_disable_interrupt( + rtems_gpio *base +); + +/** + * @brief Reads digital value into a variable. + * + * @param[in] base The pointer to the GPIO object. + * @param[out] value The pointer to the output + * variable. + * + * @retval RTEMS_SUCCESSFUL + */ +extern rtems_status_code stm32f4_gpio_read( + rtems_gpio *base, + rtems_gpio_pin_state *value +); + +/** + * @brief Writes digital value to a pin. + * + * @param[in] base The pointer to the GPIO object. + * @param value The output digital value. + * + * @retval RTEMS_SUCCESSFUL + */ +extern rtems_status_code stm32f4_gpio_write( + rtems_gpio *base, + rtems_gpio_pin_state value +); + +/** + * @brief Toggles the state of a pin. + * + * @param[in] base The pointer to the GPIO object. + * + * @retval RTEMS_SUCCESSFUL + */ +extern rtems_status_code stm32f4_gpio_toggle( + rtems_gpio *base +); + +/** @} */ + +/** + * @name Extra functionality of STM32F4 GPIO + * + * @{ + */ + +/** + * @brief Initializes clock for the GPIO port + * owning this pin. + * + * @note This function is called in stm32f4_gpio_get(). + * + * @param[in] base The pointer to the GPIO object. + * + * @retval RTEMS_SUCCESSFUL if the port argument is + * valid + * @retval RTEMS_UNSATISFIED if the port argument is + * invalid + */ +extern rtems_status_code stm32f4_gpio_init( + rtems_gpio *base +); + +/** + * @brief Lock configuration of a pin. + * + * @param[in] base The pointer to the GPIO object. + */ +extern void stm32f4_gpio_lock_pin( + rtems_gpio *base +); + +/** + * @brief Sets the alternate function for a pin. + * + * @param[in] base The pointer to the GPIO object. + * @param alternate Alternate function, from 0-15. + */ +extern void stm32f4_gpio_set_af( + rtems_gpio *base, + uint32_t alternate +); + +/** @} */ + +/** @} */ + +#endif /* LIBBSP_ARM_STM32F4_BSP_GPIO */ diff --git a/bsps/arm/stm32f4/include/bsp/stm32f4_hal.h b/bsps/arm/stm32f4/include/bsp/stm32f4_hal.h new file mode 100644 index 0000000000..9e88b39067 --- /dev/null +++ b/bsps/arm/stm32f4/include/bsp/stm32f4_hal.h @@ -0,0 +1,17 @@ +#ifndef LIBBSP_ARM_STM32F4_BSP_STM32F4HAL_H +#define LIBBSP_ARM_STM32F4_BSP_STM32F4HAL_H + +#include <stm32f4xx_hal.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void stm32f4_clk_enable(); +void stm32f4_clk_disable(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsps/arm/stm32f4/include/bsp/stm32f4_periph.h b/bsps/arm/stm32f4/include/bsp/stm32f4_periph.h new file mode 100644 index 0000000000..f8b9ad2540 --- /dev/null +++ b/bsps/arm/stm32f4/include/bsp/stm32f4_periph.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_BSP_ARM_STM32F4_PERIPH_H +#define LIBBSP_BSP_ARM_STM32F4_PERIPH_H + +#include <bsp/periph_api.h> + +/** + * @brief STM32F4 BSP's function to get a + * peripheral API. + */ +rtems_periph_api *stm32f4_periph_get_api( + rtems_gpio *pin, + rtems_periph_api_type type +); + +/** + * @brief STM32F4 BSP's function to remove a + * peripheral API assigned to a pin. + */ +rtems_status_code stm32f4_periph_remove_api( + rtems_gpio *pin +); + +#endif /* LIBBSP_BSP_ARM_STM32F4_PERIPH_H */ diff --git a/bsps/arm/stm32f4/start/bspstart.c b/bsps/arm/stm32f4/start/bspstart.c index 8589c6486c..2297430844 100644 --- a/bsps/arm/stm32f4/start/bspstart.c +++ b/bsps/arm/stm32f4/start/bspstart.c @@ -19,6 +19,7 @@ #endif /* __rtems__ */ #ifdef __rtems__ #include <stm32f4xx.h> +#include <bsp/gpio2.h> #endif /* __rtems__ */ #ifdef STM32F4_FAMILY_F4XXXX @@ -46,7 +47,7 @@ uint32_t HAL_GetTick(void) * @brief This function is executed in case of error occurrence. * @retval None */ -void Error_Handler(void) +static void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ @@ -166,7 +167,6 @@ static rtems_status_code SystemClock_Config(void) HAL_StatusTypeDef status = HAL_RCC_OscConfig(&RCC_OscInitStruct); if (status != HAL_OK) { -// Error_Handler(); return RTEMS_UNSATISFIED; } @@ -181,7 +181,6 @@ static rtems_status_code SystemClock_Config(void) if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, flash_latency) != HAL_OK) { -// Error_Handler(); return RTEMS_UNSATISFIED; } @@ -465,9 +464,7 @@ void bsp_start( void ) { init_main_osc(); -#ifdef __rtems__ stm32f4_gpio_set_config_array( &stm32f4_start_config_gpio[ 0 ] ); -#endif /* __rtems__ */ bsp_interrupt_initialize(); } diff --git a/bsps/arm/stm32f4/start/bspstarthook.c b/bsps/arm/stm32f4/start/bspstarthook.c index bbfaed615e..0aeba0ac13 100644 --- a/bsps/arm/stm32f4/start/bspstarthook.c +++ b/bsps/arm/stm32f4/start/bspstarthook.c @@ -20,4 +20,12 @@ void BSP_START_TEXT_SECTION bsp_start_hook_1(void) bsp_start_clear_bss(); /* At this point we can use objects outside the .start section */ + +#ifndef __rtems__ + +#if STM32F4_ENABLE_GENERIC_GPIO == 1 + bsp_gpio_register_controllers(); +#endif /* STM32F4_ENABLE_GENERIC_GPIO */ + +#endif /* __rtems__ */ } diff --git a/bsps/arm/stm32f4/start/periph.c b/bsps/arm/stm32f4/start/periph.c new file mode 100644 index 0000000000..7b1b0f1bb5 --- /dev/null +++ b/bsps/arm/stm32f4/start/periph.c @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <bsp/stm32f4_gpio.h> +#include <bsp/stm32f4_adc.h> +#include <bsp/stm32f4_periph.h> + +rtems_periph_api *stm32f4_periph_get_api( + rtems_gpio *pin, + rtems_periph_api_type type +) +{ + switch (type) { + case RTEMS_PERIPH_API_TYPE_ADC: + return stm32f4_adc_get(pin); + default: + return NULL; + } +} + +rtems_status_code stm32f4_periph_remove_api( + rtems_gpio *pin +) +{ + if (pin->api == NULL) + return RTEMS_UNSATISFIED; + + switch (*(rtems_periph_api_type *) pin->api) { + case RTEMS_PERIPH_API_TYPE_ADC: + return stm32f4_adc_destroy(pin); + default: + return RTEMS_UNSATISFIED; + } +} diff --git a/spec/build/bsps/arm/stm32f4/grp.yml b/spec/build/bsps/arm/stm32f4/grp.yml index 99a78e1890..99b3a10527 100644 --- a/spec/build/bsps/arm/stm32f4/grp.yml +++ b/spec/build/bsps/arm/stm32f4/grp.yml @@ -17,9 +17,11 @@ links: - role: build-dependency uid: abi - role: build-dependency - uid: obj + uid: optnumgpioctrl - role: build-dependency uid: optenhal +- role: build-dependency + uid: optengpio - role: build-dependency uid: optvariant - role: build-dependency @@ -66,6 +68,8 @@ links: uid: ../../objirq - role: build-dependency uid: ../../objmem +- role: build-dependency + uid: obj - role: build-dependency uid: ../../bspopts type: build diff --git a/spec/build/bsps/arm/stm32f4/obj.yml b/spec/build/bsps/arm/stm32f4/obj.yml index 7afe000024..6d08c618c6 100644 --- a/spec/build/bsps/arm/stm32f4/obj.yml +++ b/spec/build/bsps/arm/stm32f4/obj.yml @@ -153,6 +153,8 @@ install: - bsps/arm/stm32f4/include/bsp/stm32f10xxx_rcc.h - bsps/arm/stm32f4/include/bsp/stm32f4.h - bsps/arm/stm32f4/include/bsp/stm32f4_gpio.h + - bsps/arm/stm32f4/include/bsp/stm32f4_periph.h + - bsps/arm/stm32f4/include/bsp/stm32f4_adc.h - bsps/arm/stm32f4/include/bsp/stm32f4xxxx_adc.h - bsps/arm/stm32f4/include/bsp/stm32f4xxxx_exti.h - bsps/arm/stm32f4/include/bsp/stm32f4xxxx_flash.h @@ -261,6 +263,9 @@ source: - bsps/arm/stm32f4/hal/stm32f4xx_ll_usb.c - bsps/arm/stm32f4/hal/stm32f4xx_ll_utils.c - bsps/arm/stm32f4/hal/system_stm32f4xx.c +- bsps/arm/stm32f4/gpio/gpio.c +- bsps/arm/stm32f4/start/periph.c +- bsps/arm/stm32f4/adc/adc.c - bsps/arm/shared/irq/irq-armv7m.c - bsps/arm/shared/irq/irq-dispatch-armv7m.c - bsps/arm/shared/start/bsp-start-memcpy.S diff --git a/spec/build/bsps/arm/stm32f4/optengpio.yml b/spec/build/bsps/arm/stm32f4/optengpio.yml new file mode 100644 index 0000000000..8b15bce9a9 --- /dev/null +++ b/spec/build/bsps/arm/stm32f4/optengpio.yml @@ -0,0 +1,16 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +actions: +- get-boolean: null +- define-condition: null +build-type: option +copyrights: +- Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) +default: true +default-by-variant: [] +description: | + This option enables the BSP to use RTEMS GPIO API. +enabled-by: true +format: '{}' +links: [] +name: STM32F4_ENABLE_GENERIC_GPIO +type: build diff --git a/spec/build/bsps/arm/stm32f4/optnumgpioctrl.yml b/spec/build/bsps/arm/stm32f4/optnumgpioctrl.yml new file mode 100644 index 0000000000..da21d9c62c --- /dev/null +++ b/spec/build/bsps/arm/stm32f4/optnumgpioctrl.yml @@ -0,0 +1,16 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +actions: +- get-integer: null +- define: null +build-type: option +copyrights: +- Copyright (C) 2022 Duc Doan (dtbpkmte at gmail.com) +default: 1 +default-by-variant: [] +description: | + Number of GPIO controllers of this BSP. +enabled-by: true +format: '{}' +links: [] +name: BSP_GPIO_NUM_CONTROLLERS +type: build -- 2.36.1 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel