Fork of EFM32 Segment LCD library - used as pull request source for bugfixes
Fork of EFM32_SegmentLCD by
segmentlcd.c
- Committer:
- MarSik
- Date:
- 2017-04-01
- Revision:
- 7:c7948fbb8729
- Parent:
- 0:559902e88130
File content as of revision 7:c7948fbb8729:
/**************************************************************************//**
* @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 "clocking.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);
}