STM32的IIC应用详解3
分享:
?這兩天將STM32的IIC按照原子哥的程序,大致走了一遍,多少對IIC不是那么地陌生了,也多少有了自己的一些感悟,在這里,將這兩天的學習的一個大致步驟總結下,一來可以讓自己形成一個清晰地思路,二來,希望能給一些新手一點啟發。
? 首先IIC是一種通信協議,通信方式相對比較簡單,主要有兩條線,SDA,SCL。SDA是串行數據線,上面走命令和數據,而SCL只是一條時鐘線,其保證數據是按照時鐘節拍來進行傳輸。IIC上面可以外掛很多的IIC芯片,每一個芯片對應著不同的地址,通過地址來將不同的芯片進行分開,保證不同芯片之間的數據傳輸,由于每一個芯片都是可以獨立地收發,故,每一個芯片都是主機/從機。接下來,就是數據的傳輸過程了,
? 大致的一個數據傳輸流程是:主機向SDA線上發送一個起始信號,表示有信號進行傳輸,此時所有連接到IIC總線上的芯片都處于接收狀態,接下來,主機發送想要與其進行數據傳輸的從機地址信號,所有的從機都會接收到該地址信號并和自己固有的地址信號進行匹配,當配對成功時,接下來就在時鐘信號的帶動下進行數據傳輸,數據的傳輸是按照每8位一個單元進行數據的傳輸。每一位的傳輸過程中,在SCL高電平期間,一定要保證SDA數值的穩定,否則會出現出錯的情況,SDA數值的改變發生在SCL的低電平期間。最終8位全部傳輸完畢,從機產生一個應答信號給主機,主機在接收到該應答信號后決定接下來是發送一組新的數據還是終止發送。
? 大致按照傳輸的一個流程和模擬IIC的源碼來對數據傳輸過程進行分析:
分析之前,將一些定義告訴大家,免得有疑問:
//I/O方向設置
#define SDA_OUT() {GPIOB->CRL &= 0X0FFFFFFF;GPIOB->CRL |= 3<<28;} //0011 通用推挽輸出,50M
#define SDA_IN() {GPIOB->CRL &= 0X0FFFFFFF;GPIOB->CRL |= 8<<28;} //1000 上拉/下拉輸入
?
//I/O操作函數
#define IIC_SCL PBout(6)
#define IIC_SDA PBout(7)
#define READ_SDA PBin(7)
?
其中SDA_IN()、?SDA_OUT()設置了I/O口的方向,也就是SDA是輸入那?還是輸出那?
IIC_SCL ? 是SCL輸出管腳
IIC_SDA ? 是SDA輸出管腳
READ_SDA ?讀取SDA輸入引腳,等待應答信號時要用。
?
OK!接下來就磨刀霍霍,開始分析數據傳輸過程
1、起始信號
首先,SDA、SCL線默認是高,表示總線處于空閑狀態,接著SDA線被主機拉低,表示主機有信號進行傳輸,要么是發數據,或者是要進行讀數據,當SDA線拉低之后,SCL線也同樣被拉低,準備接下來的數據傳輸。用了原子哥的模擬IIC,此開始信號的產生如下:
聲明:此時將CPU當做了主機,而AT24C02當做了從機。
//起始信號
void IIC_Start(void)
{
SDA_OUT(); //設為主機輸出
IIC_SDA = 1;
IIC_SCL = 1;
delay_us(4);
IIC_SDA = 0; //scl為高電平時,SDA出現低電平跳變,表示傳輸開始
delay_us(5);
IIC_SCL = 0; ?? //鉗位SCL,方便接下來進行數據傳輸
?
}
此程序產生的波形就是上面起始信號所對應的波形。
2、終止信號
終止信號:就是在SCL為高電平時,SDC出現一個上升沿的跳變,即表示終止信號
源程序如下:
//終止信號
void IIC_Stop(void)
{
SDA_OUT(); ?? //設為主機輸出
IIC_SDA = 0;
IIC_SCL = 1;
delay_us(2); ??//scl為高電平,SDA出現高電平跳變,表示傳輸結束
IIC_SDA = 1;
?
}
3、等待應答信號
? 主機將一組數據發送完畢,接下來就進入等待從機發送応答信號的到來,從機會在第9個時鐘信號時,發送該應答信號,此應答信號就是將SDA信號線拉低,而又由于SDA、SCL為開漏輸出,故支持線與的形式,當從機那邊將SDA線拉低,而主機這邊依舊是將SDA線置高,主機通過判斷SDA線的高低電平就可以知道是否收到了該應答信號,當然,接收到該應答信號的前提是,使能了從機的應答功能,否則接受不到,之能通過每組發送完畢的一個長延時來保證從機接收數據完畢。
//等待應答信號到來
//返回值:1,接收應答失敗
// ? ? ? ?0,接收應答成功
u8 IIC_Wait_Ack(void)
{
u16 Errtime=0;
SDA_IN(); //此時設置為輸入
IIC_SCL = 1;
IIC_SDA = 1; //線與的關系
if(READ_SDA)
{
Errtime++;
if(Errtime > 250)
{
IIC_Stop();
return 1;
}
}
else
IIC_SCL = 0; //鉗位,方便下次傳輸
return 0;
}
程序中需要注意的地方是,雖然設置了GPIO的方向為輸入,但是GPIO的電平還是可以設置的,沒有矛盾,GPIO方向由CRL ?CRH來決定,輸出電平由ODR寄存器來決定
?
4、產生應答信號
如上所說,從機可以選擇產生應答信號,同樣也可以選擇不產生應答信號。
產生應答辛號就是從機將SDA線拉低,不產生應答辛號就是SDA線一直保持高電平
//產生應答信號
void IIC_Ack(void)
{
SDA_OUT(); ?//此時,相當于主機在接收數據,是被動方
IIC_SCL = 0;
IIC_SDA = 0;
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
}
需要注意的是,此時,CUP相當于是從機,故I/O方向為輸出
5、不產生應答信號
//不產生應答信號
void IIC_NAck(void)
{
SDA_OUT(); ?//此時,相當于主機在接收數據,是被動方
IIC_SCL = 0;
IIC_SDA = 1;
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
?
}
道理同上
?
6、寫一個字節
按照該時序圖就可以寫數據了,流程如下:
?主機:起始信號----->第一位------>第二位---------> ? ........ ? ?-------->第八位---------> ? 等待應答信號------->停止信號
SDA數據的有效性,在上面已經分析過,就是在SCL高電平期間,SDA數據不能改變,這樣才是一個有效數據,否則無效
? ? ? SDA只能在SCL低電平期間改變
//發送一個字節數據
void IIC_Send_Byte(u8 txd)
{
u8 i;
SDA_OUT();
IIC_SCL=0;//拉低時鐘開始數據傳輸
for(i=0;i<8;i++)
{
if(txd & 0x80)
IIC_SDA = 1;
else
IIC_SDA = 0;
txd <<= 1;
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
delay_us(2);
}
}?
高位先發,程序中嚴格保證了SDA的有效性
7、讀一個字節
此時CPU相當于是從機,而芯片才是主機,主機在發數據,從機在接受
//讀一個字節數據 ? ack = 1 時,產生應答 ?? ack = 0 ?,不產生應答
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();
for(i=0;i<8;i++)
{
IIC_SCL = 0;
delay_us(2);
IIC_SCL = 1; ??//高電平時數據已經穩定,故進行數據的讀取
receive <<= 1;
if(READ_SDA)
receive++;
}
if(ack)
IIC_Ack();
else
IIC_NAck();
return receive;
}
以上就是模擬IIC的函數
?
接下來就要針對AT24C02來進行程序的編寫
1、在AT24C02指定地址讀出一個數據
先來看AT24C02的隨機讀數據的時序圖
此時序圖上需要注意兩個地方,箭頭所標記的兩次開始信號,以及橢圓圈起來的寫信號和讀信號,接下來就分析該過程
首先,發送一個開始信號,接下來發送器件的地址,注意最后一位為Write標志位(0),等待應答,然后發送要讀的地址,等待應答,又要發送一個起始信號,發送器件的地址,注意此時,最后一位為Read標志位(1),等待應答,接下來數據就可以讀回來了。
器件的地址:
地址按照這個來進行設置,AT24CXX,這個XX即表示存儲量XX(K),很顯然AT24C02為2K,有256Byte,A2,A1,A0定義看芯片,如下
?
故,AT24C02的寫地址為0XA0,讀地址為0XA1,到這里,任督二脈已打通,接下來,上源碼
//在AT24CXX指定地址讀出一個數據
//ReadAddr:開始讀數的地址 ? 0-255
//返回值 ?:讀到的數據
u8 AT24C02_ReadOneByte(u8 ReadAddr)
{
u8 temp;
?
IIC_Start();
IIC_Send_Byte(0XA0); //發送器件地址
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr); //發送首地址
IIC_Wait_Ack();
?
IIC_Start();? ?? //注意,這個需要一個新的起始信號
IIC_Send_Byte(0XA1);
IIC_Wait_Ack();
?
temp = IIC_Read_Byte(0); //不產生應答
IIC_Stop();
return temp;
}
2、在AT24C02指定地址寫入一個數據
有了上面讀的分析,這里應該可以看懂,注意箭頭那位就好,嚴格按照這個流程來就好了
//在AT24CXX指定地址寫入一個數據
//WriteAddr ?:寫入數據的目的地址 ? ?
//DataToWrite:要寫入的數據
void AT24C02_WriteOneByte(u8 WriteAddr,u8 DataToWrite)
{
IIC_Start();
IIC_Send_Byte(0XA0); //發送器件地址
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr); //發送首地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite);
IIC_Wait_Ack();
IIC_Stop();
?
delay_ms(10); ?//這個延時是必須的,保證下次發送之前,上次的數據已經被接收
}
需要注意的地方是這個延時,紅色標記,因為后面要用到這個進行連續的寫操作,故這里加一個延時,保證下次寫之前,上次的數據已經被完全接收,否則有可能會出錯
?
3、在AT24C02里面的指定地址開始寫入長度為Len的數據
直接調用上面的在指定位置寫數據的函數即可
//在AT24CXX里面的指定地址開始寫入長度為Len的數據
//該函數用于寫入16bit或者32bit的數據.
//WriteAddr ?:開始寫入的地址 ?
//DataToWrite:數據數組首地址
//Len ? ? ? ?:要寫入數據的長度2,4
void AT24C02_WriteLenByte(u16 WriteAddr,u32 DataToWtie,u8 Len)
{
u8 i;
for(i=0;i<Len;i++)
{
AT24C02_WriteOneByte(WriteAddr + i,(DataToWtie >> (8*i)) & 0XFF);
}
?
}
需要注意的是,此時將數據的高位寫到了后面地址處,所以接下來讀數據的時候,要從后面開始讀,免得出錯
4、在AT24C02里面的指定地址開始讀取長度為Len的數據
同上,直接調用上面在指定位置讀取數據的函數即可
//在AT24CXX里面的指定地址開始讀取長度為Len的數據
//該函數用于讀取16bit或者32bit的數據.
//WriteAddr ?:開始寫入的地址 ?
//Len ? ? ? ?:要寫入數據的長度2,4
u32 AT24C02_ReadLenByte(u16 WriteAddr,u8 Len)
{
u8 i;
u32 temp=0;
for(i=0;i<Len;i++)
{
temp <<= 8;?
temp += AT24C02_ReadOneByte(WriteAddr + Len - i - 1);
}
return temp;
}
注意,先讀的是后面的數據
5、校驗IIC芯片是否在,是否正常
通過IIC芯片的最后一個地址255,寫入數據0X55,當能讀回這個數據時,說明芯片工作正常,否則不正常
源碼如下
//檢查AT24C02是否正常
//這里用了2402的最后一個地址(255)來存儲標志字.0x55
//返回1:檢測失敗
//返回0:檢測成功
u8 AT24C02_Check(void)
{
u8 temp=0;
temp = AT24C02_ReadOneByte(255);
if(temp == 0X55)
{
return 0;
}
else
{
AT24C02_WriteOneByte(255,0X55);
temp = AT24C02_ReadOneByte(255);
if(temp == 0X55)
return 0;
?
}
return 1;
}
上來直接讀,可以免得每次都要寫,寫一次就好了,其他時間都是讀她就行
?
6、在AT24C02里面的指定地址開始讀出指定個數的數據
?
依舊是調用上面的,不多說了,要餓死了zzzzzzzzz
源碼:
//在AT24C02里面的指定地址開始讀出指定個數的數據
//ReadAddr :開始讀出的地址 對24c02為0~255
//pBuffer ?:數據數組首地址
//NumToRead:要讀出數據的個數
void AT24C02_Read(u8 ReadAddr,u8 *pBuffer,u8 NumToRead)
{
while(NumToRead)
{
*pBuffer++ = AT24C02_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
7、在AT24C02里面的指定地址開始寫指定個數的數據
源碼:
//在AT24C02里面的指定地址開始寫指定個數的數據
//WriteAddr :開始寫數據的地址 對24c02為0~255
//pBuffer ?:數據數組首地址
//NumToWrite:要寫數據的個數
void AT24C02_Write(u8 WriteAddr,u8 *pBuffer,u8 NumToWrite)
{
while(NumToWrite)
{
AT24C02_WriteOneByte(WriteAddr++,*pBuffer++);
NumToWrite--;
}?
}?
看好DATASHEET是關鍵,我所獲得的有很多都是來自于網絡,所以要記得回饋,羊有跪乳之恩,只是當今時代發展太快,都忘記了感恩,心懷感恩,世界是這么美好,希望也能給你一點幫助吧!
總結
以上是生活随笔為你收集整理的STM32的IIC应用详解3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 已解决:IAR编译时出现duplicat
- 下一篇: STM32F1与STM32F0在GPIO