Fork of EFM32 Segment LCD library - used as pull request source for bugfixes
Fork of EFM32_SegmentLCD by
Diff: segmentlcd.c
- Revision:
- 0:559902e88130
- Child:
- 7:c7948fbb8729
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/segmentlcd.c Tue Mar 17 11:45:46 2015 -0500 @@ -0,0 +1,827 @@ +/**************************************************************************//** + * @file + * @brief EFM32 Segment LCD Display driver + * @version 3.20.9 + ****************************************************************************** + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * This file is licensensed under the Silabs License Agreement. See the file + * "Silabs_License_Agreement.txt" for details. Before using this software for + * any purpose, you must agree to the terms of that agreement. + * + ******************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdbool.h> +#include "em_device.h" +#include "em_cmu.h" +#include "em_gpio.h" + +#include "device_peripherals.h" + +#include "segmentlcd.h" + +/**************************************************************************//** + * @brief + * Defines each text symbol's segment in terms of COM and BIT numbers, + * in a way that we can enumerate each bit for each text segment in the + * following bit pattern: + * @verbatim + * -------0------ + * + * | \7 |8 /9 | + * |5 \ | / |1 + * + * --6--- ---10-- + * + * | / | \11 | + * |4 /13 |12 \ |2 + * + * -------3------ + * @endverbatim + * E.g.: First text character bit pattern #3 (above) is + * Segment 1D for Display + * Location COM 3, BIT 0 + *****************************************************************************/ +typedef struct +{ + uint8_t com[14]; /**< LCD COM line (for multiplexing) */ + uint8_t bit[14]; /**< LCD bit number */ +} CHAR_TypeDef; + + +/**************************************************************************//** + * @brief Defines segment COM and BIT fields numeric display + *****************************************************************************/ +typedef struct +{ + uint8_t com[7]; /**< LCD COM line (for multiplexing) */ + uint8_t bit[7]; /**< LCD bit number */ +} NUMBER_TypeDef; + +/**************************************************************************//** + * @brief Defines segment COM and BIT fields for Energy Modes on display + *****************************************************************************/ +typedef struct +{ + uint8_t com[5]; /**< LCD COM line (for multiplexing) */ + uint8_t bit[5]; /**< LCD bit number */ +} EM_TypeDef; + +/**************************************************************************//** + * @brief Defines segment COM and BIT fields for A-wheel (suited for Anim) + *****************************************************************************/ +typedef struct +{ + uint8_t com[8]; /**< LCD COM line (for multiplexing) */ + uint8_t bit[8]; /**< LCD bit number */ +} ARING_TypeDef; + +/**************************************************************************//** + * @brief Defines segment COM and BIT fields for A-wheel (suited for Anim) + *****************************************************************************/ +typedef struct +{ + uint8_t com[4]; /**< LCD COM line (for multiplexing) */ + uint8_t bit[4]; /**< LCD bit number */ +} BATTERY_TypeDef; + +/**************************************************************************//** + * @brief Defines prototype for all segments in display + *****************************************************************************/ +typedef struct +{ + CHAR_TypeDef Text[7]; /**< Text on display */ + NUMBER_TypeDef Number[4]; /**< Numbers on display */ + EM_TypeDef EMode; /**< Display energy mode */ + ARING_TypeDef ARing; /**< Display ring */ + BATTERY_TypeDef Battery; /**< Display battery */ +} MCU_DISPLAY; + +/**************************************************************************//** + * @brief Working instance of LCD display + *****************************************************************************/ +static const MCU_DISPLAY EFM_Display = EFM_DISPLAY_DEF; + + +/**************************************************************************//** + * @brief + * Defines higlighted segments for the alphabet, starting from "blank" (SPACE) + * Uses bit pattern as defined for text segments above. + * E.g. a capital O, would have bits 0 1 2 3 4 5 => 0x003f defined + *****************************************************************************/ +static const uint16_t EFM_Alphabet[] = { + 0x0000, /* space */ + 0x1100, /* ! */ + 0x0280, /* " */ + 0x0000, /* # */ + 0x0000, /* $ */ + 0x0602, /* % */ + 0x0000, /* & */ + 0x0020, /* ' */ + 0x0039, /* ( */ + 0x000f, /* ) */ + 0x0000, /* * */ + 0x1540, /* + */ + 0x2000, /* , */ + 0x0440, /* - */ + 0x1000, /* . */ + 0x2200, /* / */ + + 0x003f, /* 0 */ + 0x0006, /* 1 */ + 0x045b, /* 2 */ + 0x044f, /* 3 */ + 0x0466, /* 4 */ + 0x046d, /* 5 */ + 0x047d, /* 6 */ + 0x0007, /* 7 */ + 0x047f, /* 8 */ + 0x046f, /* 9 */ + + 0x0000, /* : */ + 0x0000, /* ; */ + 0x0a00, /* < */ + 0x0000, /* = */ + 0x2080, /* > */ + 0x0000, /* ? */ + 0xffff, /* @ */ + + 0x0477, /* A */ + 0x0a79, /* B */ + 0x0039, /* C */ + 0x20b0, /* D */ + 0x0079, /* E */ + 0x0071, /* F */ + 0x047d, /* G */ + 0x0476, /* H */ + 0x0006, /* I */ + 0x000e, /* J */ + 0x0a70, /* K */ + 0x0038, /* L */ + 0x02b6, /* M */ + 0x08b6, /* N */ + 0x003f, /* O */ + 0x0473, /* P */ + 0x083f, /* Q */ + 0x0c73, /* R */ + 0x046d, /* S */ + 0x1101, /* T */ + 0x003e, /* U */ + 0x2230, /* V */ + 0x2836, /* W */ + 0x2a80, /* X */ + 0x046e, /* Y */ + 0x2209, /* Z */ + + 0x0039, /* [ */ + 0x0880, /* backslash */ + 0x000f, /* ] */ + 0x0001, /* ^ */ + 0x0008, /* _ */ + 0x0100, /* ` */ + + 0x1058, /* a */ + 0x047c, /* b */ + 0x0058, /* c */ + 0x045e, /* d */ + 0x2058, /* e */ + 0x0471, /* f */ + 0x0c0c, /* g */ + 0x0474, /* h */ + 0x0004, /* i */ + 0x000e, /* j */ + 0x0c70, /* k */ + 0x0038, /* l */ + 0x1454, /* m */ + 0x0454, /* n */ + 0x045c, /* o */ + 0x0473, /* p */ + 0x0467, /* q */ + 0x0450, /* r */ + 0x0c08, /* s */ + 0x0078, /* t */ + 0x001c, /* u */ + 0x2010, /* v */ + 0x2814, /* w */ + 0x2a80, /* x */ + 0x080c, /* y */ + 0x2048, /* z */ + + 0x0000, +}; + +/**************************************************************************//** + * @brief + * Defines higlighted segments for the numeric display + *****************************************************************************/ + +static const uint16_t EFM_Numbers[] = { + 0x003f, /* 0 */ + 0x0006, /* 1 */ + 0x005b, /* 2 */ + 0x004f, /* 3 */ + 0x0066, /* 4 */ + 0x006d, /* 5 */ + 0x007d, /* 6 */ + 0x0007, /* 7 */ + 0x007f, /* 8 */ + 0x006f, /* 9 */ + 0x0077, /* A */ + 0x007c, /* b */ + 0x0039, /* C */ + 0x005e, /* d */ + 0x0079, /* E */ + 0x0071, /* F */ + 0x0040 /* - */ +}; + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ +/* sign is last element of the table */ +static const uint16_t signIndex = sizeof(EFM_Numbers)/sizeof(uint16_t) - 1 ; + +static const LCD_Init_TypeDef lcdInit = LCD_INIT_DEF; +/** @endcond */ + + +/**************************************************************************//** + * @brief Disable all segments + *****************************************************************************/ +void SegmentLCD_AllOff(void) +{ + /* Turn on low segments */ + LCD_ALL_SEGMENTS_OFF(); +} + + +/**************************************************************************//** + * @brief Enable all segments + *****************************************************************************/ +void SegmentLCD_AllOn(void) +{ + LCD_ALL_SEGMENTS_ON(); +} + + +/**************************************************************************//** + * @brief Turn all segments on alpha characters in display off + *****************************************************************************/ +void SegmentLCD_AlphaNumberOff(void) +{ + LCD_ALPHA_NUMBER_OFF(); + return; +} + + +/**************************************************************************//** + * @brief Light up or shut off Ring of Indicators + * @param anum "Segment number" on "Ring", range 0 - 7 + * @param on Zero is off, non-zero is on + *****************************************************************************/ +void SegmentLCD_ARing(int anum, int on) +{ + uint32_t com, bit; + + com = EFM_Display.ARing.com[anum]; + bit = EFM_Display.ARing.bit[anum]; + + if (on) + { + LCD_SegmentSet(com, bit, true); + } + else + { + LCD_SegmentSet(com, bit, false); + } +} + + +/**************************************************************************//** + * @brief Light up or shut off Battery Indicator + * @param batteryLevel Battery Level, 0 to 4 (0 turns all off) + *****************************************************************************/ +void SegmentLCD_Battery(int batteryLevel) +{ + uint32_t com, bit; + int i, on; + + for (i = 0; i < 4; i++) + { + if (i < batteryLevel) + { + on = 1; + } + else + { + on = 0; + } + com = EFM_Display.Battery.com[i]; + bit = EFM_Display.Battery.bit[i]; + + if (on) + { + LCD_SegmentSet(com, bit, true); + } + else + { + LCD_SegmentSet(com, bit, false); + } + } +} + + +/**************************************************************************//** + * @brief Disables LCD controller + *****************************************************************************/ +void SegmentLCD_Disable(void) +{ + /* Disable LCD */ + LCD_Enable(false); + + /* Make sure CTRL register has been updated */ + LCD_SyncBusyDelay(LCD_SYNCBUSY_CTRL); + + /* Turn off LCD clock */ + CMU_ClockEnable(cmuClock_LCD, false); + + /* Turn off voltage boost if enabled */ + CMU->LCDCTRL = 0; +} + + +/**************************************************************************//** + * @brief Light up or shut off Energy Mode indicator + * @param em Energy Mode numer 0 to 4 + * @param on Zero is off, non-zero is on + *****************************************************************************/ +void SegmentLCD_EnergyMode(int em, int on) +{ + uint32_t com, bit; + + com = EFM_Display.EMode.com[em]; + bit = EFM_Display.EMode.bit[em]; + + if (on) + { + LCD_SegmentSet(com, bit, true); + } + else + { + LCD_SegmentSet(com, bit, false); + } +} + +/**************************************************************************//** + * @brief Get frame rate division value corresponding to mux selection + * @param mux enum + *****************************************************************************/ +uint8_t SegmentLCD_GetFrameRateDiv(LCD_Mux_TypeDef muxSetting) { + switch(muxSetting) { + /** Static (segments can be multiplexed with LCD_COM[0]) */ + case lcdMuxStatic: return 2; + /** Duplex / 1/2 Duty cycle (segments can be multiplexed with LCD_COM[0:1]) */ + case lcdMuxDuplex: return 4; + /** Triplex / 1/3 Duty cycle (segments can be multiplexed with LCD_COM[0:2]) */ + case lcdMuxTriplex: return 6; + /** Quadruplex / 1/4 Duty cycle (segments can be multiplexed with LCD_COM[0:3]) */ + case lcdMuxQuadruplex: return 8; + #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /** Sextaplex / 1/6 Duty cycle (segments can be multiplexed with LCD_COM[0:5]) */ + case lcdMuxSextaplex: return 12; + /** Octaplex / 1/6 Duty cycle (segments can be multiplexed with LCD_COM[0:5]) */ + case lcdMuxOctaplex: return 16; + #endif + default: return 1; + } +} + +/**************************************************************************//** + * @brief Segment LCD Initialization routine for EFM32 STK display + * @param useBoost Set to use voltage boost + *****************************************************************************/ +void SegmentLCD_Init(bool useBoost) +{ + + /* Ensure LE modules are accessible */ + CMU_ClockEnable(cmuClock_CORELE, true); + + /* Enable LFRCO as LFACLK in CMU (will also enable oscillator if not enabled) */ + CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO); + + /* LCD Controller Prescaler */ + + /* Calculate value. Approach 50Hz for framerate. */ + uint32_t prescaler = (LOW_ENERGY_CLOCK_FREQUENCY / 32) / SegmentLCD_GetFrameRateDiv(lcdInit.mux); + + CMU_ClockDivSet(cmuClock_LCDpre, prescaler); + + /* Frame Rate */ + CMU_LCDClkFDIVSet(0); + + /* Enable clock to LCD module */ + CMU_ClockEnable(cmuClock_LCD, true); + + LCD_DISPLAY_ENABLE(); + + /* Disable interrupts */ + LCD_IntDisable(0xFFFFFFFF); + + /* Initialize and enable LCD controller */ + LCD_Init(&lcdInit); + + /* Enable all display segments */ + LCD_SEGMENTS_ENABLE(); + + /* Enable boost if necessary */ + if (useBoost) + { + LCD_VBoostSet(LCD_BOOST_LEVEL); + LCD_VLCDSelect(lcdVLCDSelVExtBoost); + CMU->LCDCTRL |= CMU_LCDCTRL_VBOOSTEN; + } + + /* Turn all segments off */ + SegmentLCD_AllOff(); + + LCD_SyncBusyDelay(0xFFFFFFFF); +} + + +/**************************************************************************//** + * @brief Write a hexadecimal number on lower alphanumeric part of + * Segment LCD display + * @param num Hexadecimal number value to put on display, in range 0 + * to 0x0FFFFFFF + *****************************************************************************/ +void SegmentLCD_LowerHex( uint32_t num ) +{ + int i; + char str[7]; + uint32_t nibble; + + SegmentLCD_Symbol(LCD_SYMBOL_MINUS, 0); + + for ( i=6; i>=0; i-- ) + { + nibble = num & 0xF; + + if ( nibble < 10 ) + str[i] = nibble + '0'; + else if ( nibble == 11 ) + str[i] = 'b'; + else if ( nibble == 13 ) + str[i] = 'd'; + else + str[i] = (nibble - 10) + 'A'; + + num >>= 4; + } + + SegmentLCD_Write(str); +} + +/**************************************************************************//** + * @brief Write number on lower alphanumeric part of Segment LCD display + * @param num Numeric value to put on display, in range -9999999 to +9999999 + *****************************************************************************/ +void SegmentLCD_LowerNumber( int num ) +{ + int i; + char str[7]; + + SegmentLCD_Symbol(LCD_SYMBOL_MINUS, 0); + + if ( ( num > 9999999 ) || ( num < -9999999 ) ) + { + SegmentLCD_Write("Ovrflow"); + return; + } + + if ( num < 0 ) + { + SegmentLCD_Symbol(LCD_SYMBOL_MINUS, 1); + num = -num; + } + + for ( i=6; i>=0; i-- ) + { + if ( ( i < 6 ) && ( num == 0 ) ) + { + str[i] = ' '; + } + else + { + str[i] = (num % 10) + '0'; + num /= 10; + } + } + + SegmentLCD_Write(str); +} + + +/**************************************************************************//** + * @brief Write number on numeric part on Segment LCD display + * @param value Numeric value to put on display, in range -999 to +9999 + *****************************************************************************/ +void SegmentLCD_Number(int value) +{ + int i, com, bit, digit, div, neg; + uint16_t bitpattern; + uint16_t num; + + /* Parameter consistancy check */ + if (value >= 9999) + { + value = 9999; + } + if (value <= -1000) + { + value = -999; + } + if (value < 0) + { + value = abs(value); + neg = 1; + } + else + { + neg = 0; + } + + /* If an update is in progress we must block, or there might be tearing */ + LCD_SyncBusyDelay(0xFFFFFFFF); + + /* Freeze updates to avoid partial refresh of display */ + LCD_FreezeEnable(true); + + /* Turn off all number LCD segments */ + SegmentLCD_NumberOff(); + + /* Extract useful digits */ + div = 1; + for (digit = 0; digit < 4; digit++) + { + num = (value / div) % 10; + if ((neg == 1) && (digit == 3)) num = signIndex; + /* Get number layout of display */ + bitpattern = EFM_Numbers[num]; + for (i = 0; i < 7; i++) + { + bit = EFM_Display.Number[digit].bit[i]; + com = EFM_Display.Number[digit].com[i]; + if (bitpattern & (1 << i)) + { + LCD_SegmentSet(com, bit, true); + } + } + div = div * 10; + } + /* Sync LCD registers to LE domain */ + LCD_FreezeEnable(false); +} + + +/**************************************************************************//** + * @brief Turn all segments on numeric digits in display off + *****************************************************************************/ +void SegmentLCD_NumberOff(void) +{ + /* Turn off all number segments */ + LCD_NUMBER_OFF(); + return; +} + + +/**************************************************************************//** + * @brief Light up or shut off various symbols on Segment LCD + * @param s Which symbol to turn on or off + * @param on Zero is off, non-zero is on + *****************************************************************************/ +void SegmentLCD_Symbol(lcdSymbol s, int on) +{ + int com = 0; + int bit = 0; + + switch (s) + { + case LCD_SYMBOL_GECKO: + com = LCD_SYMBOL_GECKO_COM; + bit = LCD_SYMBOL_GECKO_SEG; + break; + case LCD_SYMBOL_ANT: + com = LCD_SYMBOL_ANT_COM; + bit = LCD_SYMBOL_ANT_SEG; + break; + case LCD_SYMBOL_PAD0: + com = LCD_SYMBOL_PAD0_COM; + bit = LCD_SYMBOL_PAD0_SEG; + break; + case LCD_SYMBOL_PAD1: + com = LCD_SYMBOL_PAD1_COM; + bit = LCD_SYMBOL_PAD1_SEG; + break; + case LCD_SYMBOL_EFM32: + com = LCD_SYMBOL_EFM32_COM; + bit = LCD_SYMBOL_EFM32_SEG; + break; + case LCD_SYMBOL_MINUS: + com = LCD_SYMBOL_MINUS_COM; + bit = LCD_SYMBOL_MINUS_SEG; + break; + case LCD_SYMBOL_COL3: + com = LCD_SYMBOL_COL3_COM; + bit = LCD_SYMBOL_COL3_SEG; + break; + case LCD_SYMBOL_COL5: + com = LCD_SYMBOL_COL5_COM; + bit = LCD_SYMBOL_COL5_SEG; + break; + case LCD_SYMBOL_COL10: + com = LCD_SYMBOL_COL10_COM; + bit = LCD_SYMBOL_COL10_SEG; + break; +#ifdef LCD_SYMBOL_DEGC_SEG + case LCD_SYMBOL_DEGC: + com = LCD_SYMBOL_DEGC_COM; + bit = LCD_SYMBOL_DEGC_SEG; + break; +#endif +#ifdef LCD_SYMBOL_DEGF_SEG + case LCD_SYMBOL_DEGF: + com = LCD_SYMBOL_DEGF_COM; + bit = LCD_SYMBOL_DEGF_SEG; + break; +#endif +#ifdef LCD_SYMBOL_DP2_SEG + case LCD_SYMBOL_DP2: + com = LCD_SYMBOL_DP2_COM; + bit = LCD_SYMBOL_DP2_SEG; + break; +#endif +#ifdef LCD_SYMBOL_DP3_SEG + case LCD_SYMBOL_DP3: + com = LCD_SYMBOL_DP3_COM; + bit = LCD_SYMBOL_DP3_SEG; + break; +#endif +#ifdef LCD_SYMBOL_DP4_SEG + case LCD_SYMBOL_DP4: + com = LCD_SYMBOL_DP4_COM; + bit = LCD_SYMBOL_DP4_SEG; + break; +#endif +#ifdef LCD_SYMBOL_DP5_SEG + case LCD_SYMBOL_DP5: + com = LCD_SYMBOL_DP5_COM; + bit = LCD_SYMBOL_DP5_SEG; + break; +#endif + case LCD_SYMBOL_DP6: + com = LCD_SYMBOL_DP6_COM; + bit = LCD_SYMBOL_DP6_SEG; + break; + case LCD_SYMBOL_DP10: + com = LCD_SYMBOL_DP10_COM; + bit = LCD_SYMBOL_DP10_SEG; + break; +#ifdef LCD_SYMBOL_AM_SEG + case LCD_SYMBOL_AM: + com = LCD_SYMBOL_AM_COM; + bit = LCD_SYMBOL_AM_SEG; + break; +#endif +#ifdef LCD_SYMBOL_PM_SEG + case LCD_SYMBOL_PM: + com = LCD_SYMBOL_PM_COM; + bit = LCD_SYMBOL_PM_SEG; + break; +#endif +#ifdef LCD_SYMBOL_MICROAMP_SEG + case LCD_SYMBOL_MICROAMP: + com = LCD_SYMBOL_MICROAMP_COM; + bit = LCD_SYMBOL_MICROAMP_SEG; + break; +#endif +#ifdef LCD_SYMBOL_MILLIAMP_SEG + case LCD_SYMBOL_MILLIAMP: + com = LCD_SYMBOL_MILLIAMP_COM; + bit = LCD_SYMBOL_MILLIAMP_SEG; + break; +#endif + + } + if (on) + { + LCD_SegmentSet(com, bit, true); + } + else + { + LCD_SegmentSet(com, bit, false); + } +} + + +/**************************************************************************//** + * @brief Write hexadecimal number on numeric part on Segment LCD display + * @param value Numeric value to put on display, in range 0x0000-0xFFFF + *****************************************************************************/ +void SegmentLCD_UnsignedHex(uint16_t value) +{ + int num, i, com, bit, digit; + uint16_t bitpattern; + + /* Parameter consistancy check */ + if (value >= 0xffff) + { + value = 0xffff; + } + + /* If an update is in progress we must block, or there might be tearing */ + LCD_SyncBusyDelay(0xFFFFFFFF); + + /* Freeze updates to avoid partial refresh of display */ + LCD_FreezeEnable(true); + + /* Turn off all number LCD segments */ + SegmentLCD_NumberOff(); + + for (digit = 0; digit < 4; digit++) + { + num = (value >> (4 * digit)) & 0x0f; + bitpattern = EFM_Numbers[num]; + for (i = 0; i < 7; i++) + { + bit = EFM_Display.Number[digit].bit[i]; + com = EFM_Display.Number[digit].com[i]; + if (bitpattern & (1 << i)) + { + LCD_SegmentSet(com, bit, true); + } + } + } + + /* Sync LCD registers to LE domain */ + LCD_FreezeEnable(false); +} + + +/**************************************************************************//** + * @brief Write text on LCD display + * @param string Text string to show on display + *****************************************************************************/ +void SegmentLCD_Write(char *string) +{ + int data, length, index; + uint16_t bitfield; + uint32_t com, bit; + int i; + + length = strlen(string); + index = 0; + + /* If an update is in progress we must block, or there might be tearing */ + LCD_SyncBusyDelay(0xFFFFFFFF); + + /* Freeze LCD to avoid partial updates */ + LCD_FreezeEnable(true); + + /* Turn all segments off */ + SegmentLCD_AlphaNumberOff(); + + /* Fill out all characters on display */ + for (index = 0; index < 7; index++) + { + if (index < length) + { + data = (int) *string; + } + else /* Padding with space */ + { + data = 0x20; /* SPACE */ + } + /* Defined letters currently starts at "SPACE" - ASCII 0x20; */ + data = data - 0x20; + /* Get font for this letter */ + bitfield = EFM_Alphabet[data]; + + for (i = 0; i < 14; i++) + { + bit = EFM_Display.Text[index].bit[i]; + com = EFM_Display.Text[index].com[i]; + + if (bitfield & (1 << i)) + { + /* Turn on segment */ + LCD_SegmentSet(com, bit, true); + } + } + string++; + } + /* Enable update */ + LCD_FreezeEnable(false); +}