• 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

    1. #ifndef _DISPLAY_H_
    2. #define _DISPLAY_H_
    3. #include <stdint.h>
    4. //显示模式
    5. typedef enum
    6. {
    7. DI4_SEG10 = 0x00,
    8. DI5_SEG9 = 0x01,
    9. DI6_SEG8 = 0x02
    10. } display_mode;
    11. //数据读写模式
    12. typedef enum
    13. {
    14. DATA_MODE = 0x40,
    15. AUTO_ADDR_INC = 0x40,
    16. FIXED_ADDR = 0x44,
    17. NORMAL_MODE = 0x40,
    18. TEST_MODE = 0x48
    19. } wr_mode;
    20. //显示控制
    21. typedef enum
    22. {
    23. LIGHTNESS_1 = 0x88,
    24. LIGHTNESS_2 = 0x89,
    25. LIGHTNESS_3 = 0x8A,
    26. LIGHTNESS_4 = 0x8B,
    27. LIGHTNESS_5 = 0x8C,
    28. LIGHTNESS_6 = 0x8D,
    29. LIGHTNESS_7 = 0x8E,
    30. LIGHTNESS_8 = 0x8F,
    31. DIS_DISPLAY = 0x80,
    32. EN_DISPLAY = 0x88
    33. } display_ctrl_mode;
    34. //地址设置
    35. typedef enum
    36. {
    37. GRID1 = 0xC0,
    38. GRID1_EX = 0xC1,
    39. GRID2 = 0xC2,
    40. GRID2_EX = 0xC3,
    41. GRID3 = 0xC4,
    42. GRID3_EX = 0xC5,
    43. GRID4 = 0xC6,
    44. GRID4_EX = 0xC7,
    45. GRID5 = 0xC8,
    46. GRID5_EX = 0xC9,
    47. GRID6 = 0xCA,
    48. GRID6_EX = 0xCB
    49. } grid_sel;
    50. //输出哪个数字,默认不带小数点
    51. typedef enum
    52. {
    53. ZERO = 0x3F,
    54. ONE = 0x06,
    55. TWO = 0x5B,
    56. THREE = 0x4F,
    57. FOUR = 0x66,
    58. FIVE = 0x6D,
    59. SIX = 0x7D,
    60. SEVEN = 0x07,
    61. EIGHT = 0x7F,
    62. NINE = 0x6F
    63. } display_num;
    64. //是否输出小数点
    65. typedef enum
    66. {
    67. NO,
    68. YES
    69. } is_point;
    70. typedef struct
    71. {
    72. display_ctrl_mode lightness;
    73. void (*display)(grid_sel, display_num, is_point);
    74. void (*changeLightness)(void);
    75. void (*disDisplay)(void);
    76. } display_haldler;
    77. extern display_haldler displayHaldler;
    78. #endif

    display.c

    1. #include "myapplication.h"
    2. #define STB_SET HAL_GPIO_WritePin(TM1602_STB_GPIO_Port, TM1602_STB_Pin, GPIO_PIN_SET);
    3. #define STB_RESET HAL_GPIO_WritePin(TM1602_STB_GPIO_Port, TM1602_STB_Pin, GPIO_PIN_RESET);
    4. #define CLK_SET HAL_GPIO_WritePin(TM1602_CLK_GPIO_Port, TM1602_CLK_Pin, GPIO_PIN_SET);
    5. #define CLK_RESET HAL_GPIO_WritePin(TM1602_CLK_GPIO_Port, TM1602_CLK_Pin, GPIO_PIN_RESET);
    6. #define DIN_WRITE(x) HAL_GPIO_WritePin(TM1602_DIN_GPIO_Port, TM1602_DIN_Pin, (x));
    7. static void Display(grid_sel gridSel, display_num displayNum, is_point isPoint);
    8. static void ChangeLightness(void);
    9. static void DisDisplay(void);
    10. display_haldler displayHaldler =
    11. {
    12. LIGHTNESS_1,
    13. Display,
    14. ChangeLightness,
    15. DisDisplay
    16. };
    17. //最底层时序操作
    18. static void Display_Write(uint8_t dataWrited)
    19. {
    20. uint8_t i = 0;
    21. STB_RESET;
    22. for(i; i<8; i++)
    23. {
    24. CLK_RESET;
    25. DIN_WRITE(dataWrited & 0x01);
    26. CLK_SET;
    27. dataWrited >>= 1;
    28. }
    29. }
    30. static void Display_Init(void)
    31. {
    32. Display_Write(DI6_SEG8);
    33. STB_SET;
    34. Display_Write(FIXED_ADDR);
    35. STB_SET;
    36. }
    37. static void Display(grid_sel gridSel, display_num displayNum, is_point isPoint)
    38. {
    39. Display_Init();
    40. Display_Write(gridSel);
    41. if(isPoint == YES)
    42. {
    43. Display_Write(displayNum + 0x80);
    44. }
    45. else
    46. {
    47. Display_Write(displayNum);
    48. }
    49. STB_SET;
    50. Display_Write(EN_DISPLAY);
    51. STB_SET;
    52. }
    53. static void ChangeLightness(void)
    54. {
    55. if(displayHaldler.lightness > LIGHTNESS_8)
    56. {
    57. displayHaldler.lightness = LIGHTNESS_1;
    58. }
    59. Display_Write((displayHaldler.lightness)++);
    60. }
    61. static void DisDisplay(void)
    62. {
    63. Display_Write(DIS_DISPLAY);
    64. }

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

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

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

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

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

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

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

  • 相关阅读:
    QT--对象模型(对象树)
    贪心算法java实现
    前后端分离项目,vue+uni-app+php+mysql电影院售票系统设计与实现(H5移动项目)
    李航老师《统计学习方法》第2章阅读笔记
    K8S原来如此简单(四)Service+Ingress
    腾讯mini项目-【指标监控服务重构-会议记录】2023-07-06
    SOAP接口对接
    MySQL中json_extract函数说明
    力扣第200题 岛屿数量 C++ dfs bfs 深搜和广搜 附Java代码
    (CVPR 2019) 3D-SIS: 3D Semantic Instance Segmentation of RGB-D Scans
  • 原文地址:https://blog.csdn.net/qq_28576837/article/details/126823979