SPI通信

一、简介

        SPI全称Serial Peripheral Interface(串行外设接口), 是Motorola公司在20世纪80年代中期开发的一种通用数据总线,后发展成行业规范。

二、引脚定义

        SCLK:Serial Clock,时钟信号,由主机控制。
        MOSI:Master Output Slave Input,主机数据输出,从机数据输入。
        MISO:Master Input Slave Output,主机数据输入,从机数据输出。
        CS/SS:Chip Select / Slave Select,从机选择信号,由主机控制。

三、引脚连接和配置


        1、所有设备的SCLK、MOSI、MISO分别连接在一起;
        2、主机的每个SS分别和对应的从机相连;
        3、SPI通信使用单端信号,所有设备必须共地;
        4、输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入。
        推挽输出的高、低电平都有很强的驱动能力,信号上升沿和下降沿都很迅速,使得SPI通信速度很快。同时,SPI规定从机在未被选中的状态下必须切换MISO为高阻态,以防止MISO线路上多个输出之间的产生电平冲突。
        5、当MOSI和MISO只接一根时转为单工通信;当只有一个从机时转为一主一从模式。

四、通信流程

        第一步:主机拉低SS线,选择从机开始通信;
        第二步:主机和从机通过移位寄存器进行数据交换,一般是高位先行;

        第三步:主机拉高SS线,通信结束。
        通信配置:通过配置CPOL(Clock Polarity 时钟极性)和CPHA(Clock Phase 时钟相位)的值(0或1)可以在四种通信模式中选择一种。
        CPOL:为0表示SCLK空闲时为低电平;为1表示SCLK空闲时为高电平。
        CPHA:为0表示在SCLK第一个边沿移入数据,在第二个边沿移出数据;
                       为1表示在SCLK第一个边沿移出数据,在第二个边沿移入数据。
        图3和图4分别为CPOL=0 CPHA=0和CPOL=0 CPHA=1模式下的时序图,其他模式将时钟线反相即可。

五、STM32F103C8T6 软件SPI

        1. MySPI.c文件

#include "stm32f10x.h"                  // Device header

// 写入SS引脚电平
void MySPI_WriteSS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

// 写入SCLK引脚电平
void MySPI_WriteSCLK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}

// 读取MISO引脚电平
uint8_t MySPI_ReadMISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

// 写入MOSI引脚电平
void MySPI_WriteMOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

// 初始化SPI引脚
void MySPI_Init(void)
{
	// 开启GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	// 初始化GPIO口,以A4->SS,A5->SCLK,A6->MISO,A7->MOSI为例
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// SS置1
	MySPI_WriteSS(1);
	
	// SCLK置0
	MySPI_WriteSCLK(0)
}

// 通信开始
void MySPI_Start(void)
{
	MySPI_WriteSS(0);
}

// 通信结束
void MySPI_Stop(void)
{
	MySPI_WriteSS(1);
}

// 交换字节
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = ByteSend;
	for (i = 0; i < 8; i ++)
	{
		MySPI_WriteMOSI(ByteReceive & 0x80);
		ByteReceive <<= 1;
		MySPI_WriteSCLK(1);
		if (MySPI_ReadMISO() == 1)
		{
			ByteReceive |= 0x01;
		}
		MySPI_WriteSCLK(0);
	}
	return ByteReceive;
}

        2.MySPI.h文件

#ifndef __MYSPI_H
#define __MYSPI_H

void MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);

#endif

六、STM32F103C8T6 硬件SPI

       

 1. MySPI.c文件

#include "stm32f10x.h"                  // Device header

// 写入SS引脚电平
void MySPI_WriteSS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_Init(void)
{
	// 开启GPIOA和SPI1时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	
	// SS使用软件控制,配置为通用推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// SCL、MOSI由硬件SPI1控制,配置为复用推挽输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// MISO口配置为上拉输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 初始化SPI
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;	//分频系数
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//采样边沿
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;	//时钟极性
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;	//8位模式
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//全双工
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//高位先行
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;	//主机模式
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;	//软件控制SS
	SPI_Init(SPI1, &SPI_InitStructure);
	
	// 使能SPI
	SPI_Cmd(SPI1, ENABLE);
	
	// SS置1
	MySPI_WriteSS(1);
}

// 通信开始
void MySPI_Start(void)
{
	MySPI_WriteSS(0);
}

// 通信结束
void MySPI_Stop(void)
{
	MySPI_WriteSS(1);
}

// 交换字节
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	// 等待发送缓存寄存器为空(置1)
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
	
	// 发送数据
	SPI_I2S_SendData(SPI1, ByteSend);
	
	// 等待接收缓存寄存器拿到数据(置1)
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
	
	// 返回接收数据
	return SPI_I2S_ReceiveData(SPI1);
}

        2.MySPI.h文件与软件SPI相同

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值