基于stm32芯片的SPI学习

本文详细介绍了SPI接口的工作原理、时钟极性和相位设置,以及在STM32中使用标准外设库进行软硬件模拟和外设配置的示例,包括SPI_InitTypeDef结构和GPIO操作。
摘要由CSDN通过智能技术生成

一、SPI简介

1、英文全称:Serial Peripheral interface

2、中文全称:串行外设接口

3、特点:全双工、同步、传输速率快

4、传输方式:

CLK:时钟线
MOSI:主机输出,从机输入
MISO:从机输入,从机输出
CS:片选,选择通信的从机
(输出引脚配置为推挽输出模式,输入引脚配置为浮空或上拉输入模式)

5、SPI数据交换

在CLK高电平(上升沿)输出数据,CLK低电平(下降沿)读取数据
在这里插入图片描述

6、相关缩写

(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性

SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:

CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high;

CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low;

(2) CKPHA (Clock Phase) = CPHA = PHA = Phase = (时钟)相位
CPHA=0,表示第一个边沿:
①CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
②CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;

CPHA=1,表示第二个边沿:
CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;

(3) SCK=SCLK=SPI的时钟

7、STM32标准外设库SPI_InitTypeDef的定义

 
typedef struct
{
   uint16_t SPI_Direction; // 设置SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式
   uint16_t SPI_Mode; // 设置SPI 的主从模式
   uint16_t SPI_DataSize; // 为8 位还是16 位帧格式选择项
   uint16_t SPI_CPOL; // 设置时钟极性
   uint16_t SPI_CPHA; // 设置时钟相位
   uint16_t SPI_NSS;   //设置NSS 信号由硬件(NSS管脚)还是软件控制
   uint16_t SPI_BaudRatePrescaler;  //设置SPI 波特率预分频值
   uint16_t SPI_FirstBit;    //设置数据传输顺序是MSB 位在前还是LSB 位在前
   uint16_t SPI_CRCPolynomial; //设置CRC 校验多项式,提高通信可靠性,大于1 即可
}SPI_InitTypeDef;

8、代码

一、软件模拟

c文件

#include "stm32f10x.h"                  // Device header
#include "SoftWare_SPI.h"

void SPI_W_CS(uint8_t BitValue)
{
	GPIO_WriteBit(SPI_CS_Port, SPI_CS_Pin, (BitAction)BitValue);
}

void SPI_W_CLK(uint8_t BitValue)
{
	GPIO_WriteBit(SPI_CLK_Port, SPI_CLK_Pin, (BitAction)BitValue);
}

uint8_t SPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(SPI_MISO_Port, SPI_MISO_Pin);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(SPI_MOSI_Port, SPI_MOSI_Pin, (BitAction)BitValue);
}

void SW_SPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = SPI_CS_Pin | SPI_CLK_Pin | SPI_MOSI_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = SPI_MISO_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	SPI_W_CS(1);
	SPI_W_CLK(0);
}

void SPI_Start(void)
{
	SPI_W_CS(0);
}

void SPI_Stop(void)
{
	SPI_W_CS(1);
}

uint8_t SPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	
	for (i = 0; i < 8; i ++)
	{
		MySPI_W_MOSI(ByteSend & (0x80 >> i));
		SPI_W_CLK(1);
		if (SPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}
		SPI_W_CLK(0);
	}
	
	return ByteReceive;
}

h文件

#ifndef __SoftWare_SPI__H__
#define __SoftWare_SPI__H__

#define SPI_CS_Port GPIOA		//CS引脚选择
#define SPI_CS_Pin GPIO_Pin_4

#define SPI_CLK_Port GPIOA		//CLK引脚选择
#define SPI_CLK_Pin GPIO_Pin_5

#define SPI_MISO_Port GPIOA		//MISO引脚选择
#define SPI_MISO_Pin GPIO_Pin_6

#define SPI_MOSI_Port GPIOA		//MOSI引脚选择
#define SPI_MOSI_Pin GPIO_Pin_7

void SPI_W_CS(uint8_t BitValue);
void SPI_W_CLK(uint8_t BitValue);
uint8_t SPI_R_MISO(void);
void MySPI_W_MOSI(uint8_t BitValue);
void SW_SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t ByteSend);

#endif

二、SPI外设

c文件

#include "stm32f10x.h"                  // Device header
#include "HardWare_SPI.h"

void SW_SPI_W_CS(uint8_t BitValue)
{
	GPIO_WriteBit(SPI_CS_Port, SPI_CS_Pin, (BitAction)BitValue);
}

void HW_SPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;			
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//从机选择引脚,软件模拟
	GPIO_InitStructure.GPIO_Pin = SPI_CS_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//外设控制的输出,使用复用推挽输出
	GPIO_InitStructure.GPIO_Pin = SPI_CLK_Pin | SPI_MOSI_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	//主机输入,上拉输入模式
	GPIO_InitStructure.GPIO_Pin = SPI_MISO_Pin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	SPI_InitTypeDef SPI_InitSTructure;
	SPI_InitSTructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitSTructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitSTructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitSTructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitSTructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
	SPI_InitSTructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitSTructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitSTructure.SPI_NSS = SPI_NSS_Hard;
	SPI_InitSTructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitSTructure);
	
	SPI_Cmd(SPI1, ENABLE);
	
	SW_SPI_W_CS(1);//默认不选中从机
}

void HW_SPI_Start(void)
{
	SW_SPI_W_CS(0);
}

void HW_SPI_Stop(void)
{
	SW_SPI_W_CS(1);
}

uint8_t HW_SPI_SwapByte(uint8_t ByteSend)
{

	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//判断TXE(发送寄存器)是否为空,为空置一
	
	SPI_I2S_SendData(SPI1, ByteSend);//写入数据到TDR
	
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//等待RXNE为1,表示收到一个字节,表示发送时序完成
	
	//要接收必须先发送
	return SPI_I2S_ReceiveData(SPI1);//读取RDR接收的数据
}

h文件

#ifndef __HardWare_SPI__H__
#define __HardWare_SPI__H__

#define SPI_CS_Port GPIOA		//CS引脚选择
#define SPI_CS_Pin GPIO_Pin_4

#define SPI_CLK_Port GPIOA		//CLK引脚选择
#define SPI_CLK_Pin GPIO_Pin_5

#define SPI_MISO_Port GPIOA		//MISO引脚选择
#define SPI_MISO_Pin GPIO_Pin_6

#define SPI_MOSI_Port GPIOA		//MOSI引脚选择
#define SPI_MOSI_Pin GPIO_Pin_7

void SW_SPI_W_CS(uint8_t BitValue);
void HW_SPI_Init(void);
void HW_SPI_Start(void);
void HW_SPI_Stop(void);
uint8_t HW_SPI_SwapByte(uint8_t ByteSend);

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值