【SPI实验--数码管】

一、SPI概念

1.SPI总线是Motorola首先提出的全双工三线制/四线制同步串行总线
2.采用主从模式(Master
Slave)架构,支持多slave模式应用,在实际开发使用中,大多数使用单主机多从机模式
3.时钟由Master控制,在时钟移位脉
冲下,数据按位传输,高位在前,低位在后(MSB first),也可以低位在前,高位在后
4.SPI接口有2根单向数据线,为全双工
通信,目前应用中的数据速率可达几Mbps的水平
5.SPI总线被广泛地使用在FLASH、ADC、LCD等设备与MCU间通信,要求通讯速率较高的场合
6.本次实验:SOC<—SPI总线—>数码管

二、SPI硬件连接

SPI一共有四根信号线:设备选择线、时钟线、串行输出数据线、
串行输入数据线
设备选择线(片选线):NSS
时钟线:SCK
串行输出数据线:MOSI
串行输入数据线:MISO
M:master主机 O:output输出 S:slave从机 I:input输入

• (1)MOSI:主器件数据输出,从器件数据输入
• (2)MISO:主器件数据输入,从器件数据输出
• (3)SCLK :时钟信号,由主器件产生
• (4)/SS:从器件使能信号,由主器件控制(片选)

三、SPI总线通信协议

在这里插入图片描述
起始信号: NSS信号线由高变低,是SPI通讯的起始信号
结束信号:NSS信号由低变高,是SPI通讯的停止信号
数据传输:SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。
MOSI及MISO数据线在SCK的每个时钟周期传输一位数据,且数据输入输出是同时进行
的。SPI每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制

四、SPI四种通信模式

在这里插入图片描述

五、代码实现数码管

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__

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->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();
 
}
 
 
 

main.c

#include "gpio.h"
#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)
 
{
    
    
 
	unsigned char i;
 
	SPI_init();
 
	while(1)
 
	{
    
    
 
#if 0
 
		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);
 
		}
 
#else 
 
		for(i = 0; i < 4; i++)
 
		{
    
    
 
			SPI_write(0x80 >> i);
 
			SPI_write(num[i+1]);
 
			NSS_OUTPUT_L();
 
			delay_ms(1);
 
			NSS_OUTPUT_H();
 
 
 
		}
 
#endif 
 
	}
 
 
 
	return 0;
 
}

猜你喜欢

转载自blog.csdn.net/a1379292747/article/details/128554579