段码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显示屏的电压保持一致。
我们通过原理图,可以看出段码LCD显示屏与MCU连接的引脚对应关系。需要注意的是,由于EVB-L0130丰富的板载功能,有些修脚是通过拨动开关来进行功能选择的,所以在做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 },
{ GPIOA, GPIO_Pin_15, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_10, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_11, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_12, 0, 0, SLCD_IOConfigSEG },
{ GPIOD, GPIO_Pin_6, 0, 0, SLCD_IOConfigSEG },
{ GPIOB, GPIO_Pin_1, 0, 0, SLCD_IOConfigSEG },
{ GPIOB, GPIO_Pin_0, 0, 0, SLCD_IOConfigSEG },
{ GPIOA, GPIO_Pin_7, 0, 0, SLCD_IOConfigSEG },
{ GPIOA, GPIO_Pin_6, 0, 0, SLCD_IOConfigSEG },
{ GPIOD, GPIO_Pin_5, 0, 0, SLCD_IOConfigSEG },
{ GPIOD, GPIO_Pin_3, 0, 0, SLCD_IOConfigSEG },
{ GPIOD, GPIO_Pin_2, 0, 0, SLCD_IOConfigSEG },
{ GPIOA, GPIO_Pin_12, 0, 0, SLCD_IOConfigSEG },
{ GPIOA, GPIO_Pin_11, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_9, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_8, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_7, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_6, 0, 0, SLCD_IOConfigSEG },
{ GPIOB, GPIO_Pin_14, 0, 0, SLCD_IOConfigSEG },
{ GPIOB, GPIO_Pin_15, 0, 0, SLCD_IOConfigSEG },
{ GPIOB, GPIO_Pin_12, 0, 0, SLCD_IOConfigSEG },
{ GPIOB, GPIO_Pin_13, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_5, 0, 0, SLCD_IOConfigSEG },
{ GPIOC, GPIO_Pin_4, 0, 0, SLCD_IOConfigSEG },
{ GPIOD, GPIO_Pin_4, 0, 0, SLCD_IOConfigCOM },
{ GPIOC, GPIO_Pin_3, 0, 0, SLCD_IOConfigCOM },
{ GPIOC, GPIO_Pin_13, 0, 0, SLCD_IOConfigCOM },
{ GPIOD, GPIO_Pin_7, 0, 0, SLCD_IOConfigCOM },
};
如上的结构体数组,需要根据硬件原理图设计中的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:
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:
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:
RCC_HSIConfig(RCC_HSI_1M);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_LCD, ENABLE);
SLCD_DeInit();
RCC_SLCDCLKConfig(RCC_SLCDCLKSource_HSI_Div1);
break;
case 3:
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函数为例: