0%

利用STM32F030软件SPI和硬件SPI控制74HC595

环境

硬件:NUCLEO-F030R8。某宝购买的74HC595模组,包括8路继电器。

开发环境:Win10+IAR EWARM 8.22.1

74HC595原理

img

74HC595引脚图

74HC595和74hc164一样是在单片机系统中常用的芯片之一他的作用就是把串行的信号转为并行的信号,常用在各种数码管以及点阵屏的驱动芯片, 使用74HC595可以节约单片机mcu的io口资源,用3个io就可以控制8个数码管的引脚,他还具有一定的驱动能力,可以免掉三极管等放大电路,所以这块芯片是驱动数码管的神器.应用非常广泛。

img

74HC595管脚功能

74HC595的数据端

QA—QH: 八位并行输出端,可以直接控制数码管的8个段。

QH’: 级联输出端。我将它接下一个595的SI端。

SI: 串行数据输入端。

74hc595的控制端

/SCLR(10脚): 低电平时将移位寄存器的数据清零。通常我将它接Vcc。

SCK(11脚):上升沿时数据寄存器的数据移位。QA—>QB—>QC—>…—>QH;下降沿移位寄存器数据不变。(脉冲宽度:5V时,大于几十纳秒就行了。我通常都选微秒级)

控制移位寄存器

SCK 上升沿 数据 移位 SCK 下降沿 数据 保持

RCK(12脚):上升沿时移位寄存器的数据进入存储寄存器,下降沿时存储寄存器数据不变。通常我将RCK置为低电平,当移位结束后,在RCK端产生一个正脉冲(5V时,大于几十纳秒就行了。我通常都选微秒级),更新显示数据。

控制存储寄存器

RCK 上升沿 移位寄存器 的 数据进入 存储寄存器 RCK 下降沿 存储寄存器数据不变

/G(13脚): 高电平时禁止输出(高阻态)。如果单片机的引脚不紧张,用一个引脚控制它,可以方便地产生闪烁和熄灭效果。比通过数据端移位控制要省时省力。

img

74HC595真值表

img

74hc595最高电压和最低电压

img

74HC595时序图

74HC595是具有8位移位寄存器和一个存储器,三态输出功能。 移位寄存器和存储器是分别的时钟。数据在SHcp(见时序图)的上升沿输入,在STcp(见时序图)的上升沿进入的存储寄存器中去。如果两个时钟连在一起,则移位

寄存器总是比存储寄存器早一个脉冲。移位寄存器有一个串行移位输入(Ds),和一个串行输出

(Q7’),和一个异步的低电平复位,存储寄存器有一个并行8位的,具备三态的总线输出,当使

能 OE时(为低电平),存储寄存器的数据输出到总线。

img

74HC595逻辑图

原理图

只是一个示意性的原理图,我自己不想画了,虽然图片的IO脚和主芯片不同。

img

图1.原理图

我用的芯片型号为STM32F030R8。对应的实际IO接线如下:

1
2
3
4
5
              595侧                                 芯片侧
11 SCK 数据输入时钟线 SHCP Clock SPI2_SCK PB13
12 RCK 输出存储锁存时钟线 STCP Latch SPI2_NSS PB12
13 OE 输出使能 接地 OE GND
14 SI 数据线 DS1 DATA SPI2_MOSI PB15

STM32CubmeMX 配置

时钟配置

由于板子没有焊接晶振。配置方法如下:

img

图2.RCC配置图

配置 RCC 只是为了调试测试主频是否正确。时钟配置如下图:

img

图3.时钟配置图

硬件SPI配置

STM32F030有两路硬件SPI,都可以使用。这里我配置了SPI2。详细配置如下图:

img

图4.硬件SPI配置图

注意:由于 595 只收不发,MISO 引脚是没有使用的。SPI 的模式可以配置为 Transmit Only Master,这样可以节约一个 IO。

代码1

使用硬件 SPI 控制 74HC595 非常简单,上述配置完成后,只需要点击生成代码即可。然后在 main() 函数中添加 SPI 发送指令即可。如下:

1
2
3
4
5
6
7
8
/* USER CODE BEGIN 1 */
uint8_t cmd=0xAA;

/* USER CODE END 1 */
...
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_SPI_Transmit(&hspi2, &cmd, 1, 1000);

也就是向 74HC595 发送数据 0xAA,595 后级接继电器,就可以看到对应继电器的动作。

代码2

SPI:串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、 LCD 等设备与 MCU 间,要求通讯速率较高的场合。对于SPI来说,其使用主要有四根线:分别是CS、MOSI、MISO、CLK;其中片选线CS,一般用普通的GPIO口来代替。

其次,SPI是全双工通信线路,其发送的时候同时也在接收着。因此要注意发送的时候接收的数据是否是垃圾数据。

就我理解,SPI一般用于外围的部件中,如FLASH、ADC、LCD和MCU。这些部件一般都包含了SPI接口,方便和主机进行通信。但是也有用于普通的串行线,用于一般的串行传输中。如此处的和HC595通信中,基本上只使用了SPI的串行通信功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//SPI2初始化
void SPI2_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

//使能相应时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

/* Configure SPI2 pins: NSS, SCK, MISO and MOSI */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);

/*Configure PA.4(NSS)--------------------------------------------*/
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

/* SPI1 configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//SPI_CPOL_High=模式3,时钟空闲为高 //SPI_CPOL_Low=模式0,时钟空闲为低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//SPI_CPHA_2Edge;//SPI_CPHA_1Edge, SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//SPI_NSS_Soft;//SPI_NSS_Hard
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;//SPI_BaudRatePrescaler_2=18M;//SPI_BaudRatePrescaler_4=9MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//数据从高位开始发送
SPI_InitStructure.SPI_CRCPolynomial = 7;

SPI_Init(SPI2, &SPI_InitStructure);
/*Enable SPI1.NSS as a GPIO*/
//SPI_SSOutputCmd(SPI2, ENABLE);

/* Enable SPI2 */
SPI_Cmd(SPI2, ENABLE);
}

//SPI发送一个字符数据
uint8_t SPI_Send_Byte(uint8_t dat)
{
/* Loop while DR register in not emplty */
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);

/* Send byte through the SPI2 peripheral */
SPI_I2S_SendData(SPI2, dat);
/* Wait to receive a byte */
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI2);
}

主函数主要代码(注意输出锁存的数据要产生相关引脚的上升沿)

1
2
3
4
5
SPI_Send_Byte(val);

GPIO_ResetBits(SPI_CS_PORT,SPI_CS_PIN);
delay_ms(10);
GPIO_SetBits(SPI_CS_PORT,SPI_CS_PIN);

小结

个人以为,有硬件 SPI 支持,可以充分发挥硬件的强大作用。

软件SPI配置

软件 SPI 配置如下图:

img

图5.软件SPI配置图

代码

我们需要自己实现 595 发送代码。对应的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
软件SPI调试正常
SHCP -> PB13
STCP -> PB12
DS1 -> PB15
//Configure GPIO pins : PB12 PB13 PB14 PB15
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
*/
void HC595SendData(uint8_t SendVal)
{
uint8_t i;
for (i=0; i<8; i++) {
/*--step1、串行输入引脚,所谓串行就是使数据在一根信号线上按顺序一位一位地传输*/
if (((SendVal << i) & 0x01) != 0 )
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);

/*--step2、SHCP发生一次上升沿的时候,74HC595才会从DS引脚上取得当前的数据*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
}

/*--step3、当移位寄存器的8位数据全部传输完毕后,制造一次锁存器时钟引脚的上升沿(先拉低电平再拉高电平)*/
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
}

注意,代码是将 PB14 配置为输出的。这个没什么影响,因为没有使用到 PB14。正确应该将 PB14 配置为输入。我懒得重新生成代码了。

系统完整照片

用的是杜邦线连接,有点丑。测试系统将就用一下。

img

如上图所示,继电器对应的灯已经点亮。

————————————————

版权声明:本文为CSDN博主「努力的老周」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:利用STM32F030软件SPI和硬件SPI控制74HC595努力中的老周的专栏-CSDN博客硬件spi和软件spi