STM32--SPI通信协议

一、简介

SPI Serial Peripheral Interface )是由 Motorola 公司开发的一种通用数据总线
四根通信线: SCK Serial Clock )、 MOSI Master Output Slave Input )、 MISO Master Input Slave Output )、 SS Slave Select
同步,全双工(数据接收和数据发送各占一根线)(全双工存在资源浪费的现象)
支持总线挂载多设备(一主多从)
所有SPI 设备的 SCK MOSI MISO 分别连在一起主机
另外引出多条SS 控制线,分别接到各从机的 SS 引脚
输出引脚:配置为推挽输出(所以电平变化迅速,所以速度快。不像I2C那样,下降沿迅速,上升沿缓慢),
输入引脚:配置为浮空或上拉输入
        存在冲突:因为MISO都是推挽输出,且都接在一根线上,所以容易发生冲突
        解决:SPI规定SS为1时(未被选中),MISO置高阻态。

二、SPI内部的移位寄存器

上升沿两个移位寄存器数据左移到数据线(输出数据寄存器)上。下降沿经过采样进入另一个寄存器。
SPI的数据收发,都是基于字节交换 。(主机和从机移位寄存器的数据交换)

三、SPI的时序基本单元

 1.起始和终止(SS低电平有效)

起始条件: SS 从高电平切换到低电平
终止条件: SS 从低电平切换到高电平

2.交换一个字节

        CPOL和CPHA决定模式x

(Clock Polarity时钟极性)(Clock Phase 时钟相位(是表示第一个时钟采样移入还是第二个移入,不是规定上升沿采样还是下降沿采样))

模式0(比模式1提前半个SCK时钟,相当于把SS的第一个变化当作时钟下降沿作用(在此时移出数据))用的最多

                

CPOL=0 :空闲状态时,SCK为低电平

CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据

模式1(SCK上升沿输入数据,下降沿输出)

CPOL=0 :空闲状态时, SCK 为低电平
CPHA=1 SCK 第一个边沿移出数据,第二个边沿移入数据

模式2(SCK的极性时模式0的取反)

CPOL=1 :空闲状态时, SCK 为高电平
CPHA=0 SCK 第一个边沿移入数据,第二个边沿移出数据

模式3(SCK的极性时模式1的取反)

CPOL=1 :空闲状态时, SCK 为高电平
CPHA=1 SCK 第一个边沿移出数据,第二个边沿移入数据

四、软件SPI实现

        1.写SS,SCK,MOSI   读MISO(写数据GPIO设置为推挽输出。读数据GPIO为上拉)

void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6);
}

2.Start 和Stop

        SS空闲时为1.

void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

3.交换一个字节(write and read byte)

uint8_t MySPI_SwapByte(uint8_t SendByte)//模式0
{
	uint8_t ByteReceive = 0x00;
	
	//SS下降沿,移出数据(就是Start)
	for(uint8_t i = 0; i<8; ++i)
	{
		MySPI_W_MOSI(SendByte & (0x80 >> i));
		MySPI_W_SCK(1);//SCK上升沿,移入数据
		if(MySPI_R_MISO() == 1 ){ByteReceive |= (0x80 >> i);}
		MySPI_W_SCK(0);//SCK下降沿,移出数据
	}
	return ByteReceive;
}

五、硬件SPI(STM32)

1.简介

        STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担

        可配置8/16位数据帧、高位先行/低位先行(串口是低位先行)

        时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)

        支持多主机模型、主或从操作

        可精简为半双工/单工通信

        支持DMA(有大量数据传输时可以用DMA,少量数据没必要用)

        兼容I2S协议(主要传输数字音频信号)

STM32F103C8T6 硬件SPI资源:SPI1SPI2

SPI框图

简化框图

2.代码

1.初始化。NSS配置麻烦,所以用软件模拟

void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	/*SPI SS输出GPIO设置为推挽输出*/
	GPIO_InitTypeDef GPIO_InitSTructure;
	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);
	
	/*SPI输入GPIO设置为上拉输入*/
	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 SS输出GPIO设置为复用推挽输出*/
	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);
	
	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;//CRC校验位
	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_Mode = SPI_Mode_Master;//STM32是主机
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//NSS不管,靠软件模拟SS
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_Cmd(SPI1, ENABLE);
	
	
	/*空闲状态为 SS:1   */
	MySPI_W_SS(1);

}

2. SS模拟。Start 和Stop。 发送一个字节

void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

uint8_t MySPI_SwapByte(uint8_t SendByte)
{//TXE
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);
	
	SPI_I2S_SendData(SPI1, SendByte);
//RXNE	
	while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);
	
	return SPI_I2S_ReceiveData(SPI1);
}	

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值