网上没看到一眼能看懂的把所有东西一次讲清的文章,总结了一下,做个笔记留着。

1.引脚定义

74CH165一共有四根线需要接,三根控制,一根输出。
CE:Chip enable,低电平有效,三线控制可以直接接地。
PL:Parallel load,拉低会把并口8位采集到寄存器,并把最高位通过Q7输出,把PL拉高后,CLK给上升沿Q7会一个个移位输出次高位。
CLK/CP:Clock,PL拉高时,上升沿会触发移位寄存器移位。
Q7/DATA:串行数据输出脚。

2.驱动方法

四线驱动方法一般是:
把PL拉低,再把PL拉高,获取输入,拉低CE,此时输出最高位,采集最高位,然后CLK的上升沿会触发移位,逐个采集信号。
三线驱动方法:
CE默认拉低,所以PL拉低后,Q7输出最高位,然后PL拉高,CLK上升沿触发移位,需要在PL拉低和第一个上升沿之间采集最高位,每个上升沿之后采集后面的数据。
三线驱动,两个芯片级联,触发第一个芯片的最高位,采集波形如下:
三线最高位低电平触发图示
74CH165可以连接很多个,只需要把下一个的Q7连接到到前一个DS上,PL,CLK接一起,数据是依次输出第一个,第二个…

3.示例程序

写了一个基于HAL库的兼容三线四线容易移植的程序,兼容多个芯片,容易移植。
头文件如下:

/*移植需要改的地方只有GPIO,CHIP_NUM/即有几片级联,如有CE引脚,则取消CE引脚注释*/
/*使用示例
*uint8_t data[CHIP_NUM]={0};
*Init_74HC165();
*Data_capture(data);
*/


#ifndef _74HC165_driver
#define _74HC165_driver

#include "sys.h"

#define PIN_CP_Pin GPIO_PIN_14
#define PIN_CP_GPIO_Port GPIOC
#define PIN_PL_Pin GPIO_PIN_15
#define PIN_PL_GPIO_Port GPIOC
#define PIN_Q7_Pin GPIO_PIN_13
#define PIN_Q7_GPIO_Port GPIOC

//#define CE_ENABLE//定义是否有CE脚
#ifdef CE_ENABLE
#define PIN_CE_Pin GPIO_PIN_12
#define PIN_CE_GPIO_Port GPIOC
#endif

#define __HAL_RCC_165PORT_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()

#define CHIP_NUM 2

void Init_74HC165(void);
void Data_capture(uint8_t *data);

#endif


驱动函数文件如下:

#include "./74HC165/74HC165.h"

void Init_74HC165(){
    __HAL_RCC_165PORT_CLK_ENABLE();
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /*Configure ITPUT PIN*/
    GPIO_InitStruct.Pin = PIN_Q7_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(PIN_Q7_GPIO_Port, &GPIO_InitStruct);
    
    /*Configure OUTPUT PIN */
    GPIO_InitStruct.Pin = PIN_CP_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(PIN_CP_GPIO_Port, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin = PIN_PL_Pin;
    HAL_GPIO_Init(PIN_PL_GPIO_Port, &GPIO_InitStruct);
    
    HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(PIN_PL_GPIO_Port, PIN_PL_Pin, GPIO_PIN_SET);
    #ifdef CE_ENABLE
    GPIO_InitStruct.Pin = PIN_CE_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(PIN_CE_GPIO_Port, &GPIO_InitStruct);
    HAL_GPIO_WritePin(PIN_CE_GPIO_Port, PIN_CE_Pin, GPIO_PIN_SET);
    #endif
}

void Data_capture(uint8_t *data){
#ifdef CE_ENABLE
    HAL_GPIO_WritePin(PIN_CE_GPIO_Port, PIN_CE_Pin, GPIO_PIN_RESET);
#endif
    int i,j = 0;
    for(i=0;i<CHIP_NUM;i++)
    {
        data[i]=0xFF;
    }
    HAL_GPIO_WritePin(PIN_PL_GPIO_Port, PIN_PL_Pin, GPIO_PIN_RESET);
    if(HAL_GPIO_ReadPin(PIN_Q7_GPIO_Port,PIN_Q7_Pin)==GPIO_PIN_RESET)
    {
         data[0] &= (~(1<<7));
    }
    HAL_GPIO_WritePin(PIN_PL_GPIO_Port, PIN_PL_Pin, GPIO_PIN_SET);
    for(j=6;j>=0;j--)
    {
        HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_SET);
        if(HAL_GPIO_ReadPin(PIN_Q7_GPIO_Port,PIN_Q7_Pin)==GPIO_PIN_RESET)
            {
                 data[0] &= (~(1<<j));
            }
        HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_RESET);
    }

    for(i=1;i<CHIP_NUM;i++)
    {
        for(j=7;j>=0;j--){
            HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_SET);
            if(HAL_GPIO_ReadPin(PIN_Q7_GPIO_Port,PIN_Q7_Pin)==GPIO_PIN_RESET)
                {
                     data[i] &= (~(1<<j));
                }
            HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_RESET);
            }
    }

#ifdef CE_ENABLE
    HAL_GPIO_WritePin(PIN_CE_GPIO_Port, PIN_CE_Pin, GPIO_PIN_SET);
#endif

}

4.使用方法
最简单的是注释里面的

uint8_t data[CHIP_NUM]={0};
Init_74HC165();
Data_capture(data);

如果需要把状态分开放可以使用联合体:

typedef union {
    uint8_t raw_data;
    struct {
        uint8_t b0 : 1;
        uint8_t b1 : 1;
        uint8_t b2 : 1;
        uint8_t b3 : 1;
        uint8_t b4 : 1;
        uint8_t b5 : 1;
        uint8_t b6 : 1;
        uint8_t b7 : 1;
    } bits;
} BoolPack;//b0是最低位,b7是最高位,不同MCU情况可能不同,自行判断
BoolPack sample=data[0];
//此时可以使用sample.bits.b0调用最低位,需要的话自行定义数组循环赋值

5.datasheet
datasheet关键参数如下,我用的F103,输出速度为HIGH,打不到频率和时延的极限,使用其他芯片或者级联走线过长产生过大寄生电容需要自行斟酌添加时延。
74HC165 datasheet

6.写下一点点时间优化操作。

6.1 for循环代码丑可能更好用,用memset代码好看可能占空间大且效率低,因为需要调用库函数,库是为了方便调用,使用方便,两种都可以用,只用一次的话可以写丑一点
目前程序使用的是for循环赋值

for(i=0;i<CHIP_NUM;i++)
{
    data[i]=0xFF;
}

编译大小如下
使用for循环赋值数组占用资源
运行时间如下,为1.5us
for循环初始时间for循环结束时间
当然可以使用memset,需要添加头文件string.h

#include <string.h>
memset(data,0xFF,CHIP_NUM);

编译大小如下,Code增加78,RO-data增加2
在这里插入图片描述
运行时间如下,为4us
memset开始时间memset结束时间

6.2 编程与电路实际情况结合,用尽量少的执行步数来完成任务,再看下寄存器编程有多少优势
参照网友写的程序是默认数组全0,若检测到1,则执行或与操作置1,程序大概如下:

for(i=0;i<CHIP_NUM;i++)
{
    data[i]=0x00;
}

for(j=7;j>=0;j--)
{
    HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_SET);
    if(HAL_GPIO_ReadPin(PIN_Q7_GPIO_Port,PIN_Q7_Pin)==GPIO_PIN_SET)
        {
            data[i] |= (1<<j);
         }
    HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_RESET);
}

问题是我的电路默认为1,所以从头到尾都需要执行if里面的操作,看下运行时间为63.3us
在这里插入图片描述 在这里插入图片描述

修改一下,默认赋值为1,检测到按键触发才赋值为0,按键不触发时只执行轮询检测,不执行赋值

for(i=0;i<CHIP_NUM;i++)
{
    data[i]=0xFF;
}

for(j=7;j>=0;j--)
{
     HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_SET);
     if(HAL_GPIO_ReadPin(PIN_Q7_GPIO_Port,PIN_Q7_Pin)==GPIO_PIN_RESET)
            {
                data[i] &= (~(1<<j));
            }
     HAL_GPIO_WritePin(PIN_CP_GPIO_Port, PIN_CP_Pin, GPIO_PIN_RESET);
}

在这里插入图片描述在这里插入图片描述

运行时间为58.1us,好像优化了,又好像没优化

一个是置0,然后或置1,一个是置1,通过与置0,好像没啥用。

掏出唯一真神寄存器编程来试试看

for(j=7;j>=0;j--){
	GPIOA->BSRR = PIN_CP_Pin;
	if(PIN_Q7_GPIO_Port->IDR & PIN_Q7_Pin)
	    {
	         data[i] &= (~(1<<j));
	    }
	 GPIOA->BRR = PIN_CP_Pin;
}

时间降到23.1us,结论是真时间敏感的话建议寄存器编程(手动狗头)
在这里插入图片描述在这里插入图片描述

有问题随时说我随时改。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐