【51单片机快速入门指南】5:软件SPI

目录

  • 硬知识
    • SPI协议简介
    • SPI接口介绍
    • SPI接口连接图
    • SPI数据传输方向
    • SPI传输模式
  • 软件SPI程序源码
    • Software_SPI.c
    • Software_SPI.h

普中51-单核-A2
STC89C52
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0
上位机:Vofa+ 1.3.10


       源于软件模拟SPI接口程序代码(4种模式)—— 内陆的咸水鱼,有改动。

硬知识

SPI协议简介

       SPI的通信原理很简单,一般主从方式工作,这种模式通常有一个主设备和一个或多个从设备,通常采用的是4根线,它们是MISO(数据输入,针对主机来说)、MOSI(数据输出,针对主机来说)、SCLK(时钟,主机产生)、CS/SS(片选,一般由主机发送或者直接使能,通常为低电平有效

SPI接口介绍

       SCK:时钟信号,由主设备产生,所以主设备SCK信号为输出模式,从设备的SCK信号为输入模式。
       CS:片选信号,由主设备控制从设备,,所以主设备CS信号为输出模式,从设备的CS信号为输入模式。
       MOSI:主设备数据输出,从设备数据输入,所以主设备MOSI信号为输出模式,从设备的MOSI信号为输入模式。
       MISO:主设备数据输入,从设备数据输出,所以主设备MISO信号为输入模式,从设备的MISO信号为输出模式。

SPI接口连接图

【51单片机快速入门指南】5:软件SPI_第1张图片
       注意:MOSI和MISO不能交叉连接(可以把主从机理解为一个整体系统,MOSI为系统主机发送从机接收的数据线,MISO为主机接收从机发送的数据线)

SPI数据传输方向

       SPI作为全双工的的串行通信协议,数据传输时高位在前,低位在后。主机和从机公用由主机产生的SCK信号,所以在每个时钟周期内主机和从机有1bit的数据交换(因为MOSI和MISO数据线上的数据都是在时钟的边沿处被采样)。如下图:
【51单片机快速入门指南】5:软件SPI_第2张图片
       SPI协议规定数据采样是在SCK的上升沿或下降沿时刻(由SPI模式决定,下面会说到),观察上图,在SCK的边沿处(上升沿或下降沿),主机会在MISO数据线上采样(接收来从机的数据),从机会在MOSI数据线上采样(接收来自主机的数据),所以每个时钟周期中会有一bit的数据交换。
在这里插入图片描述

SPI传输模式

       SPI总线传输一共有4种模式,这4种模式分别由时钟极性(CPOL)和时钟相位(CPHA)来定义。
【51单片机快速入门指南】5:软件SPI_第3张图片
       CPOL:规定了SCK时钟信号空闲状态的电平
       CPHA:规定了数据是在SCK时钟的上升沿还是下降沿被采样
【51单片机快速入门指南】5:软件SPI_第4张图片
       模式0:CPOL=0,CPHA =0 SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据)
       模式1:CPOL=0,CPHA =1 SCK空闲为低电平,数据在SCK的下降沿被采样(提取数据)
       模式2:CPOL=1,CPHA =0 SCK空闲为高电平,数据在SCK的下降沿被采样(提取数据)
       模式3:CPOL=1,CPHA =1 SCK空闲为高电平,数据在SCK的上升沿被采样(提取数据)

       以模式0为例:SCK空闲为低电平,数据在SCK的上升沿被采样(提取数据),在SCK的下降沿切换数据线的数据。
【51单片机快速入门指南】5:软件SPI_第5张图片
◐在时钟的第1个上升沿(游标1处)(采样点)

       MOSI上数据为1,则在此边沿从机采样(提取)数据为1,采样点在MOSI数据线的中间。

       MISO上数据为0,则在此边沿主机采样(提取)数据为0,采样点在MISO数据线的中间。

◐在时钟的第1个下降沿(游标2处)(切换点)

       MOSI上数据由1切换为0,,数据在时钟下降沿时切换数据。

       MISO上数据由0切换为1,,数据在时钟下降沿时切换数据。

◐在时钟的第2~8个上升沿(采样点),主机在MISO上采样数据,从机在MOSI上采样数据。

◐在时钟的第2~8个下降沿(切换点),主机在MISO上切换数据,从机在MOSI上切换数据

软件SPI程序源码

Software_SPI.c

#include "Software_SPI.h"

void SPI_Delay()	//每步的间隔 用于等待电平稳定和控制通讯速率
{
     

}

//MOSI拉高 移植时需修改
void MOSI_H()
{
     
	SPI_MOSI = 1;
}

//MOSI拉低 移植时需修改
void MOSI_L()
{
     
	SPI_MOSI = 0;
}

//MISO拉高 移植时需修改
void MISO_H()
{
     
	SPI_MISO = 1;
}

//MISO拉低 移植时需修改
void MISO_L()
{
     
	SPI_MISO = 0;
}

//SCK拉高 移植时需修改
void SPI_SCK_H()
{
     
	SPI_SCK = 1;
}

//SCK拉低 移植时需修改
void SPI_SCK_L()
{
     
	SPI_SCK = 0;
}

//读取MISO电平 移植时需修改
uint8_t MISO_Read()
{
     
	SPI_MISO = 1;
	SPI_Delay();
	return SPI_MISO;
}

//空闲时时钟极性(CPOL)		0为低电平 1为高电平
//数据有效时钟缘相位(CPHA)	0为奇数缘 1为偶数缘

/* CPOL = 0, CPHA = 0, MSB first */
uint8_t SOFT_SPI_RW_MODE0(uint8_t write_dat)
{
     
    uint8_t i, read_dat = 0;

	SPI_SCK_L();
	SPI_Delay();
    for( i = 0; i < 8; i++ )
    {
     
        if(write_dat & 0x80)
            MOSI_H();  
        else                    
            MOSI_L();  
        write_dat <<= 1;
        SPI_Delay();
        SPI_SCK_H(); 
        read_dat <<= 1;  
		read_dat |= MISO_Read();
        SPI_SCK_L(); 
    }
    return read_dat;
}
 
/* CPOL=0,CPHA=1, MSB first */
uint8_t SOFT_SPI_RW_MODE1(uint8_t write_dat) 
{
     
    uint8_t i, read_dat = 0;

	SPI_SCK_L();
	SPI_Delay();
	for(i = 0; i < 8; i++)	//循环8次
	{
     
		SPI_SCK_H();		//拉高时钟
		if(write_dat & 0x80)
			MOSI_H();		//若最到位为高,则输出高
		else      
			MOSI_L();		//若最到位为低,则输出低
		write_dat <<= 1;	//低一位移位到最高位
		SPI_Delay();
		SPI_SCK_L();		//拉低时钟
		read_dat <<= 1;		//数据左移
		read_dat |= MISO_Read();
	}
	return read_dat;		//返回数据
}
 
/* CPOL=1,CPHA=0, MSB first */
uint8_t SOFT_SPI_RW_MODE2(uint8_t write_dat) 
{
     
    uint8_t i, read_dat = 0;

	SPI_SCK_H();
	SPI_Delay();
	for(i = 0; i < 8; i++)	// 循环8次
	{
     
		if(write_dat & 0x80)
			MOSI_H();		//若最到位为高,则输出高
		else      
			MOSI_L();		//若最到位为低,则输出低
		write_dat <<= 1;	//低一位移位到最高位
		SPI_Delay();
		SPI_SCK_L();		//拉低时钟
		read_dat <<= 1;     //数据左移
		read_dat |= MISO_Read();    
		SPI_SCK_H();		//拉高时钟
	}
	return read_dat;		//返回数据
}
 
/* CPOL = 1, CPHA = 1, MSB first */
uint8_t SOFT_SPI_RW_MODE3(uint8_t write_dat)
{
     
    uint8_t i, read_dat = 0;

	SPI_SCK_H();
	SPI_Delay();
    for( i = 0; i < 8; i++ )
    {
     
		SPI_SCK_L(); 
        if(write_dat & 0x80)
            MOSI_H();  
        else                    
            MOSI_L();  
        write_dat <<= 1;
        SPI_Delay();	
        SPI_SCK_H(); 
        read_dat <<= 1;  
        read_dat |= MISO_Read(); 
    }
    return read_dat;
}

Software_SPI.h

#ifndef SOFTWARE_I2C_H_
#define SOFTWARE_I2C_H_

#include 
#include "stdint.h"

sbit SPI_SCK	= P3^6;
sbit SPI_MOSI	= P3^4;
sbit SPI_MISO	= P3^4;

uint8_t SOFT_SPI_RW_MODE0(uint8_t write_dat);
uint8_t SOFT_SPI_RW_MODE1(uint8_t write_dat);
uint8_t SOFT_SPI_RW_MODE2(uint8_t write_dat);
uint8_t SOFT_SPI_RW_MODE3(uint8_t write_dat);

#endif

你可能感兴趣的:(51单片机,51单片机,单片机,stm32,spi,通信协议)