/***************************************************************************//**
* @file
* @brief Liquid Crystal Display (LCD) Peripheral API
*******************************************************************************
* # License
* Copyright 2018 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
*
* The licensor of this software is Silicon Laboratories Inc.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
******************************************************************************/
#include "em_lcd.h"
#if defined(LCD_COUNT) && (LCD_COUNT > 0)
#include "sl_assert.h"
#include "em_bus.h"
#include "em_gpio.h"
#include
/***************************************************************************//**
* @addtogroup lcd LCD - Liquid Crystal Display
* @brief Liquid Crystal Display (LCD) Peripheral API
* @details
* This module contains functions to control the LDC peripheral of Silicon
* Labs 32-bit MCUs and SoCs. The LCD driver can drive up to 8x36 segmented
* LCD directly. The animation feature makes it possible to have active
* animations without the CPU intervention.
* @{
******************************************************************************/
/** Frame counter uses a maximum of 5 bits (FCTOP[5:0]). */
#define LCD_FRAME_COUNTER_VAL_MAX 64
/***************************************************************************//**
* @brief
* Initialize the Liquid Crystal Display (LCD) controller.
*
* @details
* Configures the LCD controller. You must enable
* it afterwards, potentially configuring Frame Control and interrupts first
* according to requirements.
*
* @param[in] lcdInit
* A pointer to the initialization structure which configures the LCD controller.
*
******************************************************************************/
void LCD_Init(const LCD_Init_TypeDef *lcdInit)
{
uint32_t dispCtrl = LCD->DISPCTRL;
EFM_ASSERT(lcdInit != (void *) 0);
/* Disable the controller before reconfiguration. */
LCD_Enable(false);
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD_ReadyWait();
#endif
#if defined(_SILICON_LABS_32B_SERIES_2)
/* Initialize LCD registers to hardware reset state. */
LCD_Reset();
#endif
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD->CTRL &= ~_LCD_CTRL_PRESCALE_MASK;
LCD->CTRL |= lcdInit->clockPrescaler << _LCD_CTRL_PRESCALE_SHIFT;
#endif
/* Make sure the other bit fields don't get affected (i.e., voltage boost). */
dispCtrl &= ~(0
#if defined(LCD_DISPCTRL_MUXE)
| _LCD_DISPCTRL_MUXE_MASK
#endif
| _LCD_DISPCTRL_MUX_MASK
| _LCD_DISPCTRL_BIAS_MASK
| _LCD_DISPCTRL_WAVE_MASK
#if defined(_LCD_DISPCTRL_VLCDSEL_MASK)
| _LCD_DISPCTRL_VLCDSEL_MASK
#endif
#if defined(_LCD_DISPCTRL_CONCONF_MASK)
| _LCD_DISPCTRL_CONCONF_MASK
#endif
#if defined(_LCD_DISPCTRL_MODE_MASK)
| _LCD_DISPCTRL_MODE_MASK
#endif
#if defined(_LCD_DISPCTRL_CHGRDST_MASK)
| _LCD_DISPCTRL_CHGRDST_MASK
#endif
);
/* Configure the controller according to the initialization structure. */
dispCtrl |= lcdInit->mux; /* Also configures MUXE. */
dispCtrl |= lcdInit->bias;
dispCtrl |= lcdInit->wave;
#if defined(_SILICON_LABS_32B_SERIES_0)
dispCtrl |= lcdInit->vlcd;
dispCtrl |= lcdInit->contrast;
#else
dispCtrl |= (lcdInit->chargeRedistribution);
#endif
/* Update the display controller. */
LCD->DISPCTRL = dispCtrl;
#if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LABS_32B_SERIES_2)
LCD_ModeSet(lcdInit->mode);
LCD->FRAMERATE = lcdInit->frameRateDivider;
LCD_ContrastSet(lcdInit->contrastLevel);
#endif
/* Enable the controller if needed. */
if (lcdInit->enable) {
LCD_Enable(true);
}
}
#if defined(_SILICON_LABS_32B_SERIES_0)
/***************************************************************************//**
* @brief
* Select a source for VLCD.
*
* @param[in] vlcd
* Select a source for the VLCD 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;
}
#endif
/***************************************************************************//**
* @brief
* Configure Update Control.
*
* @param[in] ud
* Configures the LCD update method.
******************************************************************************/
void LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD_Enable(false); /* Ensure LCD disabled before writing WSTATIC fields. */
LCD_ReadyWait();
LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud;
LCD_Enable(true);
#else
LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud;
#endif
}
/***************************************************************************//**
* @brief
* Initialize the LCD Frame Counter.
*
* @param[in] fcInit
* A pointer to the Frame Counter initialization structure.
******************************************************************************/
void LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef *fcInit)
{
EFM_ASSERT(fcInit != (void *) 0);
/* Verify that the FC Top Counter is within limits. */
EFM_ASSERT(fcInit->top < LCD_FRAME_COUNTER_VAL_MAX);
#if defined(_SILICON_LABS_32B_SERIES_2)
uint32_t bacfg = LCD->BACFG;
/* Reconfigure the frame count configuration. */
bacfg &= ~(_LCD_BACFG_FCTOP_MASK
| _LCD_BACFG_FCPRESC_MASK);
bacfg |= (fcInit->top << _LCD_BACFG_FCTOP_SHIFT);
bacfg |= fcInit->prescale;
/* Set the Blink and Animation Control Register. */
LCD_Enable(false); /* Ensure LCD disabled before writing WSTATIC fields. */
LCD_ReadyWait();
LCD->BACFG = bacfg;
LCD_Enable(true);
#else
uint32_t bactrl = LCD->BACTRL;
/* Reconfigure the frame count configuration. */
bactrl &= ~(_LCD_BACTRL_FCTOP_MASK
| _LCD_BACTRL_FCPRESC_MASK);
bactrl |= (fcInit->top << _LCD_BACTRL_FCTOP_SHIFT);
bactrl |= fcInit->prescale;
/* Set the Blink and Animation Control Register. */
LCD->BACTRL = bactrl;
#endif
LCD_FrameCountEnable(fcInit->enable);
}
/***************************************************************************//**
* @brief
* Configure the LCD controller Animation feature.
*
* @param[in] animInit
* A pointer to the LCD Animation initialization structure.
******************************************************************************/
void LCD_AnimInit(const LCD_AnimInit_TypeDef *animInit)
{
uint32_t bactrl = LCD->BACTRL;
EFM_ASSERT(animInit != (void *) 0);
/* Set initial Animation Register Values. */
LCD->AREGA = animInit->AReg;
LCD->AREGB = animInit->BReg;
/* Configure the Animation Shift and Logic. */
bactrl &= ~(_LCD_BACTRL_AREGASC_MASK
| _LCD_BACTRL_AREGBSC_MASK
| _LCD_BACTRL_ALOGSEL_MASK
#if defined(_LCD_BACTRL_ALOC_MASK)
| _LCD_BACTRL_ALOC_MASK
#endif
);
bactrl |= (animInit->AShift << _LCD_BACTRL_AREGASC_SHIFT);
bactrl |= (animInit->BShift << _LCD_BACTRL_AREGBSC_SHIFT);
bactrl |= animInit->animLogic;
#if defined(_LCD_BACTRL_ALOC_MASK)
bactrl |= animInit->startSeg;
#endif
/* Reconfigure. */
LCD->BACTRL = bactrl;
/* Enable. */
LCD_AnimEnable(animInit->enable);
}
#if defined(_SILICON_LABS_32B_SERIES_0)
/***************************************************************************//**
* @brief
* Enables updating this range of LCD segment lines.
*
* @param[in] segmentRange
* A range of 4 LCD segment lines to enable or disable for all enabled COM
* lines.
*
* @param[in] enable
* Boolean 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);
}
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Enables a given LCD segment line.
*
* @param[in] seg_nbr
* Segment line number.
*
* @param[in] enable
* Boolean true to enable a segment, false to disable.
******************************************************************************/
void LCD_SegmentEnable(uint32_t seg_nbr, bool enable)
{
/* Series 2 parts support up to 20 segment lines. */
/* Except for xG26 which supports up to 40 segment lines. and xG28 which supports up to 28 segment lines. */
EFM_ASSERT(seg_nbr < (int)LCD_SEGMENT_LINES_MAX);
#if defined(_GPIO_LCDSEGH_MASK)
if (enable) {
if (seg_nbr > 31) {
GPIO->LCDSEGH_SET = 1 << (seg_nbr - 32);
} else {
GPIO->LCDSEGL_SET = 1 << (seg_nbr);
}
} else {
if (seg_nbr > 31) {
GPIO->LCDSEGH_CLR = 1 << (seg_nbr - 32);
} else {
GPIO->LCDSEGL_CLR = 1 << (seg_nbr);
}
}
#else /* defined(_GPIO_LCDSEGH_MASK) */
if (enable) {
GPIO->LCDSEG_SET = 1 << seg_nbr;
} else {
GPIO->LCDSEG_CLR = 1 << seg_nbr;
}
#endif /* defined(_GPIO_LCDSEGH_MASK) */
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Enables a given LCD COM line.
*
* @param[in] com
* COM line number.
*
* @param[in] enable
* Boolean true to enable a COM , false to disable.
******************************************************************************/
void LCD_ComEnable(uint8_t com, bool enable)
{
/* Series 2 parts support up to 4 COM lines except for xG28, which supports up to 8 COM lines. */
EFM_ASSERT(com < LCD_COM_LINES_MAX);
if (com < LCD_COM_NUM) {
if (enable) {
GPIO->LCDCOM_SET = 1 << com;
} else {
GPIO->LCDCOM_CLR = 1 << com;
}
}
#if defined(LCD_OCTAPLEX) && (LCD_OCTAPLEX == 1)
else {
/* On xG28, SEG lines shall be configured as COM lines */
/* for COM support above 4 COM lines */
#if defined(_GPIO_LCDSEGH_MASK)
if (enable) {
if ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART > 31) {
GPIO->LCDSEGH_SET = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART - 32);
} else {
GPIO->LCDSEGL_SET = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
}
} else {
if ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART > 31) {
GPIO->LCDSEGH_CLR = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART - 32);
} else {
GPIO->LCDSEGL_CLR = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
}
}
}
#else /* defined(_GPIO_LCDSEGH_MASK) */
if (enable) {
GPIO->LCDSEG_SET = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
} else {
GPIO->LCDSEG_CLR = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
}
}
#endif /* defined(_GPIO_LCDSEGH_MASK) */
#endif /* defined(LCD_OCTAPLEX) && (LCD_OCTAPLEX == 1) */
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Set a given DMA mode operation.
*
* @param[in] mode
* DMA mode.
******************************************************************************/
void LCD_DmaModeSet(LCD_DmaMode_Typedef mode)
{
LCD->BIASCTRL_CLR = _LCD_BIASCTRL_DMAMODE_MASK;
LCD->BIASCTRL |= mode;
}
#endif
/***************************************************************************//**
* @brief
* Turn on or clear a segment.
*
* @note
* For the Gecko Family, the maximum configuration is (COM-lines x Segment-Lines) 4x40.
* For the Tiny Gecko Family, the maximum configuration is 8x20 or 4x24.
* For the Giant Gecko Family, the maximum configuration is 8x36 or 4x40.
* For the Series 2 Family, the maximum configuration is 4x20.
* For the Series 2 xG28, the maximum configuration is 8x24 or 4x28.
*
* @param[in] com
* A COM line to change.
*
* @param[in] bit
* A bit index indicating which field to change.
*
* @param[in] enable
* True will set segment, false will clear segment.
******************************************************************************/
void LCD_SegmentSet(int com, int bit, bool enable)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
/* Series 2 parts support up to 4 COM lines except for xG26 and xG28, which supports up to 8 COM lines. */
EFM_ASSERT(com < (int)LCD_COM_LINES_MAX);
/* Series 2 parts support up to 20 segment lines. */
/* Except for xG26 which supports up to 40 segment lines. and xG28 which supports up to 28 segment lines. */
EFM_ASSERT(bit < (int)LCD_SEGMENT_LINES_MAX);
/* Use a bitband access for atomic bit set/clear of the segment. */
switch (com) {
case 0:
#if defined(_LCD_SEGD0H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD0), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD0H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD0), bit, enable);
#endif
break;
case 1:
#if defined(_LCD_SEGD1H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD1), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD1H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD1), bit, enable);
#endif
break;
case 2:
#if defined(_LCD_SEGD2H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD2), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD2H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD2), bit, enable);
#endif
break;
case 3:
#if defined(_LCD_SEGD3H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD3), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD3H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD3), bit, enable);
#endif
break;
#if defined(_LCD_SEGD4_MASK)
case 4:
#if defined(_LCD_SEGD4H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD4), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD4H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD4), bit, enable);
#endif
break;
#endif
#if defined(_LCD_SEGD5_MASK)
case 5:
#if defined(_LCD_SEGD5H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD5), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD5H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD5), bit, enable);
#endif
break;
#endif
#if defined(_LCD_SEGD6_MASK)
case 6:
#if defined(_LCD_SEGD6H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD6), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD6H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD6), bit, enable);
#endif
break;
#endif
#if defined(_LCD_SEGD7_MASK)
case 7:
#if defined(_LCD_SEGD7H_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD7), bit, enable);
} else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD7H), bit, enable);
}
#else
BUS_RegBitWrite(&(LCD->SEGD7), bit, enable);
#endif
break;
#endif
default:
EFM_ASSERT(0);
break;
}
#else
#if defined(_LCD_SEGD7L_MASK)
/* Tiny Gecko and Giant Gecko Families support 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(_LCD_SEGD0H_MASK)
EFM_ASSERT(bit < 40);
#else
/* Tiny Gecko Family supports only "low" segment registers. */
EFM_ASSERT(bit < 32);
#endif
/* Use a bitband access for atomic bit set/clear of the segment. */
switch (com) {
case 0:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD0L), bit, enable);
}
#if defined(_LCD_SEGD0H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD0H), bit, enable);
}
#endif
break;
case 1:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD1L), bit, enable);
}
#if defined(_LCD_SEGD1H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD1H), bit, enable);
}
#endif
break;
case 2:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD2L), bit, enable);
}
#if defined(_LCD_SEGD2H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD2H), bit, enable);
}
#endif
break;
case 3:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD3L), bit, enable);
}
#if defined(_LCD_SEGD3H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD3H), bit, enable);
}
#endif
break;
#if defined(_LCD_SEGD4L_MASK)
case 4:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD4L), bit, enable);
}
#if defined(_LCD_SEGD4H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD4H), bit, enable);
}
#endif
break;
#endif
#if defined(_LCD_SEGD5L_MASK)
case 5:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD5L), bit, enable);
}
#if defined(_LCD_SEGD5H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD5H), bit, enable);
}
#endif
break;
#endif
case 6:
#if defined(_LCD_SEGD6L_MASK)
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD6L), bit, enable);
}
#if defined(_LCD_SEGD6H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD6H), bit, enable);
}
#endif
break;
#endif
#if defined(_LCD_SEGD7L_MASK)
case 7:
if (bit < 32) {
BUS_RegBitWrite(&(LCD->SEGD7L), bit, enable);
}
#if defined(_LCD_SEGD7H_MASK)
else {
bit -= 32;
BUS_RegBitWrite(&(LCD->SEGD7H), bit, enable);
}
#endif
break;
#endif
default:
EFM_ASSERT(0);
break;
}
#endif
}
/***************************************************************************//**
* @brief
* Update 0-31 lowest segments on a given COM-line in one operation
* according to the bit mask.
*
* @param[in] com
* Indicates a COM line to update.
*
* @param[in] mask
* A bit mask for segments 0-31.
*
* @param[in] bits
* A bit pattern for segments 0-31.
******************************************************************************/
void LCD_SegmentSetLow(int com, uint32_t mask, uint32_t bits)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
uint32_t segData;
/* Series 2 parts support up to 4 COM lines except for xG26 and xG28, which supports up to 8 COM lines. */
EFM_ASSERT(com < (int)LCD_COM_LINES_MAX);
/* Series 2 parts support up to 20 segment lines. */
/* Except for xG26 which supports up to 40 segment lines. and xG28 which supports up to 28 segment lines. */
EFM_ASSERT(!(mask & (~_LCD_SEGD0_MASK)));
EFM_ASSERT(!(bits & (~_LCD_SEGD0_MASK)));
switch (com) {
case 0:
segData = LCD->SEGD0;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD0 = segData;
break;
case 1:
segData = LCD->SEGD1;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD1 = segData;
break;
case 2:
segData = LCD->SEGD2;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD2 = segData;
break;
case 3:
segData = LCD->SEGD3;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD3 = segData;
break;
#if defined(_LCD_SEGD4_MASK)
case 4:
segData = LCD->SEGD4;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD4 = segData;
break;
#endif
#if defined(_LCD_SEGD5_MASK)
case 5:
segData = LCD->SEGD5;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD5 = segData;
break;
#endif
#if defined(_LCD_SEGD6_MASK)
case 6:
segData = LCD->SEGD6;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD6 = segData;
break;
#endif
#if defined(_LCD_SEGD7_MASK)
case 7:
segData = LCD->SEGD7;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD7 = segData;
break;
#endif
default:
EFM_ASSERT(0);
break;
}
#else
uint32_t segData;
/* A maximum number of com lines. */
#if defined(_LCD_SEGD7L_MASK)
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(_LCD_SEGD4L_MASK)
case 4:
segData = LCD->SEGD4L;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD4L = segData;
break;
#endif
#if defined(_LCD_SEGD5L_MASK)
case 5:
segData = LCD->SEGD5L;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD5L = segData;
break;
#endif
#if defined(_LCD_SEGD6L_MASK)
case 6:
segData = LCD->SEGD6L;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD6L = segData;
break;
#endif
#if defined(_LCD_SEGD7L_MASK)
case 7:
segData = LCD->SEGD7L;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD7L = segData;
break;
#endif
default:
EFM_ASSERT(0);
break;
}
#endif
}
#if defined(_LCD_SEGD0H_MASK)
/***************************************************************************//**
* @brief
* Update the high (32-39) segments on a given COM-line in one operation.
*
* @param[in] com
* Indicates a COM line to update.
*
* @param[in] mask
* A bit mask for segments 32-39.
*
* @param[in] bits
* A bit pattern for segments 32-39.
******************************************************************************/
void LCD_SegmentSetHigh(int com, uint32_t mask, uint32_t bits)
{
uint32_t segData;
#if defined(_LCD_SEGD7H_MASK)
EFM_ASSERT(com < 8);
#else
EFM_ASSERT(com < 4);
#endif
/* A 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(_LCD_SEGD4H_MASK)
case 4:
segData = LCD->SEGD4H;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD4H = segData;
break;
#endif
#if defined(_LCD_SEGD5H_MASK)
case 5:
segData = LCD->SEGD5H;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD5H = segData;
break;
#endif
#if defined(_LCD_SEGD6H_MASK)
case 6:
segData = LCD->SEGD6H;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD6H = segData;
break;
#endif
#if defined(_LCD_SEGD7H_MASK)
case 7:
segData = LCD->SEGD7H;
segData &= ~(mask);
segData |= (mask & bits);
LCD->SEGD7H = segData;
break;
#endif
default:
break;
}
}
#endif
/***************************************************************************//**
* @brief
* Configure the contrast level on the LCD panel.
*
* @param[in] level
* The contrast level in range 0-63.
******************************************************************************/
void LCD_ContrastSet(int level)
{
#if defined(_SILICON_LABS_32B_SERIES_0)
EFM_ASSERT(level < 32);
LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONLEV_MASK)
| (level << _LCD_DISPCTRL_CONLEV_SHIFT);
#elif defined(_SILICON_LABS_32B_SERIES_1)
EFM_ASSERT(level < 64);
LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONTRAST_MASK)
| (level << _LCD_DISPCTRL_CONTRAST_SHIFT);
#else
EFM_ASSERT(level < 32);
LCD->BIASCTRL = (LCD->BIASCTRL & ~_LCD_BIASCTRL_VLCD_MASK)
| (level << _LCD_BIASCTRL_VLCD_SHIFT);
#endif
}
/***************************************************************************//**
* @brief
* Configure the bias level on the LCD panel.
*
* @param[in] bias
* The bias level.
******************************************************************************/
void LCD_BiasSet(LCD_Bias_TypeDef bias)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD_Enable(false); /* Ensure LCD disabled before writing WSTATIC fields. */
LCD_ReadyWait();
#endif
LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_BIAS_MASK) | bias;
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD_Enable(true);
#endif
}
#if defined(_SILICON_LABS_32B_SERIES_0)
/***************************************************************************//**
* @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;
}
#endif
#if defined(LCD_CTRL_DSC)
/***************************************************************************//**
* @brief
* Configure the bias level for a specific segment line for Direct Segment Control.
*
* @note
* When DSC is active, each configuration takes up 4 bits in the corresponding
* Segment Registers (SEGD0L/SEGD1H for Series 0 and 1, SEGDx/SEGDxH for Series 2)
* which defines the bias level.
* For optimal use of this feature, the entire SEGD-registers should be set
* at once in an optimized routine. Therefore, this function shows how to
* correctly configure the bias levels and should be used with care.
*
* @param[in] segmentLine
* A segment line number.
*
* @param[in] biasLevel
* The bias configuration level. This value must be within the constraints
* defined by the LCD_DISPCTRL bias settings. For more information, see the
* applicable Reference Manual and data sheet.
******************************************************************************/
void LCD_BiasSegmentSet(int segmentLine, int biasLevel)
{
volatile uint32_t *segmentRegister;
int biasRegister;
int bitShift;
#if defined(_SILICON_LABS_32B_SERIES_2)
if (segmentLine >= (int)LCD_SEG_NUM) {
return;
}
/*
* On Series 2, the following layout is used to configure bias when DSC
* is active:
*
* REGISTERS | ... | 7 .. 4 | 3 .. 0 |
* SEGD0 | ... | seg4 | seg0 |
* SEGD1 | ... | seg5 | seg1 |
* .....
*/
biasRegister = segmentLine % 4;
bitShift = (segmentLine / 4) * 4;
#if defined(_GPIO_LCDSEGH_MASK)
switch (biasRegister) {
case 0:
if (bitShift < 32) {
segmentRegister = &LCD->SEGD0;
} else {
segmentRegister = &LCD->SEGD0H;
bitShift -= 32;
}
break;
case 1:
if (bitShift < 32) {
segmentRegister = &LCD->SEGD1;
} else {
segmentRegister = &LCD->SEGD1H;
bitShift -= 32;
}
break;
case 2:
if (bitShift < 32) {
segmentRegister = &LCD->SEGD2;
} else {
segmentRegister = &LCD->SEGD2H;
bitShift -= 32;
}
break;
case 3:
if (bitShift < 32) {
segmentRegister = &LCD->SEGD3;
} else {
segmentRegister = &LCD->SEGD3H;
bitShift -= 32;
}
break;
default:
segmentRegister = NULL;
EFM_ASSERT(0);
break;
}
#else /* defined(_GPIO_LCDSEGH_MASK) */
switch (biasRegister) {
case 0:
segmentRegister = &LCD->SEGD0;
break;
case 1:
segmentRegister = &LCD->SEGD1;
break;
case 2:
segmentRegister = &LCD->SEGD2;
break;
case 3:
segmentRegister = &LCD->SEGD3;
break;
default:
segmentRegister = NULL;
EFM_ASSERT(0);
break;
}
#endif /* defined(_GPIO_LCDSEGH_MASK) */
#else
#if !defined(_LCD_SEGD0H_MASK)
EFM_ASSERT(segmentLine < 20);
/* A bias configuration 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;
}
#else
EFM_ASSERT(segmentLine < 40);
/* A bias configuration 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
#endif
/* Configure a new bias setting. */
BUS_RegMaskedWrite(segmentRegister, 0xF << bitShift, biasLevel << bitShift);
}
#endif
#if defined(LCD_CTRL_DSC)
/***************************************************************************//**
* @brief
* Configure the bias level for a specific segment line.
*
* @note
* When DSC is active, each configuration takes up 4 bits in the corresponding
* Segment Registers (SEGD4L/SEGD4H for Series 0 and 1, AREGA/AREGB for
* Series 2) which defines bias level.
* For optimal use of this feature, the entire register set should be set
* at once in a optimized routine. Therefore, this function shows how to
* correctly configure the bias levels and should be used with care.
*
* @param[in] comLine
* A COM line number, between 0 and 7 for Series 0 and 1. For Series 2, max
* is LCD_COM_NUM, defined in device-specific headers.
*
*
* @param[in] biasLevel
* The bias configuration level. This value must be within the constraints
* defined by the LCD_DISPCTRL bias settings.
* For more information, see the appropriate Reference Manual and data sheet.
******************************************************************************/
void LCD_BiasComSet(int comLine, int biasLevel)
{
#if defined(_SILICON_LABS_32B_SERIES_2)
volatile uint32_t *comRegister;
uint32_t biasRegister;
uint32_t bitShift;
if (comLine >= (int)LCD_COM_NUM) {
return;
}
biasRegister = (uint32_t) comLine % 2;
bitShift = (comLine / 2) * 4;
switch (biasRegister) {
case 0:
comRegister = &LCD->AREGA;
break;
case 1:
comRegister = &LCD->AREGB;
break;
default:
comRegister = NULL;
EFM_ASSERT(0);
break;
}
BUS_RegMaskedWrite(comRegister, 0xF << bitShift, biasLevel << bitShift);
#else
int bitShift;
EFM_ASSERT(comLine < 8);
bitShift = comLine * 4;
BUS_RegMaskedWrite(&(LCD->SEGD4L), 0xF << bitShift, biasLevel << bitShift);
#endif
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Configure the mode for the LCD panel.
*
* @param[in] mode
* A mode.
******************************************************************************/
void LCD_ModeSet(LCD_Mode_Typedef mode)
{
#if defined(_SILICON_LABS_32B_SERIES_1)
LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_MODE_MASK) | mode;
#else
LCD->BIASCTRL = (LCD->BIASCTRL & ~_LCD_BIASCTRL_MODE_MASK) | mode;
#endif
}
#endif
#if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LABS_32B_SERIES_2)
/***************************************************************************//**
* @brief
* Configure the charge redistribution cycles for the LCD panel.
*
* @param[in] cycles
* Charge redistribution cycles, range 0-4.
******************************************************************************/
void LCD_ChargeRedistributionCyclesSet(uint8_t cycles)
{
EFM_ASSERT(cycles <= 4);
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD_Enable(false); /* Ensure LCD disabled before writing WSTATIC fields. */
LCD_ReadyWait();
#endif
LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CHGRDST_MASK)
| ((uint32_t)cycles << _LCD_DISPCTRL_CHGRDST_SHIFT);
#if defined(_SILICON_LABS_32B_SERIES_2)
LCD_Enable(true);
#endif
}
#endif
/** @} (end addtogroup lcd) */
#endif /* defined(LCD_COUNT) && (LCD_COUNT > 0) */