ARM - SPI实验

一、SPI概念

1.SPI总线是Motorola首先提出的全双工三线/四线同步串行总线

2.采用主从模式(MasterSlave)架构;支持多slave模式应用

3.一般仅支持单Master,多从机

4.时钟由Master控制,在时钟移位脉冲下,数据按位传输,高位在前,低位在后(MSB first)

5.SPI接口有2根单向数据线,为全双工通信,目前应用中的数据速率可达几Mbps的水平

6.SPI总线被广泛地使用在FLASH、ADC、LCD等设备与MCU间,要求通讯速率较高的场合

7.本次实验:SOC<----SPI---->数码管

二、SPI硬件连接

1.SPI接口共有4根信号线,

    分别是:设备选择线(片选线):NSS nsc

            时钟线:SCK CLK

            串行输出数据线:MOSI

            串行输入数据线:MISO

            M:master主机   O:output输出  S:slave从机  I:input输入

2.作用:

• (1)MOSI:主器件数据输出,从器件数据输入

• (2)MISO:主器件数据输入,从器件数据输出

• (3)SCLK :时钟信号,由主器件产生

• (4)/SS:从器件使能信号,由主器件控制(片选)        

2.1 四线制

2.2 三线制

 

三、SPI总线通信协议

四、SPI总线通信模式

 

五、IIC总线和SPI总线对比总结

IIC总线和SPI总线对比:

1.相同点

    1>采用串行、同步总线

    2>都采用的是TTL电平,传输的距离和使用场景类似

    3>都采用主从模式

2.不同点:

    1>IIC总线是半双工,SPI总线是全双工

    2>IIC总线有应答机制,SPI总线无应答机制

    3>IIC总线通过从机地址选择和哪一个从机进行通信,SPI总线通过片选线选择和哪一个从机进行通信

    4>SPI总线如果有10个硬件设备,那就需要有10个片选线,比较浪费硬件资源

    5>IIC总线通信协议是通过SCL高低电平决定读写,SPI总线通过时钟极性和时钟相位决定读写

    6>IIC总线是高低电平进行数据传输,SPI总线是边沿触发,边沿采样

六、电路图

  

七、分析芯片手册 

7.1 芯片框图

 

7.2 引脚描述

 

7.3 工作原理

 

7.4 真值表

 

 主函数:

#include "spi.h"
extern void printf(const char *fmt, ...);
void delay_ms(unsigned int ms)
{
	int i, j;
	for (i = 0; i < ms; i++)
		for (j = 0; j < 1800; j++)
			;
}

extern char buffer[LEN];
unsigned char num[10] = {0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6};
int main()
{
	unsigned int i;
	si7006_init();

	SPI_init();
    while(1)
    {
        //让4位数码管都同时显示0-9,每隔1s中切换一次
        for (i = 0; i < 10; i++) 
        {
            // 先发送数码管的位,在发送数码管的段
            SPI_write(0xF0);  // 发送数码管的位
            SPI_write(num[i]);  // 发送数码管的段
            // 将移位寄存器中的数据存到索存寄存器中,让片选线产生一个上升沿
            NSS_OUTPUT_L();
            delay_ms(1);
            NSS_OUTPUT_H();

            delay_ms(1000);  // 1s切换一次显示的数据                                                                                                                                                           
        }

#endif
		
	}
	return 0;
}

SPI.C:

#include "spi.h"

// 通过GPIO引脚模拟SPI总线的通信协议
/* 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
 * 0 0 0 1 1 0 1 0    0x1A
 *
 * 如果需要显示小数点,需要在以上数据的基础之上加1即可
 * */
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->OSPEEDR |= (0x2 << 28);
	GPIOE->PUPDR &= (~(0x3 << 28));
	// MISO    PE13
	GPIOE->MODER &= (~(0x3 << 26));
	GPIOE->OSPEEDR &= (~(0x3 << 26));
//	GPIOE->OSPEEDR |= (0x2 << 26);
	GPIOE->PUPDR &= (~(0x3 << 26));
	// SCK     PE12	
	GPIOE->MODER &= (~(0x3 << 24));
	GPIOE->MODER |= (0x1 << 24);
	GPIOE->OTYPER &= (~(0x1 << 12));
	GPIOE->OSPEEDR &= (~(0x3 << 24));
//	GPIOE->OSPEEDR |= (0x2 << 24);
	GPIOE->PUPDR &= (~(0x3 << 24));
	// NSS     PE11
	GPIOE->MODER &= (~(0x3 << 22));
	GPIOE->MODER |= (0x1 << 22);
	GPIOE->OTYPER &= (~(0x1 << 11));
	GPIOE->OSPEEDR &= (~(0x3 << 22));
//	GPIOE->OSPEEDR |= (0x2 << 22);
	GPIOE->PUPDR &= (~(0x3 << 22));
	NSS_OUTPUT_L();    // 595芯片的锁存引脚拉低
	SCK_OUTPUT_L();    // SPI的时钟线拉低
}

void SPI_write(unsigned char dat)
{
	unsigned char i;
	for(i = 0; i < 8; i++)
	{
		if(dat & 0x01)  // 由于数码管的编码格式,因此需要先发低位,再发高位。
		{
			MOSI_OUTPUT_H();  // MOSI线写高
		} else {
			MOSI_OUTPUT_L();  // MOSI线写低
		}
		dat >>= 1;
		// 时钟线从低电平到高电平的变化时,MOSI数据线上的数据
		// 被写到595芯片的移位寄存器中
		SCK_OUTPUT_L();   // SCK拉低
		delay_us1(10);
		SCK_OUTPUT_H();   // SCK拉高
		delay_us1(10);
	}
	//NSS_OUTPUT_L();
	//NSS_OUTPUT_H();

}

SPI.h:

#ifndef __SPI_H__
#define __SPI_H__

#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_rcc.h"
// MOSI对应的引脚输出高低电平的信号
#define  MOSI_OUTPUT_H()	do{GPIOE->ODR |= (0x1 << 14);}while(0)
#define  MOSI_OUTPUT_L()    do{GPIOE->ODR &= (~(0x1 << 14));}while(0)

// 对应595芯片的锁存引脚输出高低电平
#define  NSS_OUTPUT_H()	    do{GPIOE->ODR |= (0x1 << 11);}while(0)
#define  NSS_OUTPUT_L()     do{GPIOE->ODR &= (~(0x1 << 11));}while(0)
	
// 时钟信号对应的引脚输出高低电平
#define  SCK_OUTPUT_H()     do{GPIOE->ODR |= (0x1 << 12);}while(0)
#define  SCK_OUTPUT_L()     do{GPIOE->ODR &= (~(0x1 << 12));}while(0)

/*
 * 函数功能: SPI初始化函数,推挽输出,高速,禁止上拉和下拉
 * 函数参数:无
 * 函数返回值:无
*/
void SPI_init(void);
/*
 * 函数功能:SPI发送数据的函数
 * 函数参数:dat : 要发送的数据
 * 函数返回值:无
 *
*/
void SPI_write(unsigned char dat);


#endif  // __SPI_H__

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coding Peasant

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值