一、SPI介绍
SPI是“全双工”通信,具有单独的发送和接收线,可以同时进行发送和接收,从机不主动发起访问,总是被动执行操作
SPI包含4根逻辑线:
MISO: 主机输入,从机输出
MOSI: 主机输出,从机输入
SCLK: 串行时钟信号,此信号由主机产生。从机不产生时钟信号。
CS: 片选信号,由主机产生,用来控制与哪个从机通信,通常是低电平为选中。
SPI有个时钟极性需要配置,也就是需要配置时钟极性(CPOL)和时钟相位(CPHA),时钟极性和相位共同决定读取数据的方式,也就是说我在什么时候进行取数据。
CPOL: 0 时钟空闲时为低电平
CPOL: 1 时钟空闲时为高电平
CPHA: 0 在时钟信号SCK的第一个跳变沿采样
CPHA: 1 在时钟信号SCK的第二个跳变沿采样
SPI的时钟极性和相位的配置通常称为 SPI模式,遵循以下约定:
SPI通讯的优势
使SPI作为串行通信接口脱颖而出的原因很多;
全双工串行通信;
高速数据传输速率。
简单的软件配置;
极其灵活的数据传输,不限于8位,它可以是任意大小的字;
非常简单的硬件结构。从站不需要唯一地址(与I2C不同)。从机使用主机时钟,不需要精密时钟振荡器/晶振(与UART不同)。不需要收发器(与CAN不同)。
SPI的缺点
没有硬件从机应答信号(主机可能在不知情的情况下无处发送);
通常仅支持一个主设备;
需要更多的引脚(与I2C不同);
没有定义硬件级别的错误检查协议;
与RS-232和CAN总线相比,只能支持非常短的距离;
二、SPI使用举例
SPI如何实现读写呢?
不同的从机芯片读写方式不同,如下为ADXL345加速度传感器的读写时序,其时序方案按照时钟极性(CPOL)= 1、 时钟相位(CPHA)= 1执行,读写如下如:
SPI写入寄存器:
ADXL345写寄存器的方式是:如上图写时先发送寄存器地址值随后是寄存器值,则SPI写寄存器的程序如下:
/* SPI向从机寄存器写数据 */
int Single_Write(unsigned char REG_Address,unsigned char REG_data)
{
int ret = 0;
uint32_t timeout = 1000;
uint8_t data[2] = {0}; //构造数据结构,用于发送spi数据
data[0] = REG_Address;
data[1] = REG_data;
uint16_t size = sizeof(data);
HAL_GPIO_WritePin(GPIOA, CS_PIN, GPIO_PIN_RESET); //片选拉低,开始通信
ret = HAL_SPI_Transmit(&hspi1, data, size, timeout); //SPI发送2字节数据
if (ret != HAL_OK)
{
printf("SPI Transmit err ret = %d\n",ret);
return -1;
}
HAL_GPIO_WritePin(GPIOA, CS_PIN, GPIO_PIN_SET); //片选拉高,通信结束
return 0;
}
SPI读取寄存器:
ADXL345 读寄存器方式:最高位为1表示读取,所以想要实现读取寄存器,要spi写一个最高位为1的数据(uint8_t txData = REG_Address | 0x80; // 设置读取标志位)。随后ADXL345被动向主机发送寄存器的值数据。SPI读寄存器程序如下:
/*SPI单字节读取*/
unsigned char Single_Read(unsigned char REG_Address)
{
int ret = 0;
uint8_t rxData;
uint8_t txData = REG_Address | 0x80; //构造要发送的数据,MSB位置1
HAL_GPIO_WritePin(GPIOA, CS_PIN, GPIO_PIN_RESET); //片选拉低,开始通信
ret = HAL_SPI_Transmit(&hspi1, &txData, 1, HAL_MAX_DELAY); //spi发送1字节数据
if (ret != HAL_OK)
{
printf("SPI Transmit err ret = %d\n",ret);
return 0;
}
HAL_SPI_Receive(&hspi1, &rxData, 1, HAL_MAX_DELAY); //SPI读取1字节数据
HAL_GPIO_WritePin(GPIOA, CS_PIN, GPIO_PIN_SET); //通信完毕,片选拉高
return rxData;
}
看下读取ADXL345设备ID,示波器抓到的时序:
注:不同的芯片SPI的读取方式可能不同,例如W25Q16之类存储芯片是通过发送不同的指令来实现读写的