打印
[其他ST产品]

STM32关于驱动段码屏显示

[复制链接]
356|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本篇文章主要记录一下我在工作中用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 */
    }
}

————————————————
版权声明:本文为CSDN博主「兰亭集旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/CSDN_Gao_16/article/details/115463499

使用特权

评论回复
沙发
原来是wjc|  楼主 | 2022-12-25 15:14 | 只看该作者
从上面的初始化代码可以知道,段码屏共有4个 COM 端,12个 SEG 端。下图是某厂商生产的段码屏的资料:

使用特权

评论回复
板凳
原来是wjc|  楼主 | 2022-12-25 15:15 | 只看该作者
最终程序中驱动段码屏显示图标的代码如下:
#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);
}

使用特权

评论回复
地板
原来是wjc|  楼主 | 2022-12-25 15:16 | 只看该作者
上面的驱动代码中 DISP_PLAY 这样的数组是怎么得来的?根据段码屏资料有如下表(表1):

使用特权

评论回复
5
原来是wjc|  楼主 | 2022-12-25 15:17 | 只看该作者
我们把上表转换一下,方便我们查看(表2):

使用特权

评论回复
6
原来是wjc|  楼主 | 2022-12-25 15:21 | 只看该作者
其中 COM1 对应 STM32 的 LCD_RAM_REGISTER 为 LCD_RAM_REGISTER0,COM2 对应 LCD_RAM_REGISTER2,COM3 对应 LCD_RAM_REGISTER4,COM4 对应 LCD_RAM_REGISTER6,如下表:

使用特权

评论回复
7
原来是wjc|  楼主 | 2022-12-25 15:22 | 只看该作者
所以,我们来看一下 DISP_PLAY[2] = {9, LCD_RAM_REGISTER0},其中第一个值 9 就是 表2 中 T5 所处的列数(SEG端);第二个值 LCD_RAM_REGISTER0 为 T5 所在的行数(COM端)。使用这种方法把段码屏中的所有图标都写下来即可。

使用特权

评论回复
8
原来是wjc|  楼主 | 2022-12-25 15:23 | 只看该作者
接下来看下 DISP_NUM[3] = {2, 5, 7} 这行是怎么来的?
变量 DISP_NUM 是记录段码屏中数字所在列位置的起始位置。例如,段码屏中的第 1 个数字最先出现在 表2 中的第 2 列;第 2 个数字最先出现在第 5 列;第 3 个数字最先出现在第 7 列。所以用一个变量 DISP_NUM 记录这些数字就得到 DISP_NUM[3] = {2, 5, 7} 。

使用特权

评论回复
9
原来是wjc|  楼主 | 2022-12-25 15:26 | 只看该作者
接下来再看下 DISP_NUM_TAB[10][4] 二维数组里面的一堆数字是怎么来的?
注意,DISP_NUM,以及 DISP_NUM_TAB 里面的内容可以按自己的方法写,这里不一定要这样写。我这样写的目的是配合 Set_Lcd_Num() 这个函数的,此函数不同的实现方法,会导致 DISP_NUM、DISP_NUM_TAB 变量里面的内容不同。

使用特权

评论回复
10
原来是wjc|  楼主 | 2022-12-25 15:27 | 只看该作者
由于 Set_Lcd_Num() 函数是一行一行显示段码屏中数字部分内容的,所以 DISP_NUM_TAB 里面的每一个一维数组从 [0] ~ [3] 都完整表达了一个数字。我们就 {3, 2, 3, 2}, //0 数字 0 来说:

使用特权

评论回复
11
原来是wjc|  楼主 | 2022-12-25 15:29 | 只看该作者

使用特权

评论回复
12
原来是wjc|  楼主 | 2022-12-25 15:30 | 只看该作者
因为 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 都是这样写的。

使用特权

评论回复
13
原来是wjc|  楼主 | 2022-12-25 15:31 | 只看该作者
接下来看一下 Set_Lcd_Num() 函数中 16 是怎么来的?

使用特权

评论回复
14
原来是wjc|  楼主 | 2022-12-25 15:32 | 只看该作者
这里就要看 STM32L152RC 数据手册了,手册中 LCD 显示缓存如下图:

使用特权

评论回复
15
原来是wjc|  楼主 | 2022-12-25 15:33 | 只看该作者
上图中 LCD_RAM 寄存器中每一位都是一个 SEG,而我们实际硬件中段码屏的 SEG 只接了 12 个,分别为 SEG16 ~ SEG 27,其它没有用到,所以最终的段码屏数据也要写到 SEG16 ~ SEG27 之中,因此,在写的时候需要偏移到第 16位,因为 0 ~ 15硬件中没使用,写入无效。比如你的硬件中段码屏使用的是 SEG18 ~ SEG26,那你写数据的时候就必须要偏移到第18位。

使用特权

评论回复
16
原来是wjc|  楼主 | 2022-12-25 15:33 | 只看该作者
最后我们来看一下 Set_Lcd_Num() 函数中 3,3,3,2 这四个数字怎么来的?

使用特权

评论回复
17
原来是wjc|  楼主 | 2022-12-25 15:34 | 只看该作者
上图中的 3,3,3,2 的意思就是在写数字之前,要先把对应位置的缓存给清除掉,看下图就会明白:

使用特权

评论回复
18
原来是wjc|  楼主 | 2022-12-25 15:34 | 只看该作者
后面还有 Clr_Lcd_Num() 函数,此函数里的内容就很简单了,搞清楚前面文章内容,此函数内容就不成问题了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

994

帖子

0

粉丝