专栏名称: 嵌入式微处理器
关注这个时代最火的嵌入式微处理器,你想知道的都在这里。
目录
相关文章推荐
掌上春城  ·  全红婵,拟被保送! ·  9 小时前  
新浪科技  ·  【#马斯克宣布Grok语音模式正式上线#】 ... ·  昨天  
36氪  ·  7万大定的智界R7,贴脸竞争特斯拉 ·  3 天前  
掌上春城  ·  多位iPhone用户遭遇新诈骗,客服建议→ ·  3 天前  
51好读  ›  专栏  ›  嵌入式微处理器

硬核干货!解析SLCD例程设计思路,加速终端产品应用开发

嵌入式微处理器  · 公众号  ·  · 2024-04-19 12:00

正文

段码LCD显示时,不管是通过驱动芯片驱动段码LCD显示的,还是使用带有驱动段码LCD能力的MCU,在实际应用中都需要实现硬件原理图、段码LCD真值表、显示缓存这三者之间的对应关系。只有这些对应一致了,应用程序才能够随心所欲的显示需求内容。

而这三者的对应关系,一直是应用设计中一个很头疼的问题,有些时候可能会因为PCB布线的方便,使得连接段码LCD的COM/SEG顺序变得错乱,这样在找对应关系的时候会更加复杂。

BUT……不用慌,今天找到一个解决这一老大难问题的程序设计思路/框架,那就是 隐藏在MM32L0160的官方示例程序中的SLCD例程!

有些小伙伴在看到这段程序时会感觉看不懂,不明白官方这么设计的思路/意图是啥,为什么要这样设计,这么设计的用意/优点体现在哪里?简单来说就是看不懂,也不知道怎么应用到自己的产品中来。

本文将结合官方的SLCD示例程序分两大类进行讲解,帮助大家理解SLCD例程的设计思路,掌握SLCD例程的移植,加速终端产品的应用开发。

一、概述

早在2022年7月,灵动微电子就发布了低功耗MM32L0130系列MCU产品。它搭载Arm Cortex-M0+处理器,集成段码LCD驱动,功耗可低至100nA,适用于更多低功耗的应用场景。

MM32L0130系列MCU集成的段码LCD驱动( SLCD ),从用户实际产品应用设计出发,优化驱动功能,最大程度地满足客户应用需求。它不仅支持待机状态下显示,最低功耗可以做到1.5uA;还支持多达40*4或者36*8个显示段,支持硬件配置刷新帧率、显示对比度、段码的闪烁功能;支持1.8V~5.5V的段码LCD显示屏,内置电荷泵,在MCU电源电压下降时可依然保证LCD的清晰显示;支持多种占空比和偏压模式的配置,适用于更多类型的段码LCD。


然而,最最厉害的,还应属于可通过内部的重映射矩阵,实现COM和SEG的任意映射,极大地方便了硬件设计和PCB布线。


二、准备工作

首先,通过灵动官网下载MM32L0130系列MCU的库函数和例程: https://www.mindmotion.com.cn/products/mm32mcu/mm32l/new_mm32l0/mm32l0130

然后,准备一个调试/下载工具,我们这边使用的是官方的MM32-LINK MINI: https://www.mindmotion.com.cn/support/development_tools/debug_and_programming_tools/mm32_link_mini


灵动官方推出了EVB-L0130开发板,该开发板搭载了MM32L0136C7P芯片,板载了段码LCD显示屏等外设模块,相应的资料可以到官网去下载: https://www.mindmotion.com.cn/support/development_tools/evaluation_boards/evboard/mm32l0136c7p


最后,我们还需要找一块与EVB-L0130开发板上板载不同的段码LCD显示屏,用来移植。为了能够直接在EVB-L0130开发板上进行移植,我们需要选用段码LCD显示屏的电压与原先板载的段码LCD显示屏的电压保持一致。

三、EVB-L0130开发板原理图

我们通过原理图,可以看出段码LCD显示屏与MCU连接的引脚对应关系。需要注意的是,由于EVB-L0130丰富的板载功能,有些修脚是通过拨动开关来进行功能选择的,所以在做SLCD例程功能演示时,需要将相应的拨动开发拨到相应的位置上,这样才可以正常显示。



四、EVB-L0130开发板板载段码LCD显示屏


五、解析SLCD例程设计思路

接下来,将对官方示例程序LibSamples_MM32L0130_V0.9.3_SLCD\Samples\LibSamples\SLCD目录下的SLCD_Basic和SLCD_Blink两个例程进行解析。

这两个例程的区别在于SLCD_Basic是通过软件操作的方式实现图标闪烁的;而SLCD_Blink则是通过配置寄存器功能的方式实现图标闪烁的(这部分后面会具体展开说明)。

(1) 在段码LCD显示应用中,所有显示的内容都是一个个段位组成的,有单独一个段位组成的图标、单位,也有多个段位组成的数字、进度条等等;而由7段/8段组成的8字型显示组合,我们可以显示数字、部分字符、定制某一段位显示,这个我们在程序中定义了个结构体。其中,ch表示显示的字符,Data数值的每一位表示对应的显示段是显示状态还是熄灭状态,然后我们根据常用的一些字符,列举了最常用的38个对应列表,如下所示:

typedef struct{    char    ch;    uint8_t Data;} SLCD_Code_TypeDef;
const SLCD_Code_TypeDef SLCD_CodeTable[38] ={ { ' ', 0x00 }, { '0', 0x3F }, { '1', 0x06 }, { '2', 0x5B }, { '3', 0x4F }, { '4', 0x66 }, { '5', 0x6D }, { '6', 0x7D }, { '7', 0x07 }, { '8', 0x7F }, { '9', 0x6F }, { 'A', 0x77 }, { 'b', 0x7C }, { 'c', 0x58 }, { 'C', 0x39 }, { 'd', 0x5E }, { 'E', 0x79 }, { 'F', 0x71 }, { 'g', 0x6F }, { 'H', 0x76 }, { 'h', 0x74 }, { 'i', 0x04 }, { 'I', 0x30 }, { 'J', 0x1E }, { 'l', 0x30 }, { 'L', 0x38 }, { 'n', 0x54 }, { 'o', 0x5C }, { 'O', 0x3F }, { 'P', 0x73 }, { 'q', 0x67 }, { 'r', 0x50 }, { 'S', 0x6D }, { 't', 0x78 }, { 'u', 0x1C }, { 'U', 0x3E }, { 'y', 0x6E }, { '-', 0x40 },};

这些显示字符和段位显示的对应关系都是固定,如果你还需要其它的定制某一段位的显示,是可以自行进行修改添加的,但需要注意数组的大小定义和使用到该数组时个遍历的个数,也需要同步修改一致。

(2) MM32L0130系列MCU引脚与LCD功能引脚的对应关系,我们可以通过芯片的数据手册来获悉。在数据手册4.2引脚定义表中,Name列是MCU引脚名称,LCD Function列是LCD功能引脚下标。通过这个表格的整理,最大LCD Function是L63,但在L0~L63之间有不存在的项。所以,为了我们定义数组的完整,我们在程序设计时,将不存在的Lx下标都定义在一个没有LCD功能的PH0引脚上,这样定义只是为了数组的完整和顺序性。

typedef struct{    GPIO_TypeDef *GPIOn;    uint16_t      PINn;    uint32_t      Line;} SLCD_Line_TypeDef;
const SLCD_Line_TypeDef SLCD_LineTable[] ={ { GPIOC, GPIO_Pin_13, SLCD_L0 }, { GPIOD, GPIO_Pin_7, SLCD_L1 }, { GPIOB, GPIO_Pin_9, SLCD_L2 }, { GPIOB, GPIO_Pin_8, SLCD_L3 }, { GPIOC, GPIO_Pin_12, SLCD_L4 }, { GPIOC, GPIO_Pin_11, SLCD_L5 }, { GPIOC, GPIO_Pin_10, SLCD_L6 }, { GPIOA, GPIO_Pin_15, SLCD_L7 }, { GPIOD, GPIO_Pin_3, SLCD_L8 }, { GPIOD, GPIO_Pin_2, SLCD_L9 }, { GPIOA, GPIO_Pin_12, SLCD_L10 }, { GPIOA, GPIO_Pin_11, SLCD_L11 }, { GPIOA, GPIO_Pin_10, SLCD_L12 }, { GPIOA, GPIO_Pin_9, SLCD_L13 }, { GPIOA, GPIO_Pin_8, SLCD_L14 }, { GPIOC, GPIO_Pin_9, SLCD_L15 }, { GPIOC, GPIO_Pin_8, SLCD_L16 }, { GPIOC, GPIO_Pin_7, SLCD_L17 }, { GPIOC, GPIO_Pin_6, SLCD_L18 }, { GPIOB, GPIO_Pin_15, SLCD_L19 }, { GPIOB, GPIO_Pin_14, SLCD_L20 }, { GPIOB, GPIO_Pin_13, SLCD_L21 }, { GPIOB, GPIO_Pin_12, SLCD_L22 }, { GPIOB, GPIO_Pin_11, SLCD_L23 }, { GPIOB, GPIO_Pin_10, SLCD_L24 }, { GPIOB, GPIO_Pin_2, SLCD_L25 }, { GPIOB, GPIO_Pin_1, SLCD_L26 }, { GPIOB, GPIO_Pin_0, SLCD_L27 }, { GPIOC, GPIO_Pin_5, SLCD_L28 }, { GPIOC, GPIO_Pin_4, SLCD_L29 }, { GPIOA, GPIO_Pin_7, SLCD_L30 }, { GPIOA, GPIO_Pin_6, SLCD_L31 }, { GPIOA, GPIO_Pin_5, SLCD_L32 }, { GPIOA, GPIO_Pin_4, SLCD_L33 }, { GPIOD, GPIO_Pin_5, SLCD_L34 }, { GPIOD, GPIO_Pin_4, SLCD_L35 }, { GPIOA, GPIO_Pin_3, SLCD_L36 }, { GPIOA, GPIO_Pin_2, SLCD_L37 }, { GPIOA, GPIO_Pin_1, SLCD_L38 }, { GPIOA, GPIO_Pin_0, SLCD_L39 }, { GPIOC, GPIO_Pin_3, SLCD_L40 }, { GPIOC, GPIO_Pin_2, SLCD_L41 }, { GPIOC, GPIO_Pin_1, SLCD_L42 }, { GPIOC, GPIO_Pin_0, SLCD_L43 }, { GPIOH, GPIO_Pin_0, SLCD_L44 }, { GPIOH, GPIO_Pin_0, SLCD_L45 }, { GPIOH, GPIO_Pin_0, SLCD_L46 }, { GPIOH, GPIO_Pin_0, SLCD_L47 }, { GPIOH, GPIO_Pin_0, SLCD_L48 }, { GPIOH, GPIO_Pin_0, SLCD_L49 }, { GPIOH, GPIO_Pin_0, SLCD_L50 }, { GPIOH, GPIO_Pin_0, SLCD_L51 }, { GPIOH, GPIO_Pin_0, SLCD_L52 }, { GPIOH, GPIO_Pin_0, SLCD_L53 }, { GPIOH, GPIO_Pin_0, SLCD_L54 }, { GPIOH, GPIO_Pin_0, SLCD_L55 }, { GPIOH, GPIO_Pin_0, SLCD_L56 }, { GPIOH, GPIO_Pin_0, SLCD_L57 }, { GPIOD, GPIO_Pin_6, SLCD_L58 }, { GPIOB, GPIO_Pin_3, SLCD_L59 }, { GPIOB, GPIO_Pin_4, SLCD_L60 }, { GPIOB, GPIO_Pin_5, SLCD_L61 }, { GPIOB, GPIO_Pin_6, SLCD_L62 }, { GPIOB, GPIO_Pin_7, SLCD_L63 },};

如上定义的结构体中,GPIOn是端口号,PINn是引脚,Line是LCD Function的下标号;如上定义的结构体数组是依据MM32L0160系列MCU引脚定义表进行列举的,属于固定内容,无需修改,也不能够修改,切记!!!定义此结构体、数组是为了在后面程序中可以通过查表的方式来配置的MCU引脚功能,不需要再通过数据手册一个个翻查对应关系了。

(3) 段码LCD显示屏真值表数组定义,这个可以直接参照段码LCD显示屏真值表的段码名,在后面显示的时候,通过真值表中的段码名可以直接进行查询,既方便又直观,快速定位到当前段码名对应真值表当中的哪个COM和SEG:

const char SLCD_NAME_Table[SLCD_COM_NUMBER][SLCD_SEG_NUMBER][4] ={    { "1D ", "DP1", "2D ", "DP2", "3D ", "DP3", "4D ", "C1 ", "C2 ", "W5 ", "L1 ", "5F ", "5A ", "6F ", "6A ", "7F ", "7A ", "S4 ", "S5 ", "8F ", "8A ", "9F ", "9A ", "10F", "10A" },    { "1E ", "1C ", "2E ", "2C ", "3E ", "3C ", "4E ", "4C ", "C3 ", "W4 ", "L2 ", "5G ", "5B ", "6G ", "6B ", "7G ", "7B ", "S3 ", "S6 ", "8G ", "8B ", "9G ", "9B ", "10G", "10B" },    { "1G ", "1B ", "2G ", "2B ", "3G ", "3B ", "4G ", "4B ", "T1 ", "W3 ", "L3 ", "5E ", "5C ", "6E ", "6C ", "7E ", "7C ", "S2 ", "S7 ", "8E ", "8C ", "9E ", "9C ", "10E", "10C" },    { "1F ", "1A ", "2F ", "2A ", "3F ", "3A ", "4F ", "4A ", "W1 ", "W2 ", "L4 ", "5D ", "DP5", "6D ", "DP6", "7D ", "DP7", "S1 ", "S8 ", "8D ", "DP8", "9D ", "DP9", "10D", "S9 " },};

(4) 段码LCD显示屏硬件连接映射表,这也是最关键的一步。我们通过定义如下结构体,来说明MCU引脚对应的LCD功能。

MCU引脚是作为SEG功能使用,还是作为COM功能使用,在结构体中GPIOn是端口号,PINn是引脚,Line是LCD Function的下标号,LineGroup是分组,Mode是引脚配置。其中,Line和LineGroup在定义结构体数组时,初始值都是0,后面程序设计中会自动根据5.2小节的功能进行自动匹配,定义LineGroup是因为在操作寄存器时有区分。所以,后面程序中将L0~L31划分为LineGroup1,将L31~L63划分到LineGroup2,以此来操作不同的显示缓存。最后,Mode就是用来区分MCU是作SEG功能使用的,还是作为COM功能使用的了:

typedef struct{    GPIO_TypeDef   *GPIOn;    uint16_t        PINn;    uint32_t        Line;    uint8_t         LineGroup;    uint8_t         Mode;} SLCD_IO_TypeDef;
SLCD_IO_TypeDef SLCD_SCH[SLCD_PIN_NUMBER] ={ { GPIOB, GPIO_Pin_8, 0, 0, SLCD_IOConfigSEG }, /* PB8 : SLCD_D0 */ { GPIOA, GPIO_Pin_15, 0, 0, SLCD_IOConfigSEG }, /* PA15 : SLCD_D1 */ { GPIOC, GPIO_Pin_10, 0, 0, SLCD_IOConfigSEG }, /* PC10 : SLCD_D2 */ { GPIOC, GPIO_Pin_11, 0, 0, SLCD_IOConfigSEG }, /* PC11 : SLCD_D3 */ { GPIOC, GPIO_Pin_12, 0, 0, SLCD_IOConfigSEG }, /* PC12 : SLCD_D4 */ { GPIOD, GPIO_Pin_6, 0, 0, SLCD_IOConfigSEG }, /* PD6 : SLCD_D5 */ { GPIOB, GPIO_Pin_1, 0, 0, SLCD_IOConfigSEG }, /* PB1 : SLCD_D6 */ { GPIOB, GPIO_Pin_0, 0, 0, SLCD_IOConfigSEG }, /* PB0 : SLCD_D7 */ { GPIOA, GPIO_Pin_7, 0, 0, SLCD_IOConfigSEG }, /* PA7 : SLCD_D8 */ { GPIOA, GPIO_Pin_6, 0, 0, SLCD_IOConfigSEG }, /* PA6 : SLCD_D9 */ { GPIOD, GPIO_Pin_5, 0, 0, SLCD_IOConfigSEG }, /* PD5 : SLCD_D10 */
{ GPIOD, GPIO_Pin_3, 0, 0, SLCD_IOConfigSEG }, /* PD3 : SLCD_D11 */ { GPIOD, GPIO_Pin_2, 0, 0, SLCD_IOConfigSEG }, /* PD2 : SLCD_D12 */ { GPIOA, GPIO_Pin_12, 0, 0, SLCD_IOConfigSEG }, /* PA12 : SLCD_D13 */ { GPIOA, GPIO_Pin_11, 0, 0, SLCD_IOConfigSEG }, /* PA11 : SLCD_D14 */ { GPIOC, GPIO_Pin_9, 0, 0, SLCD_IOConfigSEG }, /* PC9 : SLCD_D15 */ { GPIOC, GPIO_Pin_8, 0, 0, SLCD_IOConfigSEG }, /* PC8 : SLCD_D16 */ { GPIOC, GPIO_Pin_7, 0, 0, SLCD_IOConfigSEG }, /* PC7 : SLCD_D17 */ { GPIOC, GPIO_Pin_6, 0, 0, SLCD_IOConfigSEG }, /* PC6 : SLCD_D18 */ { GPIOB, GPIO_Pin_14, 0, 0, SLCD_IOConfigSEG }, /* PB14 : SLCD_D19 */ { GPIOB, GPIO_Pin_15, 0, 0, SLCD_IOConfigSEG }, /* PB15 : SLCD_D20 */ { GPIOB, GPIO_Pin_12, 0, 0, SLCD_IOConfigSEG }, /* PB12 : SLCD_D21 */ { GPIOB, GPIO_Pin_13, 0, 0, SLCD_IOConfigSEG }, /* PB13 : SLCD_D22 */ { GPIOC, GPIO_Pin_5, 0, 0, SLCD_IOConfigSEG }, /* PC5 : SLCD_D23 */ { GPIOC, GPIO_Pin_4, 0, 0, SLCD_IOConfigSEG }, /* PC4 : SLCD_D24 */
{ GPIOD, GPIO_Pin_4, 0, 0, SLCD_IOConfigCOM }, /* PD4 : SLCD_COM0 */ { GPIOC, GPIO_Pin_3, 0, 0, SLCD_IOConfigCOM }, /* PC3 : SLCD_COM1 */ { GPIOC, GPIO_Pin_13, 0, 0, SLCD_IOConfigCOM }, /* PC13 : SLCD_COM2 */ { GPIOD, GPIO_Pin_7, 0, 0, SLCD_IOConfigCOM }, /* PD7 : SLCD_COM3 */};

如上的结构体数组,需要根据硬件原理图设计中的MCU引脚和LCD引脚的实际连接来定义,并且需要按照顺序依次来定义,参照LCD_SEG0、LCD_SEG1、LCD_SEG2……LCD_SEGn、LCD_COM0、LCD_COM1……LCD_COMm这样的顺序编写。

(5) 底层函数实现,这些都是固定的,不需要客户修改,如下所示:

void SLCD_Clear(uint8_t Mode){    uint8_t i = 0;
if (0 != Mode) { for (i = 0; i < 16; i++) { SLCD->DR[i] = 0xFFFFFFFF; } } else { for (i = 0; i < 16; i++) { SLCD->DR[i] = 0x00000000; } }}
void SLCD_WriteBit(uint8_t COMn, uint32_t SEGn, uint32_t Group, uint8_t State){ uint8_t Index = COMn * 2 + Group;
if (State) { SLCD->DR[Index] |= SEGn; } else { SLCD->DR[Index] &= ~SEGn; }}
uint8_t SLCD_SearchCode(char ch){ uint8_t i = 0;
for (i = 0; i < 38; i++) { if (ch == SLCD_CodeTable[i].ch) { return (SLCD_CodeTable[i].Data); } }
return (0xFF);}
void SLCD_SearchName(char *str, uint8_t *COMn, uint8_t *SEGn){ uint8_t i = 0, j = 0;
for (i = 0; i < SLCD_COM_NUMBER; i++) { for (j = 0; j < SLCD_SEG_NUMBER; j++) { if (strcmp(str, SLCD_NAME_Table[i][j]) == 0) { *COMn = i; *SEGn = j; return; } } }
*COMn = 0xFF; *SEGn = 0xFF;}

void SLCD_Clear(uint8_t Mode)函数是清除所有LCD段位的显示,通过Mode来控制是全显示,还是全不显示。

void SLCD_WriteBit(uint8_t COMn, uint32_t SEGn, uint32_t Group, uint8_t State)函数是操作显示RAM的某一位,通过COM、SEG和Group确定操作SLCD显示RAM的位置,通过State来控制显示还是不显示。

uint8_t SLCD_SearchCode(char ch)函数是通过显示字符ch查找与之对应的段码数值。

void SLCD_SearchName(char *str, uint8_t *COMn, uint8_t *SEGn)函数是通过真值表段名查找对应的COM和SEG下标。

通过上述这些函数,就可以组合实现具体的显示应用啦!

(6) SLCD的配置,需要开启使用到GPIO的时钟、SLCD的时钟。当然,对于输入到SLCD的时钟源是可以选择的,可以根据需求进行配置;另外就是LCD功能的配置了,具体参考以下几个函数:

void SLCD_Configure(void)函数是SLCD的总配置入口,如下所示:

void SLCD_Configure(void){    SLCD_InitTypeDef SLCD_InitStructure;    uint32_t SLCD_ClockFreq   = 0;    uint32_t SLCD_Prescaler   = SLCD_Prescaler_16;    uint32_t SLCD_Divider     = SLCD_Divider_16;    uint8_t  SLCD_ClockSource = 0;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE);
switch (SLCD_ClockSource) { case 0: /* LSI */ RCC_LSICmd(ENABLE);
RCC_LSICLKConfig(RCC_LSICLKSource_40KHz);
while (RESET == RCC_GetFlagStatus(RCC_FLAG_LSIRDY)) { __NOP(); }
RCC_APB1PeriphClockCmd(RCC_APB1Periph_LCD, ENABLE);
SLCD_DeInit();
RCC_SLCDCLKConfig(RCC_SLCDCLKSource_LSI); break;
case 1: /* LSE */ RCC_LSEConfig(RCC_LSE_ON);
while (RESET == RCC_GetFlagStatus(RCC_FLAG_LSERDY)) { __NOP(); }
RCC_APB1PeriphClockCmd(RCC_APB1Periph_LCD, ENABLE);
SLCD_DeInit();
RCC_SLCDCLKConfig(RCC_SLCDCLKSource_LSE); break;
case 2: /* HSI(1MHz)/DIV1 -> 1MHz */ RCC_HSIConfig(RCC_HSI_1M);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_LCD, ENABLE);
SLCD_DeInit();
RCC_SLCDCLKConfig(RCC_SLCDCLKSource_HSI_Div1); break;
case 3: /* HSI(8MHz)/DIV8 -> 1MHz */ RCC_HSIConfig(RCC_HSI_8M);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_LCD, ENABLE);
SLCD_DeInit();
RCC_SLCDCLKConfig(RCC_SLCDCLKSource_HSI_Div8); break;
default: break; }
SLCD_ClockFreq = RCC_GetSlcdClockFreq();
printf("\r\nSLCD_ClockFreq : %dHz\r\n", SLCD_ClockFreq);
if (SLCD_ClockFreq == 0) { return; } else if (SLCD_ClockFreq == 32768UL) { SLCD_Prescaler = SLCD_Prescaler_2; SLCD_Divider = SLCD_Divider_16; } else if (SLCD_ClockFreq == 40000UL) { SLCD_Prescaler = SLCD_Prescaler_2; SLCD_Divider = SLCD_Divider_20; } else if (SLCD_ClockFreq == 1000000UL) { SLCD_Prescaler = SLCD_Prescaler_64; SLCD_Divider = SLCD_Divider_16; }
SLCD_StructInit(&SLCD_InitStructure); SLCD_InitStructure.SLCD_Prescaler = SLCD_Prescaler; SLCD_InitStructure.SLCD_Divider = SLCD_Divider; SLCD_InitStructure.SLCD_Duty = SLCD_Duty_1_4; SLCD_InitStructure.SLCD_Bias = SLCD_Bias_1_3; SLCD_InitStructure.SLCD_VoltageSource = SLCD_VoltSrcCapCharggDownVdd; SLCD_Init(&SLCD_InitStructure);
SLCD_ConfigureSEGoCOM();
SLCD_ConfigureCOMnIDX();
SLCD_ChargePumpClockDivConfig(SLCD_ChargePumpClock_Div1024); SLCD_LowPowerDriveCmd(DISABLE); SLCD_DeadTimeConfig(SLCD_DeadTime_0);
SLCD_Cmd(ENABLE);
SLCD_Clear(1); PLATFORM_DelayMS(1000); SLCD_Clear(0);
SLCD_ConfigureBlinkEN();}

void SLCD_ConfigureSEGoCOM(void)函数是用来配置MCU引脚是作LCD功能使用的。

void SLCD_ConfigureCOMnIDX(void)函数是用来配置MCU引脚作为COM功能使用的。

void SLCD_ConfigureSEGoCOM(void){    uint8_t i = 0, j = 0;    uint8_t SLCD_IOConfigTable[MAX_SLCD_PIN_NUMBER];
memset(SLCD_IOConfigTable, SLCD_IOConfigNone, MAX_SLCD_PIN_NUMBER);
for (i = 0; i < MAX_SLCD_PIN_NUMBER; i++) { for (j = 0; j < SLCD_PIN_NUMBER; j++) { if ((SLCD_LineTable[i].GPIOn == SLCD_SCH[j].GPIOn) && (SLCD_LineTable[i].PINn == SLCD_SCH[j].PINn)) { SLCD_IOConfigTable[i] = SLCD_SCH[j].Mode;
SLCD_SCH[j].Line = SLCD_LineTable[i].Line;
if (i > 31) { SLCD_SCH[j].LineGroup = 1; } else { SLCD_SCH[j].LineGroup = 0; } } } }
SLCD_IO_Config(SLCD_IOConfigTable);}
void SLCD_ConfigureCOMnIDX(void){ uint8_t i = 0, j = 0, k = 0; uint8_t SCLD_COM_IndexTable[COM_INDEX_MAX];
for (i = 0; i < COM_INDEX_MAX; i++) { SCLD_COM_IndexTable[i] = 50 + i; }
i = 0;
for (j = 0; j < SLCD_PIN_NUMBER; j++) { if (SLCD_IOConfigCOM == SLCD_SCH[j].Mode) { for (k = 0; k < MAX_SLCD_PIN_NUMBER; k++) { if ((SLCD_LineTable[k].GPIOn == SLCD_SCH[j].GPIOn) && (SLCD_LineTable[k].PINn == SLCD_SCH[j].PINn)) { SCLD_COM_IndexTable[i++] = k; } } } }
SLCD_COM_IndexInit(SCLD_COM_IndexTable);}

这两个函数都是固定的,不需要客户修改!

void SLCD_ConfigureBlinkEN(void){    uint8_t SCLD_BLINK_IndexTable[COM_INDEX_MAX * BLINK_INDEX_STEP] =    {        1, 0, 0,                       /* BLINK_Index0 is Enable, BLINK_Index0 point to COM0 and SEG0 */        1, 1, 1,                       /* BLINK_Index1 is Enable, BLINK_Index1 point to COM1 and SEG1 */        1, 2, 2,                       /* BLINK_Index2 is Enable, BLINK_Index2 point to COM2 and SEG2 */        1, 3, 3,                       /* BLINK_Index3 is Enable, BLINK_Index3 point to COM3 and SEG3 */        1, 4, 4,                       /* BLINK_Index4 is Enable, BLINK_Index4 point to COM4 and SEG4 */        1, 5, 5,                       /* BLINK_Index5 is Enable, BLINK_Index5 point to COM5 and SEG5 */        1, 6, 6,                       /* BLINK_Index6 is Enable, BLINK_Index6 point to COM6 and SEG6 */        1, 7, 7,                       /* BLINK_Index7 is Enable, BLINK_Index7 point to COM7 and SEG7 */    };
SLCD_BlinkConfig(SLCD_BlinkMode_Off, SLCD_BlinkFrequency_Div512);
SLCD_BLINK_IndexInit(SCLD_BLINK_IndexTable);}

void SLCD_ConfigureBlinkEN(void)函数是用来配置硬件闪烁功能的,这个函数可以根据应用需示进行修改。如上所示,最多也只能配置8个段位进行闪烁;如果多余8个段位,就需要使用软件来实现闪烁功能了。

(7) 在进行上述的配置之后,我们就可以来编写应用显示功能函数了,下面以例程中的SLCD_DisplayNumber1函数来说明:

void SLCD_DisplayNumber1(uint8_t Index, char ch, uint8_t Point){    char TAB[4][8][4] =    {        { "1A ", "1B ", "1C ", "1D ", "1E ", "1F ", "1G ", "DP1" },        { "2A ", "2B ", "2C ", "2D ", "2E ", "2F ", "2G ", "DP2" },        { "3A ", "3B ", "3C ", "3D ", "3E ", "3F ", "3G ", "DP3" },        { "4A ", "4B ", "4C ", "4D ", "4E ", "4F ", "4G ", "   " },    };    uint8_t COMn = 0xFF, SEGn = 0xFF;    uint8_t Code = SLCD_SearchCode(ch);    uint8_t i    = 0;
if (Code != 0xFF) { for (i = 0; i < 7; i++) { SLCD_SearchName(TAB[Index][i], &COMn, &SEGn);
if ((COMn != 0xFF) && (SEGn != 0xFF)) { SLCD_WriteBit(COMn, SLCD_SCH[SEGn].Line, SLCD_SCH[SEGn].LineGroup, (Code >> i) & 0x01); } }
SLCD_SearchName(TAB[Index][7], &COMn, &SEGn);
if ((COMn != 0xFF) && (SEGn != 0xFF)) { SLCD_WriteBit(COMn, SLCD_SCH[SEGn].Line, SLCD_SCH[SEGn].LineGroup, Point); } }}

首先,通过段码LCD显示屏的真值表构建显示组。这就比如我要4个小数字区域(左上角)第一个数字的位置显示字符,首先就需要定义第一个数字位置是由哪几个段位组成的,然后在代码中定义出来;接着通过SLCD_SearchCode函数获取要显示字符对应段码值,再将这个段码值依次写入到组成这个显示区域的各个段位中,每一个段位对应的COM和SEG则是通过SLCD_SearchName函数来获取的;最后调用SLCD_WriteBit将状态写入到显示RAM中去。

接下来,再以SLCD_DisplayTool函数为例:






请到「今天看啥」查看全文