STM32实战总结:HAL之数码管

数码管基础知识参考:

51单片机外设篇:数码管_路溪非溪的博客-CSDN博客_csdn数码管

实现功能

1、上电后,数码管每隔50ms计数;

2、触摸按键1调节数码管亮度。

原理图

根据原理图很容易就能看出来,左边的是段选信号,右边的是位选信号。

对应的驱动电路如下:

用的是芯片TM1602,输入端接到了PC0/PC1/PC2

编程的重点在于理解TM1602芯片。

我们在编程时,需要将TM1602的属性和操作进行封装,然后再使用。

MX配置

配置PC0/PC1/PC2三个端口为输出模式即可。

别忘了标签名。

程序编写

本文的程序编写并不涉及到新的HAL库内容,而是对芯片TM1602的封装。

有助于对底层HAL库封装的理解。

TM1602数据手册

具体自行查看数据手册,此处仅放置关键内容。

TM1620是一种LED(发光二极管显示器)驱动控制专用IC,内部集成有MCU数字接口、数据锁存器、LED驱动等电路。

手册里大部分内容还可以理解,但是关于“显示寄存器地址”表,表示很难理解:

这啥意思?

这里我不明白的是为什么要每个地址都要写两次。。。。还非得写00HL和00HU,直接整个8位一起写00H不行吗?

横向表示显示寄存器的地址,也就是位选所对应的地址。数码管位选端和哪个GRID绑定了,对应数码管就和哪个显存对应了,后面就会从对应显存里拿数据来显示。

封装

新建两个文件display.h和display.c,用于封装数码管操作。

display.h

#ifndef _DISPLAY_H_
#define _DISPLAY_H_

#include <stdint.h>

//显示模式
typedef enum
{
    DI4_SEG10 = 0x00,
    DI5_SEG9 = 0x01,
    DI6_SEG8 = 0x02
} display_mode;

//数据读写模式
typedef enum
{
    DATA_MODE = 0x40,
    AUTO_ADDR_INC = 0x40,
    FIXED_ADDR = 0x44,
    NORMAL_MODE = 0x40,
    TEST_MODE = 0x48
} wr_mode;

//显示控制
typedef enum
{
    LIGHTNESS_1 = 0x88,
    LIGHTNESS_2 = 0x89,
    LIGHTNESS_3 = 0x8A,
    LIGHTNESS_4 = 0x8B,
    LIGHTNESS_5 = 0x8C,
    LIGHTNESS_6 = 0x8D,
    LIGHTNESS_7 = 0x8E,
    LIGHTNESS_8 = 0x8F,
    DIS_DISPLAY = 0x80,
    EN_DISPLAY = 0x88
} display_ctrl_mode;

//地址设置
typedef enum
{
    GRID1 = 0xC0,
    GRID1_EX = 0xC1,
    GRID2 = 0xC2,
    GRID2_EX = 0xC3,
    GRID3 = 0xC4,
    GRID3_EX = 0xC5,
    GRID4 = 0xC6,
    GRID4_EX = 0xC7,
    GRID5 = 0xC8,
    GRID5_EX = 0xC9,
    GRID6 = 0xCA,
    GRID6_EX = 0xCB   
} grid_sel;

//输出哪个数字,默认不带小数点
typedef enum
{
    ZERO = 0x3F,
    ONE = 0x06,
    TWO = 0x5B,
    THREE = 0x4F,
    FOUR = 0x66,
    FIVE = 0x6D,
    SIX = 0x7D,
    SEVEN = 0x07,
    EIGHT = 0x7F,
    NINE = 0x6F
} display_num;

//是否输出小数点
typedef enum
{
    NO,
    YES
} is_point;

typedef struct
{
    display_ctrl_mode lightness;
    
    void (*display)(grid_sel, display_num, is_point);
    void (*changeLightness)(void);
    void (*disDisplay)(void);
} display_haldler;

extern display_haldler displayHaldler;

#endif

display.c

#include "myapplication.h"

#define STB_SET        HAL_GPIO_WritePin(TM1602_STB_GPIO_Port, TM1602_STB_Pin, GPIO_PIN_SET);
#define STB_RESET      HAL_GPIO_WritePin(TM1602_STB_GPIO_Port, TM1602_STB_Pin, GPIO_PIN_RESET);
#define CLK_SET        HAL_GPIO_WritePin(TM1602_CLK_GPIO_Port, TM1602_CLK_Pin, GPIO_PIN_SET);
#define CLK_RESET      HAL_GPIO_WritePin(TM1602_CLK_GPIO_Port, TM1602_CLK_Pin, GPIO_PIN_RESET);
#define DIN_WRITE(x)   HAL_GPIO_WritePin(TM1602_DIN_GPIO_Port, TM1602_DIN_Pin, (x));

static void Display(grid_sel gridSel, display_num displayNum, is_point isPoint);
static void ChangeLightness(void);
static void DisDisplay(void);

display_haldler displayHaldler =
{
    LIGHTNESS_1,
    Display,
    ChangeLightness,
    DisDisplay
};

//最底层时序操作
static void Display_Write(uint8_t dataWrited)
{
    uint8_t i = 0;
    
    STB_RESET;
    
    for(i; i<8; i++)
    {
        CLK_RESET;
        DIN_WRITE(dataWrited & 0x01);
        CLK_SET;
        dataWrited >>= 1;
    }
}

static void Display_Init(void)
{
    Display_Write(DI6_SEG8);
    STB_SET;
    Display_Write(FIXED_ADDR);
    STB_SET;
}

static void Display(grid_sel gridSel, display_num displayNum, is_point isPoint)
{
    Display_Init();
    Display_Write(gridSel);
    if(isPoint == YES)
    {
        Display_Write(displayNum + 0x80);  
    }
    else
    {
        Display_Write(displayNum);
    }
    STB_SET;
    Display_Write(EN_DISPLAY);
    STB_SET;
}
static void ChangeLightness(void)
{
    if(displayHaldler.lightness > LIGHTNESS_8)
    {
        displayHaldler.lightness = LIGHTNESS_1;
    }
    Display_Write((displayHaldler.lightness)++);
}

static void DisDisplay(void)
{
    Display_Write(DIS_DISPLAY);
}

完成了TM1602的底层封装,以及数码管的操作封装。

封装时一定要注意严格按照时序操作。

比如说上面红框中的地方,并不是每个字节传输完成后就拉高STB,所以不能在每次写字节函数中拉高STB,而是要把拉高STB的操作留给外层。要注意,拉高的时机,有的地方是一个字节后就拉高,有的地方是两个字节或者多个字节后再拉高。当然,字节传输函数中可以拉低STB,相比放外面更方便些。

如果要实现多个数码管同时显示,可以通过扫描实现,结合位选操作即可。

如果要不同数码管各显示个十百千万,可通过整除和取余来实现,比如:

//数码管显示计数值
Display.Disp(Disp_NUM_1,Cnt%10,      Disp_DP_OFF); //个位
Display.Disp(Disp_NUM_2,Cnt/10%10,   Disp_DP_OFF); //十位
Display.Disp(Disp_NUM_3,Cnt/100%10,  Disp_DP_OFF); //百位
Display.Disp(Disp_NUM_4,Cnt/1000%10, Disp_DP_OFF); //千位
Display.Disp(Disp_NUM_5,Cnt/10000%10,Disp_DP_OFF); //万位
Display.Disp(Disp_NUM_6,Cnt/100000,  Disp_DP_OFF); //十万位

时间关系,不再赘述。后面有时间再完善。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用HAL库编写STM32F103数码管代码时,首先需要配置相关的引脚和寄存器。以下是一个基本的示例代码: ```c #include "stm32f1xx_hal.h" // 定义数码管的引脚 #define SEG_A_Pin GPIO_PIN_0 #define SEG_A_GPIO_Port GPIOA #define SEG_B_Pin GPIO_PIN_1 #define SEG_B_GPIO_Port GPIOA // 其他引脚类似... // 数码管的位选引脚 #define DIGIT_1_Pin GPIO_PIN_0 #define DIGIT_1_GPIO_Port GPIOB #define DIGIT_2_Pin GPIO_PIN_1 #define DIGIT_2_GPIO_Port GPIOB // 其他位选引脚类似... // 数码管的数字显示表 const uint8_t DIGIT_TABLE[] = { 0x3F, // 显示数字 0 0x06, // 显示数字 1 0x5B, // 显示数字 2 // 其他数字的显示值... }; // 数码管显示函数 void displayDigit(uint8_t digit) { GPIOA->ODR = (GPIOA->ODR & 0xFF00) | DIGIT_TABLE[digit]; // 设置段选引脚的输出状态 GPIOB->ODR = (GPIOB->ODR & 0xFFFC) | (1 << digit); // 设置位选引脚的输出状态 } int main(void) { // 初始化HALHAL_Init(); // 配置数码管的引脚为输出模式 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = SEG_A_Pin | SEG_B_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(SEG_A_GPIO_Port, &GPIO_InitStruct); // 配置位选引脚为输出模式 GPIO_InitStruct.Pin = DIGIT_1_Pin | DIGIT_2_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(DIGIT_1_GPIO_Port, &GPIO_InitStruct); // 数码管显示循环 while (1) { for (uint8_t i = 0; i < sizeof(DIGIT_TABLE) / sizeof(DIGIT_TABLE[0]); i++) { displayDigit(i); HAL_Delay(1000); // 延时1秒 } } } ``` 以上代码假设数码管的共阳极接法,如果是共阴极接法,需要修改`DIGIT_TABLE`中的数码管显示值。此外,还需要根据硬件连接情况修改引脚定义和初始化代码。 请根据你的具体硬件连接情况和需求进行适当修改。这只是一个简单的示例代码,实际使用中可能需要根据具体情况进行更复杂的逻辑设计。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值