日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【STM32】IIC的基本原理(实例:普通IO口模拟IIC时序读取24C02)(转载)

發(fā)布時(shí)間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【STM32】IIC的基本原理(实例:普通IO口模拟IIC时序读取24C02)(转载) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
版權(quán)聲明:本文為博主原創(chuàng)文章,允許轉(zhuǎn)載,但希望標(biāo)注轉(zhuǎn)載來源。 https://blog.csdn.net/qq_38410730/article/details/80312357

IIC的基本介紹

IIC的簡介

IIC(Inter-Integrated Circuit)總線是一種由PHILIPS公司在80年代開發(fā)的兩線式串行總線,用于連接微控制器及其外圍設(shè)備。它是半雙工通信方式。

  • IIC總線最主要的優(yōu)點(diǎn)是其簡單性和有效性。由于接口直接在組件之上,因此IIC總線占用的空間非常小,減少了電路板的空間和芯片管腳的數(shù)量,降低了互聯(lián)成本。總線的長度可高達(dá)25英尺,并且能夠以10Kbps的最大傳輸速率支持40個(gè)組件。
  • IIC總線的另一個(gè)優(yōu)點(diǎn)是,它支持多主控(multimastering), 其中任何能夠進(jìn)行發(fā)送和接收的設(shè)備都可以成為主總線。一個(gè)主控能夠控制信號(hào)的傳輸和時(shí)鐘頻率。當(dāng)然,在任何時(shí)間點(diǎn)上只能有一個(gè)主控。

IIC串行總線一般有兩根信號(hào)線,一根是雙向的數(shù)據(jù)線SDA,另一根是時(shí)鐘線SCL,其時(shí)鐘信號(hào)是由主控器件產(chǎn)生。所有接到IIC總線設(shè)備上的串行數(shù)據(jù)SDA都接到總線的SDA上,各設(shè)備的時(shí)鐘線SCL接到總線的SCL上。對于并聯(lián)在一條總線上的每個(gè)IC都有唯一的地址。

一般情況下,數(shù)據(jù)線SDA和時(shí)鐘線SCL都是處于上拉電阻狀態(tài)。因?yàn)?#xff1a;在總線空閑狀態(tài)時(shí),這兩根線一般被上面所接的上拉電阻拉高,保持著高電平。

STM32的IIC接口

目前絕大多數(shù)的MCU都附帶IIC總線接口,STM32也不例外。但是在本文中,我們不使用STM32的硬件IIC來讀取24C02,而是通過軟件的方式來模擬。

原因是因?yàn)?#xff1a;STM32的硬件IIC非常復(fù)雜,更重要的是它并不穩(wěn)定,故不推薦使用。

?

IIC協(xié)議

IIC總線在傳輸數(shù)據(jù)的過程中一共有三種類型信號(hào),分別為:開始信號(hào)、結(jié)束信號(hào)和應(yīng)答信號(hào)。這些信號(hào)中,起始信號(hào)是必需的,結(jié)束信號(hào)和應(yīng)答信號(hào),都可以不要。同時(shí)我們還要介紹其空閑狀態(tài)、數(shù)據(jù)的有效性、數(shù)據(jù)傳輸。

先來看一下IIC總線的時(shí)序圖:

這可能會(huì)比較復(fù)雜,可以先看一份簡化了的時(shí)序圖:

空閑狀態(tài)

當(dāng)IIC總線的數(shù)據(jù)線SDA和時(shí)鐘線SCL兩條信號(hào)線同時(shí)處于高電平時(shí),規(guī)定為總線的空閑狀態(tài)。此時(shí)各個(gè)器件的輸出級(jí)場效應(yīng)管均處在截止?fàn)顟B(tài),即釋放總線,由兩條信號(hào)線各自的上拉電阻把電平拉高。?

起始信號(hào)與停止信號(hào)

  • 起始信號(hào):當(dāng)時(shí)鐘線SCL為高期間,數(shù)據(jù)線SDA由高到低的跳變;啟動(dòng)信號(hào)是一種電平跳變時(shí)序信號(hào),而不是一個(gè)電平信號(hào);
  • 停止信號(hào):當(dāng)時(shí)鐘線SCL為高期間,數(shù)據(jù)線SDA由低到高的跳變;停止信號(hào)也是一種電平跳變時(shí)序信號(hào),而不是一個(gè)電平信號(hào)。

應(yīng)答信號(hào)

發(fā)送器每發(fā)送一個(gè)字節(jié)(8個(gè)bit),就在時(shí)鐘脈沖9期間釋放數(shù)據(jù)線,由接收器反饋一個(gè)應(yīng)答信號(hào)。?

  • 應(yīng)答信號(hào)為低電平時(shí),規(guī)定為有效應(yīng)答位(ACK,簡稱應(yīng)答位),表示接收器已經(jīng)成功地接收了該字節(jié);
  • 應(yīng)答信號(hào)為高電平時(shí),規(guī)定為非應(yīng)答位(NACK),一般表示接收器接收該字節(jié)沒有成功。?

對于反饋有效應(yīng)答位ACK的要求是:接收器在第9個(gè)時(shí)鐘脈沖之前的低電平期間將數(shù)據(jù)線SDA拉低,并且確保在該時(shí)鐘的高電平期間為穩(wěn)定的低電平。?如果接收器是主控器,則在它收到最后一個(gè)字節(jié)后,發(fā)送一個(gè)NACK信號(hào),以通知被控發(fā)送器結(jié)束數(shù)據(jù)發(fā)送,并釋放數(shù)據(jù)線SDA,以便主控接收器發(fā)送一個(gè)停止信號(hào)P。

數(shù)據(jù)有效性

IIC總線進(jìn)行數(shù)據(jù)傳送時(shí),時(shí)鐘信號(hào)為高電平期間,數(shù)據(jù)線上的數(shù)據(jù)必須保持穩(wěn)定;只有在時(shí)鐘線上的信號(hào)為低電平期間,數(shù)據(jù)線上的高電平或低電平狀態(tài)才允許變化。?

即:數(shù)據(jù)在時(shí)鐘線SCL的上升沿到來之前就需準(zhǔn)備好。并在在下降沿到來之前必須穩(wěn)定。

數(shù)據(jù)的傳達(dá)

在IIC總線上傳送的每一位數(shù)據(jù)都有一個(gè)時(shí)鐘脈沖相對應(yīng)(或同步控制),即在SCL串行時(shí)鐘的配合下,在SDA上逐位地串行傳送每一位數(shù)據(jù)。數(shù)據(jù)位的傳輸是邊沿觸發(fā)。

延時(shí)時(shí)間

?

IIC總線的數(shù)據(jù)傳送

IIC總線上的每一個(gè)設(shè)備都可以作為主設(shè)備或者從設(shè)備,而且每一個(gè)設(shè)備都會(huì)對應(yīng)一個(gè)唯一的地址(地址通過物理接地或者拉高),主從設(shè)備之間就通過這個(gè)地址來確定與哪個(gè)器件進(jìn)行通信,在通常的應(yīng)用中,我們把CPU帶I2C總線接口的模塊作為主設(shè)備,把掛接在總線上的其他設(shè)備都作為從設(shè)備。

也就是說,主設(shè)備在傳輸有效數(shù)據(jù)之前要先指定從設(shè)備的地址,地址指定的過程和上面數(shù)據(jù)傳輸?shù)倪^程一樣,只不過大多數(shù)從設(shè)備的地址是7位的,然后協(xié)議規(guī)定再給地址添加一個(gè)最低位用來表示接下來數(shù)據(jù)傳輸?shù)姆较?#xff0c;0表示主設(shè)備向從設(shè)備寫數(shù)據(jù),1表示主設(shè)備向從設(shè)備讀數(shù)據(jù)。

  • 主設(shè)備往從設(shè)備中寫數(shù)據(jù)。數(shù)據(jù)傳輸格式如下:

淡藍(lán)色部分表示數(shù)據(jù)由主機(jī)向從機(jī)傳送,粉紅色部分則表示數(shù)據(jù)由從機(jī)向主機(jī)傳送。

寫用0來表示(低電平),讀用1來表示(高電平)。

  • 主設(shè)備從從設(shè)備中讀數(shù)據(jù)。數(shù)據(jù)傳輸格式如下:

在從機(jī)產(chǎn)生響應(yīng)時(shí),主機(jī)從發(fā)送變成接收,從機(jī)從接收變成發(fā)送。之后,數(shù)據(jù)由從機(jī)發(fā)送,主機(jī)接收,每個(gè)應(yīng)答由主機(jī)產(chǎn)生,時(shí)鐘信號(hào)仍由主機(jī)產(chǎn)生。若主機(jī)要終止本次傳輸,則發(fā)送一個(gè)非應(yīng)答信號(hào),接著主機(jī)產(chǎn)生停止條件。

  • ?主設(shè)備往從設(shè)備中寫數(shù)據(jù),然后重啟起始條件,緊接著從從設(shè)備中讀取數(shù)據(jù);或者是主設(shè)備從從設(shè)備中讀數(shù)據(jù),然后重啟起始條件,緊接著主設(shè)備往從設(shè)備中寫數(shù)據(jù)。數(shù)據(jù)傳輸格式如下:

在多主的通信系統(tǒng)中,總線上有多個(gè)節(jié)點(diǎn),它們都有自己的尋址地址,可以作為從節(jié)點(diǎn)被別的節(jié)點(diǎn)訪問,同時(shí)它們都可以作為主節(jié)點(diǎn)向其它的節(jié)點(diǎn)發(fā)送控制字節(jié)和傳送數(shù)據(jù)。但是如果有兩個(gè)或兩個(gè)以上的節(jié)點(diǎn)都向總線上發(fā)送啟動(dòng)信號(hào)并開始傳送數(shù)據(jù),這樣就形成了沖突。要解決這種沖突,就要進(jìn)行仲裁的判決,這就是I2C總線上的仲裁。

I2C總線上的仲裁分兩部分:SCL線的同步和SDA線的仲裁。

這部分就暫時(shí)不介紹了,想要了解:可以參考鏈接淺談I2C總線或I2C總線協(xié)議圖解。

?

IIC底層驅(qū)動(dòng)程序分析

現(xiàn)擬采用PB6、PB7來模擬IIC時(shí)序,其中:PB6為時(shí)鐘線,PB7為數(shù)據(jù)線。

首先進(jìn)行一些必要的宏定義:

  • //IO方向設(shè)置
  • #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
  • #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
  • //IO操作函數(shù)
  • #define IIC_SCL PBout(6) //SCL
  • #define IIC_SDA PBout(7) //SDA
  • #define READ_SDA PBin(7) //輸入SDA
  • //IIC所有操作函數(shù)
  • void IIC_Init(void); //初始化IIC的IO口
  • void IIC_Start(void); //發(fā)送IIC開始信號(hào)
  • void IIC_Stop(void); //發(fā)送IIC停止信號(hào)
  • void IIC_Send_Byte(u8 txd); //IIC發(fā)送一個(gè)字節(jié)
  • u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個(gè)字節(jié)
  • u8 IIC_Wait_Ack(void); //IIC等待ACK信號(hào)
  • void IIC_Ack(void); //IIC發(fā)送ACK信號(hào)
  • void IIC_NAck(void); //IIC不發(fā)送ACK信號(hào)
  • 由于IIC是半雙工通信方式,因而數(shù)據(jù)線SDA可能會(huì)數(shù)據(jù)輸入,也可能是數(shù)據(jù)輸出,需要定義IIC_SDA來進(jìn)行輸出、READ_SDA來進(jìn)行輸入,與此同時(shí)就要對IO口進(jìn)行模式配置:SDA_IN()和SDA_OUT()。

    而時(shí)鐘線SCL一直是輸出的,所以就沒有數(shù)據(jù)線SDA麻煩了。

  • //初始化IIC
  • void IIC_Init(void)
  • {
  • GPIO_InitTypeDef GPIO_InitStructure;
  • RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB時(shí)鐘
  • 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 輸出高,空閑狀態(tài)
  • }
  • //產(chǎn)生IIC起始信號(hào)
  • 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總線,準(zhǔn)備發(fā)送或接收數(shù)據(jù)
  • }
  • //產(chǎn)生IIC停止信號(hào)
  • 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;????//發(fā)送I2C總線結(jié)束信號(hào)
  • delay_us(4);
  • }
  • //發(fā)送數(shù)據(jù)后,等待應(yīng)答信號(hào)到來
  • //返回值:1,接收應(yīng)答失敗,IIC直接退出
  • // 0,接收應(yīng)答成功,什么都不做
  • u8 IIC_Wait_Ack(void)
  • {
  • u8 ucErrTime=0;
  • SDA_IN(); //SDA設(shè)置為輸入
  • 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;????//時(shí)鐘輸出0
  • return 0;
  • }
  • //產(chǎn)生ACK應(yīng)答
  • void IIC_Ack(void)
  • {
  • IIC_SCL=0;
  • SDA_OUT();
  • IIC_SDA=0;
  • delay_us(2);
  • IIC_SCL=1;
  • delay_us(2);
  • IIC_SCL=0;
  • }
  • //不產(chǎn)生ACK應(yīng)答
  • 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發(fā)送一個(gè)字節(jié)
  • //返回從機(jī)有無應(yīng)答
  • //1,有應(yīng)答
  • //0,無應(yīng)答
  • void IIC_Send_Byte(u8 txd)
  • {
  • u8 t;
  • SDA_OUT();
  • IIC_SCL=0;????????????//拉低時(shí)鐘開始數(shù)據(jù)傳輸
  • 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); ????//對TEA5767這三個(gè)延時(shí)都是必須的
  • IIC_SCL=1;
  • delay_us(2);
  • IIC_SCL=0;
  • delay_us(2);
  • }
  • }
  • //讀1個(gè)字節(jié),ack=1時(shí),發(fā)送ACK,ack=0,發(fā)送nACK
  • u8 IIC_Read_Byte(unsigned char ack)
  • {
  • unsigned char i,receive=0;
  • SDA_IN();????????//SDA設(shè)置為輸入
  • 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();????????//發(fā)送nACK
  • else
  • IIC_Ack(); ????????//發(fā)送ACK
  • return receive;
  • }
  • 這里是通過普通IO口(PB6、PB7)來模擬IIC時(shí)序的程序,其實(shí)本質(zhì)上都是嚴(yán)格按照IIC的時(shí)序圖進(jìn)行的,認(rèn)真讀,仔細(xì)對比,應(yīng)該是沒有什么困難的。

    就提一下:IIC_Read_Byte()函數(shù),這個(gè)函數(shù)的參數(shù)表示讀取一個(gè)字節(jié)之后,需要給對方應(yīng)答信號(hào)或非應(yīng)答信號(hào)。

    ?

    普通IO口模擬IIC時(shí)序讀取24C02

    24C02芯片介紹

    EEPROM (Electrically Erasable Programmable read only memory),帶電可擦可編程只讀存儲(chǔ)器——一種掉電后數(shù)據(jù)不丟失的存儲(chǔ)芯片。?

    24Cxx芯片是EEPROM芯片的一種,它是基于IIC總線的存儲(chǔ)器件,遵循二線制協(xié)議,由于其具有接口方便,體積小,數(shù)據(jù)掉電不丟失等特點(diǎn),在儀器儀表及工業(yè)自動(dòng)化控制中得到大量的應(yīng)用。24Cxx在電路的作用主要是在掉電的情況下保存數(shù)據(jù)。

    本文使用的是24C02芯片,總?cè)萘渴?k個(gè)bit(256個(gè)字節(jié))。這里芯片名稱里的02代表著總?cè)萘俊?/p>

    24C02芯片的引腳分布和具體的作用見下圖:

    ?

    24C02芯片的引腳說明
    引腳名稱說明
    A0-A2地址輸入線
    SDA數(shù)據(jù)線
    SCL時(shí)鐘線
    WP寫保護(hù)
    GND、VCC提供電源

    下圖是本文中24C02和STM32的引腳連接圖:

    從圖中可以看出:A0、A1、A2都為0。

    對于并聯(lián)在一條IIC總線上的每個(gè)IC都有唯一的地址。那么看一下從器件地址,可以看出對于不同大小的24Cxx,具有不同的從器件地址。由于24C02為2k容量,也就是說只需要參考圖中第一行的內(nèi)容:

    根據(jù)圖中的內(nèi)容:如果是寫24C02的時(shí)候,從器件地址為10100000(0xA0);讀24C02的時(shí)候,從器件地址為10100001(0xA1)。

    24C02芯片的時(shí)序圖

    這部分的內(nèi)容應(yīng)結(jié)合上文:I2C總線的數(shù)據(jù)傳送的內(nèi)容一起理解。

    24C02字節(jié)寫時(shí)序

    對24C02芯片進(jìn)行寫字節(jié)操作的時(shí)候,步驟如下:

  • 開始位,后面緊跟從器件地址位(0xA0),等待應(yīng)答,這是為了在IIC總線上確定24C02的從地址位置;
  • 確定操作24C02的地址,等待應(yīng)答,也就是將字節(jié)寫入到24C02中256個(gè)字節(jié)中的位置;
  • 確定需要寫入24C02芯片的字節(jié),等待應(yīng)答,停止位。
  • 24C02字節(jié)讀時(shí)序

    對24C02芯片進(jìn)行讀字節(jié)操作的時(shí)候,步驟如下:

  • 開始位,后面緊跟從器件地址位(0xA0),等待應(yīng)答,這是為了在IIC總線上確定24C02的從地址位置;
  • 確定操作24C02的地址,等待應(yīng)答,也就是從24C02中256個(gè)字節(jié)中讀取字節(jié)的位置;
  • 再次開始位,后面緊跟從器件地址位(0xA1),等待應(yīng)答;
  • 獲取從24C02芯片中讀取的字節(jié),發(fā)出非應(yīng)答信號(hào),停止位。
  • 讀取24C02芯片程序

  • #define AT24C01 127
  • #define AT24C02 255
  • #define AT24C04 511
  • #define AT24C08 1023
  • #define AT24C16 2047
  • #define AT24C32 4095
  • #define AT24C64 8191
  • #define AT24C128 16383
  • #define AT24C256 32767
  • //Mini STM32開發(fā)板使用的是24c02,所以定義EE_TYPE為AT24C02
  • #define EE_TYPE AT24C02
  • //初始化IIC接口
  • void AT24CXX_Init(void)
  • {
  • IIC_Init();
  • }
  • //在AT24CXX指定地址讀出一個(gè)數(shù)據(jù)
  • //ReadAddr:開始讀數(shù)的地址
  • //返回值 :讀到的數(shù)據(jù)
  • u8 AT24CXX_ReadOneByte(u16 ReadAddr)
  • {
  • u8 temp=0;
  • IIC_Start();
  • if(EE_TYPE>AT24C16)????????????//為了兼容24Cxx中其他的版本
  • {
  • IIC_Send_Byte(0XA0); //發(fā)送寫命令
  • IIC_Wait_Ack();
  • IIC_Send_Byte(ReadAddr>>8);????//發(fā)送高地址
  • IIC_Wait_Ack();
  • }else ????IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //發(fā)送器件地址0XA0,寫數(shù)據(jù)
  • IIC_Wait_Ack();
  • IIC_Send_Byte(ReadAddr%256); //發(fā)送低地址
  • IIC_Wait_Ack();
  • IIC_Start();
  • IIC_Send_Byte(0XA1); //進(jìn)入接收模式
  • IIC_Wait_Ack();
  • temp=IIC_Read_Byte(0); ????//讀一個(gè)字節(jié),非應(yīng)答信號(hào)信號(hào)
  • IIC_Stop();????????//產(chǎn)生一個(gè)停止條件
  • return temp;
  • }
  • //在AT24CXX指定地址寫入一個(gè)數(shù)據(jù)
  • //WriteAddr :寫入數(shù)據(jù)的目的地址
  • //DataToWrite:要寫入的數(shù)據(jù)
  • void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
  • {
  • IIC_Start();
  • if(EE_TYPE>AT24C16)
  • {
  • IIC_Send_Byte(0XA0); //發(fā)送寫命令
  • IIC_Wait_Ack();
  • IIC_Send_Byte(WriteAddr>>8);????//發(fā)送高地址
  • }else
  • {
  • IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //發(fā)送器件地址0XA0,寫數(shù)據(jù)
  • }
  • IIC_Wait_Ack();
  • IIC_Send_Byte(WriteAddr%256); //發(fā)送低地址
  • IIC_Wait_Ack();
  • IIC_Send_Byte(DataToWrite); //發(fā)送字節(jié)
  • IIC_Wait_Ack();
  • IIC_Stop();????//產(chǎn)生一個(gè)停止條件
  • delay_ms(10);
  • }
  • //在AT24CXX里面的指定地址開始寫入長度為Len的數(shù)據(jù)
  • //該函數(shù)用于寫入16bit或者32bit的數(shù)據(jù).
  • //WriteAddr :開始寫入的地址
  • //DataToWrite:數(shù)據(jù)數(shù)組首地址
  • //Len :要寫入數(shù)據(jù)的長度2,4
  • void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
  • {
  • u8 t;
  • for(t=0;t<Len;t++)
  • {
  • AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
  • }
  • }
  • //在AT24CXX里面的指定地址開始讀出長度為Len的數(shù)據(jù)
  • //該函數(shù)用于讀出16bit或者32bit的數(shù)據(jù).
  • //ReadAddr :開始讀出的地址
  • //返回值 :數(shù)據(jù)
  • //Len :要讀出數(shù)據(jù)的長度2,4
  • u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
  • {
  • u8 t;
  • u32 temp=0;
  • for(t=0;t<Len;t++)
  • {
  • temp<<=8;
  • temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
  • }
  • return temp;
  • }
  • //檢查AT24CXX是否正常
  • //這里用了24XX的最后一個(gè)地址(255)來存儲(chǔ)標(biāo)志字.
  • //如果用其他24C系列,這個(gè)地址要修改
  • //返回1:檢測失敗
  • //返回0:檢測成功
  • u8 AT24CXX_Check(void)
  • {
  • u8 temp;
  • temp=AT24CXX_ReadOneByte(255);//避免每次開機(jī)都寫AT24CXX
  • if(temp==0X55)return 0;
  • else//排除第一次初始化的情況
  • {
  • AT24CXX_WriteOneByte(255,0X55);
  • temp=AT24CXX_ReadOneByte(255);
  • if(temp==0X55)return 0;
  • }
  • return 1;
  • }
  • //在AT24CXX里面的指定地址開始讀出指定個(gè)數(shù)的數(shù)據(jù)
  • //ReadAddr :開始讀出的地址 對24c02為0~255
  • //pBuffer :數(shù)據(jù)數(shù)組首地址
  • //NumToRead:要讀出數(shù)據(jù)的個(gè)數(shù)
  • void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
  • {
  • while(NumToRead)
  • {
  • *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
  • NumToRead--;
  • }
  • }
  • //在AT24CXX里面的指定地址開始寫入指定個(gè)數(shù)的數(shù)據(jù)
  • //WriteAddr :開始寫入的地址 對24c02為0~255
  • //pBuffer :數(shù)據(jù)數(shù)組首地址
  • //NumToWrite:要寫入數(shù)據(jù)的個(gè)數(shù)
  • void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
  • {
  • while(NumToWrite--)
  • {
  • AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
  • WriteAddr++;
  • pBuffer++;
  • }
  • }
  • //要寫入到24c02的字符串?dāng)?shù)組
  • const u8 TEXT_Buffer[]={"WarShipSTM32 IIC TEST"};
  • #define SIZE sizeof(TEXT_Buffer)
  • int main(void)
  • {
  • u8 key;
  • u16 i=0;
  • u8 datatemp[SIZE];
  • delay_init(); //延時(shí)函數(shù)初始化
  • NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設(shè)置中斷優(yōu)先級(jí)分組為組2:2位搶占優(yōu)先級(jí),2位響應(yīng)優(yōu)先級(jí)
  • uart_init(115200); //串口初始化為115200
  • LED_Init(); //初始化與LED連接的硬件接口
  • LCD_Init(); //初始化LCD
  • KEY_Init(); //按鍵初始化
  • AT24CXX_Init(); //IIC初始化
  • POINT_COLOR=RED;//設(shè)置字體為紅色
  • LCD_ShowString(30,50,200,16,16,"WarShip STM32");
  • LCD_ShowString(30,70,200,16,16,"IIC TEST");
  • LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
  • LCD_ShowString(30,110,200,16,16,"2015/1/15");
  • LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read"); //顯示提示信息
  • while(AT24CXX_Check())//檢測不到24c02
  • {
  • LCD_ShowString(30,150,200,16,16,"24C02 Check Failed!");
  • delay_ms(500);
  • LCD_ShowString(30,150,200,16,16,"Please Check! ");
  • delay_ms(500);
  • LED0=!LED0;//DS0閃爍
  • }
  • LCD_ShowString(30,150,200,16,16,"24C02 Ready!");
  • POINT_COLOR=BLUE;//設(shè)置字體為藍(lán)色
  • while(1)
  • {
  • key=KEY_Scan(0);
  • if(key==KEY1_PRES)//KEY_UP按下,寫入24C02
  • {
  • LCD_Fill(0,170,239,319,WHITE);//清除半屏
  • LCD_ShowString(30,170,200,16,16,"Start Write 24C02....");
  • AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
  • LCD_ShowString(30,170,200,16,16,"24C02 Write Finished!");//提示傳送完成
  • }
  • if(key==KEY0_PRES)//KEY1按下,讀取字符串并顯示
  • {
  • LCD_ShowString(30,170,200,16,16,"Start Read 24C02.... ");
  • AT24CXX_Read(0,datatemp,SIZE);
  • LCD_ShowString(30,170,200,16,16,"The Data Readed Is: ");//提示傳送完成
  • LCD_ShowString(30,190,200,16,16,datatemp);//顯示讀到的字符串
  • }
  • i++;
  • delay_ms(10);
  • if(i==20)
  • {
  • LED0=!LED0;//提示系統(tǒng)正在運(yùn)行
  • i=0;
  • }
  • }
  • }
  • ?

    IIC總結(jié)

  • 進(jìn)行數(shù)據(jù)傳送時(shí),在SCL為高電平期間,SDA線上電平必須保持穩(wěn)定,只有SCL為低時(shí),才允許SDA線上電平改變狀態(tài)。并且每個(gè)字節(jié)傳送時(shí)都是高位在前;
  • 對于應(yīng)答信號(hào),ACK=0時(shí)為有效應(yīng)答位,說明從機(jī)已經(jīng)成功接收到該字節(jié),若為1則說明接受不成功;
  • 如果從機(jī)需要延遲下一個(gè)數(shù)據(jù)字節(jié)開始傳送的時(shí)間,可以通過把SCL電平拉低并保持來強(qiáng)制主機(jī)進(jìn)入等待狀態(tài);
  • 主機(jī)完成一次通信后還想繼續(xù)占用總線在進(jìn)行一次通信,而又不釋放總線,就要利用重啟動(dòng)信號(hào)。它既作為前一次數(shù)據(jù)傳輸?shù)慕Y(jié)束,又作為后一次傳輸?shù)拈_始;
  • 總線沖突時(shí),按“低電平優(yōu)先”的仲裁原則,把總線判給在數(shù)據(jù)線上先發(fā)送低電平的主器件;
  • 在特殊情況下,若需禁止所有發(fā)生在I2C總線上的通信,可采用封鎖或關(guān)閉總線,具體操作為在總線上的任一器件將SCL鎖定在低電平即可;
  • SDA仲裁和SCL時(shí)鐘同步處理過程沒有先后關(guān)系,而是同時(shí)進(jìn)行的。
  • ?

    轉(zhuǎn)載于:https://www.cnblogs.com/CodeWorkerLiMing/p/10830459.html

    總結(jié)

    以上是生活随笔為你收集整理的【STM32】IIC的基本原理(实例:普通IO口模拟IIC时序读取24C02)(转载)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。