314 lines
7.7 KiB
C

/*!
* \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 : <cchoutou@ece.auth.gr>
*/
#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;
}