本节我们介绍stm32的SPI总线接口,并使用SPI接口来访问气压计BMP280。
SPI(Serial Peripheral Interface)串行外围设备接口,是一种高速的,全双工,同步的通信总线,一般需要四根线:
MISO:主器件数据输入,从器件数据输出;
MOSI:主器件数据输出,从器件数据输入;
SCK:时钟信号,由主设备控制发出,MISO和MOSI上的数据在SCK信号的跳变沿发送变化;
CS(stm32中称为NSS):片选信号,由主设备控制,当CS为低电平时,表示选中从器件,主器件可以与该从器件通信。
当SPI总线上只有一个从设备时,可以把从机的片选直接接地,也即一直选中:
当SPI总线上有多个从器件时,需要给每个从设备分配一个CS信号,以保证可以和每个从设备独立通信,而其他三根线可以共用,如下图所示:
(这里我们只讲最常用的SPI总线,有3线制的,还有接线为菊花链式的不在本文讨论范围)
SPI的时钟和数据的关系可以有四种模式:
时钟极性(CPOL)定义了时钟空闲状态电平:
CPOL=0:空闲时,时钟为低电平;
CPOL=1:空闲时,时钟为高电平。
时钟相位(CPHA)定义数据的采集时间:
CPHA=0:在时钟的第一个跳变沿(上升沿或下降沿)进行数据采样;
CPHA=1:在时钟的第二个跳变沿(上升沿或下降沿)进行数据采样。
四种模式的区别,如下图所示:
使用前我们需要依据从设备的要求,把主设备设置为相同的模式。
标准的4线制SPI总线是全双工通信。
从上面的图中,我们也可以看出,MOSI和MISO是同时在传输数据的,实际上,每个时钟周期,主设备都会向从设备传输1bit数据,而同时从设备也向主设备传输了1bit数据。只是,当主设备向从设备写入数据的时候,我们只关心MOSI的数据;而当主设备读取从设备的数据时,我们只关心MISO的数据。
以气压传感器BMP280为例,它用SPI接口通信时,时序要求如下图:
则我们使用stm32的SPI接口和它通信时,需要设置为CPOL=1空闲时,时钟为高电平、CPHA=1在时钟的第二个跳变沿进行数据采样。
Stm32带了硬件SPI,下面我们就使用stm32的硬件SPI读取气压计BMP280的数据。
我们仍然以串口的工程为基础,在它上面添加设置,如下图,选择SPI接口:
需要设置的包括:
Mode:选择全双工主设备(因为我们的stm32是主控,BMP280是从设备;全双工就是我们前面讲的4线制SPI,收、发各用一条线;也用收发共用一条线的,那么就是半双工,3线制SPI);
Prescaler:设置分频系数,即SPI总线的时钟频率(BMP280手册中要求不大于10M);
CPOL和CPHA:前面有详细说过,不罗嗦了;
NSS:CS片选这里使用软件方式,主要是软件方式可以适应挂载多个从设备,以后移植起来方便很多;片选设置为软件方式就是用软件控制一个IO口作为CS信号,所以还要设置一个输出的GPIO,图中设置的是PA4。
设置完成后,芯片引脚的PA4、5、6、7被占用为SPI的几个信号线。
在硬件连接上,我们把BMP280也按四线制SPI方式连接,如下图:
CSB连接到PA4,SCK连接到PA5,SDO连接到PA6,SDI连接到PA7
生成工程代码,在keil中打开。Stm32的hal库已经将SPI的初始化、SPI的读写操作封装成了函数,我们直接调用即可。
首先在main主循环之前,增加如下代码,拉高PA4(即拉高片选CS,初始状态不选中):
之后添加如下代码,读取BMP280的芯片id并打印:
操作过程是先拉低片选CS线,再访问从设备,访问完毕后拉高CS线。
主要讲讲访问从设备的HAL_SPI_TransmitReceive这个函数,用于读取从设备的的数据。
该函数的第二个参数是要发送的数据首地址,第三个参数是接收数据存放的首地址,第四个参数是要传输的数据字节数,最后是超时。
我们结合BMP280读取数据的方法来看:
按照数据手册,读取数据时,先发送寄存器地址(一个字节),然后之后从机就会回传数据(多个字节)。
这里要注意一下,因为4线制的SPI总线,本质上是主设备和从设备在数据交换。
BMP280的芯片id存放在其寄存器地址0xD0处,所以把0xD0存到tx_data的第一个字节处,后面的我们不关心,因为只有第一个字节是起作用的。
HAL_SPI_TransmitReceive执行完之后,从机发来的数据存放在rx_data中,由于从机返回的数据是从第二个字节开始有效的,所以从机发来的数据是从rx_data[1]开始的。
我们“交换”了两个字节数据,从机返回的一个字节数据在rx_data[1]中。
程序运行结果如下,可以看到成功读取到了BMP280的id值0x58:
能够通过SPI成功读取BMP280芯片内的数据,我们就已经成功了一大半。之后的操作只需要启动转换、读取数值计算结果,就可以获取气压值了。
启动转换的过程如下,即是往0x74寄存器地址写入0xff,操作过程如下:
(注意这一步是必须的,否则回读的气压数据会一直不变化。)
然后就是读取数值、计算气压结果了;这里也需要几步,先要从芯片内读取已经存好的校准参数,然后读取芯片内的AD转换值,最后依据手册中给出的公式计算出温度、气压值结果。
读取校准参数和AD转换值的过程也是用HAL_SPI_TransmitReceive这个函数,限于篇幅就不贴代码了,可以到文末获取完整工程代码。
实测气压的分辨率很高,上下移动电路板10cm就能出现比较稳定的气压变化,实际中很多情况下用来测量相对高度的变化。另外说一下BMP280同系列的BME280,还具有测量湿度的功能。
SPI相比与I2C,需要的信号线要多一些,而且从设备越多则需要占用更多的CS线;
但是SPI的速率可以快很多,一般达到几M的速率是没问题的,而I2C一般到几百k就比较吃力了,这主要是因为I2C的高电平是靠电阻上拉实现的,而SPI总线的状态都是靠IO口推挽结构实现。
几乎所有的芯片,如果有I2C功能都会同时支持SPI功能,大家可以依据具体场景选择合适的来使用。
本文中完整工程代码可以在下面下载:
土豪通道:test12_SPI.zip-嵌入式文档类资源-CSDN下载
非土豪通道:关注公众号,留言 “资料” 可直接获取相关资料和软件: