blob: 347e986a3e4594fd552a4f1dab735c106007bf3a [file] [log] [blame]
/**************************************************************************//**
* @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_PERIOD_MS);
}
}