CH585单片机的LCD外设怎么驱动段式LCD

发布于:2025-04-22 ⋅ 阅读:(11) ⋅ 点赞:(0)

1、首先这里只讲应用,不讲具体原理。

要驱动段式LCD,这里就要知道占空比的调整,比如1/4为例就需要4个COM口。这4个COM口由单片机自行驱动,你不用管。就像硬件IIC和SPI,时序问题不用你去操心,你要做的就是向对应的缓存内去写值就行了。

首先你们的段式LCD一定有一个真值表

在这里教你怎么看这个真值表

首先会有

PIN                                                          SEG0   SEG1   SEG2  SEG3 ...

COM3                                         COM3

COM2                              COM2

COM1                   COM1

COM0      COM0

通常真值表就长这样,一般段式LCD由段码管和特殊符号组成,如果有4个断码管,它们只要写出一组,其他的可以通过便宜来进行操作。特殊符号就特殊处理。

只需关心SEG0 跟单片机的哪个SEG缓存接上了,CH585的SEG是一个寄存器装了8个SEG,最多28个。也就是4位代表1个SEG,正好对上了4个COM

然后回到如何看真值表呢,就是SEG里为1的地方会被点亮,比如你往4个SEG寄存器缓存内写1,也就是全部写0xFFFFFFFF,那就可控制这28个SEG全亮。

看表算值的时候,高位到低位是COM3~COM0 SEG(n+1)~SEG(n)这样。一般断码管只占用2个SEG,所以表示一个断码管的数字一般是8位 ,以一个虚无的断码为例

0 对应的是  

   dPIN      ~    SEG(0) SEG(1)  <-----------------

| COM3              1               1

| COM2              1               1

| COM1              0               1

v COM0              0              0

那需要完成点亮SEG0的高2位,SEG1的高三位,也就是SEG0,SEG1组合起来的值   0x ec

当然两个COM口的话同理,位序排列不错就是写1点亮对应的位,写0熄灭

2、参考代码,没有实现显示浮点数那个功能,实在是想不出来怎么处理好。

#include "CH58x_common.h"
#include "CH58x_lcd.h"
#include "lcd.h"
#include <math.h>
#include <stdio.h>  // 添加标准输入输出库
//显示浮点数有问题
NumberUnion g_num_union;
// 段码屏用到了PA8,PA9 串口1不能用来打印
void lcd_init(void)
{
    // 1 选择并打开32KHZ时钟源 官方例程里并未操作时钟

    // 2 配置要用的IO为模拟输入 浮空态(可不配置,例程未配置)
    GPIOB_ModeCfg(0, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(1, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(2, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(3, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(4, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(5, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(6, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(7, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(9, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(18, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(19, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(20, GPIO_ModeIN_Floating);
    GPIOB_ModeCfg(21, GPIO_ModeIN_Floating);
    GPIOA_ModeCfg(8, GPIO_ModeIN_Floating);
    GPIOA_ModeCfg(9, GPIO_ModeIN_Floating);

    // 3  选择要加载到RAM0~RAM3的数据
    LCD_WriteData0(0);
    LCD_WriteData1(0);
    LCD_WriteData9(0);
    LCD_WriteData10(0);
    LCD_Write_SEG25_DATA(0);
    LCD_Write_SEG26_DATA(0);
    LCD_Write_SEG9_DATA(0);

    // 4 配置LCD参数
    // COM口由配置的占空比和偏置自动驱动,只需配置SEG的RAM即可,SEG一共有28根线,但是只用到了11个SEG
    R32_PIN_IN_DIS |= 0x0000238F; // 关闭数字输入
    R32_PIN_IN_DIS |= RB_PBLx_IN_DIS; // 关闭数字输入
    R16_PIN_CONFIG |= RB_PBHx_IN_DIS; // 操作LCD时,需关闭debug
    R32_LCD_SEG_EN = 0x063C020F; // SEG0~3 9 19~21 25~26
    R8_LCD_CMD = RB_LCD_SYS_EN | RB_LCD_ON |
                 (LCD_CLK_256 << 5) | //256HZ
                 (LCD_1_4_Duty << 3) |
                 (LCD_1_3_Bias << 2);
}

// 定义一个数组来存储数字段码
const uint8_t digit_segments[10] = {
    SEG_0, SEG_1, SEG_2, SEG_3, SEG_4,
    SEG_5, SEG_6, SEG_7, SEG_8, SEG_9
};
// 定义一个宏函数指针数组来存储写数据宏
#define CALL_LCD_WRITE_DATA(index, data) \
    switch (index) { \
        case 0: LCD_WriteData0(data); break; \
        case 1: LCD_WriteData1(data); break; \
        case 2: LCD_WriteData9(data); break; \
        case 3: LCD_WriteData10(data); break; \
    }
// decFlag = 0不显示带小数点的数字  decFlag = 1 显示带小数点的数字
SEG_PARM lcd_display_NUMBER(uint8_t num, uint8_t pos, uint8_t decFlag) //通过
{
    SEG_PARM parm = PARM_OK;
    if (!(num >= 0 && num <= 9 && pos >= 0 && pos <= 3))
    {
        printf("\n LCD Num Parameter Error\n");
        return PARM_ERROR;
    }
    if (decFlag == 1)
    {
        CALL_LCD_WRITE_DATA(pos, (digit_segments[num]|(0x01<<4)));
    }
    else
    {
        CALL_LCD_WRITE_DATA(pos, digit_segments[num]);
    }
   
    
    return parm;
}

 3、头文件

SEG0~9的值的具体表示要根据真值表来确定

#ifndef _LCD_H
#define _LCD_H

 typedef enum 
{
    PARM_OK = 0,
    PARM_ERROR = 1,
}SEG_PARM;

// 定义一个结构体来存储整数部分和小数部分的每一位数字
typedef struct {
    uint8_t int_digits[4];  // 存储小数扩增的整数 11.1 -》 0111 
    uint8_t int_count;      // 整数部分的数字个数 2
    uint8_t decimal_count;  // 小数部分的数字个数 1
    uint8_t dot_pos ;
    uint8_t reserve[5];
} DecimalNumberParts;
typedef struct {
    uint8_t int_digits[4];  // 存储整数部分的每一位数字,最多 4 位
    uint8_t int_count;      // 整数部分的数字个数
    uint8_t reserve[7];
} NumberParts;

// 定义共用体
typedef union {
    DecimalNumberParts decimal;
    NumberParts integer;
} NumberUnion;
// 段码表 // 其中数码管和小数点可以进行偏移,在真值表内可以看出为SEG5~SEG12是4个8段数码管的配置基本一致
//数码管数字 0~9
#define SEG_0 0xaf
#define SEG_1 0xa0
#define SEG_2 0xcb
#define SEG_3 0xe9
#define SEG_4 0xe4
#define SEG_5 0x6d
#define SEG_6 0x6f
#define SEG_7 0xa8
#define SEG_8 0xef
#define SEG_9 0xed
#define SEG_dot 0x10

#define LCD_Write_SEG0_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xfffffff0) | ((uint32_t)d)) 
#define LCD_Write_SEG1_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xffffff0f) | ((uint32_t)d)<<4) 
#define LCD_Write_SEG2_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xfffff0ff) | ((uint32_t)d)<<8) 
#define LCD_Write_SEG3_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xffff0fff) | ((uint32_t)d)<<12) 
#define LCD_Write_SEG4_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xfff0ffff) | ((uint32_t)d)<<16) 
#define LCD_Write_SEG5_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xff0fffff) | ((uint32_t)d)<<20) 
#define LCD_Write_SEG6_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xf0ffffff) | ((uint32_t)d)<<24) 
#define LCD_Write_SEG7_DATA(d) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0x0fffffff) | ((uint32_t)d)<<28) 

#define LCD_Write_SEG8_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xfffffff0)  | ((uint32_t)d)) 
#define LCD_Write_SEG9_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xffffff0f)  | ((uint32_t)d)<<4) 
#define LCD_Write_SEG10_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xfffff0ff) | ((uint32_t)d)<<8) 
#define LCD_Write_SEG11_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xffff0fff) | ((uint32_t)d)<<12) 
#define LCD_Write_SEG12_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xfff0ffff) | ((uint32_t)d)<<16) 
#define LCD_Write_SEG13_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xff0fffff) | ((uint32_t)d)<<20) 
#define LCD_Write_SEG14_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xf0ffffff) | ((uint32_t)d)<<24) 
#define LCD_Write_SEG15_DATA(d) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0x0fffffff) | ((uint32_t)d)<<28) 

#define LCD_Write_SEG16_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xfffffff0) | ((uint32_t)d)) 
#define LCD_Write_SEG17_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xffffff0f) | ((uint32_t)d)<<4) 
#define LCD_Write_SEG18_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xfffff0ff) | ((uint32_t)d)<<8) 
#define LCD_Write_SEG19_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xffff0fff) | ((uint32_t)d)<<12) 
#define LCD_Write_SEG20_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xfff0ffff) | ((uint32_t)d)<<16) 
#define LCD_Write_SEG21_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xff0fffff) | ((uint32_t)d)<<20) 
#define LCD_Write_SEG22_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0xf0ffffff) | ((uint32_t)d)<<24) 
#define LCD_Write_SEG23_DATA(d) (R32_LCD_RAM2 = (R32_LCD_RAM2 & 0x0fffffff) | ((uint32_t)d)<<28) 

#define LCD_Write_SEG24_DATA(d) (R32_LCD_RAM3 = (R32_LCD_RAM3 & 0xfffffff0) | ((uint32_t)d)) 
#define LCD_Write_SEG25_DATA(d) (R32_LCD_RAM3 = (R32_LCD_RAM3 & 0xffffff0f) | ((uint32_t)d)<<4) 
#define LCD_Write_SEG26_DATA(d) (R32_LCD_RAM3 = (R32_LCD_RAM3 & 0xfffff0ff) | ((uint32_t)d)<<8) 
#define LCD_Write_SEG27_DATA(d) (R32_LCD_RAM3 = (R32_LCD_RAM3 & 0xffff0fff) | ((uint32_t)d)<<12) 

/**
 * @brief LCD 初始化函数
 * @description 用于初始化 LCD 相关的 IO 口、配置 LCD 参数等操作。
 * @param 无
 * @return 无
 */
extern void lcd_init(void);


/**
 * @brief 在 LCD 上显示数字
 * @description 根据输入的数字、位置和是否显示小数点的标志,在 LCD 的指定位置显示数字。
 * @param num 要显示的数字(0 到 9)
 * @param pos 显示的位置(0 到 3)
 * @param decFlag 是否显示小数点的标志,0 表示不显示,1 表示显示
 * @return SEG_PARM 类型的值,PARM_OK 表示操作成功,PARM_ERROR 表示参数错误操作失败
 */
extern SEG_PARM lcd_display_NUMBER(uint8_t num, uint8_t pos, uint8_t decFlag);


extern void lcd_clear_disp(void);//清屏

#endif

4、在测试的时候发现,如果先初始化串口再初始化LCD会导致显示异常,所以推荐LCD初始化放在串口初始化之前。具体是什么原因还不太了解


网站公告

今日签到

点亮在社区的每一天
去签到