You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
543 lines
14 KiB
C
543 lines
14 KiB
C
15 years ago
|
/**************************************************************************//**
|
||
|
* @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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|