STM32軟體模擬IIC
IIC匯流排
一、與IIC有關的知識
(1)IIC屬於半雙工通訊方式
(2)IIC的協議
1.空閒狀態:IIC的SCL和SDA兩條線均處於高電平狀態,此時即釋放匯流排
2.起始訊號(Start):SCL為高電平期間,SDA產生一個下降沿訊號
3.停止訊號(End):SCL為高電平期間,SDA產生一個上升沿訊號
4.應答訊號ACK: 傳送器每傳送一個位元組,就在時鐘脈衝9期間釋放資料線,由接收器反饋一個應答訊號。應答訊號為低電平時,規定為有效應答位(ACK簡稱應答位),表示接收器已經成功地接收了該位元組;應答訊號為高電平時,規定為非應答位(NACK),一般表示接收器接收該位元組沒有成功。
5.資料有效性:I2C匯流排進行資料傳送時,時鐘訊號為高電平期間,資料線上的資料必須保持穩定,只有在時鐘線上的訊號為低電平期間,資料線上的高電平或低電平狀態才允許變化。即:資料在SCL的上升沿到來之前就需準備好。並在在下降沿到來之前必須穩定。
6.資料傳輸:在I2C總線上傳送的每一位資料都有一個時鐘脈衝相對應(或同步控制),即在SCL序列時鐘的配合下,在SDA上逐位地序列傳送每一位資料。資料位的傳輸是邊沿觸發。
(3)24C02
1.寫資料流程
第一步,首先是 I2C 的起始訊號,接著跟上首位元組,也就是我們前邊講的 I2C 的器件地
址,並且在讀寫方向上選擇“寫”操作。
第二步,傳送資料的儲存地址。24C02 一共256 個位元組的儲存空間,地址從0x00~0xFF,
我們想把資料儲存在哪個位置,此刻寫的就是哪個地址。
第三步,傳送要儲存的資料第一個位元組、第二個位元組„„注意在寫資料的過程中,
EEPROM 每個位元組都會迴應一個“應答位 0”,來告訴我們寫 EEPROM 資料成功,如果沒有
迴應答位,說明寫入不成功。字
2.讀資料流程:
第一步,首先是 I2C 的起始訊號,接著跟上首位元組,也就是我們前邊講的 I2C 的器件地
址,並且在讀寫方向上選擇“寫”操作。這個地方可能有同學會詫異,我們明明是讀資料為
何方向也要選“寫”呢?剛才說過了, 24C02 一共有 256 個地址,我們選擇寫操作,是為了
把所要讀的資料的儲存地址先寫進去,告訴 EEPROM 我們要讀取哪個地址的資料。這就如
同我們打電話,先撥總機號碼( EEPROM 器件地址),而後還要繼續撥分機號碼(資料地址),
而撥分機號碼這個動作,主機仍然是傳送方,方向依然是“寫”。
第二步,傳送要讀取的資料的地址,注意是地址而非存在 EEPROM 中的資料,通知
EEPROM 我要哪個分機的資訊。
第三步,重新發送 I2C 起始訊號和器件地址,並且在方向位選擇“讀”操作。
這三步當中,每一個位元組實際上都是在“寫”,所以每一個位元組EEPROM 都會迴應一個
“應答位0”。
第四步,讀取從器件發回的資料,讀一個位元組,如果還想繼續讀下一個位元組,就傳送一
個“應答位 ACK(0)”,如果不想讀了,告訴EEPROM,我不想要資料了,別再發資料了,那
就傳送一個“非應答位 NAK(1)”
3.24C02地址
24C02的 7 位地址中,其中高 4 位是固定的 0b1010,而低 3 位的地址取決於具體電路的設計,由晶片上的 A2、 A1、 A0 這 3 個引腳的實際電平決定,高為1,低為0.最低位是資料方向位(R/W)0表示接下來要傳送資料(寫),1表示接下來是請求資料(讀)。
二、用到的知識
1. GPIO、串列埠
2. IIC、24C02
三、功能
先在24c02的一個地址中寫入一個字元,然後在讀取該位置的字元並通過串列埠顯示出來。
#include "iic.h"
#define iicsda PBout(7)
#define iicscl PBout(6)
#define iicsdain PBin(7)
void SDAOUT(void)//sda×÷ΪÍÆÍìÊä³ö
{
GPIO_InitTypeDef GPIO_InitTypeStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_7;
GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitTypeStruct);
}
void SDAIN(void)//SDA×÷Ϊ¸¡¿ÕÊäÈë
{
GPIO_InitTypeDef GPIO_InitTypeStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_7;
GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitTypeStruct);
}
void Myiic_INIT()//IIC³õʼ»¯
{
GPIO_InitTypeDef GPIO_InitTypeStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitTypeStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitTypeStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitTypeStruct);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
}
void Myiic_Start(void)//ÆðʼÐźÅ
{
SDAOUT();
iicsda=1;
iicscl=1;
delay_us(4);
iicsda=0;
delay_us(4);
iicscl=0;
}
void Myiic_Stop(void)//Í£Ö¹ÐźÅ
{
SDAOUT();
iicscl=0;
iicsda=0;
delay_us(4);
iicscl=1;
iicsda=1;
delay_us(4);
}
u8 Myiic_Wait_Ack(void)//µÈ´ýACK
{
u8 time=0;
SDAIN();
iicsda=1;
delay_us(1);
iicscl=1;
delay_us(1);
while(iicsdain)
{
time++;
if(time>250)
{
Myiic_Stop();
return 1;
}
}
iicscl=0;
return 0;
}
void Myiic_ACK(void)//²úÉúACKÓ¦´ð
{
iicscl=0;
SDAOUT();
iicsda=0;
delay_us(2);
iicscl=1;
delay_us(2);
iicscl=0;
}
void Myiic_NACK(void)//²»²úÉúÓ¦´ðλ
{
iicscl=0;
SDAOUT();
iicsda=1;
delay_us(2);
iicscl=1;
delay_us(2);
iicscl=0;
}
void Myiic_Sendbyte(u8 w)//·¢ËÍÒ»¸ö×Ö½Ú
{
u8 i;
SDAOUT();
iicscl=0;
for(i=0;i<8;i++)
{
iicsda=(w&0x80)>>7;
w<<=1;
delay_us(2);
iicscl=1;
delay_us(2);
iicscl=0;
delay_us(2);
}
}
u8 Myiic_Readbyte(unsigned char ack)//¶ÁÈ¡Ò»¸ö×Ö½Ú
{
unsigned char i=0,r=0;
SDAIN();
for(i=0;i<8;i++)
{
iicscl=0;
delay_us(2);
iicscl=1;
r<<=1;
if(iicsdain)
r++;
delay_us(1);
}
if(!ack)
Myiic_NACK();
else
Myiic_ACK();
return r;
}