Part1 74HC595简介
74HC595是一个8位串行输入、并行输出的位移缓存器:并行输出为三态输出。在SCK 的上升沿,串行数据由SDL输入到内部的8位位移缓存器,并由Q7'输出,而并行输出则是在LCK的上升沿将在8位位移缓存器的数据存入到8位并行输出缓存器。当串行数据输入端OE的控制信号为低使能时,并行输出端的输出值等于并行输出缓存器所存储的值。
74HC595是具有三态输出功能(即具有高电平、低电平和高阻抗三种输出状态)的门电路。输出寄存器可以直接清除。具有100MHz的移位频率。
引脚介绍

-
Qx:并行输出引脚 -
9 脚 :串行数据出口引脚。当移位寄存器中的数据多于8bit时,会把已有的bit“挤出去”,就是从这里出去的。用于595的级联。 -
10脚:MR,低电平时,清空移位寄存器中已有的bit数据,一般不用,接 高电平即可。 -
11脚:SCK,移位寄存器时钟引脚,上升沿时,移位寄存器中的bit 数据整体后移,并接受新的bit(从SER输入)。 -
12脚:RCK,存储寄存器时钟输入引脚。上升沿时,数据从移位寄存器转存带存储寄存器。 -
13脚:OE, 输出使能控制脚,它是低电才使能输出,所以接GND -
14脚:DS(SER),串行数据输入引脚
原理介绍
此部分不复杂,介绍千篇一律,直接摘录了网上比较好的一个:
595的数据来源只有这一个口,一次只能输入一个位,那么连续输入8次,就可以积攒为一个字节了。假如,我们要将二进制数据0111 1111 输入到595的移位寄存器中,下面来上一张动态图,模拟了前2个位输入的情景。
0111 1111 这个数据完全输入后是这样的

那么数据是怎么一个一个的进入移位寄存器的呢,这里是由单片机时钟脉冲控制的,就好像是钟表一样,秒针隔一秒就走一下,74HC595数据的移动是通过MCU输出时钟脉冲信号,接收到信号之后,移动数据,腾出位置为接收下一个数据做准备,即通过MCU向11引脚发送脉冲信号,HC595接收到上升沿之后,移动数据。
数据接收完成之后,如何将移位寄存器的数据转移到存储寄存器,存储寄存器是直接和8个输出引脚相通的,将移位寄存器的数据转移到存储寄存器后,Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 就可以接受带到我们开始输入的一个字节的数据。所谓存储寄存器,就是数据可以存在这个寄存器中,并不会随着一次输出就消失,只要595不断电,也没有新的数据从移位寄存器中过来,数据就一直不变且有效。新的数据过来后,存储寄存器中的数据就会被覆盖更新。
数据从位移寄存器转移到存储寄存器,也是需要时钟脉冲驱动的,这就是12脚的作用。它也是上升沿有效。

Part2 代码实现
上面对原理进行了介绍,非常的简单,接下来,来看看代码如何实现。本次代码依然是基于falling-star board,小伙伴们可以自行在自己的板子上实现,跟着做,没问题的,同时呢,本次使用的是RT-Thread平台。
新建RT_Thread工程
文件->新建->RT_Thread项目 
cubemx配置
喜欢用cubemx的小伙伴,用了rt-thread并不是意味着要放弃cubemx了,rt-thread与cubemx的完美结合,让开发变得更加轻松,接下来,且看如何结合~
选中cubemx settings,double click即可打开cubemx,慢慢等待~ 
打开之后可能会发现封装不太对,一个方式是不用改,MCU资源和操作完全一样的,不会影响,另一个办法是改了他,据小伙伴说,cubemx文件可以用记事本打开,哦呵,真的是打开之后,一大堆配置项,长见识了,修改一下就可以了。


接下来就是cubemx配置的问题了~老生常谈了,各位看官麻烦移步:cubemx的正确打开方式 从最上面的原理分析,我们可以知道,需要控制的引脚有3个,DATA、SCLK、RCLK,上图,实际上小飞哥买的是4位的,8位的多了个级联,且来看看4位的如何驱动

硬件连接为
MCU | 数码管 |
---|
VCC | 3.3V/5V | RCLK | PA0 | SCLK | PA1 | DIO | PA4 | GND | GND |

串口1配置

时钟树图

配置完,关闭cubemx即可,可以看到,配置代码已经同步更新进来 串口配置代码 GPIO配置代码
Part3代码编写
先在工程中添加一个新文件夹,迎来存放HC595的驱动代码,建立.c.h文件

先来对用到的IO做个简单的宏定义
/*
?*?Copyright?(c)?2006-2021,?RT-Thread?Development?Team
?*
?*?SPDX-License-Identifier:?Apache-2.0
?*
?*?Change?Logs:
?*?Date???????????Author???????Notes
?*?2021-07-02?????Administrator???????the?first?version
?*/
#ifndef?_BSP_HC595_DRV_H_
#define?_BSP_HC595_DRV_H_
#include<rtthread.h>
#define?HC595_RCLK??GET_PIN(A,0)
#define?HC595_SCLK??GET_PIN(A,1)
#define?HC595_DIO???GET_PIN(A,4)
#define?RT_HC595_RCLK_HIGH(x)??x?rt_pin_write(HC595_RCLK,?PIN_HIGH):rt_pin_write(HC595_RCLK,?PIN_LOW)
#define?RT_HC595_SCLK(x)???????x?rt_pin_write(HC595_SCLK,?PIN_HIGH):rt_pin_write(HC595_SCLK,?PIN_LOW)
#define?RT_HC595_DIO(x)????????x?rt_pin_write(HC595_DIO,?PIN_HIGH):rt_pin_write(HC595_DIO,?PIN_LOW)
void?rt_HC595_PIN_Init(GPIO_TypeDef?*rclk_gpiox,?uint16_t?rclk_gpio_pin,GPIO_TypeDef?*sclk_gpiox,?uint16_t?sclk_gpio_pin,GPIO_TypeDef?*dio_gpiox,?uint16_t?dio_gpio_pin);
void?rt_Hc595_Display(rt_uint16_t?data);
#endif?/*?BSP_HC595_DRV_BSP_HC595_DRV_H_?*/
GPIO初始化,为了方便更改IO,对GPIO分组及GPIO_PIN作为参数传入初始化
void?rt_HC595_PIN_Init(GPIO_TypeDef?*rclk_gpiox,?uint16_t?rclk_gpio_pin,GPIO_TypeDef?*sclk_gpiox,?uint16_t?sclk_gpio_pin,GPIO_TypeDef?*dio_gpiox,?uint16_t?dio_gpio_pin)
{
????rt_err_t?result;
???//初始化HC595用到的GPIO
????GPIO_InitTypeDef?GPIO_Initure;
????__HAL_RCC_GPIOA_CLK_ENABLE();
????GPIO_Initure.Pin?=?rclk_gpio_pin;
????GPIO_Initure.Mode?=?GPIO_MODE_OUTPUT_PP;
????GPIO_Initure.Pull?=?GPIO_PULLDOWN;
????GPIO_Initure.Speed?=?GPIO_SPEED_FREQ_HIGH;
????HAL_GPIO_Init(rclk_gpiox,?&GPIO_Initure);
????HAL_GPIO_WritePin(rclk_gpiox,?rclk_gpio_pin,?GPIO_PIN_RESET);
????GPIO_Initure.Pin?=?sclk_gpio_pin;
????GPIO_Initure.Mode?=?GPIO_MODE_OUTPUT_PP;
????GPIO_Initure.Pull?=?GPIO_PULLDOWN;
????GPIO_Initure.Speed?=?GPIO_SPEED_FREQ_HIGH;
????HAL_GPIO_Init(sclk_gpiox,?&GPIO_Initure);
????HAL_GPIO_WritePin(sclk_gpiox,?sclk_gpio_pin,?GPIO_PIN_RESET);
????GPIO_Initure.Pin?=?dio_gpio_pin;
????GPIO_Initure.Mode?=?GPIO_MODE_OUTPUT_PP;
????GPIO_Initure.Pull?=?GPIO_PULLDOWN;
????GPIO_Initure.Speed?=?GPIO_SPEED_FREQ_HIGH;
????HAL_GPIO_Init(dio_gpiox,?&GPIO_Initure);
????HAL_GPIO_WritePin(dio_gpiox,?dio_gpio_pin,?GPIO_PIN_RESET);
????return?result;
}
数码管编码
unsigned?char?LED_0F[]?=
????{?//?0???1????2????3??????4????5??????6????7??????8??9????A????b????C????d????E????F????-
????????0xC0,?0xF9,?0xA4,?0xB0,?0x99,?0x92,?0x82,?0xF8,?0x80,?0x90,?0x8C,?0xBF,?0xC6,?0xA1,?0x86,?0xFF,?0xbf};
unsigned?char?LED[8]?=?{1,?2,?3,?4,?5,?6,?7,?8};?//用于LED的8位显示缓存
单字节数据写入函数
void?rt_HC595_DataWrite(rt_uint8_t?data)
{
????rt_uint8_t?i;
????for?(i?=?8;?i?>=?1;?i--)
????{
????????if?(data?&?0x80)
????????????RT_HC595_DIO(1);
????????else
????????????RT_HC595_DIO(0);
????????data?<<=?1;
????????RT_HC595_SCLK(0);
????????display(50);
????????RT_HC595_SCLK(1);
????}
}
数码管显示函数,这里封装的比较简单,数据输入,显示不同位,共4位
void?rt_Hc595_Display(rt_uint16_t?data)
{
????unsigned?char?*led_table;?//?查表指针
????unsigned?char?i,j=0;
????rt_uint8_t?displaydata[4]?=?{0};
????displaydata[0]?=?data/1000;
????displaydata[1]?=?data/100%10;
????displaydata[2]?=?data/10%10;
????displaydata[3]?=?data%10;
????for(j=0;j<4;j++)
????{
????????led_table?=?LED_0F?+?displaydata[j];
????????i?=?*led_table;
????????//控制小数点显示
????????if(0x08>>j?==?2)
????????{
????????????i&=0x7f;
????????}
????????rt_HC595_DataWrite(i);
????????rt_HC595_DataWrite(0x08>>j);
????????RT_HC595_RCLK_HIGH(0);
????????RT_HC595_RCLK_HIGH(1);
????}
}
Part4效果演示

Part5资料获取
关注公众号,后台回复“74HC595”,节课获取本次实验源码,欢迎添加小飞哥微信,进群交流~

|