STM32F4通信协议SPI&&IIC工作原理

SPI协议:全双工 3/4根信号线

SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

MISO 主设备数据输入,从设备数据输出。(MISO:master input slave output)
MOSI 主设备数据输出,从设备数据输入。(MOSI:master output slave input)
SCLK时钟信号,由主设备产生。(SCLK:serial clock)
CS从设备片选信号,由主设备控制。(CS:chip select)这个一主多从通信的时候才会用到
在这里插入图片描述
SPI工作原理
(1)硬件上为4根线。
(2)主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
(3)串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
(4)外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。

SPI初始化配置
SPI控制器的初始化步骤有以下几步:
(1)使能SPI复用功能所映射的GPIO口时钟。
(2)SPI控制器时钟使能。
(3)配置GPIO为复用功能。(MOSI/MISO/CLK)
(4)GPIO复用功能为第几复用功能(AFx)。
(5)配置GPIO输出速率为50MHz。
(6)配置SPIx->CR1寄存器。

   //SPI 口初始化
//这里针是对 SPI1 的初始化
void SPI1_Init(void)
{
	    u16 tempreg=0;
	    RCC->AHB1ENR|=1<<0; //使能 PORTA 时钟
	    RCC->APB2ENR|=1<<12; //SPI1 时钟使能
	    GPIO_Set(GPIOB,7<<3,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,
	    GPIO_PUPD_PU); //PB3~5 复用功能输出
	     GPIO_AF_Set(GPIOB,3,5); //PB3,AF5
	    GPIO_AF_Set(GPIOB,4,5); //PB4,AF5
	    GPIO_AF_Set(GPIOB,5,5); //PB5,AF5
	    //这里只针对 SPI 口初始化
	    RCC->APB2RSTR|=1<<12; //复位 SPI1
	    RCC->APB2RSTR&=~(1<<12);//停止复位 SPI1
	    tempreg|=0<<10; //全双工模式
	    tempreg|=1<<9; //软件 nss 管理
	    tempreg|=1<<8;
	    tempreg|=1<<2; //SPI 主机
	    tempreg|=0<<11; //8 位数据格式
	    tempreg|=1<<1; //空闲模式下 SCK 为 1 CPOL=1
	    tempreg|=1<<0; //数据采样从第 2 个时间边沿开始,CPHA=1 
	    ALIENTEK 探索者 STM32F407 开发板教程
	    343
	    STM32F4 开发指南(寄存器版)
	    //对 SPI1 属于 APB2 的外设.时钟频率最大为 84Mhz 频率.
	    tempreg|=7<<3; //Fsck=Fpclk1/256
	    tempreg|=0<<7; //MSB First
	    tempreg|=1<<6; //SPI 启动
	    SPI1->CR1=tempreg; //设置 CR1
	    SPI1->I2SCFGR&=~(1<<11);//选择 SPI 模式
	    SPI1_ReadWriteByte(0xff);//启动传输
    } 
//SPI1 速度设置函数
//SpeedSet:0~7
//SPI 速度=fAPB2/2^(SpeedSet+1)
//fAPB2 时钟一般为 84Mhz
    void SPI1_SetSpeed(u8 SpeedSet)
    {
    SpeedSet&=0X07; //限制范围
    SPI1->CR1&=0XFFC7;
    SPI1->CR1|=SpeedSet<<3; //设置 SPI1 速度
    SPI1->CR1|=1<<6; //SPI 设备使能
    }
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
    u8 SPI1_ReadWriteByte(u8 TxData)
    {
    while((SPI1->SR&1<<1)==0); //等待发送区空
    SPI1->DR=TxData; //发送一个 byte
    while((SPI1->SR&1<<0)==0); //等待接收完一个 byte
    return SPI1->DR; //返回收到的数据
    }

IIC:是一个半双工同步串行通信,用于IIC设备之间通讯的协议。

半双工同步串行通信的意义为:
半双工:同一时间只能发送或者接收(数据脚收发公用)。
同步:有时钟线控制。
串行:数据位是一个一个位发出去。
两条线:一条SDA数据线和一条SCK时钟线

通信速度:
标准IIC(100KHz),快速IIC(400KHz),高速IIC(3.4MHz)。

工作原理:
1)主发送器向从接收器发送数据。主机发送的第一个字节是从机地址,接下来是数据字节。从机每接收一个字节就返回一个应答位。
2)从发送器向主接收器发送数据。主机发送的第一个字节是从机地址,然后从机返回一个应答位。接下来从机向主机发送数据字节。主机每接收一个字节都会返回一个应答位,最后一个字节除外。接收完最后一个字节后,主机返回一个"非应答位"。主机产生所有串行时钟脉冲、起始条件以及停止条件。每一帧都以一个停止条件或一个重复起始条件来结束。由于重复的起始条件也是下一帧的开始,所以将不会释放IIC 总线。

I2C 总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。
开始信号: SCL 为高电平时, SDA 由高电平向低电平跳变,开始传送数据。
结束信号: SCL 为高电平时, SDA 由低电平向高电平跳变,结束传送数据。
应答信号: 接收数据的IIC器件在接收到 8bit 数据后, 向发送数据的主控发出特定的低电平脉冲, 表示已收到数据。主控向被控制的IIC器件发出8bit 数据后,等待被控制的IIC器件发出一个应答信号,主控 接收到应答信号后, 根据实际情况作出是否继续传递数据的判断。若未收到应答信号, 则判断被控制的IIC器件出现故障。 这些信号中,起始信号时必须的, 结束信号和应答信号, 都可以不要。 IIC 总线时序如图所示:

时序图
在这里插入图片描述
关于IIC应答信号说明:
不给应答:只有主机可以不给应答,什么情况可以不给应答?主机接到最后一个数据,它不需要从机再给数据主机了,不给应答(把数据线拉高电平)。
从机收到数据,从机给出应答(谁去接收应答?主机),从机只会给应答0。
主机在时钟线SCL为高电平的时候,读取应答信号
主机收到数据,主机给出应答(谁去接收应答?从机)。给应答信号:代表主机要继续要数据。不给应答:代表主机不再需要数据。

/******************************************************************************
* INCLUDES
*/
#include "stm32f4xx.h"
#include "iic.h"
#include "gpio.h"
#include "delay.h"

//IO方向设置
#define SDA_IN()  {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;}	//PB9输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
#define SCL_H()  (GPIOB->BSRRL = 1<<8)
#define SCL_L()  (GPIOB->BSRRH = 1<<8)
#define SDA_H()  (GPIOB->BSRRL = 1<<9)
#define SDA_L()  (GPIOB->BSRRH = 1<<9)
#define RD_SDA() ((GPIOB->IDR & 1<<9)&&1)

void IIC_init(void)
{					     
  RCC->AHB1ENR|=1<<1;    //使能PORTB时钟	   	  
  GPIO_set(GPIOB,PIN8|PIN9,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PB8/PB9设置 
  SCL_H();
  SDA_H();
}

/******************************************************************************
* 函数名:        IIC_start
* 功能描述:      模拟iic启动信号
******************************************************************************/
void IIC_start(void)
{ 

  SDA_OUT();      //sda线输出
  SDA_H();
  SCL_H();
  SDA_L();
  //快速IIC:延时0.6us  标准IIC: 延时4us
  delay_us(6);
}	
/******************************************************************************
* 函数名:        IIC_stop
* 功能描述:      模拟iic停止信号
******************************************************************************/
void IIC_stop(void)
{
	SDA_OUT();//sda线输出
	SCL_L();
	SDA_L();//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	SCL_H(); 
	delay_us(4);	
    SDA_H();//发送I2C总线结束信号

}
/******************************************************************************
* 函数名:        IIC_waitAck
* 功能描述:      IIC master 等待应答信号
* 参数说明:      1,接收应答失败
*                0,接收应答成功
******************************************************************************/
u8 IIC_waitAck(void)
{

	u8 ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	SDA_H();delay_us(1);	   
	SCL_H();delay_us(1);	 
	while(RD_SDA())
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_stop();
			return 1;
		}
	}
	SCL_L();//时钟输出0 	   
	return 0; 

} 
/******************************************************************************
* 函数名:        IIC_ack
* 功能描述:      给应答信号
******************************************************************************/
void IIC_ack(void)
{

  SCL_L();
  SDA_OUT();
  SDA_H();
	delay_us(2);      
	SDA_L();
	delay_us(3);
	SCL_H();	
	delay_us(4);
	SCL_L();
	delay_us(2); 
	SDA_H();

}
/******************************************************************************
* 函数名:        IIC_nAck
* 功能描述:      不给应答信号
******************************************************************************/
void IIC_nAck(void)
{

  SCL_L();
  SDA_OUT();
  SDA_H();
	delay_us(2);      
	SDA_H();
	delay_us(3);
	SCL_H();	
	delay_us(4);
	SCL_L();
	delay_us(2); 
	SDA_H();

  
}	
/******************************************************************************
* 函数名:        IIC_sendByte
* 功能描述:      发送一个字节 
******************************************************************************/
void IIC_sendByte(u8 txd)
{ 

  u8 i; 
  SDA_OUT(); 	
  for(i=0;i<8;i++)//连续写8个位
  {
    SCL_L();
    if(txd&(1<<(7-i)))
    {
      SDA_H();
    }
    else
    {
      SDA_L();
    }
    //快速IIC:延时1.3us  标准IIC: 延时4.7us
    //delay_us(2);
    delay_us(3);
    SCL_H();
    //快速IIC:延时0.6us  标准IIC: 延时4us
    //delay_us(1);
    delay_us(3);
    SCL_L();
  }
  SDA_H();//释放管脚控制,等待应答
  

} 
/******************************************************************************
* 函数名:        IIC_readByte
* 功能描述:      读取一个字节
* 参数说明:      ack=0时,发送ACK,ack=1,发送nACK
* 返回值说明:    返回读取到的数据
******************************************************************************/
u8 IIC_readByte(unsigned char ack)
{

  unsigned char i,receive=0;
  SDA_IN();//SDA设置为输入
  for(i=0;i<8;i++)//连续的读8个位
  {
    SCL_L();
    //快速IIC:延时1.3us  标准IIC: 延时4.7us		
    delay_us(5);
    SCL_H();
    //快速IIC:延时0.6us  标准IIC: 延时4us		
    delay_us(4);
    if(RD_SDA()==1)
    {
      receive |= (1<<(7-i));
    }
    else
    {
      receive &= ~(1<<(7-i));
    }
    SCL_L();
  }			 
  if (!ack)
    IIC_ack(); //发送ACK   
  else
    IIC_nAck();//发送nACK 
  return receive;

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章