「STM32 」IIC通訊原理及其實驗


I2C兩線式串行總線通訊協議,它是由飛利浦開發的,主要用於連接微控制器及其外圍設備之間,它是由數據線SDA和信號線SCL構成的,可發送和接收數據即在MUC和I2C設備之間,I2C和I2C之間進行全雙工信號傳輸,高速I2C總線一般可達到400kbps。一般我們也稱為TWI接口。

I2C支持多主機模式:

即在這個主線上可以掛載n個I2C外設。

對於I2C協議,其實也很簡單,不要想的那么復雜,其實就是電平的變換。我們可以人為的分為6個部分

整體時序圖:

各狀態:

  • 空閑狀態

I2C總線的SCK和SDA兩個線同時處於高電平的時候,規定為總線的空閑狀態,這個就是由總線上的上拉電阻把電平拉高的。

  • 起始信號

當SCL為高電平期間,SDA由高電平變成低電平,即為起始信號。啟動信號是一種電平跳變時序信號,不是一個電平信號。

  • 停止信號

當SCL為高電平期間,SDA由低電平變為高電平,即為停止信號。停止信號也是一種電平跳變時序信號,不是一個電平信號。

  • 應答信號

發送器每發送一個字節(8bit)數據,就在時鍾脈沖(SCL)9期間釋放數據線(SDA),再由接收器來反饋一個應答信號,應答信號為低電平的時候,規定為有效應答位(ACK:應答位),表明接收器已經成功的接收了該字節,應答信號為高電平時,規定為非應答位(NACK:非應答位),表示接收器沒有成功的接收該字節。

對於反饋有效應答位(ACK):接收器在第9個時鍾脈沖之前的低電平期間將SDA拉低,並且保證在該時鍾的高電平期間,SDA為穩定的低電平。大家主要看圖,看看是不是這樣的。

要是接收器是主控器,那么它收到最后一個字節后,發送一個NACK信號,以通知被控發送器結束數據的發送,並且釋放SDA線,以便主控接收器發送一個停止信號。

  • 數據的有效性

時鍾信號(SCL)為高電平期間,數據線上的數據必須保持穩定,只有在時鍾信號(SCL)為低電平期間,數據線上的高電平或者低電平才能發生變化。

數據必須在時鍾信號(SCL)的上升沿到來之前就准備好,並且在數據信號的下降沿來到之前必須穩定。

  • 數據的傳輸

在SDA上的每一個位的數據的傳輸都需要一個時鍾脈沖,即在SCL串行時鍾的配合下,SDA上逐位的串行發送每一位數據。數據位的傳輸是邊沿觸發。

示例代碼講解

  • 初始化IIC

其實就是對兩個線的初始化,我這里使用的是PA6和PA7,開始都設置為輸出,中途會改變PA7的輸入輸出屬性,小平頭,在空閑狀態,我們知道SCL和SDA是被拉高的,所以這個地方我們給高電平。

  • 產生IIC起始信號

將SDA線設置為輸出,然后SDA和SCL都設置為高電平,延遲4us,然后將SDA拉低,延遲4us,最后將SCL拉低。這其實就是協議規定的動作了。

  • 產生IIC停止信號

同樣的道理,和協議的時序保持一致就好了。

  • 等待應答信號到來

首先我們需要把SDA設置為輸入,因為接收方要給發射方返回一個應答信號的。小平頭 就是在SCL第9個高電平的時候,釋放信號線,先拉高,然后持續等待,是不是有應答信號返回,其實就是返回一個低電平,所以我們一直在檢測READ_SDA這個電平,持續一段時間,要是沒有返回的話,我們認為超時了,就直接停止協議了,

  • 產生應答信號

即在第9個時鍾周期內,SDA都為低電平,為應答

  • 不產生應答信號

即在第9個時鍾周期內,SDA都為高電平,為不應答

  • IIC發送一個字節

發送數據,SDA設置為輸出。SCL拉低,SDA准備。

做一個8次循環,拿出1byte的數據,將你發送的數據和0x80作與運算,拿出最高位,然后右移7位,將這個數據放到最低位,這個數據要是1的話,那么SDA輸出一個高電平,要是與后的結果為低電平的話,那么SDA輸出一個低電平。這都屬於准備發送信號階段。

然后SCL拉高,完成數據的發送,然后SCL拉低,此時SDA也就可以拉低了,接着開始次高位的傳輸,直到傳輸完成。

  • 讀取數據

讀取數據,SDA要設置為輸入了。SCL開始為低電平,然后SCL為高電平,我們開始讀SDA上的數據,然后左移數據,將讀取的數據放在低位。然后檢測一下有沒有應答。

其實基本思路就是這樣了。我把源碼附上:

i2c.h

#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
#include "stm32f10x_gpio.h"
//IO方向設置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函數
#define IIC_SCL PBout(6) //SCL
#define IIC_SDA PBout(7) //SDA
#define READ_SDA PBin(7) //輸入SDA
//IIC所有操作函數
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //發送IIC開始信號
void IIC_Stop(void); //發送IIC停止信號
void IIC_Send_Byte(u8 txd); //IIC發送一個字節
u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個字節
u8 IIC_Wait_Ack(void); //IIC等待ACK信號
void IIC_Ack(void); //IIC發送ACK信號
void IIC_NAck(void); //IIC不發送ACK信號
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif

  

 本文轉自小平頭電子技術社區,轉載請注明出處:https://www.xiaopingtou.cn/article-104115.html

 

i2c.c

#include "myiic.h"
#include "delay.h"
//初始化IIC
void IIC_Init(void)
{					 
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB時鍾
	 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 輸出高
}
//產生IIC起始信號
void IIC_Start(void)
{
	SDA_OUT(); //sda線輸出
	IIC_SDA=1;	 	 
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//鉗住I2C總線,准備發送或接收數據 
}	 
//產生IIC停止信號
void IIC_Stop(void)
{
	SDA_OUT();//sda線輸出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//發送I2C總線結束信號
	delay_us(4);							 	
}
//等待應答信號到來
//返回值:1,接收應答失敗
// 0,接收應答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN(); //SDA設置為輸入 
	IIC_SDA=1;delay_us(1);	 
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//時鍾輸出0 	 
	return 0; 
} 
//產生ACK應答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不產生ACK應答		 
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				 
//IIC發送一個字節
//返回從機有無應答
//1,有應答
//0,無應答			 
void IIC_Send_Byte(u8 txd)
{ 
 u8 t; 
	SDA_OUT(); 	 
 IIC_SCL=0;//拉低時鍾開始數據傳輸
 for(t=0;t<8;t++)
 { 
 //IIC_SDA=(txd&0x80)>>7;
		if((txd&0x80)>>7)
			IIC_SDA=1;
		else
			IIC_SDA=0;
		txd<<=1; 	 
		delay_us(2); 
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
 }	 
} 	 
//讀1個字節,ack=1時,發送ACK,ack=0,發送nACK 
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA設置為輸入
 for(i=0;i<8;i++ )
	{
 IIC_SCL=0; 
 delay_us(2);
		IIC_SCL=1;
 receive<<=1;
 if(READ_SDA)receive++; 
		delay_us(1); 
 }					 
 if (!ack)
 IIC_NAck();//發送nACK
 else
 IIC_Ack(); //發送ACK 
 return receive;
}

  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM