From 0c2f7108e2f9b6fa567bc83dda9a313fffceb286 Mon Sep 17 00:00:00 2001 From: bernie Date: Fri, 4 Nov 2005 18:00:42 +0000 Subject: [PATCH] Import into DevLib. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@437 38d2e660-2303-0410-9eaa-f027e97ec537 --- drv/lcd_hd44.c | 430 +++++++++++++++++++++++++++++++++++++++++++++ drv/lcd_hd44.h | 82 +++++++++ drv/lcd_text.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++++ drv/lcd_text.h | 114 ++++++++++++ drv/ntc.c | 133 ++++++++++++++ drv/ntc.h | 77 ++++++++ drv/thermo.c | 238 +++++++++++++++++++++++++ drv/thermo.h | 91 ++++++++++ 8 files changed, 1629 insertions(+) create mode 100755 drv/lcd_hd44.c create mode 100755 drv/lcd_hd44.h create mode 100755 drv/lcd_text.c create mode 100755 drv/lcd_text.h create mode 100755 drv/ntc.c create mode 100755 drv/ntc.h create mode 100755 drv/thermo.c create mode 100755 drv/thermo.h diff --git a/drv/lcd_hd44.c b/drv/lcd_hd44.c new file mode 100755 index 00000000..ecfea365 --- /dev/null +++ b/drv/lcd_hd44.c @@ -0,0 +1,430 @@ +/*! + * \file + * + * + * \brief LM044L type LCD hardware module (impl.) + * + * \version $Id$ + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 18:00:42 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.2 2005/06/14 14:43:43 bernie + *#* Add DevLib headers. + *#* + *#* Revision 1.1 2005/05/24 09:17:58 batt + *#* Move drivers to top-level. + *#* + *#* Revision 1.9 2005/05/09 21:58:53 batt + *#* Fix doxygen tags. + *#* + *#* Revision 1.8 2005/05/09 12:52:46 batt + *#* lcd_dataRead(): Avoid bus collision; Add back *UNTESTED* 8bit bus support. + *#* + *#* Revision 1.7 2005/05/09 12:24:13 batt + *#* lcd_putc(): Fix latent bug; lcd_hw_init(): Extend timings. + *#*/ + +#include "lcd_hd44.h" +#include "lcd_bus_pz.h" +#include +#include + +#if defined(LCD_READ_H) && defined(LCD_READ_L) && defined(LCD_WRITE_H) && defined(LCD_WRITE_L) + #define CONFIG_LCD_4BIT 1 +#elif defined(LCD_READ) && defined(LCD_WRITE) + #define CONFIG_LCD_4BIT 0 +#else + #error Incomplete or missing LCD_READ/LCD_WRITE macros +#endif + +/*! Flag di stato del display */ +#define LCDF_BUSY BV(7) + +/*! + * Addresses of LCD display character positions, expanded + * for faster access (DB7 = 1). + */ +static const uint8_t lcd_address[] = +{ + /* row 0 */ + 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, + 0x8C, 0x8D, 0x8E, 0x8F, +#if LCD_COLS > 16 + 0x90, 0x91, 0x92, 0x93, +#endif + + /* row 1 */ + 0xC0, 0xC1, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, + 0xCC, 0xCD, 0xCE, 0xCF, +#if LCD_COLS > 16 + 0xD0, 0xD1, 0xD2, 0xD3, +#endif + +#if LCD_ROWS > 2 + /* row 2 */ + 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, + 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, +#if LCD_COLS > 16 + 0xA4, 0xA5, 0xA6, 0xA7, +#endif + + /* row 3 */ + 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xDB, + 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, +#if LCD_COLS > 16 + 0xE4, 0xE5, 0xE6, 0xE7, +#endif + +#endif /* LCD_ROWS > 2 */ +}; + +STATIC_ASSERT(countof(lcd_address) == LCD_ROWS * LCD_COLS); + +/*! + * Current display position. We remember this to optimize + * LCD output by avoiding to set the address every time. + */ +static lcdpos_t lcd_current_addr; + + +#if !defined(ARCH_EMUL) || !(ARCH & ARCH_EMUL) +/* __________________ + * RS + * + * R/W __________________ + * _______ + * ENA _____/ \____ + * + * DATA -<================ + */ +INLINE void lcd_dataWrite(uint8_t data) +{ +#if CONFIG_LCD_4BIT + /* Write high nibble */ + LCD_WRITE_H(data); + LCD_SET_E; + LCD_DELAY_WRITE; + LCD_CLR_E; + LCD_DELAY_WRITE; + + /* Write low nibble */ + LCD_WRITE_L(data); + LCD_SET_E; + LCD_DELAY_WRITE; + LCD_CLR_E; + LCD_DELAY_WRITE; + +#else /* !CONFIG_LCD_4BIT */ + + /* Write data */ + LCD_WRITE(data); + LCD_SET_E; + LCD_DELAY_WRITE; + LCD_CLR_E; + LCD_DELAY_WRITE; + +#endif /* !CONFIG_LCD_4BIT */ +} + +/* __________________ + * RS + * ____________ + * R/W __/ \__ + * _______ + * ENA _____/ \____ + * ______ ____ + * DATA X/ \====/ + */ +INLINE uint8_t lcd_dataRead(void) +{ + uint8_t data; + + LCD_SET_RD; + LCD_DB_IN; /* Set bus as input! */ + LCD_DELAY_READ; + +#if CONFIG_LCD_4BIT + + /* Read high nibble */ + LCD_SET_E; + LCD_DELAY_READ; + data = LCD_READ_H; + LCD_CLR_E; + LCD_DELAY_READ; + + /* Read low nibble */ + LCD_SET_E; + LCD_DELAY_READ; + data |= LCD_READ_L; + LCD_CLR_E; + LCD_DELAY_READ; + +#else /* !CONFIG_LCD_4BIT */ + + /* Read data */ + LCD_SET_E; + LCD_DELAY_READ; + data |= LCD_READ; + LCD_CLR_E; + LCD_DELAY_READ; + +#endif /* !CONFIG_LCD_4BIT */ + + LCD_CLR_RD; + LCD_DB_OUT; /* Reset bus as output! */ + + return data; +} + +/* ___ __ + * RS \___________/ + * + * READ __________________ + * _______ + * ENA _____/ \____ + * + * DATA --<=============== + */ +INLINE void lcd_regWrite(uint8_t data) +{ + LCD_CLR_RS; + lcd_dataWrite(data); + LCD_SET_RS; +} + +/* __ _ + * RS \_____________/ + * ___________ + * READ ___/ \__ + * _______ + * ENA _____/ \____ + * ______ ____ + * DATA X/ \====/ + */ +INLINE uint8_t lcd_regRead(void) +{ + uint8_t data; + + LCD_CLR_RS; + data = lcd_dataRead(); + LCD_SET_RS; + return data; +} + +#if CONFIG_LCD_4BIT + +INLINE void lcd_mode4Bit(void) +{ + LCD_CLR_RS; + + LCD_WRITE_H(LCD_CMD_SETFUNC); + LCD_SET_E; + LCD_DELAY_WRITE; + LCD_CLR_E; + LCD_DELAY_WRITE; + + LCD_SET_RS; +} + +#endif /* CONFIG_LCD_4BIT */ + +#else /* ARCH_EMUL */ + +extern void Emul_LCDWriteReg(uint8_t d); +extern uint8_t Emul_LCDReadReg(void); +extern void Emul_LCDWriteData(uint8_t d); +extern uint8_t Emul_LCDReadData(void); + +#define lcd_regWrite(d) Emul_LCDWriteReg(d) +#define lcd_regRead(d) Emul_LCDReadReg() +#define lcd_dataWrite(d) Emul_LCDWriteData(d) +#define lcd_dataRead(d) Emul_LCDReadData() + +#endif /* ARCH_EMUL */ + + +/*! + * Wait until the LCD busy flag clears. + */ +void lcd_waitBusy(void) +{ + for (;;) + { + uint8_t val = lcd_regRead(); + if (!(val & LCDF_BUSY)) + break; + } +} + + +/*! + * Move the cursor to \a addr, only if not already there. + */ +void lcd_moveTo(uint8_t addr) +{ + if (addr != lcd_current_addr) + { + lcd_waitBusy(); + lcd_regWrite(lcd_address[addr]); + lcd_current_addr = addr; + } +} + + +/*! + * Write a value in LCD data register, waiting for the busy flag. + */ +void lcd_setReg(uint8_t val) +{ + lcd_waitBusy(); + lcd_regWrite(val); +} + +#include +/*! + * Write the character \a c on display address \a addr. + * + * NOTE: argh, the HD44 lcd type is a bad beast: our + * move/write -> write optimization requires this mess + * because display lines are interleaved! + */ +void lcd_putc(uint8_t addr, uint8_t c) +{ + if (addr != lcd_current_addr) + lcd_setReg(lcd_address[addr]); + + lcd_waitBusy(); + lcd_dataWrite(c); + lcd_current_addr = addr + 1; + + /* If we are at end of display wrap the address to 0 */ + if (lcd_current_addr == LCD_COLS * LCD_ROWS) + lcd_current_addr = 0; + + /* If we are at the end of a row put the cursor at the beginning of the next */ + if (!(lcd_current_addr % LCD_COLS)) + lcd_setReg(lcd_address[lcd_current_addr]); +} + + +/*! + * Remap the glyph of a character. + * + * glyph - bitmap of 8x8 bits. + * code - must be 0-7 for the Hitachi LCD-II controller. + */ +void lcd_remapChar(const char *glyph, char code) +{ + int i; + + /* Set CG RAM address */ + lcd_setReg((uint8_t)((1<<6) | (code << 3))); + + /* Write bitmap data */ + for (i = 0; i < 8; i++) + { + lcd_waitBusy(); + lcd_dataWrite(glyph[i]); + } + + /* Move back to original address */ + lcd_setReg(lcd_address[lcd_current_addr]); +} + + +#if 0 /* unused */ +void lcd_remapfont(void) +{ + static const char lcd_glyphs[8] = + { + 0x04, 0x0E, 0x15, 0x04, 0x04, 0x04, 0x04, 0x00 /* up arrow */ + }; + int i; + + for (i = 0; i < 15; i++) + lcd_remapChar(i, bernie_char); + + + lcd_setAddr(lcd_DefLayer, 0); + for (i = 0; i < 80; i++) + lcd_putCharUnlocked(i); +} +#endif /* unused */ + +void lcd_hw_init(void) +{ + lcd_bus_init(); + + timer_delay(50); + +#if CONFIG_LCD_4BIT + lcd_mode4Bit(); + timer_delay(2); +#endif /* CONFIG_LCD_4BIT */ + + lcd_regWrite(LCD_CMD_SETFUNC); + timer_delay(2); + + lcd_regWrite(LCD_CMD_DISPLAY_ON); + timer_delay(2); + + lcd_regWrite(LCD_CMD_CLEAR); + timer_delay(2); + + //lcd_regWrite(LCD_CMD_RESET_DDRAM); 4 bit mode doesn't allow char reprogramming + + lcd_regWrite(LCD_CMD_DISPLAYMODE); + timer_delay(2); +} + +#if CONFIG_TEST + +void lcd_hw_test(void) +{ + lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 3); + timer_delay(1); + kprintf("3 -> %02X\n", lcd_regRead()); + timer_delay(1); + + for (int i = 0; i < 10; i++) + { + lcd_dataWrite('c'); + timer_delay(1); + kprintf("addr = %02X\n", lcd_regRead()); + timer_delay(1); + } + + lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 0x4a); + timer_delay(1); + kprintf("4A -> %02X\n", lcd_regRead()); + timer_delay(1); + + lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 0x52); + timer_delay(1); + kprintf("52 -> %02X\n", lcd_regRead()); + timer_delay(1); + + lcd_regWrite(LCD_CMD_SET_DDRAMADDR | 0x1F); + timer_delay(1); + kprintf("1F -> %02X\n", lcd_regRead()); + timer_delay(1); +} + +#endif /* CONFIG_TEST */ diff --git a/drv/lcd_hd44.h b/drv/lcd_hd44.h new file mode 100755 index 00000000..0413bed6 --- /dev/null +++ b/drv/lcd_hd44.h @@ -0,0 +1,82 @@ +/*! + * \file + * + * + * \brief Hitachi HD44780 and clones LCD module (interface) + * + * \version $Id$ + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 18:00:42 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.2 2005/06/14 14:43:43 bernie + *#* Add DevLib headers. + *#* + *#* Revision 1.1 2005/05/24 09:17:58 batt + *#* Move drivers to top-level. + *#* + *#* Revision 1.3 2005/04/22 13:14:58 batt + *#* Better lcd rows and cols definition. + *#* + *#* Revision 1.2 2005/04/17 22:41:39 bernie + *#* lcd_test(): Rename to lcd_hw_test() and conditionalize on CONFIG_TEST. + *#* + *#* Revision 1.1 2005/04/16 19:48:35 aleph + *#* Rename lcd driver + *#*/ + +#ifndef DRV_LCD_HD44_H +#define DRV_LCD_HD44_H + +#include /* For stdint types */ + +/*! + * \name Display dimensions (in chars) + * \{ + */ +#define LCD_ROWS 2 +#define LCD_COLS 16 +/* \} */ + +/*! + * \name Hitachi HD44 commands. + * \{ + */ +#define LCD_CMD_DISPLAY_INI 0x30 +//#define LCD_CMD_SETFUNC 0x38 /*!< 8 bits, 2 lines, 5x7 dots */ +#define LCD_CMD_SETFUNC 0x28 /*!< 4 bits, 2 lines, 5x7 dots */ +#define LCD_CMD_DISPLAY_ON 0x0F /*!< Switch on display */ +#define LCD_CMD_DISPLAY_OFF 0x08 /*!< Switch off display */ +#define LCD_CMD_CLEAR 0x01 /*!< Clear display */ +#define LCD_CMD_CURSOR_BLOCK 0x0D /*!< Show cursor (block) */ +#define LCD_CMD_CURSOR_LINE 0x0F /*!< Show cursor (line) */ +#define LCD_CMD_CURSOR_OFF 0x0C /*!< Hide cursor */ +#define LCD_CMD_DISPLAYMODE 0x06 +#define LCD_CMD_SET_CGRAMADDR 0x40 +#define LCD_CMD_RESET_DDRAM 0x80 +#define LCD_CMD_SET_DDRAMADDR 0x80 +#define LCD_CMD_DISPLAY_SHIFT 0x18 +#define LCD_CMD_MOVESHIFT_LEFT 0x00 +#define LCD_CMD_MOVESHIFT_RIGHT 0x04 +/*\}*/ + +/*! Type for combined LCD cursor position (x,y). */ +typedef uint8_t lcdpos_t; + +void lcd_waitBusy(void); +void lcd_moveTo(uint8_t addr); +void lcd_setReg(uint8_t val); +void lcd_putc(uint8_t a, uint8_t c); +void lcd_remapChar(const char *glyph, char code); +void lcd_hw_init(void); +void lcd_hw_test(void); + +#endif /* DRV_LCD_HD44_H */ diff --git a/drv/lcd_text.c b/drv/lcd_text.c new file mode 100755 index 00000000..284c8b02 --- /dev/null +++ b/drv/lcd_text.c @@ -0,0 +1,464 @@ +/*! + * \file + * + * + * \brief Generic text LCD driver (impl.). + * + * \version $Id$ + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 18:00:42 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.11 2005/06/14 14:43:43 bernie + *#* Add DevLib headers. + *#* + *#* Revision 1.10 2005/06/06 17:41:57 batt + *#* Add lcd_layerSet function. + *#* + *#* Revision 1.9 2005/06/01 16:40:07 batt + *#* Remove debug string. + *#* + *#* Revision 1.8 2005/06/01 16:38:04 batt + *#* Adapt to changes in mware/list.h. + *#* + *#* Revision 1.7 2005/06/01 10:45:22 batt + *#* lcd_setAddr(): Bugfix boundary condition; Misc cleanup. + *#* + *#* Revision 1.6 2005/06/01 10:36:23 batt + *#* Layer: Rename member variables and Doxygenize. + *#* + *#* Revision 1.5 2005/05/27 11:05:58 batt + *#* Do not write on lcd if layer is hidden. + *#*/ + +#include "lcd_text.h" +#include "lcd_hd44.h" +#include // timer_delay() +#include // _formatted_write() +#include // BV() +#include + +#include // strlen() + + +/*! Maximum number of layers. */ +#define LCD_LAYERS 6 + +#if CONFIG_KERNEL + /*! Semaphore to arbitrate access to the display. */ + static struct Semaphore lcd_semaphore; + #define LOCK_LCD sem_obtain(&lcd_semaphore) + #define UNLOCK_LCD sem_release(&lcd_semaphore) +#else /* !CONFIG_KERNEL */ + #define LOCK_LCD do {} while (0) + #define UNLOCK_LCD do {} while (0) +#endif /* !CONFIG_KERNEL */ + +DECLARE_LIST_TYPE(Layer); + +Layer *lcd_DefLayer; +static Layer lcd_LayersPool[LCD_LAYERS]; +static LIST_TYPE(Layer) lcd_Layers; +static LIST_TYPE(Layer) lcd_FreeLayers; + +/*! + * Current cursor status. + * + * One of LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK or LCD_CMD_CURSOR_LINE. + */ +static uint8_t lcd_CursorStatus; + +/*! Current cursor position, encoded as a Cursor position and status. */ +static lcdpos_t lcd_CursorAddr; + + +void lcd_setAddr(Layer *layer, lcdpos_t addr) +{ + /* Sanity check: wrap around to display limits */ + while (addr >= LCD_ROWS * LCD_COLS) + addr -= LCD_ROWS * LCD_COLS; + + layer->addr = addr; +} + +#if CONFIG_KERNEL + +void lcd_lock(void) +{ + LOCK_LCD; +} + + +void lcd_unlock(void) +{ + UNLOCK_LCD; +} + +#endif /* CONFIG_KERNEL */ + + +/*! + * Write one character to the display at the current + * cursor prosition, then move the cursor right. The + * cursor is wrapped to the next line when it moves + * beyond the end of the current line. + * + * \note Does _NOT_ lock the display semaphore. + */ +static void lcd_putCharUnlocked(char c, Layer *layer) +{ + Layer *l2; + lcdpos_t addr = layer->addr; + + /* Store character in layer buffer */ + layer->buf[addr] = c; + + /* Move to next character */ + if (++layer->addr >= LCD_COLS * LCD_ROWS) + layer->addr = 0; + + /* Do not write on LCD if layer is hidden. */ + if (layer->pri == LAYER_HIDDEN) + return; + + /* + * Check if this location is obscured by + * other layers above us. + */ + for (l2 = layer->pred; l2->pred; l2 = l2->pred) + { + if (l2->buf[addr]) + { + /* DB(kprintf("layer %04x obs %04x at %d\n", l2, layer, addr);) */ + return; + } + } + + /* Write character */ + if (c) + lcd_putc(addr, c); + else + /* FIXME: should look for layers beneath! */ + lcd_putc(addr, ' '); +} + + +void lcd_putChar(char c, Layer *layer) +{ + LOCK_LCD; + lcd_putCharUnlocked(c, layer); + UNLOCK_LCD; +} + +void lcd_layerSet(Layer *layer, char c) +{ + int i; + + LOCK_LCD; + lcd_setAddr(layer, 0); + for (i = 0; i < LCD_COLS * LCD_ROWS; i++) + lcd_putCharUnlocked(c, layer); + UNLOCK_LCD; +} + + +void lcd_clear(Layer *layer) +{ + lcd_layerSet(layer, 0); +} + + +void lcd_clearLine(Layer *layer, int y) +{ + int i; + + LOCK_LCD; + lcd_setAddr(layer, LCD_POS(0, y)); + for (i = 0; i < LCD_COLS; i++) + lcd_putCharUnlocked(0, layer); + UNLOCK_LCD; +} + + +void lcd_moveCursor(lcdpos_t addr) +{ + LOCK_LCD; + lcd_moveTo(addr); + UNLOCK_LCD; +} + + +char lcd_setCursor(char mode) +{ + static const char cursor_cmd[3] = + { + LCD_CMD_CURSOR_OFF, LCD_CMD_CURSOR_BLOCK, LCD_CMD_CURSOR_LINE + }; + char oldmode = lcd_CursorStatus; + + LOCK_LCD; + lcd_CursorStatus = mode; + lcd_setReg(cursor_cmd[(int)mode]); + if (mode) + lcd_moveCursor(lcd_CursorAddr); + UNLOCK_LCD; + + return oldmode; +} + + +int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap) +{ + int len; + + LOCK_LCD; + + /* + * Se il cursore era acceso, spegnilo durante + * l'output per evitare che salti alla posizione + * in cui si scrive. + */ + if (lcd_CursorStatus) + lcd_setReg(LCD_CMD_CURSOR_OFF); + + /* Spostamento del cursore */ + lcd_setAddr(layer, addr); + + if (mode & LCD_CENTER) + { + int pad; + + /* + * NOTE: calculating the string lenght BEFORE it gets + * printf()-formatted. Real lenght may differ. + */ + pad = (LCD_COLS - strlen(format)) / 2; + while (pad--) + lcd_putCharUnlocked(' ', layer); + } + + len = _formatted_write(format, (void (*)(char, void *))lcd_putCharUnlocked, layer, ap); + + if (mode & (LCD_FILL | LCD_CENTER)) + while (layer->addr % LCD_COLS) + lcd_putCharUnlocked(' ', layer); + + /* + * Riaccendi il cursore e riportalo alla + * vecchia posizione + */ + if (lcd_CursorStatus) + lcd_setCursor(lcd_CursorStatus); + + UNLOCK_LCD; + + return len; +} + + +int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...) +{ + int len; + va_list ap; + + va_start(ap, format); + len = lcd_vprintf(layer, addr, mode, format, ap); + va_end(ap); + + return len; +} + + +/*! + * Internal function to move a layer betweet two positions. + * + * \note The layer must be *already* enqueued in some list. + * \note The display must be already locked! + */ +static void lcd_enqueueLayer(Layer *layer, char pri) +{ + Layer *l2; + + /* Remove layer from whatever list it was in before */ + REMOVE(layer); + + layer->pri = pri; + + /* + * Search for the first layer whose priority + * is less or equal to the layer we are adding. + */ + FOREACHNODE(l2, &lcd_Layers) + if (l2->pri <= pri) + break; + + /* Enqueue layer */ + INSERTBEFORE(layer, l2); +} + +Layer *lcd_newLayer(char pri) +{ + Layer *layer; + + LOCK_LCD; + + if (ISLISTEMPTY(&lcd_FreeLayers)) + { + UNLOCK_LCD; + //ASSERT(false); + return NULL; + } + + layer = (Layer *)LIST_HEAD(&lcd_FreeLayers); + layer->addr = 0; + memset(layer->buf, 0, LCD_ROWS * LCD_COLS); + + lcd_enqueueLayer(layer, pri); + + UNLOCK_LCD; + return layer; +} + +/*! + * Redraw the display (internal). + * + * \note The display must be already locked. + */ +static void lcd_refresh(void) +{ + lcdpos_t addr; + Layer *l; + + for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr) + { + FOREACHNODE(l, &lcd_Layers) + { + //kprintf("%d %x %p\n", addr, l->buf[0], l); + if (l->pri == LAYER_HIDDEN) + break; + + if (l->buf[addr]) + { + /* Refresh location */ + lcd_putc(addr, l->buf[addr]); + goto done; + } + } + + /* Draw background */ + lcd_putc(addr, ' '); + done: + ; + } +} + +/*! + * Rearrange layer depth and refresh display accordingly. + * + * \note Setting a priority of LAYER_HIDDEN makes the layer invisible. + */ +void lcd_setLayerDepth(Layer *layer, char pri) +{ + if (pri != layer->pri) + { + LOCK_LCD; + lcd_enqueueLayer(layer, pri); + /* Vile but simple */ + lcd_refresh(); + UNLOCK_LCD; + } +} + +void lcd_deleteLayer(Layer *layer) +{ + LOCK_LCD; + +/* We use lcd_refresh() instead. Much simpler than this mess, but slower. */ +#if 0 + Layer *l2; + lcdpos_t addr; + + /* Repair damage on underlaying layers */ + for (addr = 0; addr < LCD_ROWS * LCD_COLS; ++addr) + { + /* If location was covered by us */ + if (layer->buf[addr]) + { + /* ...and it wasn't covered by others above us... */ + for (l2 = layer->pred; l2->pred; l2 = l2->pred) + if (l2->buf[addr]) + /* can't just break here! */ + goto not_visible; + + /* ...scan underlaying layers to repair damage */ + for (l2 = layer->succ; l2->succ; l2 = l2->succ) + if (l2->buf[addr]) + { + /* Refresh character */ + lcd_putc(addr, l2->buf[addr]); + + /* No need to search on deeper layers */ + break; + } + + not_visible: + ; + } + } +#endif + + // Remove layer from lcd_Layers list. + REMOVE(layer); + + /* Put layer back into free list */ + ADDHEAD(&lcd_FreeLayers, layer); + + lcd_refresh(); + + UNLOCK_LCD; +} + + +static void lcd_setDefLayer(Layer *layer) +{ + lcd_DefLayer = layer; +} + +#include +void lcd_init(void) +{ + int i; + + LIST_INIT(&lcd_Layers); + LIST_INIT(&lcd_FreeLayers); + for (i = 0; i < LCD_LAYERS; ++i) + ADDHEAD(&lcd_FreeLayers, &lcd_LayersPool[i]); + + lcd_setDefLayer(lcd_newLayer(0)); + + lcd_hw_init(); + + lcd_setCursor(0); +} + +#if CONFIG_TEST +void lcd_test(void) +{ + int i; + + for (i = 0; i < LCD_ROWS * LCD_COLS; ++i) + { + lcd_putCharUnlocked('0' + (i % 10), lcd_DefLayer); + timer_delay(100); + } +} +#endif /* CONFIG_TEST */ + diff --git a/drv/lcd_text.h b/drv/lcd_text.h new file mode 100755 index 00000000..1a0ecae3 --- /dev/null +++ b/drv/lcd_text.h @@ -0,0 +1,114 @@ +/*! + * \file + * + * + * \brief Generic text LCD driver (interface). + * + * \version $Id$ + * \author Bernardo Innocenti + * \author Stefano Fedrigo + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 18:00:42 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.5 2005/06/14 14:43:43 bernie + *#* Add DevLib headers. + *#* + *#* Revision 1.4 2005/06/06 17:41:57 batt + *#* Add lcd_layerSet function. + *#* + *#* Revision 1.3 2005/06/01 10:36:23 batt + *#* Layer: Rename member variables and Doxygenize. + *#* + *#* Revision 1.2 2005/05/26 08:31:23 batt + *#* Add layer hiding/showing. + *#* + *#* Revision 1.1 2005/05/24 09:17:58 batt + *#* Move drivers to top-level. + *#*/ + +#ifndef DRV_LCD_H +#define DRV_LCD_H + +#include "lcd_hd44.h" + +#include +#include +#include + +#include // vprintf() + + +/* flags for lcd_printf() */ +#define LCD_NORMAL 0 /* Scrittura normale */ +#define LCD_FILL BV(0) /* Fill rest of line with spaces */ +#define LCD_CENTER BV(1) /* Center string in line */ +#define LCD_NOCURSOR BV(2) /* Scrittura senza spostamento cursore */ + +/*! Special priority value for lcd_setLayerDepth(). */ +#define LAYER_HIDDEN -128 + +/* Compute LCD address from x/y coordinates */ +#define LCD_POS(x,y) ((lcdpos_t)((uint8_t)(x) + (uint8_t)(y) * (uint8_t)LCD_COLS)) +#define LCD_ROW0 (LCD_COLS * 0) +#define LCD_ROW1 (LCD_COLS * 1) +#define LCD_ROW2 (LCD_COLS * 2) +#define LCD_ROW3 (LCD_COLS * 3) + +/*! + * Overwrapping layer context. + */ +typedef struct _Layer +{ + /*! + * Active layers are linked together in a list + * sorted in top to bottom order. + */ + DECLARE_NODE_ANON(struct _Layer) + + /*! Current XY address into this layer, for write operations. */ + lcdpos_t addr; + + /*! Priority of this layer (greater in front of lesser). */ + char pri; + + /*! + * Buffer per il salvataggio ed il ripristino del + * contenuto del display. Le posizioni che contengono + * il carattere '\0' sono trasparenti. + */ + char buf[LCD_COLS * LCD_ROWS]; +} Layer; + + +/* Global variables */ +extern Layer *lcd_DefLayer; + +/* Function prototypes */ +extern void lcd_init(void); +extern void lcd_test(void); + +extern void lcd_moveCursor(lcdpos_t addr); +extern char lcd_setCursor(char state); + +extern void lcd_setAddr(Layer *layer, lcdpos_t addr); +extern void lcd_putChar(char c, Layer *layer); +extern int lcd_vprintf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, va_list ap) FORMAT(printf, 4, 0); +extern int lcd_printf(Layer *layer, lcdpos_t addr, uint8_t mode, const char *format, ...) FORMAT(printf, 4, 5); +extern void lcd_clear(Layer *layer); +extern void lcd_layerSet(Layer *layer, char c); +extern void lcd_clearLine(Layer *layer, int y); + +extern void lcd_setLayerDepth(Layer *layer, char pri); +extern Layer *lcd_newLayer(char pri); +extern void lcd_deleteLayer(Layer *layer); +extern void lcd_lock(void); +extern void lcd_unlock(void); + +#endif /* DRV_LCD_H */ diff --git a/drv/ntc.c b/drv/ntc.c new file mode 100755 index 00000000..5f024037 --- /dev/null +++ b/drv/ntc.c @@ -0,0 +1,133 @@ +/*! + * \file + * + * + * \brief Driver for NTC (reads a temperature through an ADC) + * + * \version $Id$ + * + * \author Giovanni Bajo + * \author Francesco Sacchi + * + * + * This module handles an external NTC bound to an AD converter. As usual, + * it relies on a low-level API (ntc_hw_*) (see below): + * + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 17:59:47 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.1 2005/05/24 09:17:58 batt + *#* Move drivers to top-level. + *#*/ + +#include +#include +#include + +#include + +DB(bool ntc_initialized;) + +/*! + * Find in a table of values \a orig_table of size \a size, the index which + * value is less or equal to \a val. + * + * \retval 0 When \a val is higher than the first table entry. + * \retval size When \a val is lower than the last table entry. + * \retval 1..size-1 When \a val is within the table. + */ +static size_t upper_bound(const res_t *orig_table, size_t size, res_t val) +{ + const res_t *table = orig_table; + + while (size) + { + size_t pos = size / 2; + if (val > table[pos]) + size = pos; + else + { + table += pos+1; + size -= pos+1; + } + } + + return table - orig_table; +} + + +/*! + * Read the temperature for the NTC channel \a dev. + * First read the resistence of the NTC through ntc_hw_read(), then, + * for the conversion from resistance to temperature, since the formula + * varies from device to device, we implemented a generic system using + * a table of data which maps temperature (index) to resistance (data). + * The range of the table (min/max temperature) and the step + * (temperature difference between two consecutive elements of the table) + * is variable and can be specified. Notice that values inbetween the + * table elements are still possible as the library does a linear + * interpolation using the actual calculated resistance to find out + * the exact temperature. + * + * The low-level API provides a function to get access to a description + * of the NTC (ntc_hw_getInfo()), including the resistance table. + * + */ +deg_t ntc_read(NtcDev dev) +{ + const NtcHwInfo* hw = ntc_hw_getInfo(dev); + const res_t* r = hw->resistances; + + float rx; + size_t i; + deg_t degrees; + + rx = ntc_hw_read(dev); + + + i = upper_bound(r, hw->num_resistances, rx); + ASSERT(i <= hw->num_resistances); + + if (i >= hw->num_resistances) + return NTC_SHORT_CIRCUIT; + else if (i == 0) + return NTC_OPEN_CIRCUIT; + + /* + * Interpolated value in 0.1 degrees multiplied by 10: + * delta t step t + * ---------- = ---------------- + * (rx - r[i]) (r[i-1] - r [i]) + */ + float tmp; + tmp = 10 * hw->degrees_step * (rx - r[i]) / (r[i - 1] - r[i]); + + /* + * degrees = integer part corresponding to the superior index + * in the table multiplied by 10 + * - decimal part interpolated (already multiplied by 10) + */ + degrees = (i * hw->degrees_step + hw->degrees_min) * 10 - (int)(tmp); + + //kprintf("dev= %d, I=%d, degrees = %d\n", dev, i , degrees); + + return degrees; +} + + +/*! + * Init NTC hardware. + */ +void ntc_init(void) +{ + NTC_HW_INIT; + DB(ntc_initialized = true;) +} + diff --git a/drv/ntc.h b/drv/ntc.h new file mode 100755 index 00000000..bf221369 --- /dev/null +++ b/drv/ntc.h @@ -0,0 +1,77 @@ +/*! + * \file + * + * + * \brief Driver for NTC (reads a temperature through an ADC) + * + * \version $Id$ + * + * \author Giovanni Bajo + * \author Francesco Sacchi + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 17:59:47 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.3 2005/06/10 08:56:47 batt + *#* Avoid calling DEG_T_TO_DEG(). + *#* + *#* Revision 1.2 2005/06/10 08:54:58 batt + *#* Rename deg_t conversion macros to accomplish coding standard. + *#* + *#* Revision 1.1 2005/05/24 09:17:58 batt + *#* Move drivers to top-level. + *#*/ + +#ifndef DRV_NTC_H +#define DRV_NTC_H + +#include +#include +#include + +#define NTC_OPEN_CIRCUIT -32768 +#define NTC_SHORT_CIRCUIT 32767 + +typedef int16_t deg_t; /*! type for celsius degrees deg_t = °C * 10 */ + +/*! Macro for converting from deg to deg_t type */ +#define DEG_TO_DEG_T(x) ((deg_t)((x) * 10)) + +/*! Macro for converting from deg_t to celsius degrees (returns only the integer part) */ +#define DEG_T_TO_INTDEG(x) ((x) / 10) + +/*! Macro for converting from deg_t to celsius degrees (returns only the decimal part) */ +#define DEG_T_TO_DECIMALDEG(x) ((x) % 10) + +/*! Macro for converting from deg_t to celsius degrees (returns type is float) */ +#define DEG_T_TO_FLOATDEG(x) ((x) / 10.0) + + +typedef uint32_t res_t; /*! type for resistor res_t = Ohm * 100 */ +typedef float amp_t; /*! type for defining amplifications amp_t = A, where A is a pure number */ + +DB(extern bool ntc_initialized;) + + +/*! Describe a NTC chip */ +typedef struct NtcHwInfo +{ + const res_t *resistances; //!< resistances of the NTC (ohms * 100) + size_t num_resistances; //!< number of resistances + deg_t degrees_min; //!< degrees corresponding to the first entry in the table (celsius * 10) + deg_t degrees_step; //!< difference in degrees between two consecutive elements in the table (celsius * 10) +} NtcHwInfo; + +/*! Initialize the NTC module */ +void ntc_init(void); + +/*! Read a single temperature value from the NTC */ +deg_t ntc_read(NtcDev dev); + +#endif /* DRV_NTC_H */ diff --git a/drv/thermo.c b/drv/thermo.c new file mode 100755 index 00000000..811ee25f --- /dev/null +++ b/drv/thermo.c @@ -0,0 +1,238 @@ +/*! + * \file + * + * + * \brief Thermo-control driver + * + * \version $Id$ + * + * \author Giovanni Bajo + * \author Francesco Sacchi + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 17:59:47 bernie + *#* Import into DevLib. + *#* + *#*/ +#include +#include + +#include +#include +#include + +#include +#include + + +/*! Interval at which thermo control is performed. */ +#define THERMO_INTERVAL_MS 100 + +/*! Number of different samples we interpolate over to get the hifi temperature. */ +#define THERMO_HIFI_NUM_SAMPLES 10 + +/*! Timer for thermo-regulation. */ +static Timer thermo_timer; + +typedef struct ThermoControlDev +{ + deg_t hifi_samples[THERMO_HIFI_NUM_SAMPLES]; + deg_t cur_hifi_sample; + deg_t target; + thermostatus_t status; + ticks_t expire; +} ThermoControlDev; + +/*! Array of thermo-devices. */ +ThermoControlDev devs[THERMO_CNT]; + + +/*! + * Return the status of the specific \a dev thermo-device. + */ +thermostatus_t thermo_status(ThermoDev dev) +{ + return devs[dev].status; +} + + +/*! + * Do a single thermo control for device \a dev. + */ +static void thermo_do(ThermoDev index) +{ + ThermoControlDev* dev = &devs[index]; + deg_t cur_temp; + deg_t tolerance = thermo_hw_tolerance(index); + + cur_temp = thermo_hw_read(index); + + // Store the sample into the hifi FIFO buffer for later interpolation + dev->hifi_samples[dev->cur_hifi_sample] = cur_temp; + if (++dev->cur_hifi_sample == THERMO_HIFI_NUM_SAMPLES) + dev->cur_hifi_sample = 0; + + cur_temp = thermo_read_temperature(index); + + if (cur_temp == NTC_SHORT_CIRCUIT || cur_temp == NTC_OPEN_CIRCUIT) + { + if (cur_temp == NTC_SHORT_CIRCUIT) + { + #ifdef _DEBUG + if (!(dev->status & THERMOERRF_NTCSHORT)) + kprintf("dev[%d], thermo_do: NTC_SHORT\n",index); + #endif + dev->status |= THERMOERRF_NTCSHORT; + } + else + { + #ifdef _DEBUG + if (!(dev->status & THERMOERRF_NTCOPEN)) + kprintf("dev[%d], thermo_do: NTC_OPEN\n", index); + #endif + dev->status |= THERMOERRF_NTCOPEN; + } + + /* Reset timeout when there is an ntc error */ + dev->expire = thermo_hw_timeout(index) + timer_clock(); + thermo_hw_off(index); + return; + } + dev->status &= ~(THERMOERRF_NTCOPEN | THERMOERRF_NTCSHORT); + + if ((cur_temp < dev->target - tolerance) || (cur_temp > dev->target + tolerance)) + { + dev->status &= ~THERMO_TGT_REACH; + + /* Check for timeout */ + if (timer_clock() - dev->expire > 0) + { + dev->status |= THERMOERRF_TIMEOUT; + kprintf("dev[%d], thermo_do: TIMEOUT\n", index); + } + } + else /* In target */ + { + /* Clear errors */ + dev->status &= ~THERMO_ERRMASK; + dev->status |= THERMO_TGT_REACH; + + /* Reset timeout in case we go out of target in the future */ + dev->expire = thermo_hw_timeout(index) + timer_clock(); + } + + if (cur_temp < dev->target) + dev->status = (dev->status | THERMO_HEATING) & ~THERMO_FREEZING; + else + dev->status = (dev->status & ~THERMO_HEATING) | THERMO_FREEZING; + + thermo_hw_set(index, dev->target, cur_temp); + +} + + +/*! + * Thermo soft interrupt. + */ +static void thermo_softint(void) +{ + int i; + for (i = 0; i < THERMO_CNT; ++i) + if (devs[i].status & THERMO_ACTIVE) + thermo_do((ThermoDev)i); + + timer_add(&thermo_timer); +} + + +/*! + * Set the target temperature \a temperature for a specific \a dev thermo-device. + */ +void thermo_setTarget(ThermoDev dev, deg_t temperature) +{ + ASSERT(dev < THERMO_CNT); + devs[dev].target = temperature; + devs[dev].expire = timer_clock() + thermo_hw_timeout(dev); + + kprintf("setTarget dev[%d], T[%d.%d]\n", dev, temperature / 10, temperature % 10); +} + +/*! + * Starts a thermo-regulation for channel \a dev. + */ +void thermo_start(ThermoDev dev) +{ + int i; + deg_t temp; + + ASSERT(dev < THERMO_CNT); + + devs[dev].status |= THERMO_ACTIVE; + + /* Initialize the hifi FIFO with a constant value (the current temperature) */ + temp = thermo_hw_read(dev); + for (i = 0; i < THERMO_HIFI_NUM_SAMPLES; ++i) + devs[dev].hifi_samples[i] = temp; + devs[dev].cur_hifi_sample = 0; + + /* Reset timeout */ + devs[dev].expire = timer_clock() + thermo_hw_timeout(dev); +} + +/*! + * Stops a thermo-regulation for channel \a dev. + */ +void thermo_stop(ThermoDev dev) +{ + ASSERT(dev < THERMO_CNT); + + devs[dev].status &= ~THERMO_ACTIVE; + thermo_hw_off(dev); +} + + +/*! + * Clear errors for channel \a dev. + */ +void thermo_clearErrors(ThermoDev dev) +{ + ASSERT(dev < THERMO_CNT); + devs[dev].status &= ~(THERMO_ERRMASK); +} + + +/*! + * Read the temperature of the thermo-device \a dev using mobile mean. + */ +deg_t thermo_read_temperature(ThermoDev dev) +{ + int i; + long accum = 0; + + for (i = 0; i < THERMO_HIFI_NUM_SAMPLES; i++) + accum += devs[dev].hifi_samples[i]; + + return (deg_t)(accum / THERMO_HIFI_NUM_SAMPLES); +} + + +/*! + * Init thermo-control and associated hw. + */ +void thermo_init(void) +{ + THERMO_HW_INIT; + + /* Set all status to off */ + for (int i = 0; i < THERMO_CNT; i++) + devs[i].status = THERMO_OFF; + + timer_setDelay(&thermo_timer, ms_to_ticks(THERMO_INTERVAL_MS)); + timer_set_event_softint(&thermo_timer, (Hook)thermo_softint, 0); + timer_add(&thermo_timer); +} diff --git a/drv/thermo.h b/drv/thermo.h new file mode 100755 index 00000000..361f9661 --- /dev/null +++ b/drv/thermo.h @@ -0,0 +1,91 @@ +/*! + * \file + * + * + * \brief Thermo-control driver + * + * \version $Id$ + * + * \author Giovanni Bajo + * \author Francesco Sacchi + * + * This module implements multiple thermo controls, which is the logic needed to try + * keeping the temperature of a device constant. For this module, a "device" is a black box + * whose temperature can be measured, and which has a mean to make it hotter or colder. + * For instance, a device could be the combination of a NTC (analog temperature reader) and + * a Peltier connected to the same physic block. + * + * This module relies on a low-level driver to communicate with the device (implementation + * of the black box). This low-level driver also controls the units in which the temperature + * is expressed: thermo control treats it just as a number. + * + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2005/11/04 17:59:47 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.2 2005/06/14 10:13:36 batt + *#* Better thermo errors handling. + *#* + *#* Revision 1.1 2005/05/24 09:17:58 batt + *#* Move drivers to top-level. + *#* + *#* Revision 1.4 2005/05/10 16:55:10 batt + *#* Add timeout to thermo-regulator; better thermo control handling; change thermo_getStatus() to thermo_status(). + *#* + *#* Revision 1.3 2005/05/10 09:26:54 batt + *#* Add thermo_getStatus for getting status/errors of thermo control. + *#* + *#* Revision 1.2 2005/05/09 19:18:40 batt + *#* Remove old logs. + *#* + *#* Revision 1.1 2005/05/09 16:40:44 batt + *#* Add thermo-control driver + *#*/ + + +#ifndef DRV_THERMO_H +#define DRV_THERMO_H + +#include +#include + +void thermo_init(void); + + +/*! + * Set the target temperature at which a given device should be kept. + * + * \param dev Device + * \param temperature Target temperature + */ +void thermo_setTarget(ThermoDev dev, deg_t temperature); + +/*! Start thermo control for a certain device \a dev */ +void thermo_start(ThermoDev dev); + +/*! Stop thermo control for a certain device \a dev */ +void thermo_stop(ThermoDev dev); + +/*! Clear errors for channel \a dev */ +void thermo_clearErrors(ThermoDev dev); + +/*! Return the status of the specific \a dev thermo-device. */ +thermostatus_t thermo_status(ThermoDev dev); + +/*! + * Return the current temperature of a device currently under thermo + * control. + * + * \param dev Device + * \return Current temperature (Celsius degrees * 10) + */ +deg_t thermo_read_temperature(ThermoDev dev); + + +#endif /* DRV_THERMO_H */ -- 2.25.1