M61-32SU开发板 74hc595芯片数码管驱动
翻箱倒柜找到了多年以前买的8段8位数码管,想找商家要原理图资料没想到已经把我拉黑了,还是可以看到背后的芯片是74hc595的芯片,最后还是决定点亮它
数码管
如图所示是德飞莱的数码管8段(带小数点)8位
该模块是有两个74hc595芯片级联,和两个4位数码管组成,每个数码管的段引脚全都连接在同一个74hc595芯片上,每个数码管的公共引脚全都接在另一个75hc595芯片上,这样就可以通过一个74hc595控制选择数码管的段,另一个74hc595就可以控制选择那一位数码管
数码管原理图
数码管一般有7段和8段之分,8段的多了一个小数点,每个段都是一个发光led,分为共阴极和共阳极,共阴极是每个段的负极连接在一起,共阳极是每个段的正极连接在一起,原理图如下所示
以共阴极数码管为例要想点亮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态输出
- 支持清空位移寄存器
- 支持多级串联
芯片引脚如下
DS引脚是串行输入引脚,Q0~Q7为并行输出0~7代表0~7bit位,Q7S引脚为溢出输出引脚,当串行输入的数据超过8bit是,高位就会从Q7S输出,利用这个特性可以把Q7S和另一个74hc595芯片的DS引脚就做到级联控制更多的引脚
具体的引脚功能如下
工作原理
当SHCP处于上升沿时,会从DS引脚读取1bit数据放入移位寄存器(位移寄存器的值会一位一位往前移动),当STCP处于上升沿时会把位移寄存器中的值存储到锁存器中,如果此时OE引脚是低电平就会根据锁存器里面的值更新Q0~Q7引脚的状态
8位数码管
通过前面的了解的知识可以看到该模块由两个74hc595级联来控制的8位8段共阴极数码管
其中第一个74hc595连接的是数码管的8个段引脚,另一个74hc595连接的是每个数码管的公共引负脚8位数码管也就有8个
结论: 第一个595芯片控制显示数码管的段,第二个595芯片控制的哪一位的数码管,DIO输入引脚输入16bit数据,高8bit是位选功能,控制哪个位显示,低8bit是段选功能,控制数码管的哪个段显示,根据前面的595芯片介绍DIO输入是先输入高位
代码
模块需要控制的有移位寄存器时序输入、锁存器时序输入以及串口数据输入一共三个,定义dev结构体如下
- typedef struct {
- //串口数据
- uint8_t pin_ser;
- //移位寄存器时序
- uint8_t pin_clk;
- //锁存器时序
- uint8_t pin_st;
- //设置pin状态
- int (*set_dev_data)(uint8_t pin, uint8_t data);
- //延时函数
- void (*delay_fun)(uint32_t us);
- //初始化gpio
- void (*gpio_init)(uint8_t pin, uint32_t cfgset);
- }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
显然我们可以的到控制段显示的数据为
- unsigned char digit_tab[] =
- {
- 0x3f,//0
- 0x06,//1
- 0x5b,//2
- 0x4f,//3
- 0x66,//4
- 0x6d,//5
- 0x7d,//6
- 0x07,//7
- 0x7f,//8
- 0x6f,//9
- 0x77,//a
- 0x7c,//b
- 0x39,//c
- 0x5e,//d
- 0x79,//e
- 0x71,//f
- 0x00,//全关
- };
复制代码
位码是控制哪一位数码管被点亮,因为是共阴极,所以需要把对应的电平拉低即可点亮,显然可以得到位码数据如下:
- unsigned char digit_choose_bit[] = {
- 0xfe,0xfd,0xfb,0xf7,
- 0xef,0xdf,0xbf,0x7f,
- };
复制代码
定义595对应的函数有下面三个
- //初始化设备
- void hc595_init(digit_dev_t* dev);
- /**
- *
- * @brief 动态刷新显示,需要循环调用
- * @param dev
- * @param number
- * 整数模式:12345678
- * 小数模式:1234.5678
- * 负数模式:-1234567
- * :-1234.567
- */
- void hc595_desplay(digit_dev_t* dev, double number);
- //发送高八位,第八位数据
- void hc595_send_byte2(digit_dev_t* dev, unsigned char dataH, unsigned char dataL);
复制代码
74hc595.c实现代码如下
- #include "74hc595.h"
- #include <math.h>
- unsigned char digit_tab[] =
- {
- 0x3f,//0
- 0x06,//1
- 0x5b,//2
- 0x4f,//3
- 0x66,//4
- 0x6d,//5
- 0x7d,//6
- 0x07,//7
- 0x7f,//8
- 0x6f,//9
- 0x77,//a
- 0x7c,//b
- 0x39,//c
- 0x5e,//d
- 0x79,//e
- 0x71,//f
- 0x00,//全关
- };
- unsigned char digit_choose_bit[] = {
- 0xfe,0xfd,0xfb,0xf7,
- 0xef,0xdf,0xbf,0x7f,
- };
- #define PIN_RESET(dev) \
- do{ \
- dev->set_dev_data(dev->pin_clk,0); \
- dev->set_dev_data(dev->pin_st,0); \
- }while(0)
- #define ST_REFRESH(dev) \
- do{ \
- dev->set_dev_data(dev->pin_st,0); \
- dev->set_dev_data(dev->pin_st,1); \
- }while(0)
- void hc595_init(digit_dev_t* dev)
- {
- dev->gpio_init(dev->pin_clk, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
- dev->gpio_init(dev->pin_ser, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
- dev->gpio_init(dev->pin_st, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);
- PIN_RESET(dev);
- }
- /**
- * @brief 发送 一字节数据到595串口引脚
- * @param dev
- * @param data
- */
- static void hc595_send_char_impl(digit_dev_t* dev, unsigned char data)
- {
- for (int i = 0; i < 8;i++) {
- //从高位写入
- if (data >= 0x80) {
- dev->set_dev_data(dev->pin_ser, 1);
- }
- else {
- dev->set_dev_data(dev->pin_ser, 0);
- }
- //上升沿触发写入
- dev->set_dev_data(dev->pin_clk, 0);
- dev->set_dev_data(dev->pin_clk, 1);
- data <<= 1;
- }
- }
- /**
- * @brief 计算小数点位数
- * @param number
- * @return size_t
- */
- static size_t deimal_size(double number)
- {
- size_t size = 0;
- while ((number - (int)number) != 0) {
- number *= 10;
- size++;
- }
- return size;
- }
- void hc595_send_byte2(digit_dev_t* dev, unsigned char dataH, unsigned char dataL)
- {
- PIN_RESET(dev);
- hc595_send_char_impl(dev, dataH);
- hc595_send_char_impl(dev, dataL);
- ST_REFRESH(dev);
- PIN_RESET(dev);
- }
- /**
- * @brief 更新buf到锁存器中
- * @param dev
- * @param buf
- */
- static void hc595_refresh_buf(digit_dev_t* dev, unsigned char* buf)
- {
- static int index = 0;
- for (;index < 8;index++) {
- //消隐
- hc595_send_byte2(dev, 0xff, 0x00);
- hc595_send_byte2(dev, digit_choose_bit[index], buf[index]);
- dev->delay_fun(2000);
- }
- index = 0;
- }
- void hc595_desplay(digit_dev_t* dev, double number)
- {
- PIN_RESET(dev);
- //默认0x00不显示
- unsigned char buf[8] = { 0x00 };
- if (number > 99999999 || number < -9999999) {
- printf("show range: -9999999 ~ 99999999");
- for (int i = 0; i < 8;i++) {
- buf[i] = MINUS;
- }
- //显示--------
- hc595_refresh_buf(dev, buf);
- return;
- }
- size_t size = deimal_size(number);
- if (size != 0) {
- //把number处理成整数,小数点写入buf中再处理
- number = number * pow(10, size);
- }
- long integer = number;
- uint8_t is_negative = integer < 0;
- //负数要去相反数,避免后面计算每一位的余数为负数
- if (is_negative) {
- integer = -integer;
- }
- int i = 0;
- for (; integer != 0;i++) {
- buf[7 - i] = digit_tab[integer % 10];
- //处理小数点
- if (size != 0 && i == size) {
- buf[7 - i] = DP_WRAP(buf[7 - i]);
- }
- integer /= 10;
- }
- //如果是负数在buf最前面加上负号
- if (is_negative) {
- buf[7 - i] = MINUS;
- }
- hc595_refresh_buf(dev, buf);
- }
复制代码
main代码中调用
- #include "board.h"
- #include "log.h"
- #include <stdio.h>
- #include "bflb_gpio.h"
- #include "bflb_mtimer.h"
- #include "bflb_timer.h"
- #include "74hc595.h"
- #define PIN_CLK GPIO_PIN_13
- #define PIN_SER GPIO_PIN_10
- #define PIN_ST GPIO_PIN_19
- struct bflb_device_s* gpio;
- struct bflb_device_s* timer0;
- void delay_us(uint32_t us)
- {
- bflb_mtimer_delay_us(us);
- }
- int gpio_set_data(uint8_t pin, uint8_t data)
- {
- if (data) {
- bflb_gpio_set(gpio, pin);
- }
- else {
- bflb_gpio_reset(gpio, pin);
- }
- return 0;
- }
- int gpio_init(uint8_t pin, uint32_t cfgset)
- {
- bflb_gpio_init(gpio, pin, cfgset);
- }
- void timer0_isr(int argc, void* argv)
- {
- bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);
- if (status) {
- bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
- digit_dev_t* dev = (digit_dev_t*)argv;
- hc595_desplay(dev, -1234.567);
- }
- }
- int main(void)
- {
- board_init();
- printf("74hc595 start!\n");
- gpio = bflb_device_get_by_name("gpio");
- digit_dev_t dev = {
- .pin_clk = PIN_CLK,
- .pin_ser = PIN_SER,
- .pin_st = PIN_ST,
- .delay_fun = delay_us,
- .set_dev_data = gpio_set_data,
- .gpio_init = gpio_init,
- };
- hc595_init(&dev);
-
- struct bflb_timer_config_s cfg;
- cfg.counter_mode = TIMER_COUNTER_MODE_PROLOAD;
- cfg.clock_source = TIMER_CLKSRC_XTAL;
- cfg.clock_div = 39;
- cfg.trigger_comp_id = TIMER_COMP_ID_0;
- cfg.comp0_val = 1000;
- cfg.preload_val = 0;
- timer0 = bflb_device_get_by_name("timer0");
- bflb_irq_attach(timer0->irq_num, timer0_isr, &dev);
- bflb_irq_enable(timer0->irq_num);
- bflb_timer_init(timer0, &cfg);
- bflb_timer_start(timer0);
- while (1) {
- bflb_mtimer_delay_ms(1000);
- }
- return 0;
- }
复制代码
点亮效果
|
|