一.SPI总线
SPI是全双工三/四总线制串行总线,支持多主机多从机模式,常用单主机多从机模式
数据传输可以先传输高位也可以先传输低位。
四线(单主机多从机):
MOSI:主机输出,从机输入
MISO:主机输入,从机输出
SCLK: 时钟信号
/SS:片选线,从器件使能信号
三线:(单主机单从机)
MOSI:主机输出,从机输入
MISO:主机输入,从机输出
SCLK: 时钟信号
IIC和SPI的异同点
相同点:1.都采用同步串行总线的通信方式
2.都采用主从模式
不同点:1.IIC总线采用半双工通信方式,SPI采用全双工通信方式
2.IIC有应答机制,SPI没有应答机制
3.IIC通过总线寻找从机,SPI通过片选线寻找从机
4.IICSCL高电平读数据,低电平写数据
SPI边缘采样,边缘触发
二.通信协议
通信协议
边缘触发,边缘采样
时钟极性 0:SCL空闲状态为低电平 1:SCL空闲状态为高电平
时钟相位 0:MOSI MISO 在奇数边采样 1:MOSI MISO 在偶数边采样
起始信号: NSS信号线由高变低,是SPI通讯的起始信号
结束信号:NSS信号由低变高,是SPI通讯的停止信号
数据传输:SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。 MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行 的。SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制
三.M74HC595芯片
工作原理:
1.分析引脚作用
2.分析真值表
四.代码
spi.c
#include "spi.h"
/* SPI4_NSS ----> PE11
* SPI4_SCK ----> PE12
* SPI4_MOSI ----> PE14
* SPI4_MISO ----> PE13
* */
/* 数码管的编码, 先发送低位,在发送高位
* A B C D E F G DP
* 1 1 1 1 1 1 0 0 0xFC 0
* 0 1 1 0 0 0 0 0 0x60 1
* 1 1 0 1 1 0 1 0 0xDA 2
* 1 1 1 1 0 0 1 0 0xF2 3
* 0 1 1 0 0 1 1 0 0x66 4
* 1 0 1 1 0 1 1 0 0xB6 5
* 1 0 1 1 1 1 1 0 0xBE 6
* 1 1 1 0 0 0 0 0 0xE0 7
* 1 1 1 1 1 1 1 0 0xFE 8
* 1 1 1 1 0 1 1 0 0xF6 9
* */
void delay_us1(unsigned int us)
{
int i,j;
for(i = 0; i < us;i++)
for (j = 0; j < 1;j++);
}
void SPI_init(void)
{
RCC->MP_AHB4ENSETR |= (0x1 << 4);
// MOSI PE14
GPIOE->MODER &= (~(0x3 << 28));
GPIOE->MODER |= (0x1 << 28);
GPIOE->OTYPER &= (~(0x1 << 14));
GPIOE->OSPEEDR &= (~(0x3 << 28));
GPIOE->PUPDR &= (~(0x3 << 28));
// MISO PE13
GPIOE->MODER &= (~(0x3 << 26));
GPIOE->OSPEEDR &= (~(0x3 << 26));
GPIOE->PUPDR &= (~(0x3 << 26));
// SCK PE12
GPIOE->MODER &= (~(0x3 << 24));
GPIOE->MODER |= (0x1 << 24);
GPIOE->OTYPER &= (~(0x1 << 12));
GPIOE->OSPEEDR &= (~(0x3 << 24));
GPIOE->PUPDR &= (~(0x3 << 24));
// NSS PE11
GPIOE->MODER &= (~(0x3 << 22));
GPIOE->MODER |= (0x1 << 22);
GPIOE->OTYPER &= (~(0x1 << 11));
GPIOE->OSPEEDR &= (~(0x3 << 22));
GPIOE->PUPDR &= (~(0x3 << 22));
NSS_OUTPUT_L(); // 595芯片的锁存引脚拉低
SCK_OUTPUT_L(); // SPI的时钟线拉低
}
void SPI_write(unsigned char dat)
{
//for循环
//if判断条件,先发送低位
//if成立MOSI线写高
//else MOSI线写低
//移位寄存器时钟从低到高的变化
for( unsigned char i=0;i<8;i++){
delay_us1(100);
if(dat & 0x01){ //从低位到高位发送,从低位开始判断
MOSI_OUTPUT_H(); 1---写高电平
}else{
MOSI_OUTPUT_L(); 0----写低电平
}
SCK_OUTPUT_L(); //根据真值表,移位寄存器存储L,H,移位时钟寄存器需要由低电平到高电平
delay_us1(100);
SCK_OUTPUT_H();
delay_us1(100);
dat>>=1;
}
}
main.c
#include "spi.h"
extern void printf(const char *fmt, ...);
void delay_ms(int ms)
{
int i,j;
for(i = 0; i < ms;i++)
for (j = 0; j < 1800; j++);
}
int num[10] = {0xFC,0x60,0xDA,0xF2,0x66,0xB6,0xBE,0xE0,0xFE,0xF6};
int main(void){
//for循环条件
//内容:先发送位,在发送段
//0xF0 num[i]
SPI_init();
while(1){
for(int i=0;i<4;i++){
//SPI_write(0xF0);
SPI_write(0x80>>i); //发送位
SPI_write(num[i+1]); //发送段
NSS_OUTPUT_L(); //根据真值表,移位寄存器中数据锁存到锁存寄存器中,锁存时钟寄存器需要由低电平到高电平
delay_ms(100);
NSS_OUTPUT_H();
delay_ms(100);
}
}
return 0;
}