1 /**************************************************************************//**
\r
3 * @brief EFM32 Segment LCD Display driver
\r
5 ******************************************************************************
\r
7 * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
\r
8 *******************************************************************************
\r
10 * This file is licensed under the Silabs License Agreement. See the file
\r
11 * "Silabs_License_Agreement.txt" for details. Before using this software for
\r
12 * any purpose, you must agree to the terms of that agreement.
\r
14 ******************************************************************************/
\r
20 #include <stdbool.h>
\r
21 #include "em_device.h"
\r
23 #include "em_gpio.h"
\r
25 #include "segmentlcd.h"
\r
27 /**************************************************************************//**
\r
29 * Defines each text symbol's segment in terms of COM and BIT numbers,
\r
30 * in a way that we can enumerate each bit for each text segment in the
\r
31 * following bit pattern:
\r
45 * E.g.: First text character bit pattern #3 (above) is
\r
46 * Segment 1D for Display
\r
47 * Location COM 3, BIT 0
\r
48 *****************************************************************************/
\r
51 uint8_t com[14]; /**< LCD COM line (for multiplexing) */
\r
52 uint8_t bit[14]; /**< LCD bit number */
\r
56 /**************************************************************************//**
\r
57 * @brief Defines segment COM and BIT fields numeric display
\r
58 *****************************************************************************/
\r
61 uint8_t com[7]; /**< LCD COM line (for multiplexing) */
\r
62 uint8_t bit[7]; /**< LCD bit number */
\r
65 /**************************************************************************//**
\r
66 * @brief Defines segment COM and BIT fields for Energy Modes on display
\r
67 *****************************************************************************/
\r
70 uint8_t com[5]; /**< LCD COM line (for multiplexing) */
\r
71 uint8_t bit[5]; /**< LCD bit number */
\r
74 /**************************************************************************//**
\r
75 * @brief Defines segment COM and BIT fields for A-wheel (suited for Anim)
\r
76 *****************************************************************************/
\r
79 uint8_t com[8]; /**< LCD COM line (for multiplexing) */
\r
80 uint8_t bit[8]; /**< LCD bit number */
\r
83 /**************************************************************************//**
\r
84 * @brief Defines segment COM and BIT fields for A-wheel (suited for Anim)
\r
85 *****************************************************************************/
\r
88 uint8_t com[4]; /**< LCD COM line (for multiplexing) */
\r
89 uint8_t bit[4]; /**< LCD bit number */
\r
92 /**************************************************************************//**
\r
93 * @brief Defines prototype for all segments in display
\r
94 *****************************************************************************/
\r
97 CHAR_TypeDef Text[7]; /**< Text on display */
\r
98 NUMBER_TypeDef Number[4]; /**< Numbers on display */
\r
99 EM_TypeDef EMode; /**< Display energy mode */
\r
100 ARING_TypeDef ARing; /**< Display ring */
\r
101 BATTERY_TypeDef Battery; /**< Display battery */
\r
104 /**************************************************************************//**
\r
105 * @brief Working instance of LCD display
\r
106 *****************************************************************************/
\r
107 static const MCU_DISPLAY EFM_Display = EFM_DISPLAY_DEF;
\r
110 /**************************************************************************//**
\r
112 * Defines higlighted segments for the alphabet, starting from "blank" (SPACE)
\r
113 * Uses bit pattern as defined for text segments above.
\r
114 * E.g. a capital O, would have bits 0 1 2 3 4 5 => 0x003f defined
\r
115 *****************************************************************************/
\r
116 static const uint16_t EFM_Alphabet[] = {
\r
117 0x0000, /* space */
\r
181 0x0880, /* backslash */
\r
217 /**************************************************************************//**
\r
219 * Defines higlighted segments for the numeric display
\r
220 *****************************************************************************/
\r
222 static const uint16_t EFM_Numbers[] = {
\r
242 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
\r
243 /* sign is last element of the table */
\r
244 static const uint16_t signIndex = sizeof(EFM_Numbers)/sizeof(uint16_t) - 1 ;
\r
246 static const LCD_Init_TypeDef lcdInit = LCD_INIT_DEF;
\r
250 /**************************************************************************//**
\r
251 * @brief Disable all segments
\r
252 *****************************************************************************/
\r
253 void SegmentLCD_AllOff(void)
\r
255 /* Turn on low segments */
\r
256 LCD_ALL_SEGMENTS_OFF();
\r
260 /**************************************************************************//**
\r
261 * @brief Enable all segments
\r
262 *****************************************************************************/
\r
263 void SegmentLCD_AllOn(void)
\r
265 LCD_ALL_SEGMENTS_ON();
\r
269 /**************************************************************************//**
\r
270 * @brief Turn all segments on alpha characters in display off
\r
271 *****************************************************************************/
\r
272 void SegmentLCD_AlphaNumberOff(void)
\r
274 LCD_ALPHA_NUMBER_OFF();
\r
279 /**************************************************************************//**
\r
280 * @brief Light up or shut off Ring of Indicators
\r
281 * @param anum "Segment number" on "Ring", range 0 - 7
\r
282 * @param on Zero is off, non-zero is on
\r
283 *****************************************************************************/
\r
284 void SegmentLCD_ARing(int anum, int on)
\r
288 com = EFM_Display.ARing.com[anum];
\r
289 bit = EFM_Display.ARing.bit[anum];
\r
293 LCD_SegmentSet(com, bit, true);
\r
297 LCD_SegmentSet(com, bit, false);
\r
302 /**************************************************************************//**
\r
303 * @brief Light up or shut off Battery Indicator
\r
304 * @param batteryLevel Battery Level, 0 to 4 (0 turns all off)
\r
305 *****************************************************************************/
\r
306 void SegmentLCD_Battery(int batteryLevel)
\r
311 for (i = 0; i < 4; i++)
\r
313 if (i < batteryLevel)
\r
321 com = EFM_Display.Battery.com[i];
\r
322 bit = EFM_Display.Battery.bit[i];
\r
326 LCD_SegmentSet(com, bit, true);
\r
330 LCD_SegmentSet(com, bit, false);
\r
336 /**************************************************************************//**
\r
337 * @brief Disables LCD controller
\r
338 *****************************************************************************/
\r
339 void SegmentLCD_Disable(void)
\r
344 /* Make sure CTRL register has been updated */
\r
345 LCD_SyncBusyDelay(LCD_SYNCBUSY_CTRL);
\r
347 /* Turn off LCD clock */
\r
348 CMU_ClockEnable(cmuClock_LCD, false);
\r
350 /* Turn off voltage boost if enabled */
\r
355 /**************************************************************************//**
\r
356 * @brief Light up or shut off Energy Mode indicator
\r
357 * @param em Energy Mode numer 0 to 4
\r
358 * @param on Zero is off, non-zero is on
\r
359 *****************************************************************************/
\r
360 void SegmentLCD_EnergyMode(int em, int on)
\r
364 com = EFM_Display.EMode.com[em];
\r
365 bit = EFM_Display.EMode.bit[em];
\r
369 LCD_SegmentSet(com, bit, true);
\r
373 LCD_SegmentSet(com, bit, false);
\r
378 /**************************************************************************//**
\r
379 * @brief Segment LCD Initialization routine for EFM32 STK display
\r
380 * @param useBoost Set to use voltage boost
\r
381 *****************************************************************************/
\r
382 void SegmentLCD_Init(bool useBoost)
\r
385 /* Ensure LE modules are accessible */
\r
386 CMU_ClockEnable(cmuClock_CORELE, true);
\r
388 /* Enable LFRCO as LFACLK in CMU (will also enable oscillator if not enabled) */
\r
389 CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
\r
391 /* LCD Controller Prescaler */
\r
392 CMU_ClockDivSet(cmuClock_LCDpre, LCD_CMU_CLK_PRE);
\r
395 CMU_LCDClkFDIVSet(LCD_CMU_CLK_DIV);
\r
397 /* Enable clock to LCD module */
\r
398 CMU_ClockEnable(cmuClock_LCD, true);
\r
400 LCD_DISPLAY_ENABLE();
\r
402 /* Disable interrupts */
\r
403 LCD_IntDisable(0xFFFFFFFF);
\r
405 /* Initialize and enable LCD controller */
\r
406 LCD_Init(&lcdInit);
\r
408 /* Enable all display segments */
\r
409 LCD_SEGMENTS_ENABLE();
\r
411 /* Enable boost if necessary */
\r
414 LCD_VBoostSet(LCD_BOOST_LEVEL);
\r
415 LCD_VLCDSelect(lcdVLCDSelVExtBoost);
\r
416 CMU->LCDCTRL |= CMU_LCDCTRL_VBOOSTEN;
\r
419 /* Turn all segments off */
\r
420 SegmentLCD_AllOff();
\r
422 LCD_SyncBusyDelay(0xFFFFFFFF);
\r
426 /**************************************************************************//**
\r
427 * @brief Write a hexadecimal number on lower alphanumeric part of
\r
428 * Segment LCD display
\r
429 * @param num Hexadecimal number value to put on display, in range 0
\r
431 *****************************************************************************/
\r
432 void SegmentLCD_LowerHex( uint32_t num )
\r
438 SegmentLCD_Symbol(LCD_SYMBOL_MINUS, 0);
\r
440 for ( i=6; i>=0; i-- )
\r
442 nibble = num & 0xF;
\r
445 str[i] = nibble + '0';
\r
446 else if ( nibble == 11 )
\r
448 else if ( nibble == 13 )
\r
451 str[i] = (nibble - 10) + 'A';
\r
456 SegmentLCD_Write(str);
\r
459 /**************************************************************************//**
\r
460 * @brief Write number on lower alphanumeric part of Segment LCD display
\r
461 * @param num Numeric value to put on display, in range -9999999 to +9999999
\r
462 *****************************************************************************/
\r
463 void SegmentLCD_LowerNumber( int num )
\r
468 SegmentLCD_Symbol(LCD_SYMBOL_MINUS, 0);
\r
470 if ( ( num > 9999999 ) || ( num < -9999999 ) )
\r
472 SegmentLCD_Write("Ovrflow");
\r
478 SegmentLCD_Symbol(LCD_SYMBOL_MINUS, 1);
\r
482 for ( i=6; i>=0; i-- )
\r
484 if ( ( i < 6 ) && ( num == 0 ) )
\r
490 str[i] = (num % 10) + '0';
\r
495 SegmentLCD_Write(str);
\r
499 /**************************************************************************//**
\r
500 * @brief Write number on numeric part on Segment LCD display
\r
501 * @param value Numeric value to put on display, in range -999 to +9999
\r
502 *****************************************************************************/
\r
503 void SegmentLCD_Number(int value)
\r
505 int i, com, bit, digit, div, neg;
\r
506 uint16_t bitpattern;
\r
509 /* Parameter consistancy check */
\r
514 if (value <= -1000)
\r
520 value = abs(value);
\r
528 /* If an update is in progress we must block, or there might be tearing */
\r
529 LCD_SyncBusyDelay(0xFFFFFFFF);
\r
531 /* Freeze updates to avoid partial refresh of display */
\r
532 LCD_FreezeEnable(true);
\r
534 /* Turn off all number LCD segments */
\r
535 SegmentLCD_NumberOff();
\r
537 /* Extract useful digits */
\r
539 for (digit = 0; digit < 4; digit++)
\r
541 num = (value / div) % 10;
\r
542 if ((neg == 1) && (digit == 3)) num = signIndex;
\r
543 /* Get number layout of display */
\r
544 bitpattern = EFM_Numbers[num];
\r
545 for (i = 0; i < 7; i++)
\r
547 bit = EFM_Display.Number[digit].bit[i];
\r
548 com = EFM_Display.Number[digit].com[i];
\r
549 if (bitpattern & (1 << i))
\r
551 LCD_SegmentSet(com, bit, true);
\r
556 /* Sync LCD registers to LE domain */
\r
557 LCD_FreezeEnable(false);
\r
561 /**************************************************************************//**
\r
562 * @brief Turn all segments on numeric digits in display off
\r
563 *****************************************************************************/
\r
564 void SegmentLCD_NumberOff(void)
\r
566 /* Turn off all number segments */
\r
572 /**************************************************************************//**
\r
573 * @brief Light up or shut off various symbols on Segment LCD
\r
574 * @param s Which symbol to turn on or off
\r
575 * @param on Zero is off, non-zero is on
\r
576 *****************************************************************************/
\r
577 void SegmentLCD_Symbol(lcdSymbol s, int on)
\r
584 case LCD_SYMBOL_GECKO:
\r
585 com = LCD_SYMBOL_GECKO_COM;
\r
586 bit = LCD_SYMBOL_GECKO_SEG;
\r
588 case LCD_SYMBOL_ANT:
\r
589 com = LCD_SYMBOL_ANT_COM;
\r
590 bit = LCD_SYMBOL_ANT_SEG;
\r
592 case LCD_SYMBOL_PAD0:
\r
593 com = LCD_SYMBOL_PAD0_COM;
\r
594 bit = LCD_SYMBOL_PAD0_SEG;
\r
596 case LCD_SYMBOL_PAD1:
\r
597 com = LCD_SYMBOL_PAD1_COM;
\r
598 bit = LCD_SYMBOL_PAD1_SEG;
\r
600 case LCD_SYMBOL_EFM32:
\r
601 com = LCD_SYMBOL_EFM32_COM;
\r
602 bit = LCD_SYMBOL_EFM32_SEG;
\r
604 case LCD_SYMBOL_MINUS:
\r
605 com = LCD_SYMBOL_MINUS_COM;
\r
606 bit = LCD_SYMBOL_MINUS_SEG;
\r
608 case LCD_SYMBOL_COL3:
\r
609 com = LCD_SYMBOL_COL3_COM;
\r
610 bit = LCD_SYMBOL_COL3_SEG;
\r
612 case LCD_SYMBOL_COL5:
\r
613 com = LCD_SYMBOL_COL5_COM;
\r
614 bit = LCD_SYMBOL_COL5_SEG;
\r
616 case LCD_SYMBOL_COL10:
\r
617 com = LCD_SYMBOL_COL10_COM;
\r
618 bit = LCD_SYMBOL_COL10_SEG;
\r
620 #ifdef LCD_SYMBOL_DEGC_SEG
\r
621 case LCD_SYMBOL_DEGC:
\r
622 com = LCD_SYMBOL_DEGC_COM;
\r
623 bit = LCD_SYMBOL_DEGC_SEG;
\r
626 #ifdef LCD_SYMBOL_DEGF_SEG
\r
627 case LCD_SYMBOL_DEGF:
\r
628 com = LCD_SYMBOL_DEGF_COM;
\r
629 bit = LCD_SYMBOL_DEGF_SEG;
\r
632 #ifdef LCD_SYMBOL_DP2_SEG
\r
633 case LCD_SYMBOL_DP2:
\r
634 com = LCD_SYMBOL_DP2_COM;
\r
635 bit = LCD_SYMBOL_DP2_SEG;
\r
638 #ifdef LCD_SYMBOL_DP3_SEG
\r
639 case LCD_SYMBOL_DP3:
\r
640 com = LCD_SYMBOL_DP3_COM;
\r
641 bit = LCD_SYMBOL_DP3_SEG;
\r
644 #ifdef LCD_SYMBOL_DP4_SEG
\r
645 case LCD_SYMBOL_DP4:
\r
646 com = LCD_SYMBOL_DP4_COM;
\r
647 bit = LCD_SYMBOL_DP4_SEG;
\r
650 #ifdef LCD_SYMBOL_DP5_SEG
\r
651 case LCD_SYMBOL_DP5:
\r
652 com = LCD_SYMBOL_DP5_COM;
\r
653 bit = LCD_SYMBOL_DP5_SEG;
\r
656 case LCD_SYMBOL_DP6:
\r
657 com = LCD_SYMBOL_DP6_COM;
\r
658 bit = LCD_SYMBOL_DP6_SEG;
\r
660 case LCD_SYMBOL_DP10:
\r
661 com = LCD_SYMBOL_DP10_COM;
\r
662 bit = LCD_SYMBOL_DP10_SEG;
\r
664 #ifdef LCD_SYMBOL_AM_SEG
\r
665 case LCD_SYMBOL_AM:
\r
666 com = LCD_SYMBOL_AM_COM;
\r
667 bit = LCD_SYMBOL_AM_SEG;
\r
670 #ifdef LCD_SYMBOL_PM_SEG
\r
671 case LCD_SYMBOL_PM:
\r
672 com = LCD_SYMBOL_PM_COM;
\r
673 bit = LCD_SYMBOL_PM_SEG;
\r
676 #ifdef LCD_SYMBOL_MICROAMP_SEG
\r
677 case LCD_SYMBOL_MICROAMP:
\r
678 com = LCD_SYMBOL_MICROAMP_COM;
\r
679 bit = LCD_SYMBOL_MICROAMP_SEG;
\r
682 #ifdef LCD_SYMBOL_MILLIAMP_SEG
\r
683 case LCD_SYMBOL_MILLIAMP:
\r
684 com = LCD_SYMBOL_MILLIAMP_COM;
\r
685 bit = LCD_SYMBOL_MILLIAMP_SEG;
\r
692 LCD_SegmentSet(com, bit, true);
\r
696 LCD_SegmentSet(com, bit, false);
\r
701 /**************************************************************************//**
\r
702 * @brief Write hexadecimal number on numeric part on Segment LCD display
\r
703 * @param value Numeric value to put on display, in range 0x0000-0xFFFF
\r
704 *****************************************************************************/
\r
705 void SegmentLCD_UnsignedHex(uint16_t value)
\r
707 int num, i, com, bit, digit;
\r
708 uint16_t bitpattern;
\r
710 /* Parameter consistancy check */
\r
711 if (value >= 0xffff)
\r
716 /* If an update is in progress we must block, or there might be tearing */
\r
717 LCD_SyncBusyDelay(0xFFFFFFFF);
\r
719 /* Freeze updates to avoid partial refresh of display */
\r
720 LCD_FreezeEnable(true);
\r
722 /* Turn off all number LCD segments */
\r
723 SegmentLCD_NumberOff();
\r
725 for (digit = 0; digit < 4; digit++)
\r
727 num = (value >> (4 * digit)) & 0x0f;
\r
728 bitpattern = EFM_Numbers[num];
\r
729 for (i = 0; i < 7; i++)
\r
731 bit = EFM_Display.Number[digit].bit[i];
\r
732 com = EFM_Display.Number[digit].com[i];
\r
733 if (bitpattern & (1 << i))
\r
735 LCD_SegmentSet(com, bit, true);
\r
740 /* Sync LCD registers to LE domain */
\r
741 LCD_FreezeEnable(false);
\r
745 /**************************************************************************//**
\r
746 * @brief Write text on LCD display
\r
747 * @param string Text string to show on display
\r
748 *****************************************************************************/
\r
749 void SegmentLCD_Write(char *string)
\r
751 int data, length, index;
\r
756 length = strlen(string);
\r
759 /* If an update is in progress we must block, or there might be tearing */
\r
760 LCD_SyncBusyDelay(0xFFFFFFFF);
\r
762 /* Freeze LCD to avoid partial updates */
\r
763 LCD_FreezeEnable(true);
\r
765 /* Turn all segments off */
\r
766 SegmentLCD_AlphaNumberOff();
\r
768 /* Fill out all characters on display */
\r
769 for (index = 0; index < 7; index++)
\r
771 if (index < length)
\r
773 data = (int) *string;
\r
775 else /* Padding with space */
\r
777 data = 0x20; /* SPACE */
\r
779 /* Defined letters currently starts at "SPACE" - ASCII 0x20; */
\r
780 data = data - 0x20;
\r
781 /* Get font for this letter */
\r
782 bitfield = EFM_Alphabet[data];
\r
784 for (i = 0; i < 14; i++)
\r
786 bit = EFM_Display.Text[index].bit[i];
\r
787 com = EFM_Display.Text[index].com[i];
\r
789 if (bitfield & (1 << i))
\r
791 /* Turn on segment */
\r
792 LCD_SegmentSet(com, bit, true);
\r
797 /* Enable update */
\r
798 LCD_FreezeEnable(false);
\r