/*! * \file * NUCLEO_F401RE.h * \brief * Nucleo F401RE port file. This file contain the implementation of driver * calls for F401RE board. * * Created on: May 23, 2020 * Author: Christos Choutouridis AEM: 8997 * email : */ #include "NUCLEO_F401RE.h" /* * =============== System =============== */ static clock_t volatile __ticks; //!< CPU time static time_t volatile __now; //!< Time in UNIX seconds past 1-Jan-70 static clock_t volatile __sys_freq; //!< The CPU's time frequency (SysTick freq) /*! * \brief * This is the SysTick ISR, micro-system time base service for CPU time. * \note * This service implements the SysTick callback function in order * to provide micro system - os like functionalities to an application * without RTOS */ void SysTick_Handler(void) { // Time ++__ticks; if ( !(__ticks % __sys_freq ) ) ++__now; // Do not update __now when we have external time system } /*! * \brief This function configures the source of the time base. * The time source is configured to have 1ms time base with a dedicated * Tick interrupt priority. * \param sf Tick interrupt frequency. * \retval HAL status */ __weak HAL_StatusTypeDef HAL_SysTick_Init(clock_t sf) { SystemCoreClockUpdate (); /* Configure the SysTick to have interrupt in sf time basis */ if (SysTick_Config (SystemCoreClock/sf) != 0) return HAL_ERROR; __sys_freq = sf; /*Configure the SysTick IRQ priority */ NVIC_SetPriority (SysTick_IRQn, 3U); /* Return function status */ return HAL_OK; } /*! * Select the system frequency without calling the Setting functionality * \param sf The desired value * \return The desired value (enable chaining) */ __INLINE clock_t HAL_SelectSysTickFreq (clock_t sf){ return __sys_freq =sf; } /*! * \brief Get the __sys_freq. */ __INLINE clock_t HAL_GetSysTickFreq (void){ return __sys_freq; } /*! * \brief Reconfigure the SysTick and update __sys_freq * \param sf Tick interrupt frequency (CPU time) * \return status of the operation * \arg 0 Success * \arg 1 Fail */ int HAL_SetSysTickFreq (clock_t sf) { /*Configure the SysTick to have interrupt in sf time basis*/ if (__sys_freq != sf) { // Time base configuration SystemCoreClockUpdate (); if (SysTick_Config ( (SystemCoreClock>>3)/sf) != 0) return 1; else { __sys_freq = sf; return 0; } } return 0; } // Take over control of SysTick from HAL library //! disable HAL_InitTick implementation HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { return HAL_OK; } //! Chain GetTick to our implementation uint32_t HAL_GetTick(void) { return clock(); } /*! * \brief This function provides minimum delay (in CPU time) based * on variable incremented. * \param Delay specifies the delay time length, in CPU time. * \note * uint32_t is implicitly convertible to clock_t and vice versa. */ void HAL_Delay(uint32_t Delay) { uint32_t tickstart = clock(); while((clock() - tickstart) < Delay) ; } /* * ================ Jiffies ====================== */ int JF_setfreq (uint32_t jf_freq, uint32_t jiffies) { uint32_t psc=0; JF_TIMER_CLK_ENABLE(); SystemCoreClockUpdate (); if (jf_freq) psc = SystemCoreClock / jf_freq - 1; if (psc < 0xFFFF) JF_TIMER->PSC = psc; else return 1; if (jiffies < 0xFFFF) JF_TIMER->ARR = jiffies; else return 1; JF_TIMER->CR1 |= TIM_CR1_CEN; return 0; } /* * ======== OS like Functionalities ============ */ //! SysTick frequency getter __INLINE clock_t get_freq (void) { return __sys_freq; } //! SysTick frequency setter //! \return True on failure int set_freq (clock_t sf) { return HAL_SetSysTickFreq (sf); } /*! * \brief * determines the processor time. * \return * the implementation's best approximation to the processor time * used by the program since program invocation. The time in * seconds is the value returned divided by the value of the macro * CLK_TCK or CLOCKS_PER_SEC */ __INLINE clock_t clock (void) { return (clock_t) __ticks; } /*! * \brief * Set the processor time used. * \param c The new CPU time value * \return * The implementation's best approximation to the processor time * used by the program since program invocation. The time in * seconds is the value returned divided by the value of the macro * CLK_TCK or CLOCKS_PER_SEC */ clock_t setclock (clock_t c) { return __ticks = c; } /*! * \brief * determines the current calendar time. The encoding of the value is * unspecified. * \return * The implementations best approximation to the current calendar * time. If timer is not a null pointer, the return value * is also assigned to the object it points to. */ time_t time (time_t *timer) { if (timer) *timer = (time_t)__now; return (time_t)__now; } /*! * \brief * Sets the system's idea of the time and date. The time, * pointed to by t, is measured in seconds since the Epoch, 1970-01-01 * 00:00:00 +0000 (UTC). * \param t Pointer to new system's time and date. * \return On success, zero is returned. On error, -1 is returned */ int settime (const time_t *t) { if (t) { __now = *t; return 0; } else return -1; } /* * ============== Cycle count ============== */ /*! * Initialize CPU cycle measurement functionality based on DBG * \return The status of the operation * \arg LLD_OK Success * \arg LLD_ERROR Failure */ LLD_Status_en CYCLE_Init (void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // enable trace //DWT->LAR = 0xC5ACCE55; // <-- added unlock access to DWT (ITM, etc.)registers DWT->CYCCNT = 0; // clear DWT cycle counter DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // enable DWT cycle counter return LLD_OK; } //! CPU cycle getter __INLINE clock_t CYCLE_Get (void) { return (clock_t)DWT->CYCCNT; } //! Helper digital input pin getter __INLINE uint8_t _DINx (GPIO_TypeDef *port, uint32_t pin) { return ((port->IDR & pin) != 0) ? 1:0; } //! Helper digital output pin setter __INLINE uint8_t _DOUTx (GPIO_TypeDef *port, uint32_t pin, uint8_t st) { if (st) port->BSRR = (uint32_t)pin; else port->BSRR = (uint32_t)pin << 16; return st; } /* * =============== Digital I/O =============== * BTN -- PC13 * LED -- PA5 (SB42 is in place) [SB29: PB13] */ /*! * Initialize GPIO port pins for Nucleo Board * \return The status of the operation * \arg LLD_OK Success * \arg LLD_ERROR Failure */ LLD_Status_en NUCLEO_Port_Init (void) { GPIO_InitTypeDef GPIO_InitType; // Enable Port clock __HAL_RCC_GPIOA_CLK_ENABLE (); __HAL_RCC_GPIOC_CLK_ENABLE (); // BTN port configuration GPIO_InitType.Mode = GPIO_MODE_INPUT; GPIO_InitType.Pin = GPIO_PIN_13; GPIO_InitType.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOC, &GPIO_InitType); GPIO_InitType.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitType.Speed = GPIO_SPEED_LOW; GPIO_InitType.Pin = GPIO_PIN_5; HAL_GPIO_Init(GPIOA, &GPIO_InitType); return LLD_OK; } //! Nucleo's user button reader uint8_t NUCLEO_BTN (void) { return _DINx (GPIOC, GPIO_PIN_13); } //! Nucleo's LD2 led setter void NUCLEO_LED (uint8_t on) { _DOUTx(GPIOA, GPIO_PIN_5, on); } /*! Low level driver init functionality * \return The status of the operation * \arg LLD_OK Success * \arg LLD_ERROR Failure */ LLD_Status_en NUCLEO_Init (clock_t sys_freq) { HAL_Init(); HAL_SysTick_Init (sys_freq); CYCLE_Init (); NUCLEO_Port_Init (); return LLD_OK; }