/**************************************************************************//** | |
* @file | |
* @brief LCD Controller driver | |
* @author Energy Micro AS | |
* @version 1.0.1 | |
****************************************************************************** | |
* @section License | |
* <b>(C) Copyright 2009 Energy Micro AS, http://www.energymicro.com</b> | |
****************************************************************************** | |
* | |
* This source code is the property of Energy Micro AS. The source and compiled | |
* code may only be used on Energy Micro "EFM32" microcontrollers. | |
* | |
* This copyright notice may not be removed from the source code nor changed. | |
* | |
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no | |
* obligation to support this Software. Energy Micro AS is providing the | |
* Software "AS IS", with no express or implied warranties of any kind, | |
* including, but not limited to, any implied warranties of merchantability | |
* or fitness for any particular purpose or warranties against infringement | |
* of any proprietary rights of a third party. | |
* | |
* Energy Micro AS will not be liable for any consequential, incidental, or | |
* special damages, or any other relief, or for any claim by any third party, | |
* arising from your use of this Software. | |
* | |
*****************************************************************************/ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include "efm32.h" | |
#include "lcdcontroller.h" | |
#include "lcddisplay.h" | |
/** Counts every n'th frame */ | |
int frameCounter = 0; | |
/**************************************************************************//** | |
* @brief LCD Interrupt Handler, triggers on frame counter, every n'th frame | |
*****************************************************************************/ | |
void LCD_IRQHandler(void) | |
{ | |
LCD_TypeDef *lcd = LCD; | |
/* clear interrupt */ | |
lcd->IFC = 0xFFFFFFFF; | |
frameCounter++; | |
} | |
/**************************************************************************//** | |
* @brief Enables a segment on the LCD display | |
* @param lcd Pointer to LCD register block | |
* @param com COM segment number | |
* @param bitvalue Bit value for segment | |
*****************************************************************************/ | |
static void LCD_enableSegment(LCD_TypeDef * lcd, int com, int bitvalue) | |
{ | |
switch (com) | |
{ | |
case 0: | |
lcd->SEGD0L |= bitvalue; | |
break; | |
case 1: | |
lcd->SEGD1L |= bitvalue; | |
break; | |
case 2: | |
lcd->SEGD2L |= bitvalue; | |
break; | |
case 3: | |
lcd->SEGD3L |= bitvalue; | |
break; | |
case 4: | |
lcd->SEGD0H |= bitvalue; | |
break; | |
case 5: | |
lcd->SEGD1H |= bitvalue; | |
break; | |
case 6: | |
lcd->SEGD2H |= bitvalue; | |
break; | |
case 7: | |
lcd->SEGD3H |= bitvalue; | |
break; | |
} | |
} | |
/**************************************************************************//** | |
* @brief Disables a segment on the LCD Display | |
* @param lcd Pointer to LCD register structure | |
* @param com COM segment number | |
* @param bitvalue Bit value for segment | |
*****************************************************************************/ | |
static void LCD_disableSegment(LCD_TypeDef * lcd, int com, int bitvalue) | |
{ | |
switch (com) | |
{ | |
case 0: | |
lcd->SEGD0L &= ~bitvalue; | |
break; | |
case 1: | |
lcd->SEGD1L &= ~bitvalue; | |
break; | |
case 2: | |
lcd->SEGD2L &= ~bitvalue; | |
break; | |
case 3: | |
lcd->SEGD3L &= ~bitvalue; | |
break; | |
case 4: | |
lcd->SEGD0H &= ~bitvalue; | |
break; | |
case 5: | |
lcd->SEGD1H &= ~bitvalue; | |
break; | |
case 6: | |
lcd->SEGD2H &= ~bitvalue; | |
break; | |
case 7: | |
lcd->SEGD3H &= ~bitvalue; | |
break; | |
} | |
} | |
/**************************************************************************//** | |
* @brief Write number on numeric part on LCD display | |
* @param lcd Pointer to LCD control block | |
* @param value Numeric value to put on display, in range -999 to +9999 | |
*****************************************************************************/ | |
void LCD_Number(LCD_TypeDef *lcd, int value) | |
{ | |
int num, i, com, bit, digit, div, neg; | |
uint16_t bitpattern; | |
/* Parameter consistancy check */ | |
if (value >= 9999) | |
{ | |
value = 9999; | |
} | |
if (value <= -1000) | |
{ | |
value = -999; | |
} | |
if (value < 0) | |
{ | |
value = abs(value); | |
neg = 1; | |
} | |
else | |
{ | |
neg = 0; | |
} | |
/* Extract useful digits */ | |
div = 1; | |
for (digit = 0; digit < 4; digit++) | |
{ | |
num = (value / div) % 10; | |
if ((neg == 1) && (digit == 3)) num = 10; | |
bitpattern = EM_Numbers[num]; | |
for (i = 0; i < 7; i++) | |
{ | |
bit = EFMDisplay.Number[digit].bit[i]; | |
com = EFMDisplay.Number[digit].com[i]; | |
if (bitpattern & (1 << i)) | |
{ | |
LCD_enableSegment(lcd, com, 1 << bit); | |
} | |
else | |
{ | |
LCD_disableSegment(lcd, com, 1 << bit); | |
} | |
} | |
div = div * 10; | |
} | |
} | |
/**************************************************************************//** | |
* @brief Turn all segments on numeric display off | |
* @param lcd Pointer to LCD register structure | |
*****************************************************************************/ | |
void LCD_NumberOff(LCD_TypeDef *lcd) | |
{ | |
int digit, i, bit, com; | |
/* Turn off all segments */ | |
for (digit = 0; digit < 4; digit++) | |
{ | |
for (i = 0; i < 7; i++) | |
{ | |
bit = EFMDisplay.Number[digit].bit[i]; | |
com = EFMDisplay.Number[digit].com[i]; | |
LCD_disableSegment(lcd, com, 1 << bit); | |
} | |
} | |
return; | |
} | |
/**************************************************************************//** | |
* @brief Write text on LCD display | |
* @param lcd Pointer to LCD register structure | |
* @param string Text string to show on display | |
*****************************************************************************/ | |
void LCD_Write(LCD_TypeDef *lcd, char *string) | |
{ | |
int data, length, index; | |
uint16_t bitfield; | |
uint32_t value; | |
uint32_t com, bit; | |
int i; | |
length = strlen(string); | |
index = 0; | |
/* 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" - 0x20; */ | |
data = data - 0x20; | |
bitfield = EM_alphabet[data]; | |
for (i = 0; i < 14; i++) | |
{ | |
bit = EFMDisplay.Text[index].bit[i]; | |
com = EFMDisplay.Text[index].com[i]; | |
value = (1 << bit); | |
if (bitfield & (1 << i)) | |
{ | |
/* Turn on segment */ | |
LCD_enableSegment(lcd, com, value); | |
} | |
else | |
{ | |
/* Turn off segment */ | |
LCD_disableSegment(lcd, com, value); | |
} | |
} | |
string++; | |
} | |
while (lcd->SYNCBUSY) ; | |
} | |
/**************************************************************************//** | |
* @brief LCD Disable all segments | |
* @param lcd Pointer to LCD register block | |
*****************************************************************************/ | |
void LCD_AllOff(LCD_TypeDef *lcd) | |
{ | |
lcd->SEGD0L = 0x00000000; | |
lcd->SEGD0H = 0x00000000; | |
lcd->SEGD1L = 0x00000000; | |
lcd->SEGD1H = 0x00000000; | |
lcd->SEGD2L = 0x00000000; | |
lcd->SEGD2H = 0x00000000; | |
lcd->SEGD3L = 0x00000000; | |
lcd->SEGD3H = 0x00000000; | |
while (lcd->SYNCBUSY) ; | |
} | |
/**************************************************************************//** | |
* @brief LCD Enable all segments | |
* @param lcd Pointer to LCD register block | |
*****************************************************************************/ | |
void LCD_AllOn(LCD_TypeDef *lcd) | |
{ | |
lcd->SEGD0L = 0xffffffff; | |
lcd->SEGD0H = 0xffffffff; | |
lcd->SEGD1L = 0xffffffff; | |
lcd->SEGD1H = 0xffffffff; | |
lcd->SEGD2L = 0xffffffff; | |
lcd->SEGD2H = 0xffffffff; | |
lcd->SEGD3L = 0xffffffff; | |
lcd->SEGD3H = 0xffffffff; | |
while (lcd->SYNCBUSY) ; | |
} | |
/**************************************************************************//** | |
* @brief LCD Light up or shut off Energy Mode indicator | |
* @param lcd Pointer to LCD register block | |
* @pararm em Energy Mode numer 0 to 4 | |
* @param on Zero is off, non-zero is on | |
*****************************************************************************/ | |
void LCD_EnergyMode(LCD_TypeDef *lcd, int em, int on) | |
{ | |
uint32_t com, bitvalue; | |
com = EFMDisplay.EMode.com[em]; | |
bitvalue = 1 << EFMDisplay.EMode.bit[em]; | |
if (on) | |
{ | |
LCD_enableSegment(lcd, com, bitvalue); | |
} | |
else | |
{ | |
LCD_disableSegment(lcd, com, bitvalue); | |
} | |
} | |
/**************************************************************************//** | |
* @brief LCD Light up or shut off Ring of Indicators | |
* @param lcd Pointer to LCD register block | |
* @param anum "Segment number" on "Ring", range 0 - 7 | |
* @param on Zero is off, non-zero is on | |
*****************************************************************************/ | |
void LCD_ARing(LCD_TypeDef *lcd, int anum, int on) | |
{ | |
uint32_t com, bitvalue; | |
com = EFMDisplay.ARing.com[anum]; | |
bitvalue = 1 << EFMDisplay.ARing.bit[anum]; | |
if (on) | |
{ | |
LCD_enableSegment(lcd, com, bitvalue); | |
} | |
else | |
{ | |
LCD_disableSegment(lcd, com, bitvalue); | |
} | |
} | |
/**************************************************************************//** | |
* @brief LCD Light up or shut off various symbols on LCD Display | |
* @param lcd Pointer to LCD register block | |
* @param s Which symbol to turn on or off | |
* @param on Zero is off, non-zero is on | |
*****************************************************************************/ | |
void LCD_Symbol(LCD_TypeDef *lcd, lcdSymbol s, int on) | |
{ | |
int com, bit; | |
switch (s) | |
{ | |
case LCD_SYMBOL_GECKO: | |
com = 3; bit = 8; | |
break; | |
case LCD_SYMBOL_ANT: | |
com = 3; bit = 1; | |
break; | |
case LCD_SYMBOL_PAD0: | |
com = 1; bit = 8; | |
break; | |
case LCD_SYMBOL_PAD1: | |
com = 2; bit = 8; | |
break; | |
case LCD_SYMBOL_AM: | |
com = 4; bit = 0; | |
break; | |
case LCD_SYMBOL_PM: | |
com = 4; bit = 3; | |
break; | |
case LCD_SYMBOL_EFM32: | |
com = 0; bit = 8; | |
break; | |
case LCD_SYMBOL_MINUS: | |
com = 0; bit = 9; | |
break; | |
case LCD_SYMBOL_COL3: | |
com = 0; bit = 16; | |
break; | |
case LCD_SYMBOL_COL5: | |
com = 0; bit = 24; | |
break; | |
case LCD_SYMBOL_COL10: | |
com = 4; bit = 7; | |
break; | |
case LCD_SYMBOL_DP2: | |
com = 4; bit = 2; | |
break; | |
case LCD_SYMBOL_DP3: | |
com = 5; bit = 2; | |
break; | |
case LCD_SYMBOL_DP4: | |
com = 6; bit = 2; | |
break; | |
case LCD_SYMBOL_DP5: | |
com = 7; bit = 2; | |
break; | |
case LCD_SYMBOL_DP6: | |
com = 0; bit = 21; | |
break; | |
case LCD_SYMBOL_DP10: | |
com = 4; bit = 5; | |
break; | |
} | |
if (on) | |
{ | |
LCD_enableSegment(lcd, com, 1 << bit); | |
} | |
else | |
{ | |
LCD_disableSegment(lcd, com, 1 << bit); | |
} | |
} | |
/**************************************************************************//** | |
* @brief LCD Light up or shut off Battery Indicator | |
* @param lcd Pointer to LCD register block | |
* @param batteryLevel Battery Level, 0 to 4 (0 turns all off) | |
*****************************************************************************/ | |
void LCD_Battery(LCD_TypeDef *lcd, int batteryLevel) | |
{ | |
uint32_t com, bitvalue; | |
int i, on; | |
for (i = 0; i < 4; i++) | |
{ | |
if (i < batteryLevel) | |
{ | |
on = 1; | |
} | |
else | |
{ | |
on = 0; | |
} | |
com = EFMDisplay.Battery.com[i]; | |
bitvalue = 1 << EFMDisplay.Battery.bit[i]; | |
if (on) | |
{ | |
LCD_enableSegment(lcd, com, bitvalue); | |
} | |
else | |
{ | |
LCD_disableSegment(lcd, com, bitvalue); | |
} | |
} | |
} | |
/**************************************************************************//** | |
* @brief LCD Initialization routine for EFM32 DVK display | |
* @param lcd Pointer to LCD register block | |
*****************************************************************************/ | |
void LCD_Init(LCD_TypeDef *lcd) | |
{ | |
CMU_TypeDef *cmu = CMU; | |
/* Enable LFXO oscillator */ | |
cmu->OSCENCMD |= CMU_OSCENCMD_LFXOEN; | |
while (!(cmu->STATUS & CMU_STATUS_LFXORDY)) ; | |
/* Enable LCD clock in CMU */ | |
cmu->LFACLKEN0 |= CMU_LFACLKEN0_LCD; | |
/* Select LFXO for LCD */ | |
cmu->LFCLKSEL = CMU_LFCLKSEL_LFA_LFXO | CMU_LFCLKSEL_LFB_LFXO; | |
/* LCD Controller Prescaler (divide by 1) */ | |
/* CLKlcd = 0.25 kHz */ | |
cmu->LFAPRESC0 &= ~_CMU_LFAPRESC0_LCD_MASK; | |
cmu->LFAPRESC0 |= _CMU_LFAPRESC0_LCD_DIV128 << _CMU_LFAPRESC0_LCD_SHIFT; | |
/* Set up interrupt handler */ | |
lcd->IEN = 0; | |
while (lcd->SYNCBUSY) ; | |
/* Clear pending interrupts */ | |
lcd->IFC = ~0; | |
/* Enable interrupt */ | |
NVIC_EnableIRQ(LCD_IRQn); | |
lcd->IEN = LCD_IEN_FC; | |
/* Frame rate is 32Hz, 0.25Khz LFCLK128, QUADRUPLEX mode, FDIV=0 */ | |
lcd->DISPCTRL = LCD_DISPCTRL_MUX_QUADRUPLEX | | |
LCD_DISPCTRL_BIAS_ONETHIRD | | |
LCD_DISPCTRL_WAVE_LOWPOWER | | |
LCD_DISPCTRL_CONLEV_MAX | | |
LCD_DISPCTRL_VLCDSEL_VDD | | |
LCD_DISPCTRL_VBLEV_3V00; | |
/* No voltage boost, framerate 32Hz */ | |
cmu->LCDCTRL = 0; | |
/* Turn all segments off */ | |
LCD_AllOff(lcd); | |
/* Enable all segment registers */ | |
lcd->SEGEN = 0x000003FF; | |
lcd->CTRL = LCD_CTRL_EN | LCD_CTRL_UDCTRL_FRAMESTART; | |
while (lcd->SYNCBUSY) ; | |
/* Configure LCD to give a frame counter interrupt every 8th frame. */ | |
lcd->BACTRL = LCD_BACTRL_FCEN | (7 << _LCD_BACTRL_FCTOP_SHIFT) | (0 << _LCD_BACTRL_FCPRESC_SHIFT); | |
while (lcd->SYNCBUSY) ; | |
lcd->IFC = LCD_IFC_FC; | |
lcd->IEN = LCD_IEN_FC; | |
} | |
/**************************************************************************//** | |
* @brief Disables LCD controller | |
* @param lcd Pointer to LCD register block | |
*****************************************************************************/ | |
void LCD_Disable(LCD_TypeDef *lcd) | |
{ | |
CMU_TypeDef *cmu = CMU; | |
/* Turn off interrupts */ | |
lcd->IEN = 0x00000000; | |
lcd->IFC = LCD_IFC_FC; | |
NVIC_DisableIRQ(LCD_IRQn); | |
/* Disable LCD */ | |
lcd->CTRL = 0; | |
/* Turn off LCD clock */ | |
cmu->LFACLKEN0 &= ~(CMU_LFACLKEN0_LCD); | |
/* Turn off voltage boost if enabled */ | |
cmu->LCDCTRL = 0; | |
} | |
/**************************************************************************//** | |
* @brief LCD scrolls a text over the display, sort of "polled printf" | |
* @param lcd Pointer to LCD register block | |
*****************************************************************************/ | |
void LCD_ScrollText(LCD_TypeDef *lcd, char *scrolltext) | |
{ | |
int i, len; | |
char buffer[8]; | |
buffer[7] = 0x00; | |
len = strlen(scrolltext); | |
if (len < 7) return; | |
for (i = 0; i < (len - 7); i++) | |
{ | |
memcpy(buffer, scrolltext + i, 7); | |
LCD_Write(lcd, buffer); | |
vTaskDelay(100/portTICK_RATE_MS); | |
} | |
} | |