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;
}