#技术资源# #申请原创#
之前有一个产品,需要通过MCU读取8位拨码开关的数据,但是吧,由于MCU资源有限, 后来硬件上增加了一个并转串芯片U74HC165G,即通过MCU+U74HC165G+拨码开关的方式来实现,拓扑结构差不多就是下面这样的: 显而易见,GPIO资源占用情况由原来的8个变成现在的3个,这样,库里常用的MCU就可以用上了。 接下来我们就通过芯片手册解读,驱动代码和时序波形图来解读一下U74HC165G芯片。 一、芯片手册解读 U74HC165G是一款8位的并行输入转串行输出的移位寄存器芯片,其核心功能当然是并转串,即可以同时读取8个并行输入引脚(A~H)上的数据,然后通过串行总线的方式将输出数据到MCU端,就非常适合在需要节约MCU的GPIO引脚资源的场景下应用,比如你需要读取多个按键状态或者多个IO信号(类似于我们这种应用),只需要通过3个GPIO引脚与芯片连接,就可以完成8个输入信号的读取,可以节约5个GPIO引脚资源。另外,U74HC165G还支持菊花链级联功能,多于超过8个输入信号的应用,也是非常友好的。
U74HC165G的引脚图如下所示: 各个引脚的主要功能描述如下: SH/LD:移位装载引脚 为低电平时,芯片处于装载模式,A~H引脚上的并行数据被同步锁存到内部寄存器; 为高电平时,芯片处于移位模式,在时钟上升沿的时候数据从内部寄存器中逐位移出。 CLK:时钟输入引脚 在移位模式时,每个CLK上升沿触发一次内部数据移位; 在装载模式时,CLK则无效。 CLK/INH:时钟禁止引脚 当此引脚为高电平时,会禁止时钟CLK,使其无效,因此在没有特殊应用的时候,通常为了方便,可以将其直接接地,即始终允许时钟CLK工作;如果需要用此引脚,可以将其连接到MCU的GPIO引脚,并通过配置GPIO引脚为输出模式来控制此引脚。 SER:串行数据输入引脚 用于将多个U74HC165G芯片级联以扩展输入位数,当级联时,前一级的QH'引脚应连接到下一级的SER引脚;如果单独使用,此引脚通常悬空或接固定电平接口。 QH:串行数据输出引脚 这是主输出引脚,在时钟CLK上升沿的时候,当前寄存器最高位的值会在此引脚输出,遵循MSB在前原则,即第一位输出的数据是H引脚锁存的值,最后一位输出的数据是A引脚锁存的值。 QH’:互补串行数据输出引脚 是QH的反相信号(即QH为高,QH'为低),主要用途是用于级联到下一级芯片的SER输入因为在移位时,QH’提供的是经过一个时钟延迟后的与下一级时序匹配的数据。 A~H:并行数据输入引脚 VCC:电源引脚
供电范围支持2~6V,一般都用3.3V或者5V吧: GND:接地引脚。
通过下面这个功能表也可以看出其工作逻辑: 上面说的一些引脚的功能,也是会体现在时序波形上面的,如下图所示,软件代码上也是需要严格按照这个来编码的,也可以看出,QH上的信号是按照MSB优先的,即以从H到A的顺序发送的: 而对于各个信号的建立时间(setup time)和保持时间(hold time)这样的时间参数,先解释一下: 建立时间(setup time): 指时钟上升沿触发前数据需保持稳定的最短时间,若持续时间不足则当前周期无法采样数据,需延迟至下一周期; 保持时间(hold time): 指时钟上升沿触发后数据需继续维持稳定的最短时间;
而一般涉及到这种时序波形的芯片都会有的,我们也来看下,基本上都是纳秒级别的,对于我们这样的us级别以上的时序,建立时间和保持时间基本上不会有什么问题: 然后对于芯片的最大时钟频率这个参数,我们也是需要知道的,差不多是36MHz,当然,这个时钟频率的设置需要结合实际的硬件设计进行,可能还要考虑到抗干扰性能: 芯片手册的解读就到这了,接下来我们看看驱动代码吧。 二、驱动代码解读 其实芯片的驱动代码的编写技巧,主要还是得先看一下芯片数据手册,特别是引脚功能,通信方式和时序波形说明这些内容,有个基本了解以后,我们就可以开始编写驱动代码了。
虽然这个芯片的驱动代码比较简单,但是为了代码可维护性,我还是通过源文件+头文件的方式来管理U74HC165G的驱动代码,其中头文件命名为u74hc165.h,主要代码如下: - #ifndef __U74HC165_H__
- #define __U74HC165_H__
- //delay config
- #define HC595_LD_DELAY 3
- #define HC595_CLK_DELAY 30
- #define HC165_GPIO GPIOA
- #define HC165_LD_GPIO GPIOA
- #define HC165_CLK_GPIO GPIOA
- #define HC165_INH_GPIO GPIOA
- #define HC165_QH_GPIO GPIOA
- #define HC165_LD_PIN GPIO_PINS_6
- #define HC165_CLK_PIN GPIO_PINS_5
- #define HC165_INH_PIN GPIO_PINS_7
- #define HC165_QH_PIN GPIO_PINS_3
- #define HC165_LD_HIGH() gpio_bits_write(HC165_LD_GPIO, HC165_LD_PIN, TRUE)
- #define HC165_LD_LOW() gpio_bits_write(HC165_LD_GPIO, HC165_LD_PIN, FALSE)
- #define HC165_CLK_HIGH() gpio_bits_write(HC165_CLK_GPIO, HC165_CLK_PIN, TRUE)
- #define HC165_CLK_LOW() gpio_bits_write(HC165_CLK_GPIO, HC165_CLK_PIN, FALSE)
- #define HC165_INH_HIGH() gpio_bits_write(HC165_INH_GPIO, HC165_INH_PIN, TRUE)
- #define HC165_INH_LOW() gpio_bits_write(HC165_INH_GPIO, HC165_INH_PIN, FALSE)
- #define HC165_READ_QH_VALUE() gpio_input_data_bit_read(HC165_QH_GPIO, HC165_QH_PIN)
- extern void hc165_init(void);
- extern u8 hc165_read_data(void);
- #endif /* __U74HC165_H__ */
源文件命名为u74hc165.c,主要代码如下: - #include "u74hc165.h"
- static void hc165_delay(uint16_t cnt)
- {
- uint16_t i;
- for(i = 0; i < cnt; i++)
- {
- ;
- }
- }
- /*u74hc165 chip init config*/
- void hc165_init(void)
- {
- HC165_LD_HIGH();
- HC165_CLK_LOW();
- HC165_INH_LOW(); //low is clk active
- }
- /*u74hc165 data read*/
- u8 hc165_read_data(void)
- {
- u8 i = 0;
- u8 temp = 0;
- HC165_LD_LOW();
- hc165_delay(HC595_LD_DELAY);
- HC165_LD_HIGH();
- for(i=0; i<8; i++)
- {
- temp = temp << 1;
- HC165_CLK_LOW();
- hc165_delay(HC595_CLK_DELAY);
- HC165_CLK_HIGH();
- if(HC165_READ_QH_VALUE())
- {
- temp |= 0x01;
- }
- hc165_delay(HC595_CLK_DELAY);
- }
- return temp;
- }
主要代码解读一下如下: HC595_LD_DELAY是移位装载引脚信号的低电平保持时间,当然这个是软件非精确计数; HC595_CLK_DELAY是时钟输入信号的高低电平保持时间,这个即决定了时钟频率是多少,可以通信自行修改和实际测试来满足需要; 相关GPIO口的设置与硬件绑定; hc165_init是芯片初始化接口,主要是配置芯片上电时的相关主要引脚的电平状态,可以理解为空闲配置吧; hc165_read_data是数据读取主接口,循环执行,在设置SH/LD为上升沿的时候,循环操作8次:配置CLK为上升沿,然后读取QH引脚的数据,并通过移位和位或运算将结果作为函数返回值,即返回值即为我们需要的值; hc165_delay是非精确的软件延时接口,其入参就是HC595_LD_DELAY或者HC595_CLK_DELAY,这个入参对应的实际时间,可以根据所用MCU的主频来计算,一般的公式是下面这样的,不过计算出来也是有些偏差的: 实际时间 = 4*软件延时计数值/MCU主频 所以有条件的话,更建议直接用示波器或者逻辑分析仪来确认实际时间和时钟频率。 芯片驱动代码的解读就到这了,最后我们来看看实际应用我抓的波形吧。 三、实际波形解读 不管怎么玩,最后最重要的就是成功应用,没有bug吧。
将hc165_init接口放在上电初始化代码的位置: - //u74hc165 init
- hc165_init();
将hc165_read_data接口放在需要读取输入信号的位置: - uint8_t get_dipswitch_value(void)
- {
- return (hc165_read_data());
- }
- addr_data = get_dipswitch_value(); //需要的数据
设置好拨码开关的位置后,使用示波器抓取到的时序波形如下图所示: 从波形中可以看到,SH/LD有一段上升沿的波形后,开始一边操作CLK,一边读取数据,因为实际是在CLK为高电平时候的读取的,所以此时如果单从波形上来解析数据的话,读取到的数据应该是0111 1010,即0x7A。 另外,从CLK波形上来看,当前配置对应的实际时钟频率大概是641KHz,并且波形看上去毛刺少,上升沿和下降沿也比较好,这个频率匹配的当前硬件设计应该没什么问题。
以上通过对U74HC165G芯片的手册解读、驱动代码解读和实际波形解读等三个方面与大家分享了一款并转串芯片U74HC165G,在合适的场景下可以考虑试试效果~~
|