@@ -11,6 +11,7 @@ deliver/ | |||
*.ld | |||
Debug/ | |||
Release/ | |||
*.launch | |||
# Keil related | |||
Keil/ | |||
@@ -0,0 +1,313 @@ | |||
/*! | |||
* \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; | |||
} |
@@ -0,0 +1,158 @@ | |||
/*! | |||
* \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> | |||
*/ | |||
#ifndef NUCLEO_F401RE_H_ | |||
#define NUCLEO_F401RE_H_ | |||
#include <stm32f4xx.h> | |||
#include <stm32f4xx_hal.h> | |||
#include <core_cm4.h> | |||
/* | |||
* ========= Data types ======== | |||
*/ | |||
//! Driver status return type | |||
typedef enum { | |||
LLD_OK = 0, //!< Indicate successful operation | |||
LLD_ERROR //!< Indicate Error | |||
}LLD_Status_en; | |||
typedef uint8_t din_t; | |||
//typedef int adc_t; | |||
#define OFF (0) | |||
#define ON (!OFF) | |||
#ifndef FALSE | |||
#define FALSE (0) | |||
#endif | |||
#ifndef TRUE | |||
#define TRUE (!FALSE) | |||
#endif | |||
/* | |||
* =============== System =============== | |||
*/ | |||
#if defined ( __GNUC__ ) && !defined (__CC_ARM) | |||
#include <sys/types.h> | |||
#endif | |||
#include <limits.h> | |||
/* | |||
* Also defined in types.h | |||
*/ | |||
#ifndef _CLOCK_T_ | |||
#if defined (__CC_ARM) | |||
#define _CLOCK_T_ unsigned int | |||
#else | |||
#define _CLOCK_T_ unsigned long | |||
#endif | |||
typedef _CLOCK_T_ clock_t; /*!< CPU time type */ | |||
#endif | |||
#ifndef _TIME_T_ | |||
#if defined (__CC_ARM) | |||
#define _TIME_T_ unsigned int | |||
#else | |||
#define _TIME_T_ unsigned long | |||
#endif | |||
typedef _TIME_T_ time_t; /*!< date/time in unix secs past 1-Jan-70 type for 68 years*/ | |||
#endif | |||
/* | |||
* Helper macros | |||
*/ | |||
#define _CLOCK_T_MAX_VALUE_ (ULONG_MAX) //!< Helper macro for maximum signed CPU time calculations | |||
/*! | |||
* Calculate the positive time difference of _t2_ and _t1_, where | |||
* _t1_, _t2_ are clock_t values | |||
* \note | |||
* _t2_ event comes is AFTER _t1_ | |||
* | |||
* ex: | |||
* 0 1 2 3 4 5 6 7 8 9 | |||
* ^ ^ | |||
* | | | |||
* a b | |||
* | |||
* if : t1=a, t2=b then dt = b-a = t2 - t1 | |||
* if : t1=b, t2=a then dt = 9 - (b-a) + 1 = UMAX - (t1-t2) + 1 | |||
* | |||
*/ | |||
#define _CLOCK_DIFF(_t2_, _t1_) ( ((_t2_)>(_t1_)) ? ((_t2_)-(_t1_)) : (_CLOCK_T_MAX_VALUE_ - ((_t1_) - (_t2_)) + 1) ) | |||
/* | |||
* CPU time macros | |||
*/ | |||
#define msec2CPUtime(_ms_) (((_ms_) * get_freq()) / 1000) | |||
#define sec2CPUtime(_s_) ((_s_) * get_freq()) | |||
#define CPUtime2msec(_t_) (((_t_) * 1000) / get_freq()) | |||
#define CPUtime2sec(_t_) ((_t_) / get_freq()) | |||
HAL_StatusTypeDef HAL_SysTick_Init(clock_t sf); | |||
clock_t HAL_SelectSysTickFreq (clock_t sf); | |||
clock_t HAL_GetSysTickFreq (void); | |||
int HAL_SetSysTickFreq (clock_t sf); | |||
/* | |||
* ================ Jiffies ===================== | |||
*/ | |||
#define JF_TIMER TIM5 | |||
#define JF_TIM_CLOCK_FREQ (1000000) //1MHz it's OK | |||
#define JF_TIM_VALUE (JF_TIMER->CNT) | |||
#define JF_TIMER_CLK_ENABLE __HAL_RCC_TIM5_CLK_ENABLE | |||
int JF_setfreq (uint32_t jf_freq, uint32_t jiffies); | |||
/* | |||
* OS like Functionalities | |||
*/ | |||
clock_t get_freq (void); | |||
int set_freq (clock_t sf); | |||
clock_t clock (void); | |||
clock_t setclock (clock_t c); | |||
time_t time (time_t *timer); | |||
int settime (const time_t *t); | |||
/* | |||
* ============== Cycle count ============== | |||
*/ | |||
LLD_Status_en CYCLE_Init (void); | |||
clock_t CYCLE_Get (void); | |||
uint8_t _DINx (GPIO_TypeDef *port, uint32_t pin); | |||
uint8_t _DOUTx (GPIO_TypeDef *port, uint32_t pin, uint8_t st); | |||
/* | |||
* =============== Digital I/O =============== | |||
* BTN -- PC13 | |||
* LED -- PA5 (SB42 is in place) [SB29: PB13] | |||
*/ | |||
LLD_Status_en NUCLEO_Port_Init (void); | |||
uint8_t NUCLEO_BTN (void); | |||
void NUCLEO_LED (uint8_t on); | |||
/* | |||
* ============= Board Init ============== | |||
*/ | |||
LLD_Status_en NUCLEO_Init (clock_t sys_freq); | |||
#endif /* NUCLEO_F401RE_H_ */ |
@@ -0,0 +1,507 @@ | |||
/*! | |||
* \file alcd.c | |||
* \brief | |||
* A target independent Alpharithmetic LCD driver | |||
* | |||
* Copyright (C) 2014 Houtouridis Christos (http://www.houtouridis.net) | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#include "alcd.h" | |||
static int _inc_x (alcd_t *alcd); | |||
static int _dec_x (alcd_t *alcd); | |||
static void _inc_y (alcd_t *alcd); | |||
static void _dec_y (alcd_t *alcd); | |||
static void _set_bus (alcd_t *alcd, int8_t db); | |||
static void _write_data (alcd_t *alcd, int8_t data); | |||
static void _command (alcd_t *alcd, uint8_t c); | |||
static void _character (alcd_t *alcd, uint8_t c); | |||
static void _set_cursor (alcd_t *alcd, uint8_t x, uint8_t y); | |||
/*! | |||
* \brief | |||
* increase cursor's x position. Positions start from 1. | |||
* If the cursor loops returns 1, else returns 0. | |||
* \param alcd pointer to active alcd. | |||
* \return Change line status | |||
*/ | |||
static int _inc_x (alcd_t *alcd) { | |||
int ret=0; | |||
if (++alcd->c.x > alcd->columns) { | |||
alcd->c.x = 1; | |||
ret = 1; | |||
} | |||
return ret; | |||
} | |||
/*! | |||
* \brief | |||
* Decrease cursor's x position. Positions start from 1. | |||
* If the cursor loops returns 1, else returns 0. | |||
* \param alcd pointer to active alcd. | |||
* \return none | |||
*/ | |||
static int _dec_x (alcd_t *alcd) { | |||
int ret = 0; | |||
if (--alcd->c.x < 1) { | |||
alcd->c.x = alcd->columns; | |||
ret = 1; | |||
} | |||
return ret; | |||
} | |||
/*! | |||
* \brief | |||
* increase cursor's y position. Positions start from 1. | |||
* \param alcd pointer to active alcd. | |||
* \return none | |||
*/ | |||
static void _inc_y (alcd_t *alcd) { | |||
if (++alcd->c.y > alcd->lines) { | |||
alcd->c.y = 1; | |||
} | |||
} | |||
/*! | |||
* \brief | |||
* Decrease cursor's y position. Positions start from 1. | |||
* \param alcd pointer to active alcd. | |||
* \return none | |||
*/ | |||
static void _dec_y (alcd_t *alcd) { | |||
if (--alcd->c.y < 1) { | |||
alcd->c.y = alcd->lines; | |||
} | |||
} | |||
/*! | |||
* \brief | |||
* Update the bus I/O and send it to the device. | |||
* \param alcd pointer to active alcd. | |||
* \param db the bus data. | |||
* \return none | |||
*/ | |||
static void _set_bus (alcd_t *alcd, int8_t db) | |||
{ | |||
alcd->io.db4 (db & 0x01); //Update port | |||
alcd->io.db5 (db & 0x02); | |||
alcd->io.db6 (db & 0x04); | |||
alcd->io.db7 (db & 0x08); | |||
jf_delay_us (10); // Wait to settle | |||
alcd->io.en (1); // Pulse out the data | |||
jf_delay_us (10); | |||
alcd->io.en (0); | |||
jf_delay_us (10); // Data hold | |||
} | |||
/*! | |||
* \brief | |||
* Write the byte date to the bus using _set_bus () | |||
* \param alcd pointer to active alcd. | |||
* \param data the data byte. | |||
* \return none | |||
*/ | |||
static void _write_data (alcd_t *alcd, int8_t data) | |||
{ | |||
_set_bus (alcd, data >> 4); | |||
_set_bus (alcd, data & 0x0F); | |||
} | |||
/*! | |||
* \brief | |||
* Send a command to alcd | |||
* \param alcd pointer to active alcd. | |||
* \param c the command byte. | |||
* \return none | |||
*/ | |||
static void _command (alcd_t *alcd, uint8_t c) | |||
{ | |||
alcd->io.rs(0); // Enter command mode | |||
jf_delay_us (100); // Wait | |||
_write_data (alcd, c); // Send | |||
} | |||
/*! | |||
* \brief | |||
* Send a character to alcd | |||
* \param alcd pointer to active alcd. | |||
* \param c the character byte. | |||
* \return none | |||
*/ | |||
static void _character (alcd_t *alcd, uint8_t c) | |||
{ | |||
alcd->io.rs(1); // Enter character mode | |||
jf_delay_us (100); // Wait | |||
_write_data (alcd, c); // Send | |||
} | |||
/*! | |||
* \brief | |||
* Set the Cursor to LCD's position line (y), column (x) starts from 1,2,...n | |||
* \param alcd pointer to active alcd. | |||
* \param x the x position. | |||
* \param y the y position. | |||
* \return none | |||
*/ | |||
static void _set_cursor (alcd_t *alcd, uint8_t x, uint8_t y) | |||
{ | |||
uint8_t cmd; | |||
alcd->c.x = x; // Update alcd data | |||
alcd->c.y = y; | |||
// Calculate address | |||
switch (y) { | |||
default: | |||
case 1: cmd = 0x0; break; | |||
case 2: cmd = 0x40; break; | |||
case 3: cmd = 0x0 + alcd->columns; break; | |||
case 4: cmd = 0x40 + alcd->columns; break; | |||
} | |||
cmd |= (x - 1); | |||
// Calculate command | |||
cmd |= LCD_DDRAMMask; | |||
// Command out the alcd | |||
_command (alcd, cmd); | |||
} | |||
/* | |||
* ============================ Public Functions ============================ | |||
*/ | |||
/* | |||
* Link and Glue functions | |||
*/ | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's DB4 pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_db4 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db4 = pfun; } | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's DB5 pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_db5 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db5 = pfun; } | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's DB6 pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_db6 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db6 = pfun; } | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's DB7 pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_db7 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db7 = pfun; } | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's RS pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_rs (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.rs = pfun; } | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's EN pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_en (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.en = pfun; } | |||
/*! | |||
* \brief | |||
* Link driver's db4 pin function to io struct. | |||
* \param alcd pointer to active alcd. | |||
* \param pfun driver's BL pin function | |||
* \return none | |||
*/ | |||
inline void alcd_link_bl (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.bl = pfun; } | |||
/*! | |||
* \brief | |||
* Send an ascii character to alcd. | |||
* \param alcd pointer to active alcd. | |||
* \param ch the character to send | |||
* \return the character send. | |||
* | |||
* \note | |||
* This is the driver's "putchar()" functionality to glue. | |||
* Tailor this function to redirect stdout to alcd. | |||
*/ | |||
int alcd_putchar (alcd_t *alcd, int ch) | |||
{ | |||
alcd->status = DRV_BUSY; | |||
// LCD Character dispatcher | |||
switch (ch) { | |||
case 0: | |||
// don't send null termination to device | |||
break; | |||
case '\n': | |||
_inc_y (alcd); | |||
//break; This "no break" is intentional | |||
case '\r': | |||
_set_cursor (alcd, 1, alcd->c.y); | |||
break; | |||
case '\v': | |||
alcd->c.x = alcd->c.y = 1; | |||
_command (alcd, LCD_RETHOME); | |||
jf_delay_us(2000); | |||
break; | |||
case '\f': | |||
//alcd->c.x = alcd->c.y = 1; | |||
_set_cursor (alcd, 1, 1); | |||
//_command (alcd, LCD_CLRSCR); | |||
//jf_delay_us(5000); | |||
//_command (alcd, LCD_RETHOME); | |||
//_set_cursor (alcd, alcd->c.x, alcd->c.y); | |||
jf_delay_us(2000); | |||
break; | |||
case '\b': | |||
if (_dec_x (alcd)) _dec_y (alcd); | |||
_set_cursor (alcd, alcd->c.x, alcd->c.y); | |||
_character (alcd, ' '); | |||
_set_cursor (alcd, alcd->c.x, alcd->c.y); | |||
break; | |||
default: | |||
_character (alcd, ch); | |||
// Increase cursor and loop inside the same line | |||
if (_inc_x (alcd)) _set_cursor (alcd, alcd->c.x, alcd->c.y); | |||
break; | |||
} | |||
// Restore status | |||
alcd->status = DRV_READY; | |||
//ANSI C (C99) compatible mode | |||
return ch; | |||
} | |||
/* | |||
* Set functions | |||
*/ | |||
/*! | |||
* \brief | |||
* Set the number of lines for the attached lcd display | |||
* \param alcd pointer to active alcd. | |||
* \param lines The number of lines (usually 2 or 4) | |||
* \return None. | |||
*/ | |||
void alcd_set_lines (alcd_t *alcd, int lines) { | |||
alcd->lines = lines; | |||
} | |||
/*! | |||
* \brief | |||
* Set the number of columns for the attached lcd display | |||
* \param alcd pointer to active alcd. | |||
* \param lines The number of columns (usually 16 or 20) | |||
* \return None. | |||
*/ | |||
void alcd_set_columns (alcd_t *alcd, int columns) { | |||
alcd->columns = columns; | |||
} | |||
/* | |||
* User Functions | |||
*/ | |||
/*! | |||
* \brief | |||
* De-initialize the alcd. | |||
* \param alcd pointer to active alcd. | |||
* \return none | |||
*/ | |||
void alcd_deinit (alcd_t *alcd) | |||
{ | |||
memset ((void*)alcd, 0, sizeof (alcd_t)); | |||
/*!< | |||
* This leaves the status DRV_NOINIT | |||
*/ | |||
} | |||
/*! | |||
* \brief | |||
* Initialize the alcd. | |||
* \param alcd pointer to active alcd. | |||
* \return Zero on success, non zero on error | |||
*/ | |||
drv_status_en alcd_init (alcd_t *alcd, alcd_funset_en fs) | |||
{ | |||
#define _lcd_assert(_x) if (!_x) return alcd->status = DRV_ERROR; | |||
drv_status_en st = jf_probe (); | |||
if (st == DRV_NODEV || st == DRV_BUSY) | |||
return alcd->status = DRV_ERROR; | |||
_lcd_assert (alcd->io.db4); | |||
_lcd_assert (alcd->io.db5); | |||
_lcd_assert (alcd->io.db6); | |||
_lcd_assert (alcd->io.db7); | |||
_lcd_assert (alcd->io.rs); | |||
_lcd_assert (alcd->io.en); | |||
//_lcd_assert (alcd->io.bl); | |||
/* | |||
* We are here, so all its OK. We can (re)initialize alcd. | |||
*/ | |||
alcd->status = DRV_NOINIT; | |||
alcd->c.x = alcd->c.y = 1; | |||
alcd->io.en (0); | |||
alcd->io.rs (0); | |||
jf_delay_us (100000); | |||
//Pre-Init phase 8bit at this point | |||
_set_bus (alcd, 0x3); | |||
jf_delay_us(50000); | |||
_set_bus (alcd, 0x3); | |||
jf_delay_us(5000); | |||
_set_bus (alcd, 0x3); | |||
jf_delay_us(5000); | |||
_set_bus (alcd, 0x2); //4bit selection | |||
jf_delay_us(10000); | |||
_command (alcd, fs); //4bit selection and Function Set | |||
jf_delay_us(5000); | |||
_command (alcd, LCD_DISP_OFF); //Display Off Control 4bit for now on | |||
jf_delay_us(5000); | |||
_command (alcd, LCD_CLRSCR); //Clear Display | |||
jf_delay_us(5000); | |||
_command (alcd, LCD_ENTRYMODE); //Entry Mode Set | |||
jf_delay_us(5000); | |||
_command (alcd, LCD_RETHOME); | |||
jf_delay_us(10000); | |||
_command (alcd, LCD_DISP_ON); | |||
jf_delay_us(5000); | |||
//alcd_backlight (alcd, 1); | |||
return alcd->status = DRV_READY; | |||
#undef _lcd_assert | |||
} | |||
/*! | |||
* \brief | |||
* Enables and disables the lcd backlight. | |||
* \param alcd pointer to active alcd. | |||
* \param on | |||
* \arg 0 disable the backlight | |||
* \arg 1 enable the backlight | |||
* \return none | |||
*/ | |||
void alcd_backlight (alcd_t *alcd, uint8_t on) { | |||
if (alcd->io.bl) | |||
alcd->io.bl ((on)?1:0); | |||
} | |||
/*! | |||
* \brief | |||
* Enables and disables the entire lcd (+backlight). | |||
* \param alcd pointer to active alcd. | |||
* \param on | |||
* \arg 0 disable the backlight | |||
* \arg 1 enable the backlight | |||
* \return none | |||
*/ | |||
void alcd_enable (alcd_t *alcd, uint8_t on) | |||
{ | |||
if (on) { | |||
_command (alcd, LCD_DISP_ON); | |||
alcd_backlight (alcd, 1); | |||
} else { | |||
_command (alcd, LCD_DISP_OFF); | |||
alcd_backlight (alcd, 0); | |||
} | |||
} | |||
/*! | |||
* \brief | |||
* Clears screen and returns cursor at home position (1,1). | |||
* \param alcd pointer to active alcd. | |||
* \return none | |||
*/ | |||
void alcd_cls (alcd_t *alcd) | |||
{ | |||
_command(alcd, LCD_CLRSCR); | |||
jf_delay_us(2000); | |||
_command (alcd, LCD_RETHOME); | |||
jf_delay_us(2000); | |||
} | |||
/*! | |||
* \brief | |||
* Shift alcd left or right for a \a pos characters. | |||
* \param alcd pointer to active alcd. | |||
* \param pos The number of position to shift. | |||
* A positive number shifts lcd data to left, so screen shows the data in the right. | |||
* A negative number shifts lcd data to right, so screen shows the data in the left. | |||
* \return none | |||
*/ | |||
void alcd_shift (alcd_t *alcd, int pos) | |||
{ | |||
uint8_t i, cmd = LCD_SHIFT_LEFT; | |||
if (pos<0) { | |||
pos = -pos; | |||
cmd = LCD_SHIFT_RIGHT; | |||
} | |||
for (i=0 ; i<pos ; ++i) { | |||
_command (alcd, cmd); | |||
jf_delay_us(100); | |||
} | |||
} | |||
// Allows us to fill the first 8 CGRAM locations | |||
// with custom characters | |||
void alcd_createChar (alcd_t *alcd, uint8_t location, uint8_t charmap[]) | |||
{ | |||
location &= 0x7; // we only have 8 locations 0-7 | |||
_command (alcd, LCD_SETCGRAMADDR | (location << 3)); | |||
for (int i=0; i<8; i++) { | |||
_character (alcd, charmap[i]); | |||
} | |||
} | |||
@@ -0,0 +1,189 @@ | |||
/*! | |||
* \file alcd.c | |||
* \brief | |||
* A target independent Alpharithmetic LCD driver | |||
* | |||
* Copyright (C) 2014 Christos Choutouridis (http://www.houtouridis.net) | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#ifndef __alcd_h__ | |||
#define __alcd_h__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#include "jiffies.h" | |||
#include "driver_types.h" | |||
#include <stdint.h> | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include <time.h> | |||
/* | |||
* General Defines | |||
*/ | |||
#define ALCD_CLOCK (CLOCK) | |||
/*! | |||
* ---------------------------------------------------- | |||
* Hitachi HD44780 - Samsung KS0066U | |||
* ---------------------------------------------------- | |||
* | |||
* | |||
* Entry Mode Set -----> 0 0 0 0 0 1 I/D S | |||
* ---------------------------------------------------- | |||
* I/D = 1 -->Inciment Curs I/D = 0 Decriment | |||
* S = 1 -->Display shift S = 0 Not | |||
* | |||
* | |||
* DispOnOffControll --> 0 0 0 0 1 D C B | |||
* ------------------------------------------------- | |||
* D = Display On | |||
* C = Cursor On | |||
* B = Blinking On | |||
* | |||
* | |||
* Cursor/Display Shift --> 0 0 0 1 S/C R/L x x | |||
* --------------------------------------------------- | |||
* S/C = 1 -->Display Shift S/C = 0 -->Cursor Shift | |||
* R/L = 1 -->Shift Right R/L = 0 -->Shift left | |||
* | |||
* | |||
* FunctionSet ------> 0 0 1 DL N F x x | |||
* --------------------------------------------------- | |||
* DL = 1 -->8bit DL = 0 -->4bit | |||
* N = 1 -->2 lines N = 0 -->1 line | |||
* F = 1 -->5x10 dots F = 0 -->5x8 dots | |||
* | |||
*/ | |||
//#define LCD_LINES (4) | |||
//#define LCD_ROWS (16) | |||
#define LCD_CLRSCR (0x01) /*!< Clear Srean Command Number */ | |||
#define LCD_RETHOME (0x02) /*!< Cursor home Un-Shift display DDRam as is */ | |||
#define LCD_ENTRYMODE (0x06) /*!< Inc Cursor, Don't shift display */ | |||
#define LCD_DISP_ON (0x0C) /*!< No Cursos and Blink */ | |||
#define LCD_DISP_OFF (0x08) | |||
#define LCD_CUR_DISP (0x14) /*!< Cursor shift right */ | |||
#define LCD_FUNSET_2L8 (0x28) /*!< 4bit, 2lines, 5x8 dots */ | |||
#define LCD_FUNSET_1L8 (0x20) /*!< 4bit, 1lines, 5x8 dots */ | |||
#define LCD_FUNSET_1L10 (0x24) /*!< 4bit, 1lines, 5x10 dots */ | |||
#define LCD_SETCGRAMADDR (0x40) | |||
#define LCD_DDRAMMask (0x80) /*!< DDRAM ------------> 1 ADD[7..0] */ | |||
#define LCD_BFMask (0x80) /*!< IR ------------> BF AC[6..0] */ | |||
#define LCD_ACMask (0x7f) /*!< ____________________________| */ | |||
#define LCD_SHIFT_RIGHT (0x1C) | |||
#define LCD_SHIFT_LEFT (0x18) | |||
typedef enum { | |||
ALCD_1Line_5x8 = LCD_FUNSET_1L8, | |||
ALCD_1Line_5x10 = LCD_FUNSET_1L10, | |||
ALCD_2Lines_5x8 = LCD_FUNSET_2L8 | |||
} alcd_funset_en; | |||
/*! | |||
* Alpharithmetic LCD Cursor | |||
*/ | |||
typedef volatile struct | |||
{ | |||
uint8_t x; | |||
uint8_t y; | |||
}alcd_cursor_t; | |||
/*! | |||
* Alpharithmetic LCD Pin assignements. | |||
* Each one can be called xx.DB4(1); or xx.DB4(0); in order to set | |||
* or clear the corresponding pin. | |||
* | |||
* \note These pointers MUST to be assigned from main application. | |||
*/ | |||
typedef volatile struct | |||
{ | |||
//drv_pinout_ft db0; | |||
//drv_pinout_ft db1; | |||
//drv_pinout_ft db2; | |||
//drv_pinout_ft db3; | |||
drv_pinout_ft db4; /*!< Pointer for DB4 pin */ | |||
drv_pinout_ft db5; /*!< Pointer for DB5 pin */ | |||
drv_pinout_ft db6; /*!< Pointer for DB6 pin */ | |||
drv_pinout_ft db7; /*!< Pointer for DB7 pin */ | |||
drv_pinout_ft rs; /*!< Pointer for RS pin */ | |||
drv_pinout_ft en; /*!< Pointer for EN pin */ | |||
drv_pinout_ft bl; /*!< Pointer for Back Light pin*/ | |||
}alcd_io_t; | |||
/*! | |||
* Alpharithmetic LCD Public Data struct | |||
*/ | |||
typedef volatile struct | |||
{ | |||
alcd_io_t io; //!< Link to IO struct | |||
alcd_cursor_t c; //!< Link to Cursor struct | |||
uint8_t lines; //!< The lines of attached lcd | |||
uint8_t columns; //!< The columns of attached lcd | |||
//uint8_t bus; //!< Bus length, 4 or 8 bit | |||
drv_status_en status; //!< alcd driver status | |||
}alcd_t; | |||
/* | |||
* ============= PUBLIC ALCD API ============= | |||
*/ | |||
/* | |||
* Link and Glue functions | |||
*/ | |||
void alcd_link_db4 (alcd_t *alcd, drv_pinout_ft pfun); | |||
void alcd_link_db5 (alcd_t *alcd, drv_pinout_ft pfun); | |||
void alcd_link_db6 (alcd_t *alcd, drv_pinout_ft pfun); | |||
void alcd_link_db7 (alcd_t *alcd, drv_pinout_ft pfun); | |||
void alcd_link_rs (alcd_t *alcd, drv_pinout_ft pfun); | |||
void alcd_link_en (alcd_t *alcd, drv_pinout_ft pfun); | |||
void alcd_link_bl (alcd_t *alcd, drv_pinout_ft pfun); | |||
int alcd_putchar (alcd_t *alcd, int ch); | |||
/* | |||
* Set functions | |||
*/ | |||
void alcd_set_lines (alcd_t *alcd, int lines); | |||
void alcd_set_columns (alcd_t *alcd, int columns); | |||
/* | |||
* User Functions | |||
*/ | |||
void alcd_deinit (alcd_t *alcd); /*!< For compatibility */ | |||
drv_status_en alcd_init (alcd_t *alcd, alcd_funset_en fs); /*!< For compatibility */ | |||
void alcd_backlight (alcd_t *alcd, uint8_t on); /*!< For compatibility */ | |||
void alcd_enable (alcd_t *alcd, uint8_t on); /*!< For compatibility */ | |||
void alcd_cls (alcd_t *alcd); /*!< For compatibility */ | |||
void alcd_shift (alcd_t *alcd, int pos); /*!< For compatibility */ | |||
void alcd_createChar (alcd_t *alcd, uint8_t location, uint8_t charmap[]); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif //#ifndef __alcd_h__ |
@@ -0,0 +1,223 @@ | |||
/*! | |||
* \file deque08.c | |||
* \brief | |||
* This file provides double ended queue capability based on a ring buffer | |||
* | |||
* Copyright (C) 2014 Houtouridis Christos <houtouridis.ch@gmail.com> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#include "deque08.h" | |||
/* | |||
* ============= Private Queue API ============= | |||
*/ | |||
static iterator_t _preInc(deque08_t *q, iterator_t* it) { | |||
return (++*it >= q->capacity) ? *it=0 : *it; | |||
} | |||
static iterator_t _preDec(deque08_t *q, iterator_t* it) { | |||
return (--*it < 0) ? *it=q->capacity-1 : *it; | |||
} | |||
static iterator_t _postInc(deque08_t *q, iterator_t* it) { | |||
iterator_t ret = *it; | |||
if (++*it >= q->capacity) *it=0; | |||
return ret; | |||
} | |||
static iterator_t _postDec(deque08_t *q, iterator_t* it) { | |||
iterator_t ret = *it; | |||
if (--*it < 0) *it=q->capacity-1; | |||
return ret; | |||
} | |||
/* | |||
* ============= Public Queue API ============= | |||
*/ | |||
/* | |||
* Link and Glue functions | |||
*/ | |||
void deque08_link_buffer (deque08_t *q, byte_t* buf) { | |||
q->m = buf; | |||
} | |||
/* | |||
* Set functions | |||
*/ | |||
inline void deque08_set_capacity (deque08_t *q, size_t capacity) { | |||
q->capacity = capacity; | |||
} | |||
/* | |||
* User Functions | |||
*/ | |||
/*! | |||
* \brief | |||
* Check if deque is full | |||
* \param q Which deque to check | |||
* \return | |||
* \arg 0 Not full | |||
* \arg 1 Full | |||
*/ | |||
int deque08_is_full (deque08_t *q) { | |||
return (q->items == q->capacity) ? 1:0; | |||
} | |||
/*! | |||
* \brief | |||
* Check if deque is empty | |||
* \param q Which deque to check | |||
* \return | |||
* \arg 0 Not empty | |||
* \arg 1 Empty | |||
*/ | |||
int deque08_is_empty (deque08_t *q) { | |||
return (q->items) ? 0:1; | |||
} | |||
/*! | |||
* \brief | |||
* Return the number of items on deque | |||
* \param q Which deque to check | |||
*/ | |||
int deque08_size (deque08_t *q) { | |||
return q->items; | |||
} | |||
/*! | |||
* \brief | |||
* Discard all items in deque | |||
* \param q Which deque to check | |||
*/ | |||
void deque08_flush (deque08_t *q) { | |||
deque08_init(q); | |||
} | |||
/*! | |||
* \brief | |||
* Initialize the queue | |||
* \param queue Which queue to init | |||
*/ | |||
void deque08_init (deque08_t *q) { | |||
q->f = 0; | |||
q->r = -1; | |||
q->items =0; | |||
} | |||
/*! | |||
* \brief | |||
* This function push a byte in front of deque. | |||
* \param q Pointer to deque to use | |||
* \param b byte to push | |||
* \return | |||
* \arg 0 Full queue | |||
* \arg 1 Done | |||
*/ | |||
int deque08_push_front (deque08_t *q, byte_t b) { | |||
if (deque08_is_full (q) == 1) //full queue | |||
return 0; | |||
q->m [_preDec (q, &q->f)] = b; | |||
++q->items; | |||
return 1; | |||
} | |||
/*! | |||
* \brief | |||
* This function pops a byte from the front of the deque. | |||
* \param q Pointer to deque to use | |||
* \param b Pointer to byte to return | |||
* \return | |||
* \arg 0 Empty queue | |||
* \arg 1 Done | |||
*/ | |||
int deque08_pop_front (deque08_t *q, byte_t *b) { | |||
if (deque08_is_empty (q) == 1) //empty queue | |||
return 0; | |||
*b = q->m [_postInc (q, &q->f)]; | |||
--q->items; | |||
return 1; | |||
} | |||
/*! | |||
* \brief | |||
* This function push a byte in the back of deque. | |||
* \param q Pointer to deque to use | |||
* \param b byte to push | |||
* \return | |||
* \arg 0 Full queue | |||
* \arg 1 Done | |||
*/ | |||
int deque08_push_back (deque08_t *q, byte_t b) { | |||
if (deque08_is_full (q) == 1) //full queue | |||
return 0; | |||
q->m [_preInc (q, &q->r)] = b; | |||
++q->items; | |||
return 1; | |||
} | |||
/*! | |||
* \brief | |||
* This function pops a byte from the back of the deque. | |||
* \param q Pointer to deque to use | |||
* \param b Pointer to byte to return | |||
* \return | |||
* \arg 0 Empty queue | |||
* \arg 1 Done | |||
*/ | |||
int deque08_pop_back (deque08_t *q, byte_t *b) { | |||
if (deque08_is_empty (q) == 1) //empty queue | |||
return 0; | |||
*b = q->m [_postDec (q, &q->r)]; | |||
--q->items; | |||
return 1; | |||
} | |||
/*! | |||
* \brief | |||
* This function gives the last item in the back of deque. | |||
* \param q Pointer to deque to use | |||
* \param b Pointer to byte to return | |||
* \return | |||
* \arg 0 Empty queue | |||
* \arg 1 Done | |||
*/ | |||
int deque08_back (deque08_t *q, byte_t *b) { | |||
if (deque08_is_empty (q) == 1) //empty queue | |||
return 0; | |||
*b = q->m [q->r]; | |||
return 1; | |||
} | |||
/*! | |||
* \brief | |||
* This function gives the first item in the front of deque. | |||
* \param q Pointer to deque to use | |||
* \param b Pointer to byte to return | |||
* \return | |||
* \arg 0 Empty queue | |||
* \arg 1 Done | |||
*/ | |||
int deque08_front (deque08_t *q, byte_t *b) { | |||
if (deque08_is_empty (q) == 1) //empty queue | |||
return 0; | |||
*b = q->m [q->f]; | |||
return 1; | |||
} |
@@ -0,0 +1,77 @@ | |||
/*! | |||
* \file deque08.h | |||
* \brief | |||
* This file provides double ended queue capability based on a ring buffer | |||
* | |||
* Copyright (C) 2014 Houtouridis Christos <houtouridis.ch@gmail.com> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#ifndef __deque08_h__ | |||
#define __deque08_h__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#include "driver_types.h" | |||
#include <string.h> | |||
typedef struct { | |||
byte_t *m; /*!< pointer to queue's buffer */ | |||
iterator_t capacity; /*!< queue's max item capacity */ | |||
iterator_t items; /*!< current item count */ | |||
iterator_t f, r; /*!< queue iterators */ | |||
}deque08_t; | |||
/* | |||
* ============= PUBLIC EE API ============= | |||
*/ | |||
/* | |||
* Link and Glue functions | |||
*/ | |||
void deque08_link_buffer (deque08_t *q, byte_t* buf); | |||
/* | |||
* Set functions | |||
*/ | |||
void deque08_set_capacity (deque08_t *q, size_t capacity); | |||
/* | |||
* User Functions | |||
*/ | |||
int deque08_is_full (deque08_t *q); | |||
int deque08_is_empty (deque08_t *q); | |||
int deque08_size (deque08_t *q); | |||
void deque08_flush (deque08_t *q); | |||
void deque08_init (deque08_t *q); | |||
int deque08_push_front (deque08_t *q, byte_t b); | |||
int deque08_pop_front (deque08_t *q, byte_t *b); | |||
int deque08_push_back (deque08_t *q, byte_t b); | |||
int deque08_pop_back (deque08_t *q, byte_t *b); | |||
int deque08_back (deque08_t *q, byte_t *b); | |||
int deque08_front (deque08_t *q, byte_t *b); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif //#ifndef __deque08_h__ |
@@ -0,0 +1,76 @@ | |||
/*! | |||
* \file driver_types.h | |||
* | |||
* Copyright (C) 2020 Choutouridis Christos <cchoutou@ece.auth.gr> | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#ifndef DRIVERS_DRIVER_TYPES_H_ | |||
#define DRIVERS_DRIVER_TYPES_H_ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#include <stdint.h> | |||
typedef uint8_t byte_t; /*!< 8 bits wide */ | |||
typedef uint16_t word_t; /*!< 16 bits wide */ | |||
typedef uint32_t dword_t; /*!< 32 bits wide */ | |||
typedef int32_t iterator_t; /*!< general iterator type */ | |||
/*! | |||
* This is a driver wide generic driver status type. | |||
* \note | |||
* DRV_NOINIT = 0, so after memset to zero called by XXXX_deinit() the | |||
* module/device will automatically set to NOINIT state. | |||
*/ | |||
typedef enum { | |||
DRV_NODEV=-1, /*!< No device/module */ //!< DRV_NODEV | |||
DRV_NOINIT=0, /*!< Module/Device exist but no initialized *///!< DRV_NOINIT | |||
DRV_READY, /*!< Module/Device initialized succesfully */ //!< DRV_READY | |||
DRV_BUSY, /*!< Module/Device busy */ //!< DRV_BUSY | |||
//DRV_COMPLETE, /*!< Module/device operation complete status */ | |||
DRV_ERROR /*!< Module/Device error */ //!< DRV_ERROR | |||
}drv_status_en; | |||
typedef enum { | |||
drv_pin_disable = 0, | |||
drv_pin_input, | |||
drv_pin_output | |||
}drv_pin_dir_en; | |||
/*! | |||
* Pin function pointers | |||
* \note | |||
* These function pointers do not correspond to pin levels. | |||
* They correspond to the enable/disable functionality of that pin. | |||
*/ | |||
//! @{ | |||
typedef uint8_t (*drv_pinin_ft) (void); | |||
typedef void (*drv_pinout_ft) (uint8_t); | |||
typedef uint8_t (*drv_pinio_ft) (uint8_t); | |||
typedef void (*drv_pindir_ft) (drv_pin_dir_en); | |||
//! @} | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* DRIVERS_DRIVER_TYPES_H_ */ |
@@ -0,0 +1,209 @@ | |||
/*! | |||
* \file hal.c | |||
* | |||
* Copyright (C) 2020 Choutouridis Christos (http://www.houtouridis.net) | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#include "hal.h" | |||
/* | |||
* Public data types / classes | |||
*/ | |||
alcd_t alcd; | |||
ow_uart_t ow; | |||
proximity_t prox; | |||
/*! | |||
* Initialize all hardware | |||
* @return | |||
*/ | |||
int hal_hw_init (void) { | |||
// Init base board | |||
NUCLEO_Init(1000); | |||
SHIELD_Init (); | |||
// Start Jiffy functionality | |||
jf_link_setfreq (JF_setfreq); | |||
jf_link_value ((jiffy_t*)&JF_TIM_VALUE); | |||
jf_init (JF_TIM_CLOCK_FREQ, 1000); | |||
return 0; | |||
} | |||
/* | |||
* ========== LCD =========== | |||
*/ | |||
/*! | |||
* \brief | |||
* Initialize the lcd data. Not the LCD module | |||
*/ | |||
void lcd_init (void) { | |||
alcd_link_db4 (&alcd, SHIELD_LCD_DB4); | |||
alcd_link_db5 (&alcd, SHIELD_LCD_DB5); | |||
alcd_link_db6 (&alcd, SHIELD_LCD_DB6); | |||
alcd_link_db7 (&alcd, SHIELD_LCD_DB7); | |||
alcd_link_rs (&alcd, SHIELD_LCD_RS); | |||
alcd_link_en (&alcd, SHIELD_LCD_EN); | |||
alcd_link_bl (&alcd, SHIELD_LCD_BL); | |||
alcd_set_lines (&alcd, 2); | |||
alcd_set_columns (&alcd, 16); | |||
alcd_init (&alcd, (alcd_funset_en)LCD_FUNSET_2L8); | |||
} | |||
__INLINE void lcd_enable (uint8_t en) { | |||
alcd_enable(&alcd, en); | |||
} | |||
__INLINE int lcd_putchar (char c) { | |||
return alcd_putchar(&alcd, c); | |||
} | |||
int lcd_puts (const char *s) { | |||
int i =0; | |||
while (alcd_putchar(&alcd, *s++)) | |||
++i; | |||
return i; | |||
} | |||
/* | |||
* ========== Led interface ========== | |||
*/ | |||
__INLINE void led_red (uint8_t en) { LED_RED (en); } | |||
__INLINE void led_green (uint8_t en) { LED_GREEN (en); } | |||
/* | |||
* ========= Relay =========== | |||
*/ | |||
void relay (uint8_t on) { | |||
switch (on) { | |||
case 0: | |||
SHIELD_BR1(1); | |||
HAL_Delay(100); | |||
SHIELD_BR1(0); | |||
break; | |||
default: | |||
SHIELD_BR2(1); | |||
HAL_Delay(100); | |||
SHIELD_BR2(0); | |||
break; | |||
} | |||
} | |||
/* | |||
* ========= Temperature =========== | |||
*/ | |||
uint8_t ds18b20_rom[8]; | |||
uint8_t zero_rom[8] = {0}; | |||
int temp_init (uint8_t resolution) { | |||
if (!IS_TEMP_RESOLUTION(resolution)) | |||
return 1; | |||
ow_uart_link_rw (&ow, (ow_uart_rw_ft)SHIELD_1W_RW); | |||
ow_uart_link_br (&ow, (ow_uart_br_ft)SHIELD_1W_UART_BR); | |||
ow_uart_set_timing (&ow, OW_UART_T_STANDARD); | |||
ow_uart_init (&ow); | |||
ow_uart_search(&ow, ds18b20_rom); | |||
if (!memcmp ((const void*)ds18b20_rom, (const void*)zero_rom, 8)) | |||
return 1; | |||
// set resolution to 9-bit | |||
ow_uart_reset(&ow); | |||
ow_uart_tx (&ow, SKIPROM); // Skip rom | |||
ow_uart_tx (&ow, WRITESCRATCH); // write scratchpad | |||
ow_uart_tx (&ow, (byte_t)127); // Th | |||
ow_uart_tx (&ow, (byte_t)-128); // Tl | |||
ow_uart_tx (&ow, resolution); // Configuration 9 bit | |||
HAL_Delay(100); | |||
return 0; | |||
} | |||
float temp_read (void) { | |||
uint8_t t[2]; | |||
ow_uart_reset(&ow); | |||
ow_uart_tx (&ow, SKIPROM); // Skip rom | |||
ow_uart_tx (&ow, STARTCONV); // convert temperature | |||
while (ow_uart_rx(&ow) == 0) // Wait for slave to free the bus | |||
; | |||
//HAL_Delay (100); | |||
ow_uart_reset(&ow); | |||
ow_uart_tx (&ow, SKIPROM); // Skip rom | |||
ow_uart_tx (&ow, READSCRATCH); // read scratchpad | |||
t[0] = ow_uart_rx(&ow); // LSB | |||
t[1] = ow_uart_rx(&ow); // MSB | |||
ow_uart_reset(&ow); | |||
t[1] <<= 4; | |||
t[1] |= (t[0] >> 4); | |||
t[0] &= 0x0F; | |||
return t[1] + t[0]/16.0; | |||
} | |||
/* | |||
* ========= Proximity =========== | |||
*/ | |||
void proximity_init (proximity_t* p){ | |||
for (int i =0 ; i<PROX_READINGS ; ++i) | |||
proximity(p); | |||
} | |||
float_t proximity (proximity_t* p){ | |||
float_t ret; | |||
clock_t t1, t2, mark; | |||
SHIELD_TRIG(1); // send pulse and mark cycles | |||
jf_delay_us(10); | |||
SHIELD_TRIG(0); | |||
// wait for response with timeout | |||
mark = clock(); | |||
do { | |||
t1 = CYCLE_Get(); | |||
if (clock() - mark >= PROX_TIME_MAX) | |||
return -1; | |||
} while (!SHIELD_ECHO()); | |||
mark = clock(); | |||
do { | |||
t2 = CYCLE_Get(); | |||
if (clock() - mark >= PROX_TIME_MAX) | |||
return -1; | |||
} while (SHIELD_ECHO()); | |||
SystemCoreClockUpdate(); | |||
uint32_t c_usec = SystemCoreClock / 1000000; | |||
// Load value | |||
p->readings[p->iter++] = (c_usec) ? ((t2 - t1)/c_usec) / 58.2 : PROX_MAX_DISTANSE; | |||
p->iter %= PROX_READINGS; | |||
// Return filtered distance | |||
ret =0; | |||
for (int i=0 ; i<PROX_READINGS ; ++i) | |||
ret += p->readings[i]; | |||
return ret/PROX_READINGS; | |||
} | |||
@@ -0,0 +1,109 @@ | |||
/*! | |||
* \file hal.h | |||
* | |||
* Copyright (C) 2020 Choutouridis Christos (http://www.houtouridis.net) | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#ifndef DRIVERS_HAL_H_ | |||
#define DRIVERS_HAL_H_ | |||
#include <thermostat_shield.h> | |||
#include <alcd.h> | |||
#include <jiffies.h> | |||
#include <onewire_uart.h> | |||
#include <deque08.h> | |||
#include <thermostat.h> | |||
#include <stdio.h> | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
int hal_hw_init (void); | |||
/* | |||
* ========== LCD =========== | |||
*/ | |||
void lcd_init (void); | |||
void lcd_enable (uint8_t en) ; | |||
int lcd_putchar (char c); | |||
int lcd_puts (const char *s); | |||
/* | |||
* ========== Led interface ========== | |||
*/ | |||
void led_red (uint8_t en); | |||
void led_green (uint8_t en); | |||
/* | |||
* ========= Relay =========== | |||
*/ | |||
void relay(uint8_t on); | |||
/* | |||
* ========= Temperature =========== | |||
*/ | |||
// OneWire commands | |||
#define SKIPROM 0xCC // Skip ROM matching transition | |||
#define STARTCONV 0x44 // Tells device to take a temperature reading and put it on the scratchpad | |||
#define COPYSCRATCH 0x48 // Copy EEPROM | |||
#define READSCRATCH 0xBE // Read EEPROM | |||
#define WRITESCRATCH 0x4E // Write to EEPROM | |||
#define RECALLSCRATCH 0xB8 // Reload from last known | |||
#define READPOWERSUPPLY 0xB4 // Determine if device needs parasite power | |||
#define ALARMSEARCH 0xEC // Query bus for devices with an alarm condition | |||
// Device resolution | |||
#define TEMP_9_BIT 0x1F // 9 bit | |||
#define TEMP_10_BIT 0x3F // 10 bit | |||
#define TEMP_11_BIT 0x5F // 11 bit | |||
#define TEMP_12_BIT 0x7F // 12 bit | |||
#define IS_TEMP_RESOLUTION(x) \ | |||
((x == TEMP_9_BIT) || (x == TEMP_10_BIT) || (x == TEMP_11_BIT) || (x == TEMP_12_BIT)) | |||
int temp_init (uint8_t resolution); | |||
float temp_read (void); | |||
/* | |||
* ========= Proximity =========== | |||
*/ | |||
#define PROX_TIME_MAX 25 // [msec] | |||
#define PROX_READINGS 7 // How many readings for averaging | |||
#define PROX_MAX_DISTANSE 450 // [cm] | |||
typedef struct { | |||
float_t readings[7]; | |||
int iter; | |||
} proximity_t; | |||
void proximity_init (proximity_t* p); | |||
float_t proximity (proximity_t* p); | |||
/* | |||
* Public data types / classes | |||
*/ | |||
extern alcd_t alcd; | |||
extern ow_uart_t ow; | |||
extern proximity_t prox; | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* DRIVERS_HAL_H_ */ |
@@ -0,0 +1,367 @@ | |||
/* | |||
* \file jiffies.c | |||
* \brief | |||
* A target independent jiffy functionality | |||
* | |||
* Copyright (C) 2014 Houtouridis Christos (http://www.houtouridis.net) | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Lesser General Public License as | |||
* published by the Free Software Foundation, either version 3 | |||
* of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
#include "jiffies.h" | |||
static jf_t _jf; | |||
#define JF_MAX_TIM_VALUE (0xFFFF) // 16bit counters | |||
/* | |||
* ====================== Public functions ====================== | |||
*/ | |||
/* | |||
* Link and Glue functions | |||
*/ | |||
/*! | |||
* \brief | |||
* Connect the Driver's Set frequency function to jiffy struct | |||
* \note | |||
* This function get a freq value and returns the timers max jiffy value | |||
* (usual this refers to timer'sauto reload value). | |||
*/ | |||
void jf_link_setfreq (jf_setfreq_pt pfun) { | |||
_jf.setfreq = (pfun != 0) ? pfun : 0; | |||
} | |||
/*! | |||
* \brief | |||
* Connect the timer's value to jiffy struct | |||
*/ | |||
void jf_link_value (jiffy_t* v) { | |||
_jf.value = (v != 0) ? v : 0; | |||
} | |||
/* | |||
* User Functions | |||
*/ | |||
/*! | |||
* \brief | |||
* Check jiffy's status | |||
* \return status | |||
*/ | |||
inline drv_status_en jf_probe (void) { | |||
return _jf.status; | |||
} | |||
/*! | |||
* \brief | |||
* De-Initialize the jf data and un-connect the functions | |||
* from the driver | |||
*/ | |||
void jf_deinit (void) | |||
{ | |||
if (_jf.setfreq) _jf.setfreq (0, 0); | |||
memset ((void*)&_jf, 0, sizeof (jf_t)); | |||
_jf.status = DRV_NODEV; | |||
} | |||
/*! | |||
* \brief | |||
* Initialize the jf to a desired jiffy frequency | |||
* \note | |||
* This function has no effect if the inner jiffy struct | |||
* is un-connected to driver. So you have to call | |||
* \sa jf_connect_setfreq() and \sa jf_connect_value() first. | |||
* \return The status of the operation | |||
* \arg DRV_ERROR If the init process fail | |||
* \arg DRV_NODEV If there is no linked jiffy HW, no setfreq function | |||
* \arg DRV_READY Success | |||
*/ | |||
drv_status_en jf_init (uint32_t jf_freq, jiffy_t jiffies) | |||
{ | |||
if (_jf.setfreq) { | |||
_jf.status = DRV_NOINIT; | |||
if ( _jf.setfreq (jf_freq, jiffies) ) | |||
return DRV_ERROR; | |||
_jf.jiffies = jiffies; | |||
_jf.freq = jf_freq; | |||
_jf.jp1ms = jf_per_msec (); | |||
_jf.jp1us = jf_per_usec (); | |||
_jf.jp100ns = jf_per_100nsec (); | |||
return _jf.status = DRV_READY; | |||
} | |||
return _jf.status = DRV_NODEV; | |||
} | |||
/*! | |||
* \brief | |||
* Return the maximum jiffy value. | |||
*/ | |||
jiffy_t jf_get_jiffies (void){ | |||
return _jf.jiffies; | |||
} | |||
/*! | |||
* \brief | |||
* Return the current jiffy value. | |||
* \note | |||
* Usual this function returns the value of a register from a timer peripheral | |||
* in the MCU. Keep in mind that its value is a moving target! | |||
*/ | |||
jiffy_t jf_get_jiffy (void){ | |||
return *_jf.value; | |||
} | |||
/*! | |||
* \brief | |||
* Return the systems best approximation for jiffies per msec | |||
* \return | |||
* The calculated value or zero if no calculation can apply | |||
* | |||
* \note | |||
* The result tend to differ as the jiffies and freq values decreasing | |||
*/ | |||
jiffy_t jf_per_msec (void) | |||
{ | |||
jiffy_t jf = (jiffy_t)(_jf.freq / 1000); | |||
/* 1 | |||
* 1000Hz = ----- , Its not a magic number | |||
* 1msec | |||
*/ | |||
if (jf <= 1) return 1; | |||
else return jf; | |||
} | |||
/*! | |||
* \brief | |||
* Return the systems best approximation for jiffies per usec | |||
* \return | |||
* The calculated value or zero if no calculation can apply | |||
* | |||
* \note | |||
* The result tend to differ as the jiffies and freq values decreasing | |||
*/ | |||
jiffy_t jf_per_usec (void) | |||
{ | |||
jiffy_t jf = (jiffy_t)(_jf.freq / 1000000); | |||
/* 1 | |||
* 1000000Hz = ------ , Its not a magic number | |||
* 1usec | |||
*/ | |||
if (jf <= 1) return 1; | |||
else return jf; | |||
} | |||
/*! | |||
* \brief | |||
* Return the systems best approximation for jiffies per usec | |||
* \return | |||
* The calculated value or zero if no calculation can apply | |||
* | |||
* \note | |||
* The result tend to differ as the jiffies and freq values decreasing | |||
*/ | |||
jiffy_t jf_per_100nsec (void) | |||
{ | |||
jiffy_t jf = (jiffy_t)(_jf.freq / 10000000); | |||
/* 1 | |||
* 10000000Hz = ------- , Its not a magic number | |||
* 100nsec | |||
*/ | |||
if (jf <= 1) return 1; | |||
else return jf; | |||
} | |||
/*! | |||
* \brief | |||
* A code based delay implementation, using jiffies for timing. | |||
* This is NOT accurate but it ensures that the time passed is always | |||
* more than the requested value. | |||
* The delay values are multiplications of 1 msec. | |||
* \param msec Time in msec for delay | |||
*/ | |||
void jf_delay_ms (jtime_t msec) | |||
{ | |||
jtime_t m, m2, m1 = (jtime_t)*_jf.value; | |||
msec *= _jf.jp1ms; | |||
// Eat the time difference from msec value. | |||
do { | |||
m2 = (jtime_t)(*_jf.value); | |||
m = m2 - m1; | |||
msec -= (m>=0) ? m : _jf.jiffies + m; | |||
m1 = m2; | |||
} while (msec>0); | |||
} | |||
/*! | |||
* \brief | |||
* A code based delay implementation, using jiffies for timing. | |||
* This is NOT accurate but it ensures that the time passed is always | |||
* more than the requested value. | |||
* The delay values are multiplications of 1 usec. | |||
* \param usec Time in usec for delay | |||
*/ | |||
void jf_delay_us (jtime_t usec) | |||
{ | |||
jtime_t m, m2, m1 = (jtime_t)*_jf.value; | |||
usec *= _jf.jp1us; | |||
if ((jtime_t)(*_jf.value) - m1 > usec) // Very small delays may return here. | |||
return; | |||
// Eat the time difference from usec value. | |||
do { | |||
m2 = (jtime_t)(*_jf.value); | |||
m = m2 - m1; | |||
usec -= (m>=0) ? m : _jf.jiffies + m; | |||
m1 = m2; | |||
} while (usec>0); | |||
} | |||
/*! | |||
* \brief | |||
* A code based delay implementation, using jiffies for timing. | |||
* This is NOT accurate but it ensures that the time passed is always | |||
* more than the requested value. | |||
* The delay values are multiplications of 100 nsec. | |||
* \param _100nsec Time in 100nsec for delay | |||
*/ | |||
void jf_delay_100ns (jtime_t _100nsec) | |||
{ | |||
jtime_t m, m2, m1 = (jtime_t)*_jf.value; | |||
_100nsec *= _jf.jp100ns; | |||
if ((jtime_t)(*_jf.value) - m1 > _100nsec) // Very small delays may return here. | |||
return; | |||
// Eat the time difference from _100nsec value. | |||
do { | |||
m2 = (jtime_t)(*_jf.value); | |||
m = m2 - m1; | |||
_100nsec -= (m>=0) ? m : _jf.jiffies + m; | |||
m1 = m2; | |||
} while (_100nsec>0); | |||
} | |||
/*! | |||
* \brief | |||
* A code based polling version delay implementation, using jiffies for timing. | |||
* This is NOT accurate but it ensures that the time passed is always | |||
* more than the requested value. | |||
* The delay values are multiplications of 1 msec. | |||
* \param msec Time in msec for delay | |||
* \return The status of ongoing delay | |||
* \arg 0: Delay time has passed | |||
* \arg 1: Delay is ongoing, keep calling | |||
*/ | |||
int jf_check_msec (jtime_t msec) | |||
{ | |||
static jtime_t m1=-1, cnt; | |||
jtime_t m, m2; | |||
if (m1 == -1) { | |||
m1 = *_jf.value; | |||
cnt = _jf.jp1ms * msec; | |||
} | |||
// Eat the time difference from msec value. | |||
if (cnt>0) { | |||
m2 = (jtime_t)(*_jf.value); | |||
m = m2-m1; | |||
cnt -= (m>=0) ? m : _jf.jiffies + m; | |||
m1 = m2; | |||
return 1; // wait | |||
} | |||
else { | |||
m1 = -1; | |||
return 0; // do not wait any more | |||
} | |||
} | |||
/*! | |||
* \brief | |||
* A code based polling version delay implementation, using jiffies for timing. | |||
* This is NOT accurate but it ensures that the time passed is always | |||
* more than the requested value. | |||
* The delay values are multiplications of 1 usec. | |||
* \param usec Time in usec for delay | |||
* \return The status of ongoing delay | |||
* \arg 0: Delay time has passed | |||
* \arg 1: Delay is ongoing, keep calling | |||
*/ |