1 /**************************************************************************//**
\r
3 * @brief LCD Controller driver
\r
4 * @author Energy Micro AS
\r
6 ******************************************************************************
\r
8 * <b>(C) Copyright 2009 Energy Micro AS, http://www.energymicro.com</b>
\r
9 ******************************************************************************
\r
11 * This source code is the property of Energy Micro AS. The source and compiled
\r
12 * code may only be used on Energy Micro "EFM32" microcontrollers.
\r
14 * This copyright notice may not be removed from the source code nor changed.
\r
16 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
\r
17 * obligation to support this Software. Energy Micro AS is providing the
\r
18 * Software "AS IS", with no express or implied warranties of any kind,
\r
19 * including, but not limited to, any implied warranties of merchantability
\r
20 * or fitness for any particular purpose or warranties against infringement
\r
21 * of any proprietary rights of a third party.
\r
23 * Energy Micro AS will not be liable for any consequential, incidental, or
\r
24 * special damages, or any other relief, or for any claim by any third party,
\r
25 * arising from your use of this Software.
\r
27 *****************************************************************************/
\r
28 #include "FreeRTOS.h"
\r
35 #include "lcdcontroller.h"
\r
36 #include "lcddisplay.h"
\r
38 /** Counts every n'th frame */
\r
39 int frameCounter = 0;
\r
41 /**************************************************************************//**
\r
42 * @brief LCD Interrupt Handler, triggers on frame counter, every n'th frame
\r
43 *****************************************************************************/
\r
44 void LCD_IRQHandler(void)
\r
46 LCD_TypeDef *lcd = LCD;
\r
48 /* clear interrupt */
\r
49 lcd->IFC = 0xFFFFFFFF;
\r
53 /**************************************************************************//**
\r
54 * @brief Enables a segment on the LCD display
\r
55 * @param lcd Pointer to LCD register block
\r
56 * @param com COM segment number
\r
57 * @param bitvalue Bit value for segment
\r
58 *****************************************************************************/
\r
59 static void LCD_enableSegment(LCD_TypeDef * lcd, int com, int bitvalue)
\r
64 lcd->SEGD0L |= bitvalue;
\r
67 lcd->SEGD1L |= bitvalue;
\r
70 lcd->SEGD2L |= bitvalue;
\r
73 lcd->SEGD3L |= bitvalue;
\r
76 lcd->SEGD0H |= bitvalue;
\r
79 lcd->SEGD1H |= bitvalue;
\r
82 lcd->SEGD2H |= bitvalue;
\r
85 lcd->SEGD3H |= bitvalue;
\r
90 /**************************************************************************//**
\r
91 * @brief Disables a segment on the LCD Display
\r
92 * @param lcd Pointer to LCD register structure
\r
93 * @param com COM segment number
\r
94 * @param bitvalue Bit value for segment
\r
95 *****************************************************************************/
\r
96 static void LCD_disableSegment(LCD_TypeDef * lcd, int com, int bitvalue)
\r
101 lcd->SEGD0L &= ~bitvalue;
\r
104 lcd->SEGD1L &= ~bitvalue;
\r
107 lcd->SEGD2L &= ~bitvalue;
\r
110 lcd->SEGD3L &= ~bitvalue;
\r
113 lcd->SEGD0H &= ~bitvalue;
\r
116 lcd->SEGD1H &= ~bitvalue;
\r
119 lcd->SEGD2H &= ~bitvalue;
\r
122 lcd->SEGD3H &= ~bitvalue;
\r
127 /**************************************************************************//**
\r
128 * @brief Write number on numeric part on LCD display
\r
129 * @param lcd Pointer to LCD control block
\r
130 * @param value Numeric value to put on display, in range -999 to +9999
\r
131 *****************************************************************************/
\r
132 void LCD_Number(LCD_TypeDef *lcd, int value)
\r
134 int num, i, com, bit, digit, div, neg;
\r
135 uint16_t bitpattern;
\r
137 /* Parameter consistancy check */
\r
142 if (value <= -1000)
\r
148 value = abs(value);
\r
155 /* Extract useful digits */
\r
157 for (digit = 0; digit < 4; digit++)
\r
159 num = (value / div) % 10;
\r
160 if ((neg == 1) && (digit == 3)) num = 10;
\r
161 bitpattern = EM_Numbers[num];
\r
162 for (i = 0; i < 7; i++)
\r
164 bit = EFMDisplay.Number[digit].bit[i];
\r
165 com = EFMDisplay.Number[digit].com[i];
\r
166 if (bitpattern & (1 << i))
\r
168 LCD_enableSegment(lcd, com, 1 << bit);
\r
172 LCD_disableSegment(lcd, com, 1 << bit);
\r
179 /**************************************************************************//**
\r
180 * @brief Turn all segments on numeric display off
\r
181 * @param lcd Pointer to LCD register structure
\r
182 *****************************************************************************/
\r
183 void LCD_NumberOff(LCD_TypeDef *lcd)
\r
185 int digit, i, bit, com;
\r
187 /* Turn off all segments */
\r
188 for (digit = 0; digit < 4; digit++)
\r
190 for (i = 0; i < 7; i++)
\r
192 bit = EFMDisplay.Number[digit].bit[i];
\r
193 com = EFMDisplay.Number[digit].com[i];
\r
194 LCD_disableSegment(lcd, com, 1 << bit);
\r
201 /**************************************************************************//**
\r
202 * @brief Write text on LCD display
\r
203 * @param lcd Pointer to LCD register structure
\r
204 * @param string Text string to show on display
\r
205 *****************************************************************************/
\r
206 void LCD_Write(LCD_TypeDef *lcd, char *string)
\r
208 int data, length, index;
\r
214 length = strlen(string);
\r
216 /* fill out all characters on display */
\r
217 for (index = 0; index < 7; index++)
\r
219 if (index < length)
\r
221 data = (int) *string;
\r
223 else /* padding with space */
\r
225 data = 0x20; /* SPACE */
\r
227 /* defined letters currently starts at "SPACE" - 0x20; */
\r
228 data = data - 0x20;
\r
229 bitfield = EM_alphabet[data];
\r
232 for (i = 0; i < 14; i++)
\r
234 bit = EFMDisplay.Text[index].bit[i];
\r
235 com = EFMDisplay.Text[index].com[i];
\r
236 value = (1 << bit);
\r
238 if (bitfield & (1 << i))
\r
240 /* Turn on segment */
\r
241 LCD_enableSegment(lcd, com, value);
\r
245 /* Turn off segment */
\r
246 LCD_disableSegment(lcd, com, value);
\r
251 while (lcd->SYNCBUSY) ;
\r
254 /**************************************************************************//**
\r
255 * @brief LCD Disable all segments
\r
256 * @param lcd Pointer to LCD register block
\r
257 *****************************************************************************/
\r
258 void LCD_AllOff(LCD_TypeDef *lcd)
\r
260 lcd->SEGD0L = 0x00000000;
\r
261 lcd->SEGD0H = 0x00000000;
\r
262 lcd->SEGD1L = 0x00000000;
\r
263 lcd->SEGD1H = 0x00000000;
\r
264 lcd->SEGD2L = 0x00000000;
\r
265 lcd->SEGD2H = 0x00000000;
\r
266 lcd->SEGD3L = 0x00000000;
\r
267 lcd->SEGD3H = 0x00000000;
\r
268 while (lcd->SYNCBUSY) ;
\r
271 /**************************************************************************//**
\r
272 * @brief LCD Enable all segments
\r
273 * @param lcd Pointer to LCD register block
\r
274 *****************************************************************************/
\r
275 void LCD_AllOn(LCD_TypeDef *lcd)
\r
277 lcd->SEGD0L = 0xffffffff;
\r
278 lcd->SEGD0H = 0xffffffff;
\r
279 lcd->SEGD1L = 0xffffffff;
\r
280 lcd->SEGD1H = 0xffffffff;
\r
281 lcd->SEGD2L = 0xffffffff;
\r
282 lcd->SEGD2H = 0xffffffff;
\r
283 lcd->SEGD3L = 0xffffffff;
\r
284 lcd->SEGD3H = 0xffffffff;
\r
285 while (lcd->SYNCBUSY) ;
\r
288 /**************************************************************************//**
\r
289 * @brief LCD Light up or shut off Energy Mode indicator
\r
290 * @param lcd Pointer to LCD register block
\r
291 * @pararm em Energy Mode numer 0 to 4
\r
292 * @param on Zero is off, non-zero is on
\r
293 *****************************************************************************/
\r
294 void LCD_EnergyMode(LCD_TypeDef *lcd, int em, int on)
\r
296 uint32_t com, bitvalue;
\r
298 com = EFMDisplay.EMode.com[em];
\r
299 bitvalue = 1 << EFMDisplay.EMode.bit[em];
\r
303 LCD_enableSegment(lcd, com, bitvalue);
\r
307 LCD_disableSegment(lcd, com, bitvalue);
\r
311 /**************************************************************************//**
\r
312 * @brief LCD Light up or shut off Ring of Indicators
\r
313 * @param lcd Pointer to LCD register block
\r
314 * @param anum "Segment number" on "Ring", range 0 - 7
\r
315 * @param on Zero is off, non-zero is on
\r
316 *****************************************************************************/
\r
317 void LCD_ARing(LCD_TypeDef *lcd, int anum, int on)
\r
319 uint32_t com, bitvalue;
\r
321 com = EFMDisplay.ARing.com[anum];
\r
322 bitvalue = 1 << EFMDisplay.ARing.bit[anum];
\r
326 LCD_enableSegment(lcd, com, bitvalue);
\r
330 LCD_disableSegment(lcd, com, bitvalue);
\r
334 /**************************************************************************//**
\r
335 * @brief LCD Light up or shut off various symbols on LCD Display
\r
336 * @param lcd Pointer to LCD register block
\r
337 * @param s Which symbol to turn on or off
\r
338 * @param on Zero is off, non-zero is on
\r
339 *****************************************************************************/
\r
340 void LCD_Symbol(LCD_TypeDef *lcd, lcdSymbol s, int on)
\r
346 case LCD_SYMBOL_GECKO:
\r
349 case LCD_SYMBOL_ANT:
\r
352 case LCD_SYMBOL_PAD0:
\r
355 case LCD_SYMBOL_PAD1:
\r
358 case LCD_SYMBOL_AM:
\r
361 case LCD_SYMBOL_PM:
\r
364 case LCD_SYMBOL_EFM32:
\r
367 case LCD_SYMBOL_MINUS:
\r
370 case LCD_SYMBOL_COL3:
\r
373 case LCD_SYMBOL_COL5:
\r
376 case LCD_SYMBOL_COL10:
\r
379 case LCD_SYMBOL_DP2:
\r
382 case LCD_SYMBOL_DP3:
\r
385 case LCD_SYMBOL_DP4:
\r
388 case LCD_SYMBOL_DP5:
\r
391 case LCD_SYMBOL_DP6:
\r
394 case LCD_SYMBOL_DP10:
\r
400 LCD_enableSegment(lcd, com, 1 << bit);
\r
404 LCD_disableSegment(lcd, com, 1 << bit);
\r
408 /**************************************************************************//**
\r
409 * @brief LCD Light up or shut off Battery Indicator
\r
410 * @param lcd Pointer to LCD register block
\r
411 * @param batteryLevel Battery Level, 0 to 4 (0 turns all off)
\r
412 *****************************************************************************/
\r
413 void LCD_Battery(LCD_TypeDef *lcd, int batteryLevel)
\r
415 uint32_t com, bitvalue;
\r
418 for (i = 0; i < 4; i++)
\r
420 if (i < batteryLevel)
\r
428 com = EFMDisplay.Battery.com[i];
\r
429 bitvalue = 1 << EFMDisplay.Battery.bit[i];
\r
433 LCD_enableSegment(lcd, com, bitvalue);
\r
437 LCD_disableSegment(lcd, com, bitvalue);
\r
442 /**************************************************************************//**
\r
443 * @brief LCD Initialization routine for EFM32 DVK display
\r
444 * @param lcd Pointer to LCD register block
\r
445 *****************************************************************************/
\r
446 void LCD_Init(LCD_TypeDef *lcd)
\r
448 CMU_TypeDef *cmu = CMU;
\r
450 /* Enable LFXO oscillator */
\r
451 cmu->OSCENCMD |= CMU_OSCENCMD_LFXOEN;
\r
452 while (!(cmu->STATUS & CMU_STATUS_LFXORDY)) ;
\r
454 /* Enable LCD clock in CMU */
\r
455 cmu->LFACLKEN0 |= CMU_LFACLKEN0_LCD;
\r
457 /* Select LFXO for LCD */
\r
458 cmu->LFCLKSEL = CMU_LFCLKSEL_LFA_LFXO | CMU_LFCLKSEL_LFB_LFXO;
\r
460 /* LCD Controller Prescaler (divide by 1) */
\r
461 /* CLKlcd = 0.25 kHz */
\r
462 cmu->LFAPRESC0 &= ~_CMU_LFAPRESC0_LCD_MASK;
\r
463 cmu->LFAPRESC0 |= _CMU_LFAPRESC0_LCD_DIV128 << _CMU_LFAPRESC0_LCD_SHIFT;
\r
465 /* Set up interrupt handler */
\r
467 while (lcd->SYNCBUSY) ;
\r
469 /* Clear pending interrupts */
\r
471 /* Enable interrupt */
\r
472 NVIC_EnableIRQ(LCD_IRQn);
\r
473 lcd->IEN = LCD_IEN_FC;
\r
475 /* Frame rate is 32Hz, 0.25Khz LFCLK128, QUADRUPLEX mode, FDIV=0 */
\r
476 lcd->DISPCTRL = LCD_DISPCTRL_MUX_QUADRUPLEX |
\r
477 LCD_DISPCTRL_BIAS_ONETHIRD |
\r
478 LCD_DISPCTRL_WAVE_LOWPOWER |
\r
479 LCD_DISPCTRL_CONLEV_MAX |
\r
480 LCD_DISPCTRL_VLCDSEL_VDD |
\r
481 LCD_DISPCTRL_VBLEV_3V00;
\r
483 /* No voltage boost, framerate 32Hz */
\r
486 /* Turn all segments off */
\r
489 /* Enable all segment registers */
\r
490 lcd->SEGEN = 0x000003FF;
\r
491 lcd->CTRL = LCD_CTRL_EN | LCD_CTRL_UDCTRL_FRAMESTART;
\r
492 while (lcd->SYNCBUSY) ;
\r
494 /* Configure LCD to give a frame counter interrupt every 8th frame. */
\r
495 lcd->BACTRL = LCD_BACTRL_FCEN | (7 << _LCD_BACTRL_FCTOP_SHIFT) | (0 << _LCD_BACTRL_FCPRESC_SHIFT);
\r
496 while (lcd->SYNCBUSY) ;
\r
497 lcd->IFC = LCD_IFC_FC;
\r
498 lcd->IEN = LCD_IEN_FC;
\r
502 /**************************************************************************//**
\r
503 * @brief Disables LCD controller
\r
504 * @param lcd Pointer to LCD register block
\r
505 *****************************************************************************/
\r
506 void LCD_Disable(LCD_TypeDef *lcd)
\r
508 CMU_TypeDef *cmu = CMU;
\r
510 /* Turn off interrupts */
\r
511 lcd->IEN = 0x00000000;
\r
512 lcd->IFC = LCD_IFC_FC;
\r
513 NVIC_DisableIRQ(LCD_IRQn);
\r
516 /* Turn off LCD clock */
\r
517 cmu->LFACLKEN0 &= ~(CMU_LFACLKEN0_LCD);
\r
518 /* Turn off voltage boost if enabled */
\r
522 /**************************************************************************//**
\r
523 * @brief LCD scrolls a text over the display, sort of "polled printf"
\r
524 * @param lcd Pointer to LCD register block
\r
525 *****************************************************************************/
\r
526 void LCD_ScrollText(LCD_TypeDef *lcd, char *scrolltext)
\r
532 len = strlen(scrolltext);
\r
533 if (len < 7) return;
\r
534 for (i = 0; i < (len - 7); i++)
\r
536 memcpy(buffer, scrolltext + i, 7);
\r
537 LCD_Write(lcd, buffer);
\r
538 vTaskDelay(100/portTICK_RATE_MS);
\r