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;

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