1 /***************************************************************************//**
2  * @file
3  * @brief Liquid Crystal Display (LCD) Peripheral API
4  *******************************************************************************
5  * # License
6  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7  *******************************************************************************
8  *
9  * SPDX-License-Identifier: Zlib
10  *
11  * The licensor of this software is Silicon Laboratories Inc.
12  *
13  * This software is provided 'as-is', without any express or implied
14  * warranty. In no event will the authors be held liable for any damages
15  * arising from the use of this software.
16  *
17  * Permission is granted to anyone to use this software for any purpose,
18  * including commercial applications, and to alter it and redistribute it
19  * freely, subject to the following restrictions:
20  *
21  * 1. The origin of this software must not be misrepresented; you must not
22  *    claim that you wrote the original software. If you use this software
23  *    in a product, an acknowledgment in the product documentation would be
24  *    appreciated but is not required.
25  * 2. Altered source versions must be plainly marked as such, and must not be
26  *    misrepresented as being the original software.
27  * 3. This notice may not be removed or altered from any source distribution.
28  *
29  ******************************************************************************/
30 
31 #include "em_lcd.h"
32 #if defined(LCD_COUNT) && (LCD_COUNT > 0)
33 #include "sl_assert.h"
34 #include "em_bus.h"
35 #include "em_gpio.h"
36 
37 #include <stddef.h>
38 
39 /***************************************************************************//**
40  * @addtogroup lcd LCD - Liquid Crystal Display
41  * @brief Liquid Crystal Display (LCD) Peripheral API
42  * @details
43  *  This module contains functions to control the LDC peripheral of Silicon
44  *  Labs 32-bit MCUs and SoCs. The LCD driver can drive up to 8x36 segmented
45  *  LCD directly. The animation feature makes it possible to have active
46  *  animations without the CPU intervention.
47  * @{
48  ******************************************************************************/
49 
50 /** Frame counter uses a maximum of 5 bits (FCTOP[5:0]). */
51 #define LCD_FRAME_COUNTER_VAL_MAX  64
52 
53 /***************************************************************************//**
54  * @brief
55  *   Initialize the Liquid Crystal Display (LCD) controller.
56  *
57  * @details
58  *   Configures the LCD controller. You must enable
59  *   it afterwards, potentially configuring Frame Control and interrupts first
60  *   according to requirements.
61  *
62  * @param[in] lcdInit
63  *   A pointer to the initialization structure which configures the LCD controller.
64  *
65  ******************************************************************************/
LCD_Init(const LCD_Init_TypeDef * lcdInit)66 void LCD_Init(const LCD_Init_TypeDef *lcdInit)
67 {
68   uint32_t dispCtrl = LCD->DISPCTRL;
69 
70   EFM_ASSERT(lcdInit != (void *) 0);
71 
72   /* Disable the controller before reconfiguration. */
73   LCD_Enable(false);
74 #if defined(_SILICON_LABS_32B_SERIES_2)
75   LCD_ReadyWait();
76 #endif
77 
78 #if defined(_SILICON_LABS_32B_SERIES_2)
79   /* Initialize LCD registers to hardware reset state. */
80   LCD_Reset();
81 #endif
82 
83 #if defined(_SILICON_LABS_32B_SERIES_2)
84   LCD->CTRL &= ~_LCD_CTRL_PRESCALE_MASK;
85   LCD->CTRL |= lcdInit->clockPrescaler << _LCD_CTRL_PRESCALE_SHIFT;
86 #endif
87 
88   /* Make sure the other bit fields don't get affected (i.e., voltage boost). */
89   dispCtrl &= ~(0
90 #if defined(LCD_DISPCTRL_MUXE)
91                 | _LCD_DISPCTRL_MUXE_MASK
92 #endif
93                 | _LCD_DISPCTRL_MUX_MASK
94                 | _LCD_DISPCTRL_BIAS_MASK
95                 | _LCD_DISPCTRL_WAVE_MASK
96 #if defined(_LCD_DISPCTRL_VLCDSEL_MASK)
97                 | _LCD_DISPCTRL_VLCDSEL_MASK
98 #endif
99 #if defined(_LCD_DISPCTRL_CONCONF_MASK)
100                 | _LCD_DISPCTRL_CONCONF_MASK
101 #endif
102 #if defined(_LCD_DISPCTRL_MODE_MASK)
103                 | _LCD_DISPCTRL_MODE_MASK
104 #endif
105 #if defined(_LCD_DISPCTRL_CHGRDST_MASK)
106                 | _LCD_DISPCTRL_CHGRDST_MASK
107 #endif
108                 );
109 
110   /* Configure the controller according to the initialization structure. */
111   dispCtrl |= lcdInit->mux; /* Also configures MUXE. */
112   dispCtrl |= lcdInit->bias;
113   dispCtrl |= lcdInit->wave;
114 #if defined(_SILICON_LABS_32B_SERIES_0)
115   dispCtrl |= lcdInit->vlcd;
116   dispCtrl |= lcdInit->contrast;
117 #else
118   dispCtrl |= (lcdInit->chargeRedistribution);
119 #endif
120 
121   /* Update the display controller. */
122   LCD->DISPCTRL = dispCtrl;
123 
124 #if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LABS_32B_SERIES_2)
125   LCD_ModeSet(lcdInit->mode);
126   LCD->FRAMERATE = lcdInit->frameRateDivider;
127   LCD_ContrastSet(lcdInit->contrastLevel);
128 #endif
129 
130   /* Enable the controller if needed. */
131   if (lcdInit->enable) {
132     LCD_Enable(true);
133   }
134 }
135 
136 #if defined(_SILICON_LABS_32B_SERIES_0)
137 /***************************************************************************//**
138  * @brief
139  *   Select a source for VLCD.
140  *
141  * @param[in] vlcd
142  *   Select a source for the VLCD voltage.
143  ******************************************************************************/
LCD_VLCDSelect(LCD_VLCDSel_TypeDef vlcd)144 void LCD_VLCDSelect(LCD_VLCDSel_TypeDef vlcd)
145 {
146   uint32_t dispctrl = LCD->DISPCTRL;
147 
148   /* Select VEXT or VDD */
149   dispctrl &= ~_LCD_DISPCTRL_VLCDSEL_MASK;
150   switch (vlcd) {
151     case lcdVLCDSelVExtBoost:
152       dispctrl |= LCD_DISPCTRL_VLCDSEL_VEXTBOOST;
153       break;
154     case lcdVLCDSelVDD:
155       dispctrl |= LCD_DISPCTRL_VLCDSEL_VDD;
156       break;
157     default:
158       break;
159   }
160 
161   LCD->DISPCTRL = dispctrl;
162 }
163 #endif
164 
165 /***************************************************************************//**
166  * @brief
167  *   Configure Update Control.
168  *
169  * @param[in] ud
170  *   Configures the LCD update method.
171  ******************************************************************************/
LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud)172 void LCD_UpdateCtrl(LCD_UpdateCtrl_TypeDef ud)
173 {
174 #if defined(_SILICON_LABS_32B_SERIES_2)
175   LCD_Enable(false);  /* Ensure LCD disabled before writing WSTATIC fields. */
176   LCD_ReadyWait();
177   LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud;
178   LCD_Enable(true);
179 #else
180   LCD->CTRL = (LCD->CTRL & ~_LCD_CTRL_UDCTRL_MASK) | ud;
181 #endif
182 }
183 
184 /***************************************************************************//**
185  * @brief
186  *   Initialize the LCD Frame Counter.
187  *
188  * @param[in] fcInit
189  *   A pointer to the Frame Counter initialization structure.
190  ******************************************************************************/
LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef * fcInit)191 void LCD_FrameCountInit(const LCD_FrameCountInit_TypeDef *fcInit)
192 {
193   EFM_ASSERT(fcInit != (void *) 0);
194 
195   /* Verify that the FC Top Counter is within limits. */
196   EFM_ASSERT(fcInit->top < LCD_FRAME_COUNTER_VAL_MAX);
197 
198 #if defined(_SILICON_LABS_32B_SERIES_2)
199   uint32_t bacfg = LCD->BACFG;
200 
201   /* Reconfigure the frame count configuration. */
202   bacfg &= ~(_LCD_BACFG_FCTOP_MASK
203              | _LCD_BACFG_FCPRESC_MASK);
204   bacfg |= (fcInit->top << _LCD_BACFG_FCTOP_SHIFT);
205   bacfg |= fcInit->prescale;
206 
207   /* Set the Blink and Animation Control Register. */
208   LCD_Enable(false);  /* Ensure LCD disabled before writing WSTATIC fields. */
209   LCD_ReadyWait();
210   LCD->BACFG = bacfg;
211   LCD_Enable(true);
212 #else
213   uint32_t bactrl = LCD->BACTRL;
214 
215   /* Reconfigure the frame count configuration. */
216   bactrl &= ~(_LCD_BACTRL_FCTOP_MASK
217               | _LCD_BACTRL_FCPRESC_MASK);
218   bactrl |= (fcInit->top << _LCD_BACTRL_FCTOP_SHIFT);
219   bactrl |= fcInit->prescale;
220 
221   /* Set the Blink and Animation Control Register. */
222   LCD->BACTRL = bactrl;
223 #endif
224 
225   LCD_FrameCountEnable(fcInit->enable);
226 }
227 
228 /***************************************************************************//**
229  * @brief
230  *   Configure the LCD controller Animation feature.
231  *
232  * @param[in] animInit
233  *   A pointer to the LCD Animation initialization structure.
234  ******************************************************************************/
LCD_AnimInit(const LCD_AnimInit_TypeDef * animInit)235 void LCD_AnimInit(const LCD_AnimInit_TypeDef *animInit)
236 {
237   uint32_t bactrl = LCD->BACTRL;
238 
239   EFM_ASSERT(animInit != (void *) 0);
240 
241   /* Set initial Animation Register Values. */
242   LCD->AREGA = animInit->AReg;
243   LCD->AREGB = animInit->BReg;
244 
245   /* Configure the Animation Shift and Logic. */
246   bactrl &= ~(_LCD_BACTRL_AREGASC_MASK
247               | _LCD_BACTRL_AREGBSC_MASK
248               | _LCD_BACTRL_ALOGSEL_MASK
249 #if defined(_LCD_BACTRL_ALOC_MASK)
250               | _LCD_BACTRL_ALOC_MASK
251 #endif
252               );
253 
254   bactrl |= (animInit->AShift << _LCD_BACTRL_AREGASC_SHIFT);
255   bactrl |= (animInit->BShift << _LCD_BACTRL_AREGBSC_SHIFT);
256   bactrl |= animInit->animLogic;
257 #if defined(_LCD_BACTRL_ALOC_MASK)
258   bactrl |= animInit->startSeg;
259 #endif
260 
261   /* Reconfigure. */
262   LCD->BACTRL = bactrl;
263 
264   /* Enable. */
265   LCD_AnimEnable(animInit->enable);
266 }
267 
268 #if defined(_SILICON_LABS_32B_SERIES_0)
269 /***************************************************************************//**
270  * @brief
271  *   Enables updating this range of LCD segment lines.
272  *
273  * @param[in] segmentRange
274  *   A range of 4 LCD segment lines to enable or disable for all enabled COM
275  *   lines.
276  *
277  * @param[in] enable
278  *   Boolean true to enable segment updates, false to disable updates.
279  ******************************************************************************/
LCD_SegmentRangeEnable(LCD_SegmentRange_TypeDef segmentRange,bool enable)280 void LCD_SegmentRangeEnable(LCD_SegmentRange_TypeDef segmentRange, bool enable)
281 {
282   if (enable) {
283     LCD->SEGEN |= segmentRange;
284   } else {
285     LCD->SEGEN &= ~((uint32_t)segmentRange);
286   }
287 }
288 #endif
289 
290 #if defined(_SILICON_LABS_32B_SERIES_2)
291 /***************************************************************************//**
292  * @brief
293  *   Enables a given LCD segment line.
294  *
295  * @param[in] seg_nbr
296  *   Segment line number.
297  *
298  * @param[in] enable
299  *   Boolean true to enable a segment, false to disable.
300  ******************************************************************************/
LCD_SegmentEnable(uint32_t seg_nbr,bool enable)301 void LCD_SegmentEnable(uint32_t seg_nbr, bool enable)
302 {
303   /* Series 2 parts support up to 20 segment lines. */
304   /* Except for xG26 which supports up to 40 segment lines. and xG28 which supports up to 28 segment lines. */
305   EFM_ASSERT(seg_nbr < (int)LCD_SEGMENT_LINES_MAX);
306 
307 #if defined(_GPIO_LCDSEGH_MASK)
308   if (enable) {
309     if (seg_nbr > 31) {
310       GPIO->LCDSEGH_SET = 1 << (seg_nbr - 32);
311     } else {
312       GPIO->LCDSEGL_SET = 1 << (seg_nbr);
313     }
314   } else {
315     if (seg_nbr > 31) {
316       GPIO->LCDSEGH_CLR = 1 << (seg_nbr - 32);
317     } else {
318       GPIO->LCDSEGL_CLR = 1 << (seg_nbr);
319     }
320   }
321 #else /* defined(_GPIO_LCDSEGH_MASK) */
322   if (enable) {
323     GPIO->LCDSEG_SET = 1 << seg_nbr;
324   } else {
325     GPIO->LCDSEG_CLR = 1 << seg_nbr;
326   }
327 #endif /* defined(_GPIO_LCDSEGH_MASK) */
328 }
329 #endif
330 
331 #if defined(_SILICON_LABS_32B_SERIES_2)
332 /***************************************************************************//**
333  * @brief
334  *   Enables a given LCD COM line.
335  *
336  * @param[in] com
337  *   COM line number.
338  *
339  * @param[in] enable
340  *   Boolean true to enable a COM , false to disable.
341  ******************************************************************************/
LCD_ComEnable(uint8_t com,bool enable)342 void LCD_ComEnable(uint8_t com, bool enable)
343 {
344   /* Series 2 parts support up to 4 COM lines except for xG28, which supports up to 8 COM lines. */
345   EFM_ASSERT(com < LCD_COM_LINES_MAX);
346 
347   if (com < LCD_COM_NUM) {
348     if (enable) {
349       GPIO->LCDCOM_SET = 1 << com;
350     } else {
351       GPIO->LCDCOM_CLR = 1 << com;
352     }
353   }
354 #if defined(LCD_OCTAPLEX) && (LCD_OCTAPLEX == 1)
355   else {
356     /* On xG28, SEG lines shall be configured as COM lines  */
357     /* for COM support above 4 COM lines                    */
358 #if defined(_GPIO_LCDSEGH_MASK)
359     if (enable) {
360       if ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART > 31) {
361         GPIO->LCDSEGH_SET = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART - 32);
362       } else {
363         GPIO->LCDSEGL_SET = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
364       }
365     } else {
366       if ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART > 31) {
367         GPIO->LCDSEGH_CLR = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART - 32);
368       } else {
369         GPIO->LCDSEGL_CLR = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
370       }
371     }
372   }
373 #else /* defined(_GPIO_LCDSEGH_MASK) */
374     if (enable) {
375       GPIO->LCDSEG_SET = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
376     } else {
377       GPIO->LCDSEG_CLR = 1 << ((com - LCD_COM_NUM) + LCD_SEGASCOM_SEGSTART);
378     }
379   }
380 #endif /* defined(_GPIO_LCDSEGH_MASK) */
381 #endif /* defined(LCD_OCTAPLEX) && (LCD_OCTAPLEX == 1) */
382 }
383 #endif
384 
385 #if defined(_SILICON_LABS_32B_SERIES_2)
386 /***************************************************************************//**
387  * @brief
388  *   Set a given DMA mode operation.
389  *
390  * @param[in] mode
391  *   DMA mode.
392  ******************************************************************************/
LCD_DmaModeSet(LCD_DmaMode_Typedef mode)393 void LCD_DmaModeSet(LCD_DmaMode_Typedef mode)
394 {
395   LCD->BIASCTRL_CLR = _LCD_BIASCTRL_DMAMODE_MASK;
396   LCD->BIASCTRL |= mode;
397 }
398 #endif
399 
400 /***************************************************************************//**
401  * @brief
402  *   Turn on or clear a segment.
403  *
404  * @note
405  *    For the Gecko Family, the maximum configuration is (COM-lines x Segment-Lines) 4x40.
406  *    For the Tiny Gecko Family, the maximum configuration is 8x20 or 4x24.
407  *    For the Giant Gecko Family, the maximum configuration is 8x36 or 4x40.
408  *    For the Series 2 Family, the maximum configuration is 4x20.
409  *    For the Series 2 xG28, the maximum configuration is 8x24 or 4x28.
410  *
411  * @param[in] com
412  *   A COM line to change.
413  *
414  * @param[in] bit
415  *   A bit index indicating which field to change.
416  *
417  * @param[in] enable
418  *   True will set segment, false will clear segment.
419  ******************************************************************************/
LCD_SegmentSet(int com,int bit,bool enable)420 void LCD_SegmentSet(int com, int bit, bool enable)
421 {
422 #if defined(_SILICON_LABS_32B_SERIES_2)
423   /* Series 2 parts support up to 4 COM lines except for xG26 and xG28, which supports up to 8 COM lines. */
424   EFM_ASSERT(com < (int)LCD_COM_LINES_MAX);
425 
426   /* Series 2 parts support up to 20 segment lines. */
427   /* Except for xG26 which supports up to 40 segment lines. and xG28 which supports up to 28 segment lines. */
428   EFM_ASSERT(bit < (int)LCD_SEGMENT_LINES_MAX);
429 
430   /* Use a bitband access for atomic bit set/clear of the segment. */
431   switch (com) {
432     case 0:
433 #if defined(_LCD_SEGD0H_MASK)
434       if (bit < 32) {
435         BUS_RegBitWrite(&(LCD->SEGD0), bit, enable);
436       } else {
437         bit -= 32;
438         BUS_RegBitWrite(&(LCD->SEGD0H), bit, enable);
439       }
440 #else
441       BUS_RegBitWrite(&(LCD->SEGD0), bit, enable);
442 #endif
443       break;
444 
445     case 1:
446 #if defined(_LCD_SEGD1H_MASK)
447       if (bit < 32) {
448         BUS_RegBitWrite(&(LCD->SEGD1), bit, enable);
449       } else {
450         bit -= 32;
451         BUS_RegBitWrite(&(LCD->SEGD1H), bit, enable);
452       }
453 #else
454       BUS_RegBitWrite(&(LCD->SEGD1), bit, enable);
455 #endif
456       break;
457 
458     case 2:
459 #if defined(_LCD_SEGD2H_MASK)
460       if (bit < 32) {
461         BUS_RegBitWrite(&(LCD->SEGD2), bit, enable);
462       } else {
463         bit -= 32;
464         BUS_RegBitWrite(&(LCD->SEGD2H), bit, enable);
465       }
466 #else
467       BUS_RegBitWrite(&(LCD->SEGD2), bit, enable);
468 #endif
469       break;
470 
471     case 3:
472 #if defined(_LCD_SEGD3H_MASK)
473       if (bit < 32) {
474         BUS_RegBitWrite(&(LCD->SEGD3), bit, enable);
475       } else {
476         bit -= 32;
477         BUS_RegBitWrite(&(LCD->SEGD3H), bit, enable);
478       }
479 #else
480       BUS_RegBitWrite(&(LCD->SEGD3), bit, enable);
481 #endif
482       break;
483 
484 #if defined(_LCD_SEGD4_MASK)
485     case 4:
486 #if defined(_LCD_SEGD4H_MASK)
487       if (bit < 32) {
488         BUS_RegBitWrite(&(LCD->SEGD4), bit, enable);
489       } else {
490         bit -= 32;
491         BUS_RegBitWrite(&(LCD->SEGD4H), bit, enable);
492       }
493 #else
494       BUS_RegBitWrite(&(LCD->SEGD4), bit, enable);
495 #endif
496       break;
497 #endif
498 
499 #if defined(_LCD_SEGD5_MASK)
500     case 5:
501 #if defined(_LCD_SEGD5H_MASK)
502       if (bit < 32) {
503         BUS_RegBitWrite(&(LCD->SEGD5), bit, enable);
504       } else {
505         bit -= 32;
506         BUS_RegBitWrite(&(LCD->SEGD5H), bit, enable);
507       }
508 #else
509       BUS_RegBitWrite(&(LCD->SEGD5), bit, enable);
510 #endif
511       break;
512 #endif
513 
514 #if defined(_LCD_SEGD6_MASK)
515     case 6:
516 #if defined(_LCD_SEGD6H_MASK)
517       if (bit < 32) {
518         BUS_RegBitWrite(&(LCD->SEGD6), bit, enable);
519       } else {
520         bit -= 32;
521         BUS_RegBitWrite(&(LCD->SEGD6H), bit, enable);
522       }
523 #else
524       BUS_RegBitWrite(&(LCD->SEGD6), bit, enable);
525 #endif
526       break;
527 #endif
528 
529 #if defined(_LCD_SEGD7_MASK)
530     case 7:
531 #if defined(_LCD_SEGD7H_MASK)
532       if (bit < 32) {
533         BUS_RegBitWrite(&(LCD->SEGD7), bit, enable);
534       } else {
535         bit -= 32;
536         BUS_RegBitWrite(&(LCD->SEGD7H), bit, enable);
537       }
538 #else
539       BUS_RegBitWrite(&(LCD->SEGD7), bit, enable);
540 #endif
541       break;
542 #endif
543 
544     default:
545       EFM_ASSERT(0);
546       break;
547   }
548 #else
549 #if defined(_LCD_SEGD7L_MASK)
550   /* Tiny Gecko and Giant Gecko Families support up to 8 COM lines. */
551   EFM_ASSERT(com < 8);
552 #else
553   /* Gecko Family supports up to 4 COM lines. */
554   EFM_ASSERT(com < 4);
555 #endif
556 
557 #if defined(_LCD_SEGD0H_MASK)
558   EFM_ASSERT(bit < 40);
559 #else
560   /* Tiny Gecko Family supports only "low" segment registers. */
561   EFM_ASSERT(bit < 32);
562 #endif
563 
564   /* Use a bitband access for atomic bit set/clear of the segment. */
565   switch (com) {
566     case 0:
567       if (bit < 32) {
568         BUS_RegBitWrite(&(LCD->SEGD0L), bit, enable);
569       }
570 #if defined(_LCD_SEGD0H_MASK)
571       else {
572         bit -= 32;
573         BUS_RegBitWrite(&(LCD->SEGD0H), bit, enable);
574       }
575 #endif
576       break;
577     case 1:
578       if (bit < 32) {
579         BUS_RegBitWrite(&(LCD->SEGD1L), bit, enable);
580       }
581 #if defined(_LCD_SEGD1H_MASK)
582       else {
583         bit -= 32;
584         BUS_RegBitWrite(&(LCD->SEGD1H), bit, enable);
585       }
586 #endif
587       break;
588     case 2:
589       if (bit < 32) {
590         BUS_RegBitWrite(&(LCD->SEGD2L), bit, enable);
591       }
592 #if defined(_LCD_SEGD2H_MASK)
593       else {
594         bit -= 32;
595         BUS_RegBitWrite(&(LCD->SEGD2H), bit, enable);
596       }
597 #endif
598       break;
599     case 3:
600       if (bit < 32) {
601         BUS_RegBitWrite(&(LCD->SEGD3L), bit, enable);
602       }
603 #if defined(_LCD_SEGD3H_MASK)
604       else {
605         bit -= 32;
606         BUS_RegBitWrite(&(LCD->SEGD3H), bit, enable);
607       }
608 #endif
609       break;
610 #if defined(_LCD_SEGD4L_MASK)
611     case 4:
612       if (bit < 32) {
613         BUS_RegBitWrite(&(LCD->SEGD4L), bit, enable);
614       }
615 #if defined(_LCD_SEGD4H_MASK)
616       else {
617         bit -= 32;
618         BUS_RegBitWrite(&(LCD->SEGD4H), bit, enable);
619       }
620 #endif
621       break;
622 #endif
623 #if defined(_LCD_SEGD5L_MASK)
624     case 5:
625       if (bit < 32) {
626         BUS_RegBitWrite(&(LCD->SEGD5L), bit, enable);
627       }
628 #if defined(_LCD_SEGD5H_MASK)
629       else {
630         bit -= 32;
631         BUS_RegBitWrite(&(LCD->SEGD5H), bit, enable);
632       }
633 #endif
634       break;
635 #endif
636     case 6:
637 #if defined(_LCD_SEGD6L_MASK)
638       if (bit < 32) {
639         BUS_RegBitWrite(&(LCD->SEGD6L), bit, enable);
640       }
641 #if defined(_LCD_SEGD6H_MASK)
642       else {
643         bit -= 32;
644         BUS_RegBitWrite(&(LCD->SEGD6H), bit, enable);
645       }
646 #endif
647       break;
648 #endif
649 #if defined(_LCD_SEGD7L_MASK)
650     case 7:
651       if (bit < 32) {
652         BUS_RegBitWrite(&(LCD->SEGD7L), bit, enable);
653       }
654 #if defined(_LCD_SEGD7H_MASK)
655       else {
656         bit -= 32;
657         BUS_RegBitWrite(&(LCD->SEGD7H), bit, enable);
658       }
659 #endif
660       break;
661 #endif
662 
663     default:
664       EFM_ASSERT(0);
665       break;
666   }
667 #endif
668 }
669 
670 /***************************************************************************//**
671  * @brief
672  *   Update 0-31 lowest segments on a given COM-line in one operation
673  *   according to the bit mask.
674  *
675  * @param[in] com
676  *   Indicates a COM line to update.
677  *
678  * @param[in] mask
679  *   A bit mask for segments 0-31.
680  *
681  * @param[in] bits
682  *   A bit pattern for segments 0-31.
683  ******************************************************************************/
LCD_SegmentSetLow(int com,uint32_t mask,uint32_t bits)684 void LCD_SegmentSetLow(int com, uint32_t mask, uint32_t bits)
685 {
686 #if defined(_SILICON_LABS_32B_SERIES_2)
687   uint32_t segData;
688 
689   /* Series 2 parts support up to 4 COM lines except for xG26 and xG28, which supports up to 8 COM lines. */
690   EFM_ASSERT(com < (int)LCD_COM_LINES_MAX);
691 
692   /* Series 2 parts support up to 20 segment lines. */
693   /* Except for xG26 which supports up to 40 segment lines. and xG28 which supports up to 28 segment lines. */
694   EFM_ASSERT(!(mask & (~_LCD_SEGD0_MASK)));
695   EFM_ASSERT(!(bits & (~_LCD_SEGD0_MASK)));
696 
697   switch (com) {
698     case 0:
699       segData     = LCD->SEGD0;
700       segData    &= ~(mask);
701       segData    |= (mask & bits);
702       LCD->SEGD0 = segData;
703       break;
704 
705     case 1:
706       segData     = LCD->SEGD1;
707       segData    &= ~(mask);
708       segData    |= (mask & bits);
709       LCD->SEGD1 = segData;
710 
711       break;
712     case 2:
713       segData     = LCD->SEGD2;
714       segData    &= ~(mask);
715       segData    |= (mask & bits);
716       LCD->SEGD2 = segData;
717       break;
718 
719     case 3:
720       segData     = LCD->SEGD3;
721       segData    &= ~(mask);
722       segData    |= (mask & bits);
723       LCD->SEGD3 = segData;
724       break;
725 
726 #if defined(_LCD_SEGD4_MASK)
727     case 4:
728       segData     = LCD->SEGD4;
729       segData    &= ~(mask);
730       segData    |= (mask & bits);
731       LCD->SEGD4 = segData;
732       break;
733 #endif
734 
735 #if defined(_LCD_SEGD5_MASK)
736     case 5:
737       segData     = LCD->SEGD5;
738       segData    &= ~(mask);
739       segData    |= (mask & bits);
740       LCD->SEGD5 = segData;
741       break;
742 #endif
743 
744 #if defined(_LCD_SEGD6_MASK)
745     case 6:
746       segData     = LCD->SEGD6;
747       segData    &= ~(mask);
748       segData    |= (mask & bits);
749       LCD->SEGD6 = segData;
750       break;
751 #endif
752 
753 #if defined(_LCD_SEGD7_MASK)
754     case 7:
755       segData     = LCD->SEGD7;
756       segData    &= ~(mask);
757       segData    |= (mask & bits);
758       LCD->SEGD7 = segData;
759       break;
760 #endif
761 
762     default:
763       EFM_ASSERT(0);
764       break;
765   }
766 #else
767   uint32_t segData;
768 
769   /* A maximum number of com lines. */
770 #if defined(_LCD_SEGD7L_MASK)
771   EFM_ASSERT(com < 8);
772 #else
773   /* Gecko Family supports up to 4 COM lines. */
774   EFM_ASSERT(com < 4);
775 #endif
776 
777   switch (com) {
778     case 0:
779       segData     = LCD->SEGD0L;
780       segData    &= ~(mask);
781       segData    |= (mask & bits);
782       LCD->SEGD0L = segData;
783       break;
784     case 1:
785       segData     = LCD->SEGD1L;
786       segData    &= ~(mask);
787       segData    |= (mask & bits);
788       LCD->SEGD1L = segData;
789       break;
790     case 2:
791       segData     = LCD->SEGD2L;
792       segData    &= ~(mask);
793       segData    |= (mask & bits);
794       LCD->SEGD2L = segData;
795       break;
796     case 3:
797       segData     = LCD->SEGD3L;
798       segData    &= ~(mask);
799       segData    |= (mask & bits);
800       LCD->SEGD3L = segData;
801       break;
802 #if defined(_LCD_SEGD4L_MASK)
803     case 4:
804       segData     = LCD->SEGD4L;
805       segData    &= ~(mask);
806       segData    |= (mask & bits);
807       LCD->SEGD4L = segData;
808       break;
809 #endif
810 #if defined(_LCD_SEGD5L_MASK)
811     case 5:
812       segData     = LCD->SEGD5L;
813       segData    &= ~(mask);
814       segData    |= (mask & bits);
815       LCD->SEGD5L = segData;
816       break;
817 #endif
818 #if defined(_LCD_SEGD6L_MASK)
819     case 6:
820       segData     = LCD->SEGD6L;
821       segData    &= ~(mask);
822       segData    |= (mask & bits);
823       LCD->SEGD6L = segData;
824       break;
825 #endif
826 #if defined(_LCD_SEGD7L_MASK)
827     case 7:
828       segData     = LCD->SEGD7L;
829       segData    &= ~(mask);
830       segData    |= (mask & bits);
831       LCD->SEGD7L = segData;
832       break;
833 #endif
834     default:
835       EFM_ASSERT(0);
836       break;
837   }
838 #endif
839 }
840 
841 #if defined(_LCD_SEGD0H_MASK)
842 /***************************************************************************//**
843  * @brief
844  *   Update the high (32-39) segments on a given COM-line in one operation.
845  *
846  * @param[in] com
847  *   Indicates a COM line to update.
848  *
849  * @param[in] mask
850  *   A bit mask for segments 32-39.
851  *
852  * @param[in] bits
853  *   A bit pattern for segments 32-39.
854  ******************************************************************************/
LCD_SegmentSetHigh(int com,uint32_t mask,uint32_t bits)855 void LCD_SegmentSetHigh(int com, uint32_t mask, uint32_t bits)
856 {
857   uint32_t segData;
858 
859 #if defined(_LCD_SEGD7H_MASK)
860   EFM_ASSERT(com < 8);
861 #else
862   EFM_ASSERT(com < 4);
863 #endif
864 
865   /* A maximum number of com lines. */
866   switch (com) {
867     case 0:
868       segData     = LCD->SEGD0H;
869       segData    &= ~(mask);
870       segData    |= (mask & bits);
871       LCD->SEGD0H = segData;
872       break;
873     case 1:
874       segData     = LCD->SEGD1H;
875       segData    &= ~(mask);
876       segData    |= (mask & bits);
877       LCD->SEGD1H = segData;
878       break;
879     case 2:
880       segData     = LCD->SEGD2H;
881       segData    &= ~(mask);
882       segData    |= (mask & bits);
883       LCD->SEGD2H = segData;
884       break;
885     case 3:
886       segData     = LCD->SEGD3H;
887       segData    &= ~(mask);
888       segData    |= (mask & bits);
889       LCD->SEGD3H = segData;
890       break;
891 #if defined(_LCD_SEGD4H_MASK)
892     case 4:
893       segData     = LCD->SEGD4H;
894       segData    &= ~(mask);
895       segData    |= (mask & bits);
896       LCD->SEGD4H = segData;
897       break;
898 #endif
899 #if defined(_LCD_SEGD5H_MASK)
900     case 5:
901       segData     = LCD->SEGD5H;
902       segData    &= ~(mask);
903       segData    |= (mask & bits);
904       LCD->SEGD5H = segData;
905       break;
906 #endif
907 #if defined(_LCD_SEGD6H_MASK)
908     case 6:
909       segData     = LCD->SEGD6H;
910       segData    &= ~(mask);
911       segData    |= (mask & bits);
912       LCD->SEGD6H = segData;
913       break;
914 #endif
915 #if defined(_LCD_SEGD7H_MASK)
916     case 7:
917       segData     = LCD->SEGD7H;
918       segData    &= ~(mask);
919       segData    |= (mask & bits);
920       LCD->SEGD7H = segData;
921       break;
922 #endif
923     default:
924       break;
925   }
926 }
927 #endif
928 
929 /***************************************************************************//**
930  * @brief
931  *   Configure the contrast level on the LCD panel.
932  *
933  * @param[in] level
934  *   The contrast level in range 0-63.
935  ******************************************************************************/
LCD_ContrastSet(int level)936 void LCD_ContrastSet(int level)
937 {
938 #if defined(_SILICON_LABS_32B_SERIES_0)
939   EFM_ASSERT(level < 32);
940 
941   LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONLEV_MASK)
942                   | (level << _LCD_DISPCTRL_CONLEV_SHIFT);
943 
944 #elif defined(_SILICON_LABS_32B_SERIES_1)
945   EFM_ASSERT(level < 64);
946 
947   LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CONTRAST_MASK)
948                   | (level << _LCD_DISPCTRL_CONTRAST_SHIFT);
949 #else
950   EFM_ASSERT(level < 32);
951 
952   LCD->BIASCTRL = (LCD->BIASCTRL & ~_LCD_BIASCTRL_VLCD_MASK)
953                   | (level << _LCD_BIASCTRL_VLCD_SHIFT);
954 #endif
955 }
956 
957 /***************************************************************************//**
958  * @brief
959  *   Configure the bias level on the LCD panel.
960  *
961  * @param[in] bias
962  *   The bias level.
963  ******************************************************************************/
LCD_BiasSet(LCD_Bias_TypeDef bias)964 void LCD_BiasSet(LCD_Bias_TypeDef bias)
965 {
966 #if defined(_SILICON_LABS_32B_SERIES_2)
967   LCD_Enable(false); /* Ensure LCD disabled before writing WSTATIC fields. */
968   LCD_ReadyWait();
969 #endif
970 
971   LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_BIAS_MASK) | bias;
972 
973 #if defined(_SILICON_LABS_32B_SERIES_2)
974   LCD_Enable(true);
975 #endif
976 }
977 
978 #if defined(_SILICON_LABS_32B_SERIES_0)
979 /***************************************************************************//**
980  * @brief
981  *   Configure voltage booster
982  *
983  * The resulting voltage level is described in each part number's data sheet
984  *
985  * @param[in] vboost
986  *   Voltage boost level
987  ******************************************************************************/
LCD_VBoostSet(LCD_VBoostLevel_TypeDef vboost)988 void LCD_VBoostSet(LCD_VBoostLevel_TypeDef vboost)
989 {
990   /* Reconfigure Voltage Boost */
991   LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_VBLEV_MASK) | vboost;
992 }
993 #endif
994 
995 #if defined(LCD_CTRL_DSC)
996 /***************************************************************************//**
997  * @brief
998  *   Configure the bias level for a specific segment line for Direct Segment Control.
999  *
1000  * @note
1001  *   When DSC is active, each configuration takes up 4 bits in the corresponding
1002  *   Segment Registers (SEGD0L/SEGD1H for Series 0 and 1, SEGDx/SEGDxH for Series 2)
1003  *   which defines the bias level.
1004  *   For optimal use of this feature, the entire SEGD-registers should be set
1005  *   at once in an optimized routine. Therefore, this function shows how to
1006  *   correctly configure the bias levels and should be used with care.
1007  *
1008  * @param[in] segmentLine
1009  *   A segment line number.
1010  *
1011  * @param[in] biasLevel
1012  *   The bias configuration level. This value must be within the constraints
1013  *   defined by the LCD_DISPCTRL bias settings. For more information, see the
1014  *   applicable Reference Manual and data sheet.
1015  ******************************************************************************/
LCD_BiasSegmentSet(int segmentLine,int biasLevel)1016 void LCD_BiasSegmentSet(int segmentLine, int biasLevel)
1017 {
1018   volatile uint32_t *segmentRegister;
1019   int               biasRegister;
1020   int               bitShift;
1021 
1022 #if defined(_SILICON_LABS_32B_SERIES_2)
1023   if (segmentLine >= (int)LCD_SEG_NUM) {
1024     return;
1025   }
1026 
1027   /*
1028    * On Series 2, the following layout is used to configure bias when DSC
1029    * is active:
1030    *
1031    * REGISTERS | ... | 7 .. 4 | 3 .. 0 |
1032    *   SEGD0   | ... |  seg4  |  seg0  |
1033    *   SEGD1   | ... |  seg5  |  seg1  |
1034    *   .....
1035    */
1036   biasRegister = segmentLine % 4;
1037   bitShift     = (segmentLine / 4) * 4;
1038 
1039 #if defined(_GPIO_LCDSEGH_MASK)
1040   switch (biasRegister) {
1041     case 0:
1042       if (bitShift < 32) {
1043         segmentRegister = &LCD->SEGD0;
1044       } else {
1045         segmentRegister = &LCD->SEGD0H;
1046         bitShift       -= 32;
1047       }
1048       break;
1049     case 1:
1050       if (bitShift < 32) {
1051         segmentRegister = &LCD->SEGD1;
1052       } else {
1053         segmentRegister = &LCD->SEGD1H;
1054         bitShift       -= 32;
1055       }
1056       break;
1057     case 2:
1058       if (bitShift < 32) {
1059         segmentRegister = &LCD->SEGD2;
1060       } else {
1061         segmentRegister = &LCD->SEGD2H;
1062         bitShift       -= 32;
1063       }
1064       break;
1065     case 3:
1066       if (bitShift < 32) {
1067         segmentRegister = &LCD->SEGD3;
1068       } else {
1069         segmentRegister = &LCD->SEGD3H;
1070         bitShift       -= 32;
1071       }
1072       break;
1073     default:
1074       segmentRegister = NULL;
1075       EFM_ASSERT(0);
1076       break;
1077   }
1078 
1079 #else /* defined(_GPIO_LCDSEGH_MASK) */
1080 
1081   switch (biasRegister) {
1082     case 0:
1083       segmentRegister = &LCD->SEGD0;
1084       break;
1085     case 1:
1086       segmentRegister = &LCD->SEGD1;
1087       break;
1088     case 2:
1089       segmentRegister = &LCD->SEGD2;
1090       break;
1091     case 3:
1092       segmentRegister = &LCD->SEGD3;
1093       break;
1094     default:
1095       segmentRegister = NULL;
1096       EFM_ASSERT(0);
1097       break;
1098   }
1099 #endif /* defined(_GPIO_LCDSEGH_MASK) */
1100 
1101 #else
1102 
1103 #if !defined(_LCD_SEGD0H_MASK)
1104   EFM_ASSERT(segmentLine < 20);
1105 
1106   /* A bias configuration for 8 segment lines per SEGDnL register. */
1107   biasRegister = segmentLine / 8;
1108   bitShift     = (segmentLine % 8) * 4;
1109 
1110   switch (biasRegister) {
1111     case 0:
1112       segmentRegister = &LCD->SEGD0L;
1113       break;
1114     case 1:
1115       segmentRegister = &LCD->SEGD1L;
1116       break;
1117     case 2:
1118       segmentRegister = &LCD->SEGD2L;
1119       break;
1120     case 3:
1121       segmentRegister = &LCD->SEGD3L;
1122       break;
1123     default:
1124       segmentRegister = (uint32_t *)0x00000000;
1125       EFM_ASSERT(0);
1126       break;
1127   }
1128 #else
1129   EFM_ASSERT(segmentLine < 40);
1130 
1131   /* A bias configuration for 10 segment lines per SEGDn L+H registers. */
1132   biasRegister = segmentLine / 10;
1133   bitShift     = (segmentLine % 10) * 4;
1134 
1135   switch (biasRegister) {
1136     case 0:
1137       if (bitShift < 32) {
1138         segmentRegister = &LCD->SEGD0L;
1139       } else {
1140         segmentRegister = &LCD->SEGD0H;
1141         bitShift       -= 32;
1142       }
1143       break;
1144     case 1:
1145       if (bitShift < 32) {
1146         segmentRegister = &LCD->SEGD1L;
1147       } else {
1148         segmentRegister = &LCD->SEGD1H;
1149         bitShift       -= 32;
1150       }
1151       break;
1152     case 2:
1153       if (bitShift < 32) {
1154         segmentRegister = &LCD->SEGD2L;
1155       } else {
1156         segmentRegister = &LCD->SEGD1H;
1157         bitShift       -= 32;
1158       }
1159       break;
1160     case 3:
1161       if (bitShift < 32) {
1162         segmentRegister = &LCD->SEGD3L;
1163       } else {
1164         segmentRegister = &LCD->SEGD3H;
1165         bitShift       -= 32;
1166       }
1167       break;
1168     default:
1169       segmentRegister = (uint32_t *)0x00000000;
1170       EFM_ASSERT(0);
1171       break;
1172   }
1173 #endif
1174 
1175 #endif
1176 
1177   /* Configure a new bias setting. */
1178   BUS_RegMaskedWrite(segmentRegister, 0xF << bitShift, biasLevel << bitShift);
1179 }
1180 #endif
1181 
1182 #if defined(LCD_CTRL_DSC)
1183 /***************************************************************************//**
1184  * @brief
1185  *   Configure the bias level for a specific segment line.
1186  *
1187  * @note
1188  *   When DSC is active, each configuration takes up 4 bits in the corresponding
1189  *   Segment Registers (SEGD4L/SEGD4H for Series 0 and 1, AREGA/AREGB for
1190  *   Series 2) which defines bias level.
1191  *   For optimal use of this feature, the entire register set should be set
1192  *   at once in a optimized routine. Therefore, this function shows how to
1193  *   correctly configure the bias levels and should be used with care.
1194  *
1195  * @param[in] comLine
1196  *   A COM line number, between 0 and 7 for Series 0 and 1. For Series 2, max
1197  *   is LCD_COM_NUM, defined in device-specific headers.
1198  *
1199  *
1200  * @param[in] biasLevel
1201  *   The bias configuration level. This value must be within the constraints
1202  *   defined by the LCD_DISPCTRL bias settings.
1203  *   For more information, see the appropriate Reference Manual and data sheet.
1204  ******************************************************************************/
LCD_BiasComSet(int comLine,int biasLevel)1205 void LCD_BiasComSet(int comLine, int biasLevel)
1206 {
1207 #if defined(_SILICON_LABS_32B_SERIES_2)
1208   volatile uint32_t *comRegister;
1209   uint32_t biasRegister;
1210   uint32_t bitShift;
1211 
1212   if (comLine >= (int)LCD_COM_NUM) {
1213     return;
1214   }
1215 
1216   biasRegister = (uint32_t) comLine % 2;
1217   bitShift     = (comLine / 2) * 4;
1218 
1219   switch (biasRegister) {
1220     case 0:
1221       comRegister = &LCD->AREGA;
1222       break;
1223     case 1:
1224       comRegister = &LCD->AREGB;
1225       break;
1226     default:
1227       comRegister = NULL;
1228       EFM_ASSERT(0);
1229       break;
1230   }
1231 
1232   BUS_RegMaskedWrite(comRegister, 0xF << bitShift, biasLevel << bitShift);
1233 
1234 #else
1235   int bitShift;
1236   EFM_ASSERT(comLine < 8);
1237 
1238   bitShift    = comLine * 4;
1239   BUS_RegMaskedWrite(&(LCD->SEGD4L), 0xF << bitShift, biasLevel << bitShift);
1240 #endif
1241 }
1242 #endif
1243 
1244 #if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LABS_32B_SERIES_2)
1245 /***************************************************************************//**
1246  * @brief
1247  *   Configure the mode for the LCD panel.
1248  *
1249  * @param[in] mode
1250  *   A mode.
1251  ******************************************************************************/
LCD_ModeSet(LCD_Mode_Typedef mode)1252 void LCD_ModeSet(LCD_Mode_Typedef mode)
1253 {
1254 #if defined(_SILICON_LABS_32B_SERIES_1)
1255   LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_MODE_MASK) | mode;
1256 #else
1257   LCD->BIASCTRL = (LCD->BIASCTRL & ~_LCD_BIASCTRL_MODE_MASK) | mode;
1258 #endif
1259 }
1260 #endif
1261 
1262 #if defined(_SILICON_LABS_32B_SERIES_1) || defined(_SILICON_LABS_32B_SERIES_2)
1263 /***************************************************************************//**
1264  * @brief
1265  *   Configure the charge redistribution cycles for the LCD panel.
1266  *
1267  * @param[in] cycles
1268  *   Charge redistribution cycles, range 0-4.
1269  ******************************************************************************/
LCD_ChargeRedistributionCyclesSet(uint8_t cycles)1270 void LCD_ChargeRedistributionCyclesSet(uint8_t cycles)
1271 {
1272   EFM_ASSERT(cycles <= 4);
1273 
1274 #if defined(_SILICON_LABS_32B_SERIES_2)
1275   LCD_Enable(false); /* Ensure LCD disabled before writing WSTATIC fields. */
1276   LCD_ReadyWait();
1277 #endif
1278 
1279   LCD->DISPCTRL = (LCD->DISPCTRL & ~_LCD_DISPCTRL_CHGRDST_MASK)
1280                   | ((uint32_t)cycles << _LCD_DISPCTRL_CHGRDST_SHIFT);
1281 
1282 #if defined(_SILICON_LABS_32B_SERIES_2)
1283   LCD_Enable(true);
1284 #endif
1285 }
1286 #endif
1287 
1288 /** @} (end addtogroup lcd) */
1289 
1290 #endif /* defined(LCD_COUNT) && (LCD_COUNT > 0) */
1291