/***************************************************************************//** * @file * @brief Liquid Crystal Display (LCD) Peripheral API for EFM32 * @author Energy Micro AS * @version 2.3.2 ******************************************************************************* * @section License * (C) Copyright 2010 Energy Micro AS, http://www.energymicro.com ******************************************************************************* * * 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 "efm32_lcd.h" #if defined(LCD_COUNT) && (LCD_COUNT > 0) #include "efm32_assert.h" #include "efm32_bitband.h" /***************************************************************************//** * @addtogroup EFM32_Library * @{ ******************************************************************************/ /***************************************************************************//** * @addtogroup LCD * @brief Liquid Crystal Display (LCD) Peripheral API for EFM32 * @{ ******************************************************************************/ /***************************************************************************//** * @brief * Initalize Liquid Crystal Display (LCD) controller * * @details * This function call will only configure the LCD controller. You must enable * it afterwards, potentially configuring Frame Control and interrupts first * according to requirements. * * @param[in] lcdInit * Pointer to initialization structure which configures LCD controller. * ******************************************************************************/ void LCD_Initialize(const LCD_Init_TypeDef *lcdInit) { uint32_t dispCtrl = LCD->DISPCTRL; EFM_ASSERT(lcdInit != (void *) 0); /* Disable controller before reconfiguration */ LCD_Enable(false); /* Make sure we don't touch other bit fields (i.e. voltage boost) */ dispCtrl &= ~( #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) _LCD_DISPCTRL_MUXE_MASK | #endif _LCD_DISPCTRL_MUX_MASK | _LCD_DISPCTRL_BIAS_MASK | _LCD_DISPCTRL_WAVE_MASK | _LCD_DISPCTRL_VLCDSEL_MASK | _LCD_DISPCTRL_CONCONF_MASK); /* Configure controller according to initialization structure */ dispCtrl |= lcdInit->mux; /* also configures MUXE */ dispCtrl |= lcdInit->bias; dispCtrl |= lcdInit->wave; dispCtrl |= lcdInit->vlcd; dispCtrl |= lcdInit->contrast; /* Update display controller */ LCD->DISPCTRL = dispCtrl; /* Enable controller if wanted */ if (lcdInit->enable) { LCD_Enable(true); } } /***************************************************************************//** * @brief * Select source for VLCD * * @param[in] vlcd * Select source for VLD voltage ******************************************************************************/ void LCD_VLCDSelect(LCD_VLCDSel_TypeDef vlcd) { uint32_t dispctrl = LCD->DISPCTRL; /* Select VEXT or VDD */ dispctrl &= ~(_LCD_DISPCTRL_VLCDSEL_MASK); switch (vlcd) { case lcdVLCDSelVExtBoost: dispctrl |= LCD_DISPCTRL_VLCDSEL_VEXTBOOST; break; case lcdVLCDSelVDD: dispctrl |= LCD_DISPCTRL_VLCDSEL_VDD; break; default: break; } LCD->DISPCTRL = dispctrl; } /***************************************************************************//** * @brief * Configure Update Control * * @param[in] ud * Configures LCD update method ******************************************************************************/ void LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud) { LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud; } /***************************************************************************//** * @brief * Initialize LCD Frame Counter * * @param[in] fcInit * Pointer to Frame Counter initialization structure ******************************************************************************/ void LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef *fcInit) { uint32_t bactrl = LCD->BACTRL; EFM_ASSERT(fcInit != (void *) 0); /* Verify FC Top Counter to be within limits */ EFM_ASSERT(fcInit->top < 64); /* Reconfigure frame count configuration */ bactrl &= ~(_LCD_BACTRL_FCTOP_MASK | _LCD_BACTRL_FCPRESC_MASK); bactrl |= (fcInit->top << _LCD_BACTRL_FCTOP_SHIFT); bactrl |= fcInit->prescale; /* Set Blink and Animation Control Register */ LCD->BACTRL = bactrl; LCD_FrameCountEnable(fcInit->enable); } /***************************************************************************//** * @brief * Configures LCD controller Animation feature * * @param[in] animInit * Pointer to LCD Animation initialization structure ******************************************************************************/ void LCD_AnimInit(const LCD_AnimInit_TypeDef *animInit) { uint32_t bactrl = LCD->BACTRL; EFM_ASSERT(animInit != (void *) 0); /* Set Animation Register Values */ LCD->AREGA = animInit->AReg; LCD->AREGB = animInit->BReg; /* Configure Animation Shift and Logic */ bactrl &= ~(_LCD_BACTRL_AREGASC_MASK | _LCD_BACTRL_AREGBSC_MASK | _LCD_BACTRL_ALOGSEL_MASK); bactrl |= (animInit->AShift << _LCD_BACTRL_AREGASC_SHIFT); bactrl |= (animInit->BShift << _LCD_BACTRL_AREGBSC_SHIFT); bactrl |= animInit->animLogic; #if defined(_EFM32_GIANT_FAMILY) if(animInit->startSeg == 0) { bactrl |= LCD_BACTRL_ALOC_SEG0TO7; } else if(animInit->startSeg == 8) { bactrl |= LCD_BACTRL_ALOC_SEG8TO15; } #endif /* Reconfigure */ LCD->BACTRL = bactrl; /* Enable */ LCD_AnimEnable(animInit->enable); } /***************************************************************************//** * @brief * Enables update of this range of LCD segment lines * * @param[in] segmentRange * Range of 4 LCD segments lines to enable or disable, for all enabled COM * lines * * @param[in] enable * Bool true to enable segment updates, false to disable updates ******************************************************************************/ void LCD_SegmentRangeEnable(LCD_SegmentRange_TypeDef segmentRange, bool enable) { if (enable) { LCD->SEGEN |= segmentRange; } else { LCD->SEGEN &= ~((uint32_t)segmentRange); } } /***************************************************************************//** * @brief * Turn on or clear a segment * * @note * On Gecko Family, max configuration is (COM-lines x Segment-Lines) 4x40 * On Tiny Family, max configuration is 8x20 or 4x24 * On Giant Family, max configuration is 8x36 or 4x40 * * @param[in] com * COM line to change * * @param[in] bit * Bit index of which field to change * * @param[in] enable * When true will set segment, when false will clear segment ******************************************************************************/ void LCD_SegmentSet(int com, int bit, bool enable) { #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) /* Tiny and Giant Family supports up to 8 COM lines */ EFM_ASSERT(com < 8); #else /* Gecko Family supports up to 4 COM lines */ EFM_ASSERT(com < 4); #endif #if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_GIANT_FAMILY) EFM_ASSERT(bit < 40); #else /* Tiny Gecko Family supports only "low" segment registers */ EFM_ASSERT(bit < 32); #endif /* Use bitband access for atomic bit set/clear of segment */ switch (com) { case 0: if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD0L), bit, (unsigned int)enable); } #if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD0H), bit, (unsigned int)enable); } #endif break; case 1: if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD1L), bit, (unsigned int)enable); } #if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD1H), bit, (unsigned int)enable); } #endif break; case 2: if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD2L), bit, (unsigned int)enable); } #if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD2H), bit, (unsigned int)enable); } #endif break; case 3: if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD3L), bit, (unsigned int)enable); } #if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD3H), bit, (unsigned int)enable); } #endif break; case 4: #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD4L), bit, (unsigned int)enable); } #endif #if defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD4H), bit, (unsigned int)enable); } #endif break; case 5: #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD5L), bit, (unsigned int)enable); } #endif #if defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD5H), bit, (unsigned int)enable); } #endif break; case 6: #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD6L), bit, (unsigned int)enable); } #endif #if defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD6H), bit, (unsigned int)enable); } #endif break; case 7: #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) if (bit < 32) { BITBAND_Peripheral(&(LCD->SEGD7L), bit, (unsigned int)enable); } #endif #if defined(_EFM32_GIANT_FAMILY) else { bit -= 32; BITBAND_Peripheral(&(LCD->SEGD7H), bit, (unsigned int)enable); } #endif break; default: EFM_ASSERT(0); break; } } /***************************************************************************//** * @brief * Updates the 0-31 lowest segments on a given COM-line in one operation, * according to bit mask * * @param[in] com * Which COM line to update * * @param[in] mask * Bit mask for segments 0-31 * * @param[in] bits * Bit pattern for segments 0-31 ******************************************************************************/ void LCD_SegmentSetLow(int com, uint32_t mask, uint32_t bits) { uint32_t segData; /* Maximum number of com lines */ #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) EFM_ASSERT(com < 8); #else /* Gecko Family supports up to 4 COM lines */ EFM_ASSERT(com < 4); #endif switch (com) { case 0: segData = LCD->SEGD0L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD0L = segData; break; case 1: segData = LCD->SEGD1L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD1L = segData; break; case 2: segData = LCD->SEGD2L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD2L = segData; break; case 3: segData = LCD->SEGD3L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD3L = segData; break; #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) case 4: segData = LCD->SEGD4L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD4L = segData; break; #endif #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) case 5: segData = LCD->SEGD5L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD5L = segData; break; #endif #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) case 6: segData = LCD->SEGD6L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD6L = segData; break; #endif #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) case 7: segData = LCD->SEGD7L; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD7L = segData; break; #endif default: EFM_ASSERT(0); break; } } #if defined(_EFM32_GECKO_FAMILY) || defined(_EFM32_GIANT_FAMILY) /***************************************************************************//** * @brief * Updated the high (32-39) segments on a given COM-line in one operation * * @param[in] com * Which COM line to update * * @param[in] mask * Bit mask for segments 32-39 * * @param[in] bits * Bit pattern for segments 32-39 ******************************************************************************/ void LCD_SegmentSetHigh(int com, uint32_t mask, uint32_t bits) { uint32_t segData; #if defined(_EFM32_GIANT_FAMILY) EFM_ASSERT(com < 8); #endif #if defined(_EFM32_GECKO_FAMILY) EFM_ASSERT(com < 4); #endif /* Maximum number of com lines */ switch (com) { case 0: segData = LCD->SEGD0H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD0H = segData; break; case 1: segData = LCD->SEGD1H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD1H = segData; break; case 2: segData = LCD->SEGD2H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD2H = segData; break; case 3: segData = LCD->SEGD3H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD3H = segData; break; #if defined(_EFM32_GIANT_FAMILY) case 4: segData = LCD->SEGD4H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD4H = segData; break; #endif #if defined(_EFM32_GIANT_FAMILY) case 5: segData = LCD->SEGD5H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD5H = segData; break; #endif #if defined(_EFM32_GIANT_FAMILY) case 6: segData = LCD->SEGD6H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD6H = segData; break; #endif #if defined(_EFM32_GIANT_FAMILY) case 7: segData = LCD->SEGD7H; segData &= ~(mask); segData |= (mask & bits); LCD->SEGD7H = segData; break; #endif default: break; } } #endif /***************************************************************************//** * @brief * Configure contrast level on LCD panel * * @param[in] level * Contrast level in the range 0-31 ******************************************************************************/ void LCD_ContrastSet(int level) { EFM_ASSERT(level < 32); LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONLEV_MASK) | (level << _LCD_DISPCTRL_CONLEV_SHIFT); } /***************************************************************************//** * @brief * Configure voltage booster * * The resulting voltage level is described in each part number's data sheet * * @param[in] vboost * Voltage boost level ******************************************************************************/ void LCD_VBoostSet(LCD_VBoostLevel_TypeDef vboost) { /* Reconfigure Voltage Boost */ LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_VBLEV_MASK) | vboost; } #if defined(_EFM32_TINY_FAMILY) || defined(_EFM32_GIANT_FAMILY) /***************************************************************************//** * @brief * Configure bias level for a specific segment line for Direct Segment Control * * @note * When DSC is active, each configuration takes up 4 bits in the Segment * Registers (SEGD0L/SEGD1H) which defines bias level. * For optimal use of this feature, the entire SEGD-registers should be set * at once in a optimized routine, so this function is mainly here to * demonstrate how to correctly configure the bias levels, and should be used * with care. * * @param[in] segmentLine * Segment line number * * @param[in] biasLevel * Bias configuration level, 0-4. This value must be within the constraint * defined by the LCD_DISPCTRL bias setting, see Reference Manual/Datasheet ******************************************************************************/ void LCD_BiasSegmentSet(int segmentLine, int biasLevel) { int biasRegister; int bitShift; volatile uint32_t *segmentRegister; #if defined(_EFM32_TINY_FAMILY) EFM_ASSERT(segmentLine < 20); #endif #if defined(_EFM32_GIANT_FAMILY) EFM_ASSERT(segmentLine < 40); #endif #if defined(_EFM32_TINY_FAMILY) /* Bias config for 8 segment lines per SEGDnL register */ biasRegister = segmentLine / 8; bitShift = (segmentLine % 8) * 4; switch (biasRegister) { case 0: segmentRegister = &LCD->SEGD0L; break; case 1: segmentRegister = &LCD->SEGD1L; break; case 2: segmentRegister = &LCD->SEGD2L; break; case 3: segmentRegister = &LCD->SEGD3L; break; default: segmentRegister = (uint32_t *)0x00000000; EFM_ASSERT(0); break; } #endif #if defined(_EFM32_GIANT_FAMILY) /* Bias config for 10 segment lines per SEGDn L+H registers */ biasRegister = segmentLine / 10; bitShift = (segmentLine % 10) * 4; switch (biasRegister) { case 0: if (bitShift < 32) { segmentRegister = &LCD->SEGD0L; } else { segmentRegister = &LCD->SEGD0H; bitShift -= 32; } break; case 1: if (bitShift < 32) { segmentRegister = &LCD->SEGD1L; } else { segmentRegister = &LCD->SEGD1H; bitShift -= 32; } break; case 2: if (bitShift < 32) { segmentRegister = &LCD->SEGD2L; } else { segmentRegister = &LCD->SEGD1H; bitShift -= 32; } break; case 3: if (bitShift < 32) { segmentRegister = &LCD->SEGD3L; } else { segmentRegister = &LCD->SEGD3H; bitShift -= 32; } break; default: segmentRegister = (uint32_t *)0x00000000; EFM_ASSERT(0); break; } #endif /* Configure new bias setting */ *segmentRegister = (*segmentRegister & ~(0xF << bitShift)) | (biasLevel << bitShift); } /***************************************************************************//** * @brief * Configure bias level for a specific segment line * * @note * When DSC is active, each configuration takes up 4 bits in the Segment * Registers (SEGD4L/SEGD4H) which defines bias level. * For optimal use of this feature, the entire SEGD-registers should be set * at once in a optimized routine, so this function is mainly here to * demonstrate how to correctly configure the bias levels, and should be used * with care. * * @param[in] comLine * COM line number, 0-7 * * @param[in] biasLevel * Bias configuration level, 0-4. This value must be within the constraint * defined by the LCD_DISPCTRL bias setting, see Reference Manual/Datasheet ******************************************************************************/ void LCD_BiasComSet(int comLine, int biasLevel) { int bitShift; EFM_ASSERT(comLine < 8); bitShift = comLine * 4; LCD->SEGD4L = (LCD->SEGD4L & ~(0xF << bitShift)) | (biasLevel << bitShift); } #endif /** @} (end addtogroup LCD) */ /** @} (end addtogroup EFM32_Library) */ #endif /* defined(LCD_COUNT) && (LCD_COUNT > 0) */