M61-32SU开发板点亮74hc595芯片8段8位数码管

[复制链接]
查看574 | 回复9 | 2024-6-3 13:46:44 | 显示全部楼层 |阅读模式
M61-32SU开发板 74hc595芯片数码管驱动

翻箱倒柜找到了多年以前买的8段8位数码管,想找商家要原理图资料没想到已经把我拉黑了,还是可以看到背后的芯片是74hc595的芯片,最后还是决定点亮它

数码管

数码管.png


如图所示是德飞莱的数码管8段(带小数点)8位
该模块是有两个74hc595芯片级联,和两个4位数码管组成,每个数码管的段引脚全都连接在同一个74hc595芯片上,每个数码管的公共引脚全都接在另一个75hc595芯片上,这样就可以通过一个74hc595控制选择数码管的段,另一个74hc595就可以控制选择那一位数码管

数码管原理图

数码管一般有7段和8段之分,8段的多了一个小数点,每个段都是一个发光led,分为共阴极和共阳极,共阴极是每个段的负极连接在一起,共阳极是每个段的正极连接在一起,原理图如下所示

数码管原理图.jpg


以共阴极数码管为例要想点亮a段就需要把数码管的a脚接高电平,公共端接GND就可以点亮,如果要显示数字 1,则需要把b和c接高电平,其它接低电平,公共端同样接GND即可点亮b和c段显示为数字1

存在的问题

一位的数码管都有8个段引脚,要想控制8位的岂不是要64个GPIO才可以控制吗?单片机的GPIO资源是很宝贵的,也不会只控制几位的数码管就浪费掉那么多的GPIO资源

解决问题也很简单,既然是不想浪费那么多io口,那么找到可以利用少量的io口分别控制多个输出的芯片就可以了,74hc595正是这样的芯片

74hc595芯片

74hc595是一个8位的高速的CMOS芯片,支持TTL电平控制,把输入的串行数据转成并行数据,有以下特点

  • 8bit串行输入
  • 8bit串行输出或并行输出
  • 内部含有存储寄存器控制3态输出
  • 支持清空位移寄存器
  • 支持多级串联


芯片引脚如下

74hc595芯片引脚.png


DS引脚是串行输入引脚,Q0~Q7为并行输出0~7代表0~7bit位,Q7S引脚为溢出输出引脚,当串行输入的数据超过8bit是,高位就会从Q7S输出,利用这个特性可以把Q7S和另一个74hc595芯片的DS引脚就做到级联控制更多的引脚

具体的引脚功能如下

595引脚功能.png


工作原理

595芯片工作原理.gif


当SHCP处于上升沿时,会从DS引脚读取1bit数据放入移位寄存器(位移寄存器的值会一位一位往前移动),当STCP处于上升沿时会把位移寄存器中的值存储到锁存器中,如果此时OE引脚是低电平就会根据锁存器里面的值更新Q0~Q7引脚的状态

8位数码管

8位数码管原理图.png


通过前面的了解的知识可以看到该模块由两个74hc595级联来控制的8位8段共阴极数码管

其中第一个74hc595连接的是数码管的8个段引脚,另一个74hc595连接的是每个数码管的公共引负脚8位数码管也就有8个

结论: 第一个595芯片控制显示数码管的段,第二个595芯片控制的哪一位的数码管,DIO输入引脚输入16bit数据,高8bit是位选功能,控制哪个位显示,低8bit是段选功能,控制数码管的哪个段显示,根据前面的595芯片介绍DIO输入是先输入高位

代码

模块需要控制的有移位寄存器时序输入、锁存器时序输入以及串口数据输入一共三个,定义dev结构体如下

  1. typedef struct {

  2.     //串口数据

  3.     uint8_t pin_ser;

  4.     //移位寄存器时序

  5.     uint8_t pin_clk;

  6.     //锁存器时序

  7.     uint8_t pin_st;

  8.     //设置pin状态

  9.     int (*set_dev_data)(uint8_t pin, uint8_t data);

  10.     //延时函数

  11.     void (*delay_fun)(uint32_t us);

  12.     //初始化gpio

  13.     void (*gpio_init)(uint8_t pin, uint32_t cfgset);

  14. }digit_dev_t;
复制代码


另外还定义了两组数据,分别是控制段码的数据和控制位码的数据

根据上面的8位数码管模块的原理图可以得到数码管的a-h段引脚分别接到595芯片的Q0~Q7引脚,也就是1个字节的第一位控制a,第二位控制b。。。第8位控制h段,如果要显示1需要点亮b和c两段即可,因为是共阴极数码管,所以对应的b和c要是高电平,也就是段a-h对应的数据分别为0000 0110,转成16进制为0x06

显然我们可以的到控制段显示的数据为

  1. unsigned char digit_tab[] =

  2. {

  3. 0x3f,//0

  4. 0x06,//1

  5. 0x5b,//2

  6. 0x4f,//3

  7. 0x66,//4

  8. 0x6d,//5

  9. 0x7d,//6

  10. 0x07,//7

  11. 0x7f,//8

  12. 0x6f,//9

  13. 0x77,//a

  14. 0x7c,//b

  15. 0x39,//c

  16. 0x5e,//d

  17. 0x79,//e

  18. 0x71,//f

  19. 0x00,//全关

  20. };
复制代码


位码是控制哪一位数码管被点亮,因为是共阴极,所以需要把对应的电平拉低即可点亮,显然可以得到位码数据如下:

  1. unsigned char digit_choose_bit[] = {

  2. 0xfe,0xfd,0xfb,0xf7,

  3. 0xef,0xdf,0xbf,0x7f,

  4. };
复制代码


定义595对应的函数有下面三个

  1. //初始化设备

  2.     void hc595_init(digit_dev_t* dev);



  3.     /**

  4.      *

  5.      * @brief 动态刷新显示,需要循环调用

  6.      * @param  dev

  7.      * @param  number

  8.      *          整数模式:12345678

  9.      *          小数模式:1234.5678

  10.      *          负数模式:-1234567

  11.      *                  :-1234.567

  12.      */

  13.     void hc595_desplay(digit_dev_t* dev, double number);



  14.     //发送高八位,第八位数据

  15.     void hc595_send_byte2(digit_dev_t* dev, unsigned char dataH, unsigned char dataL);
复制代码


74hc595.c实现代码如下

  1. #include "74hc595.h"

  2. #include <math.h>



  3. unsigned char digit_tab[] =

  4. {

  5. 0x3f,//0

  6. 0x06,//1

  7. 0x5b,//2

  8. 0x4f,//3

  9. 0x66,//4

  10. 0x6d,//5

  11. 0x7d,//6

  12. 0x07,//7

  13. 0x7f,//8

  14. 0x6f,//9

  15. 0x77,//a

  16. 0x7c,//b

  17. 0x39,//c

  18. 0x5e,//d

  19. 0x79,//e

  20. 0x71,//f

  21. 0x00,//全关

  22. };



  23. unsigned char digit_choose_bit[] = {

  24. 0xfe,0xfd,0xfb,0xf7,

  25. 0xef,0xdf,0xbf,0x7f,

  26. };



  27. #define PIN_RESET(dev)                          \

  28.     do{                                         \

  29.         dev->set_dev_data(dev->pin_clk,0);      \

  30.         dev->set_dev_data(dev->pin_st,0);       \

  31.     }while(0)



  32. #define ST_REFRESH(dev)                         \

  33.     do{                                         \

  34.         dev->set_dev_data(dev->pin_st,0);       \

  35.         dev->set_dev_data(dev->pin_st,1);       \

  36.     }while(0)



  37. void hc595_init(digit_dev_t* dev)

  38. {

  39.     dev->gpio_init(dev->pin_clk, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

  40.     dev->gpio_init(dev->pin_ser, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

  41.     dev->gpio_init(dev->pin_st, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);



  42.     PIN_RESET(dev);



  43. }



  44. /**

  45. * @brief 发送 一字节数据到595串口引脚

  46. * @param  dev              

  47. * @param  data            

  48. */

  49. static void hc595_send_char_impl(digit_dev_t* dev, unsigned char data)

  50. {

  51.     for (int i = 0; i < 8;i++) {

  52.         //从高位写入

  53.         if (data >= 0x80) {

  54.             dev->set_dev_data(dev->pin_ser, 1);

  55.         }

  56.         else {

  57.             dev->set_dev_data(dev->pin_ser, 0);

  58.         }

  59.         //上升沿触发写入

  60.         dev->set_dev_data(dev->pin_clk, 0);

  61.         dev->set_dev_data(dev->pin_clk, 1);

  62.         data <<= 1;

  63.     }

  64. }



  65. /**

  66. * @brief 计算小数点位数

  67. * @param  number           

  68. * @return size_t

  69. */

  70. static size_t deimal_size(double number)

  71. {

  72.     size_t size = 0;

  73.     while ((number - (int)number) != 0) {

  74.         number *= 10;

  75.         size++;

  76.     }

  77.     return size;

  78. }

  79. void hc595_send_byte2(digit_dev_t* dev, unsigned char dataH, unsigned char dataL)

  80. {

  81.     PIN_RESET(dev);

  82.     hc595_send_char_impl(dev, dataH);

  83.     hc595_send_char_impl(dev, dataL);

  84.     ST_REFRESH(dev);

  85.     PIN_RESET(dev);

  86. }



  87. /**

  88. * @brief 更新buf到锁存器中

  89. * @param  dev              

  90. * @param  buf              

  91. */

  92. static void hc595_refresh_buf(digit_dev_t* dev, unsigned char* buf)

  93. {

  94.     static int index = 0;

  95.     for (;index < 8;index++) {

  96.         //消隐

  97.         hc595_send_byte2(dev, 0xff, 0x00);

  98.         hc595_send_byte2(dev, digit_choose_bit[index], buf[index]);

  99.         dev->delay_fun(2000);

  100.     }

  101.     index = 0;

  102. }



  103. void hc595_desplay(digit_dev_t* dev, double number)

  104. {

  105.     PIN_RESET(dev);

  106.     //默认0x00不显示

  107.     unsigned char buf[8] = { 0x00 };



  108.     if (number > 99999999 || number < -9999999) {

  109.         printf("show range: -9999999 ~ 99999999");

  110.         for (int i = 0; i < 8;i++) {

  111.             buf[i] = MINUS;

  112.         }

  113.         //显示--------

  114.         hc595_refresh_buf(dev, buf);

  115.         return;

  116.     }



  117.     size_t size = deimal_size(number);



  118.     if (size != 0) {

  119.         //把number处理成整数,小数点写入buf中再处理

  120.         number = number * pow(10, size);

  121.     }



  122.     long integer = number;



  123.     uint8_t is_negative = integer < 0;

  124.     //负数要去相反数,避免后面计算每一位的余数为负数

  125.     if (is_negative) {

  126.         integer = -integer;

  127.     }

  128.     int i = 0;

  129.     for (; integer != 0;i++) {

  130.         buf[7 - i] = digit_tab[integer % 10];

  131.         //处理小数点

  132.         if (size != 0 && i == size) {

  133.             buf[7 - i] = DP_WRAP(buf[7 - i]);

  134.         }

  135.         integer /= 10;

  136.     }

  137.     //如果是负数在buf最前面加上负号

  138.     if (is_negative) {

  139.         buf[7 - i] = MINUS;

  140.     }



  141.     hc595_refresh_buf(dev, buf);

  142. }
复制代码


main代码中调用

  1. #include "board.h"
  2. #include "log.h"
  3. #include <stdio.h>
  4. #include "bflb_gpio.h"
  5. #include "bflb_mtimer.h"
  6. #include "bflb_timer.h"

  7. #include "74hc595.h"

  8. #define PIN_CLK GPIO_PIN_13
  9. #define PIN_SER GPIO_PIN_10
  10. #define PIN_ST  GPIO_PIN_19

  11. struct bflb_device_s* gpio;
  12. struct bflb_device_s* timer0;

  13. void delay_us(uint32_t us)
  14. {
  15.     bflb_mtimer_delay_us(us);
  16. }

  17. int gpio_set_data(uint8_t pin, uint8_t data)
  18. {
  19.     if (data) {
  20.         bflb_gpio_set(gpio, pin);
  21.     }
  22.     else {
  23.         bflb_gpio_reset(gpio, pin);
  24.     }
  25.     return 0;
  26. }

  27. int gpio_init(uint8_t pin, uint32_t cfgset)
  28. {
  29.     bflb_gpio_init(gpio, pin, cfgset);
  30. }

  31. void timer0_isr(int argc, void* argv)
  32. {
  33.     bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);
  34.     if (status) {
  35.         bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
  36.         digit_dev_t* dev = (digit_dev_t*)argv;
  37.         hc595_desplay(dev, -1234.567);
  38.     }
  39. }

  40. int main(void)
  41. {
  42.     board_init();
  43.     printf("74hc595 start!\n");

  44.     gpio = bflb_device_get_by_name("gpio");
  45.     digit_dev_t dev = {
  46.         .pin_clk = PIN_CLK,
  47.         .pin_ser = PIN_SER,
  48.         .pin_st = PIN_ST,
  49.         .delay_fun = delay_us,
  50.         .set_dev_data = gpio_set_data,
  51.         .gpio_init = gpio_init,
  52.     };
  53.     hc595_init(&dev);

  54.    
  55.     struct bflb_timer_config_s cfg;
  56.     cfg.counter_mode = TIMER_COUNTER_MODE_PROLOAD;
  57.     cfg.clock_source = TIMER_CLKSRC_XTAL;
  58.     cfg.clock_div = 39;
  59.     cfg.trigger_comp_id = TIMER_COMP_ID_0;
  60.     cfg.comp0_val = 1000;
  61.     cfg.preload_val = 0;

  62.     timer0 = bflb_device_get_by_name("timer0");

  63.     bflb_irq_attach(timer0->irq_num, timer0_isr, &dev);
  64.     bflb_irq_enable(timer0->irq_num);

  65.     bflb_timer_init(timer0, &cfg);

  66.     bflb_timer_start(timer0);

  67.     while (1) {
  68.         bflb_mtimer_delay_ms(1000);
  69.     }
  70.     return 0;
  71. }
复制代码


点亮效果

点亮效果.png



回复

使用道具 举报

lovzx | 2024-6-3 13:49:09 | 显示全部楼层
回复 支持 反对

使用道具 举报

WT_0213 | 2024-6-3 14:49:13 | 显示全部楼层
回复

使用道具 举报

iiv | 2024-6-3 22:59:44 | 显示全部楼层
这个优秀
回复

使用道具 举报

hrqwe | 2024-6-4 01:05:05 | 显示全部楼层
牛哇
日拱一卒,功不唐捐
回复

使用道具 举报

浅末哈哈 | 2024-6-4 08:31:42 | 显示全部楼层
顶一顶
回复

使用道具 举报

爱笑 | 2024-6-4 08:33:59 | 显示全部楼层
不错不错!
用心做好保姆工作
回复

使用道具 举报

lazy | 2024-6-4 11:33:56 | 显示全部楼层
回复

使用道具 举报

jkernet | 2024-6-4 21:22:04 | 显示全部楼层
写得好.感谢分享
回复 支持 反对

使用道具 举报

知行合一 | 2024-7-28 11:54:28 | 显示全部楼层
牛哇
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则