SPI的四线与三线制及二线制

SPI的四线制与三线制及二线制

  • 前言
  • 1、 四线的SPI
  • 2、 三线的SPI
  • 3、两线的SPI
  • 4、 总结


前言

SPI即Serial Peripheral Interface的缩写,全名串行外设接口,是一种高速的支持全双工同步通讯的接口技术。通讯速率可达几M到几十M。

1、 四线的SPI

一般的常用的SPI是四根线:

SDO/MOSI:主设备数据输出,从设备数据输入,如主机读取命令;

SDI/MISO:主设备数据输入,从设备数据输,如从机返回数据;

SCLK: 时钟信号,由主设备产生,用于数据同步;

CS/SS: 从设备使能信号,由主设备控制来选择与哪一个从机进行通讯;
四线是一种全双工
SPI的四线与三线制及二线制_第1张图片
ST的MCU配置四线SPI:
SPI的四线与三线制及二线制_第2张图片
spi.c

#include "spi.h"
#include "main.h"

SPI_HandleTypeDef hspi1;

void MX_SPI1_Init(void)
{
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA4     ------> SPI1_NSS
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
		
	GPIO_InitStruct.Pin = GPIO_PIN_4;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{

  if(spiHandle->Instance==SPI1)
  {
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();

    /**SPI1 GPIO Configuration
    PA4     ------> SPI1_NSS
    PA5     ------> SPI1_SCK
    PA6     ------> SPI1_MISO
    PA7     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
  }
}

unsigned char spiTxBuf[256],spiRxBuf[256];
unsigned char SPI_readNBytes (	unsigned char devAddr, 
                                unsigned char regAddr, 
                                unsigned char readLen, 
                                unsigned char *readBuf)
{
  HAL_GPIO_WritePin(SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_RESET);
  spiTxBuf[0] =   regAddr|0x80;
  for (short k = 1;k< readLen;k++)
  {   
		spiTxBuf[k] = 0xFF;
  }  
  HAL_SPI_TransmitReceive(  &hspi1, spiTxBuf,spiRxBuf,readLen+1,readLen+1);
  HAL_GPIO_WritePin( SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_SET);  
  
  while (readLen > 0)
  {
    readBuf[readLen-1] = spiRxBuf[readLen];
    readLen--;
  }
  return (0);
}

unsigned char SPI_writeNBytes(	unsigned char devAddr, 
                                unsigned char regAddr, 
                                unsigned char writeLen, 
                                unsigned char *writeBuf)
{ hbgnh
  HAL_GPIO_WritePin(SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_RESET);
  spiTxBuf[0] =   regAddr & 0x7F;
  for (int k = 0;k<writeLen;k++)
  {
      spiTxBuf[k+1] =  writeBuf[k];
  }
  HAL_SPI_TransmitReceive(  &hspi1, spiTxBuf,spiRxBuf,writeLen+1,writeLen+1);
  HAL_GPIO_WritePin( SPI_CS_GPIO_Port,SPI_CS_Pin,GPIO_PIN_SET);
	return (0);
}	

总结:当我们程序设置 hspi1.Init.Direction = SPI_DIRECTION_2LINE.
HAL_SPI_TransmitReceive可以在发送的同时也接收。

2、 三线的SPI

但是还有一种SPI通讯为了减少线路和管脚,会采用3线制,网上很多人认为3线制是没有CS片选,这是不对的,真正的SPI三线制通讯模式是指SDO/MOSI与SDI/MISO共用一条总线的通讯方式,采用的是半双工通讯。这里又要说到什么是全双工什么是半双工了。全双工是允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工指可以同时进行信号的双向传输。
半双工是数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。
SPI的四线与三线制及二线制_第3张图片
ST的MCU配置三线SPI:
SPI的四线与三线制及二线制_第4张图片
SPI.c

#include "spi.h"
#include "main.h"

SPI_HandleTypeDef hspi1;

void MX_SPI1_Init(void)
{
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_1LINE; /* 半双工三线SPI */
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI1)
  {
    /* SPI1 clock enable */
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**SPI1 GPIO Configuration
    PA4     ------> SPI1_NSS
    PA5     ------> SPI1_SCK
    PA7     ------> SPI1_MOSI
    */
		
	GPIO_InitStruct.Pin = GPIO_PIN_4;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{

  if(spiHandle->Instance==SPI1)
  {
    /* Peripheral clock disable */
    __HAL_RCC_SPI1_CLK_DISABLE();

    /**SPI1 GPIO Configuration
    PA4     ------> SPI1_NSS
    PA5     ------> SPI1_SCK
    PA7     ------> SPI1_MOSI
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_7);
  }
}

unsigned char spiTxBuf[256],spiRxBuf[256];
unsigned char SPI3_readNBytes (	unsigned char devAddr, 
                                unsigned char regAddr, 
                                unsigned short readLen, 
                                unsigned char *readBuf)
{
  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); /* 拉低片选 */
  spiTxBuf[0] =   regAddr | 0x80;
  HAL_SPI_Transmit(  &hspi1, spiTxBuf, 1, 1); /* 发送(只发送) */
  HAL_SPI_Receive (  &hspi1, readBuf, readLen, readLen); /* 接收(只接收) */
  HAL_GPIO_WritePin( GPIOA,GPIO_PIN_4,GPIO_PIN_SET); /* 拉高片选 */
  return 0;
}

unsigned char SPI3_writeNBytes(	unsigned char devAddr, 
                                unsigned char regAddr, 
                                unsigned short writeLen, 
                                unsigned char *writeBuf)
{
  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); /* 拉低片选 */
  spiTxBuf[0] =   regAddr & 0x7F;
  for (int k = 0;k<writeLen;k++) {
    spiTxBuf[k+1] =  writeBuf[k];
  }
  HAL_SPI_Transmit(  &hspi1, spiTxBuf, writeLen+1, writeLen+1);
  HAL_GPIO_WritePin( GPIOA,GPIO_PIN_4,GPIO_PIN_SET); /* 拉高片选 */
  return 0;
}

总结:当我们程序设置 hspi1.Init.Direction = SPI_DIRECTION_1LINE当使用HAL_SPI_Transmit函数
底层会设置成只发送。当使用HAL_SPI_Receive函数底层会设置成只接收。
具体控制BIDIMODE位
SPI的四线与三线制及二线制_第5张图片

3、两线的SPI

因为SDO/MOSI与SDI/MISO共用了一条总线,所以并不能同时传输,也就是所谓的半双工通讯。而通讯过程中同样要用到CS/SS线进行片选,并不是没有CS/SS。而所谓的不需要CS/SS 线是指的如果SPI线上有一个主机和一个从机的情况下,是可以省略CS/SS片选线的,因为只有1个从机,所以该从机的CS/SS可以设置成常选状态,不能采用CS/SS拉高来作为结束,如果出现数据错误后果会很严重,后面会一直错误,这种3线确实也是3条线,但是跟半双工3线制模式的SPI通讯还是有区别的,往往很多IC所明确的只能采用3线制一般都是指半双工模式。
SPI的四线与三线制及二线制_第6张图片

4、 总结

主要介绍SPI的几种模式及应用

你可能感兴趣的:(STM32,stm32,单片机)