STM32关于驱动段码屏显示

本篇文章主要记录一下我在工作中用STM32单片机驱动段码屏显示内容,不讲解具体的驱动原理,只是单纯记录如何编写驱动屏幕图标显示的代码,以便我日后查看。

单片机:STM32L152RCT6A
IDE:Keil5.25.2.0
代码生成:STM32CUBEMX4.23.0

具体段码屏的驱动代码我直接贴出来:

/* LCD init function */
static void MX_LCD_Init(void)
{
    hlcd.Instance = LCD;
    hlcd.Init.Prescaler = LCD_PRESCALER_1;
    hlcd.Init.Divider = LCD_DIVIDER_31;
    hlcd.Init.Duty = LCD_DUTY_1_4;
    hlcd.Init.Bias = LCD_BIAS_1_3;
    hlcd.Init.VoltageSource = LCD_VOLTAGESOURCE_INTERNAL;
    hlcd.Init.Contrast = LCD_CONTRASTLEVEL_3;
    hlcd.Init.DeadTime = LCD_DEADTIME_0;
    hlcd.Init.PulseOnDuration = LCD_PULSEONDURATION_4;
    hlcd.Init.MuxSegment = LCD_MUXSEGMENT_DISABLE;
    hlcd.Init.BlinkMode = LCD_BLINKMODE_OFF;
    hlcd.Init.BlinkFrequency = LCD_BLINKFREQUENCY_DIV8;
    if (HAL_LCD_Init(&hlcd) != HAL_OK)
    {
        _Error_Handler(__FILE__, __LINE__);
    }
}


void HAL_LCD_MspInit(LCD_HandleTypeDef* hlcd)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    if(hlcd->Instance==LCD)
    {
        /* USER CODE BEGIN LCD_MspInit 0 */

        /* USER CODE END LCD_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_LCD_CLK_ENABLE();

        /**LCD GPIO Configuration
        PB8     ------> LCD_SEG16
        PA15    ------> LCD_SEG17
        PC0     ------> LCD_SEG18
        PC1     ------> LCD_SEG19
        PC2     ------> LCD_SEG20
        PC3     ------> LCD_SEG21
        PC4     ------> LCD_SEG22
        PC5     ------> LCD_SEG23
        PC6     ------> LCD_SEG24
        PC7     ------> LCD_SEG25
        PC8     ------> LCD_SEG26
		PC9     ------> LCD_SEG27
        PA8     ------> LCD_COM0
        PA9     ------> LCD_COM1
        PA10    ------> LCD_COM2
        PB9     ------> LCD_COM3
        */
        GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                              |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
                              |GPIO_PIN_8|GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF11_LCD;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_15;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF11_LCD;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_8;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF11_LCD;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* USER CODE BEGIN LCD_MspInit 1 */

        /* USER CODE END LCD_MspInit 1 */
    }
}

void HAL_LCD_MspDeInit(LCD_HandleTypeDef* hlcd)
{

    if(hlcd->Instance==LCD)
    {
        /* USER CODE BEGIN LCD_MspDeInit 0 */

        /* USER CODE END LCD_MspDeInit 0 */
        /* Peripheral clock disable */
        __HAL_RCC_LCD_CLK_DISABLE();

        /**LCD GPIO Configuration
        PB8     ------> LCD_SEG16
        PA15    ------> LCD_SEG17
        PC0     ------> LCD_SEG18
        PC1     ------> LCD_SEG19
        PC2     ------> LCD_SEG20
        PC3     ------> LCD_SEG21
        PC4     ------> LCD_SEG22
        PC5     ------> LCD_SEG23
        PC6     ------> LCD_SEG24
        PC7     ------> LCD_SEG25
        PC8     ------> LCD_SEG26
        PC9     ------> LCD_SEG27
        PA8     ------> LCD_COM0
        PA9     ------> LCD_COM1
        PA10     ------> LCD_COM2
        PB9     ------> LCD_COM3
        */
        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                        |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
                        |GPIO_PIN_8|GPIO_PIN_9);

        HAL_GPIO_DeInit(GPIOA, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_15);

        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9|GPIO_PIN_8);

        /* USER CODE BEGIN LCD_MspDeInit 1 */

        /* USER CODE END LCD_MspDeInit 1 */
    }
}

从上面的初始化代码可以知道,段码屏共有4个 COM 端,12个 SEG 端。下图是某厂商生产的段码屏的资料:
在这里插入图片描述
最终程序中驱动段码屏显示图标的代码如下:

#include "main.h"
extern LCD_HandleTypeDef hlcd;

//T1 minus
//T2 gps
//T3 cloud
//T4 bluetooth
//T5 play
//T6 stop
//T7 charge

const u32 DISP_PLAY[2] = {9, LCD_RAM_REGISTER0};
const u32 DISP_STOP[2] = {9, LCD_RAM_REGISTER2};
const u32 DISP_GPS[2] = {4, LCD_RAM_REGISTER0};		
const u32 DISP_CLOUD[2] = {4, LCD_RAM_REGISTER2};	
const u32 DISP_BT[2] = {4, LCD_RAM_REGISTER4};		
const u32 DISP_CHARGE[2] = {9, LCD_RAM_REGISTER4};	
const u32 DISP_BATCASE[2] = {9, LCD_RAM_REGISTER6};
const u32 DISP_NOSIGN[2] = {11, LCD_RAM_REGISTER6};
const u32 DISP_EMAX[2] = {1, LCD_RAM_REGISTER2};
const u32 DISP_EMIN[2] = {1, LCD_RAM_REGISTER6};
const u32 DISP_DGREE[2] = {11, LCD_RAM_REGISTER0};
const u32 DISP_RH[2] = {11, LCD_RAM_REGISTER4};
const u32 DISP_DAY[2] = {11, LCD_RAM_REGISTER2};	
const u32 DISP_DOT[2] = {7, LCD_RAM_REGISTER6};
const u32 DISP_MINUS[2] = {1, LCD_RAM_REGISTER4};
const u32 DISP_BAT[4][2] = {
	{10, LCD_RAM_REGISTER2}, //BAR1
	{10, LCD_RAM_REGISTER4}, //BAR2
	{10, LCD_RAM_REGISTER0}, //BAR3
	{10, LCD_RAM_REGISTER6}, //BAR4
};
const u32 DISP_SIGN[5][2] = {
	{0, LCD_RAM_REGISTER6}, //L1
	{0, LCD_RAM_REGISTER4}, //L2
	{0, LCD_RAM_REGISTER2}, //L3
	{0, LCD_RAM_REGISTER0}, //L4
	{1, LCD_RAM_REGISTER0}, //L5
};

const u8 DISP_NUM[3] = {2, 5, 7};
const u8 DISP_NUM_TAB[10][4] =
{
	{3, 2, 3, 2}, 	//0
	{0, 2, 2, 0}, 	//1
	{2, 3, 1, 2}, 	//2
	{2, 3, 2, 2}, 	//3
	{1, 3, 2, 0},	//4
	{3, 1, 2, 2}, 	//5
	{3, 1, 3, 2}, 	//6
	{2, 2, 2, 0}, 	//7
	{3, 3, 3, 2}, 	//8
	{3, 3, 2, 2},	//9
};

void Set_Lcd_Dot(const u32 *dot)
{
    HAL_LCD_Write(&hlcd, dot[1], (u32)~(1<<(dot[0]+16)), (u32)(1<<(dot[0]+16)));
}

void Clr_Lcd_Dot(const u32 *dot)
{
    HAL_LCD_Write(&hlcd, dot[1], (u32)~(1<<(dot[0]+16)), 0);
}

void Set_Lcd_Num(const u8 c, u8 num)
{
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER0, (u32)~(3<<(DISP_NUM[c]+16)), (u32)(DISP_NUM_TAB[num][0]<<(DISP_NUM[c]+16)));
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER2, (u32)~(3<<(DISP_NUM[c]+16)), (u32)(DISP_NUM_TAB[num][1]<<(DISP_NUM[c]+16)));
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER4, (u32)~(3<<(DISP_NUM[c]+16)), (u32)(DISP_NUM_TAB[num][2]<<(DISP_NUM[c]+16)));
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER6, (u32)~(2<<(DISP_NUM[c]+16)), (u32)(DISP_NUM_TAB[num][3]<<(DISP_NUM[c]+16)));
}

void Clr_Lcd_Num(const u8 c)
{
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER0, (u32)~(3<<(DISP_NUM[c]+16)), 0);
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER2, (u32)~(3<<(DISP_NUM[c]+16)), 0);
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER4, (u32)~(3<<(DISP_NUM[c]+16)), 0);
    HAL_LCD_Write(&hlcd, LCD_RAM_REGISTER6, (u32)~(2<<(DISP_NUM[c]+16)), 0);
}

上面的驱动代码中 DISP_PLAY 这样的数组是怎么得来的?根据段码屏资料有如下表(表1):

PIN12345678910111213141516
COM1---COM1L4L51F1AT22F2A3F3AT5BAR3C
COM2--COM2-L3Max1G1BT32G2B3G3BT6BAR1Day
COM3-COM3--L2T11E1CT42E2C3E3CT7BAR2%RH
COM4COM4---L1Min-1D--2DDP3DBAR5BAR4X

我们把上表转换一下,方便我们查看(表2):

01234567891011
COM1L4L51F1AT22F2A3F3AT5BAR3C
COM2L3Max1G1BT32G2B3G3BT6BAR1Day
COM3L2T11E1CT42E2C3E3CT7BAR2%RH
COM4L1Min-1D--2DDP3DBAR5BAR4X

其中 COM1 对应 STM32 的 LCD_RAM_REGISTER 为 LCD_RAM_REGISTER0,COM2 对应 LCD_RAM_REGISTER2,COM3 对应 LCD_RAM_REGISTER4,COM4 对应 LCD_RAM_REGISTER6,如下表:

COMRAM
COM1LCD_RAM_REGISTER0
COM2LCD_RAM_REGISTER2
COM3LCD_RAM_REGISTER4
COM4LCD_RAM_REGISTER6
COM(n)LCD_RAM_REGISTER 2*(n-1)

所以,我们来看一下 DISP_PLAY[2] = {9, LCD_RAM_REGISTER0},其中第一个值 9 就是 表2 中 T5 所处的列数(SEG端);第二个值 LCD_RAM_REGISTER0 为 T5 所在的行数(COM端)。使用这种方法把段码屏中的所有图标都写下来即可。

接下来看下 DISP_NUM[3] = {2, 5, 7} 这行是怎么来的?
变量 DISP_NUM 是记录段码屏中数字所在列位置的起始位置。例如,段码屏中的第 1 个数字最先出现在 表2 中的第 2 列;第 2 个数字最先出现在第 5 列;第 3 个数字最先出现在第 7 列。所以用一个变量 DISP_NUM 记录这些数字就得到 DISP_NUM[3] = {2, 5, 7} 。

接下来再看下 DISP_NUM_TAB[10][4] 二维数组里面的一堆数字是怎么来的?
注意,DISP_NUM,以及 DISP_NUM_TAB 里面的内容可以按自己的方法写,这里不一定要这样写。我这样写的目的是配合 Set_Lcd_Num() 这个函数的,此函数不同的实现方法,会导致 DISP_NUM、DISP_NUM_TAB 变量里面的内容不同。

由于 Set_Lcd_Num() 函数是一行一行显示段码屏中数字部分内容的,所以 DISP_NUM_TAB 里面的每一个一维数组从 [0] ~ [3] 都完整表达了一个数字。我们就 {3, 2, 3, 2}, //0 数字 0 来说:
在这里插入图片描述
因为 3 个数字的段码一样,所以我们只解析一个数字,把 DISP_NUM_TAB 里的内容填满即可。因为段码每个段都是独立的,显示时不能相互干扰,所以有上图中的权值,就相当于左移,避免段码显示时干扰。那我们来先来填 0,数字 0 占用段码为 A,B,C,D,E,F(只看其中一个数字),第一行 F*1+A*2 = 3; 第二行 0*1+B*2 = 2;第三行 E*1+C*2 = 3;第四行 0*1+D*2 = 2,所以最终数字 0 为 {3, 2, 3, 2};我们再写一个数字 1,数字 1 点段码 B,C,第一行没有B,C,所以为 0*1+0*2 = 0;第二行 0*1+B*2 = 2;第三行 0*1+C*2 = 2;第四行 0*0+0*0 = 0,所以数字 1 最终表示为 {0, 2, 2, 0},后面 2 ~ 9 都是这样写的。

接下来看一下 Set_Lcd_Num() 函数中 16 是怎么来的?
在这里插入图片描述
这里就要看 STM32L152RC 数据手册了,手册中 LCD 显示缓存如下图:
在这里插入图片描述
上图中 LCD_RAM 寄存器中每一位都是一个 SEG,而我们实际硬件中段码屏的 SEG 只接了 12 个,分别为 SEG16 ~ SEG 27,其它没有用到,所以最终的段码屏数据也要写到 SEG16 ~ SEG27 之中,因此,在写的时候需要偏移到第 16位,因为 0 ~ 15硬件中没使用,写入无效。比如你的硬件中段码屏使用的是 SEG18 ~ SEG26,那你写数据的时候就必须要偏移到第18位。

最后我们来看一下 Set_Lcd_Num() 函数中 3,3,3,2 这四个数字怎么来的?
在这里插入图片描述
上图中的 3,3,3,2 的意思就是在写数字之前,要先把对应位置的缓存给清除掉,看下图就会明白:
在这里插入图片描述

后面还有 Clr_Lcd_Num() 函数,此函数里的内容就很简单了,搞清楚前面文章内容,此函数内容就不成问题了。

  • 5
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
STM32驱动段码是指使用STM32系列微控制器来驱动具有段码显示功能的液晶段码一般由多个数字和字母组成的字符组成,每个字符都使用多个段码来表示。通过STM32的IO口和适当的驱动电路,可以将微控制器输出的数字信号转换成适合液晶的信号。 为了驱动段码,首先需要了解液晶的型号和接口。常见的段码一般采用7段数码管、14段数码管、16段数码管等不同类型。接口通常有并口和串口两种。根据不同的型号和接口,可以选择相应的驱动方式。 在使用STM32驱动段码时,需要使用IO口控制液晶。通过STM32的GPIO寄存器,可以设置IO口为输出模式或输入模式,并输出或读取相应的电平信号。通过适当的程序设计,可以将需要显示的字符信息转换为相应的段码信号,再通过IO口将信号发送给液晶。 在驱动段码时,需要注意以下几点: 1. 准确理解液晶的信号要求,包括电平、时序等。根据液晶的数据手册,设置STM32的IO口参数,以满足液晶的需求。 2. 根据液晶的结构和使用场景,设计适当的程序算法,将需要显示的字符转换为相应的段码信号。可以使用查表法或逻辑运算等方法进行转换。 3. 合理规划IO口资源,避免冲突和资源浪费。合理分配IO口,确保各个段码信号的输出和读取能够正常进行。 4. 对于需要显示动态内容的段码,需要使用定时器和中断来控制刷新频率和内容的更新。 综上所述,STM32驱动段码需要根据液晶的型号和接口,正确设置IO口参数,并通过适当的程序设计将需要显示的字符转换为相应的段码信号,最终将信号发送给液晶,实现字符的显示
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值