轉自:https://blog.csdn.net/weixin_41995541/article/details/89843330
摘要
I2C通信協議
簡介
補充
空閑狀態
start和stop信號
應答信號
數據有效性規定
數據傳輸
延時
I2C協議的實現源碼
硬件說明
頭文件
sys.h
主函數
初始化I2C
產生開始和停止信號
等待應答信號
產生或不產生應答
I2C寫操作
I2C讀操作
對24C02操作
24C02的時序圖
頭文件
初始化IIC接口
寫數據(一個字節)
寫數據(長度為Len)
寫數據(指定長度)
讀數據(一個字節)
讀數據(長度為Len)
讀數據(指定長度)
檢查AT24CXX是否正常
主函數
實驗結果截圖
摘要
參考資料:
【STM32】IIC的基本原理(實例:普通IO口模擬IIC時序讀取24C02) https://blog.csdn.net/qq_38410730/article/details/80312357 STM32下模擬I2C的C語言實現 https://blog.csdn.net/qq_38410730/article/details/80312357 I2C協議小結 https://blog.csdn.net/huangkangying/article/details/73182432
I2C通信協議
簡介
I2C–(IIC,Inter-Integrated Circuit),內部集成電路,兩線式串行總線
由 SDA 和 SCL 時鍾線構成,可發送和接收數據
在 CPU 與被控 IC 之間、IC 與 IC 之間進行雙向傳送
通信協議:I2C 協議
傳輸距離:短距離傳輸
同步/異步:同步
傳輸信號:TTL 電平信號
通信方式(單/半/全雙工):半雙工
通信線:兩根線,SCL 和 SDA
SCL:I2C 是串行同步通信,需要 SCL 線傳輸同步脈沖信號
SDA:用於半雙工傳輸信號,數據/地址/控制信號復用SDA線
片選:通過軟件地址,發送不同的設備地址,選擇與不同的 I2C 設備通信
I/O接口(I2C 控制器),可能是獨立的,也可能和 CPU 集成在一起,如MCU
IO橋:PC機才有IO橋,單片機沒有
I2C 通信作用:
用於連接各種使用 I2C 通信的設備,這些設備有EEPROM, LCD,AD. RTC (時鍾) ,藍牙、ZigBee、wifi等無線通信模塊。
使用 I2C 屬於簡單通信,使用 I2C 通信的設備也都比較簡單
使用 I2C 通信的設備,一般都是直接做在了電路板上。
補充
作者:huangkangying
來源:CSDN
原文:https://blog.csdn.net/huangkangying/article/details/73182432
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
兩線串行協議(SCL, SDA),主從模式,支持多主控,但在同一時刻只能有一個主控
地址組成:7bit 地址數組+1bit讀寫位, 共8bit。地址范圍0~127, 0地址為廣播地址。
一次發送8bit數據,數據格式為大端模式。
速率:標准模式100Kbps, 快速模式400Kbps,高速模式3.4Mbps。
SCL由master提供,只有master才能同時控制SCL和SDA
SCL為低電平時才能改變數據,SCL為高電平時,數據有效。
起始條件: 當SCL為高電平時,master拉低SDA,總線進入start狀態。
結束條件: 當SCL為高電平時,master拉高SDA,總線進入stop狀態。
無論是發送地址還是數據,其后都緊跟着一個ACK/NACK。
當master寫數據里,ACK和NACK由slave提供。
當master讀數據里,ACK和NACK由master提供。
下面為master寫數據示例:ACK:在第9個CLK周期,slave拉低SDA,發送一個ACK
NACK: 在第9個CLK周期,slave保持SDA為高,發送一個NACK
一次可以傳輸多個字節,直到master發送start/stop。
如果slave設備來不及處理data,它可以保持SCL為低,強制master進入等待狀態。???
空閑狀態
規定空閑狀態 SDA 和 SCL 都必須拉高
SDA保持高電平
SCL保持高電平
start和stop信號
有頭有尾還有肚
開始信號:SCL 高電平期間,SDA 由高–>低電平
停止信號:SCL 高電平期間,SDA 由低–>高電平
起始信號:當SCL為高期間, SDA由高到低的跳變;
啟動信號是一種電平跳變時序信號,而不是一個電平信號。
停止信號:當SCL為高期間, SDA由低到高的跳變;
停止信號也是一種電平跳變時序信號,而不是一個電平信號。
應答信號
應答信號 ACK
發送器每發送一個字節(8bit),就在時鍾脈沖 9 期間釋放數據線,由接收器反饋一個應答信號。
應答信號為低電平時,規定為有效應答位(ACK),表示接收器已經成功地接收了該字節;
應答信號為高電平時,規定為非應答位(NACK),一般表示接收器該字節接收失敗
反饋有效應答位的要求:接收器在第 9 個時鍾脈沖之前的低電平期間將 SDA 線拉低,並且確保在該時鍾(第 9 個時鍾脈沖)的高電平期間為穩定的低電平。
如果接收機是主機,則它在接收到最后一個字節后,發送一個 NACK 信號,一通知被控發送器(從機)結束數據發送,並釋放 SDA 線,以便主控接收器發送一個停止信號。
數據有效性規定
猶如物流:
快遞車(SCL)運送期間(SCL高電平),貨物(數據)在車內(SDA保持)保持不動
當快遞車(SCL)停下(SCL低電平),才允許快遞小哥(器件)派送(SDA可變)貨物(傳送數據)
SCL高電平,SDA須保持
SCL低電平,SDA可變
為了與開始和停止信號區別
即:數據在SCL的上升沿到來之前就需准備好。並在下降沿到來之前必須穩定。
數據傳輸
在 I2C 總線上傳送的每一位數據都有一個時鍾脈沖相對應(或同步控制) ,
即在SCL串行時鍾的配合下,在 SDA上逐位地串行傳送每一位數據。數據位的傳輸是邊沿觸發。
延時
I2C協議的實現源碼
硬件說明
采用了正點原子的 STM32F103RCT6 開發板
實驗目的:對 24C02 進行操作
頭文件
#ifndef __IIC_H
#define __IIC_H
#include "sys.h" //實現了IO口的位帶操作
//IO方向設置,操作寄存器
//簡單點:就是把SDA的PC11口設為輸入或輸出
#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}
#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}
//IO操作函數,IO口位帶操作
//簡單點:就是可以對SCL的PC12和SDA的PC11進行賦值操作
#define IIC_SCL PCout(12) //SCL
#define IIC_SDA PCout(11) //SDA
#define READ_SDA PCin(11) //輸入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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
sys.h
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS 0 //定義系統文件夾是否支持UCOS
//位帶操作,實現51類似的GPIO控制功能
//具體實現思想,參考<<CM3權威指南>>第五章(87頁~92頁).
//IO口操作宏定義
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO口操作,只對單一的IO口!
//確保n的值小於16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //輸出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //輸入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //輸出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //輸入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //輸出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //輸入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //輸出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //輸入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //輸出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //輸入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //輸出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //輸入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //輸出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //輸入
//以下為匯編函數
void WFI_SET(void); //執行WFI指令
void INTX_DISABLE(void);//關閉所有中斷
void INTX_ENABLE(void); //開啟所有中斷
void MSR_MSP(u32 addr); //設置堆棧地址
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
主函數
參考24C02的時序圖
初始化I2C
主機初始化,配置引腳,開啟相應的時鍾
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//RCC->APB2ENR|=1<<4; //先使能外設IO PORTC時鍾
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE ); //使能外設IO PORTC時鍾
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_11; //同時設置PC11和PC12引腳
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//空閑狀態,主機的SCL和SDA都要拉高
IIC_SCL=1;
IIC_SDA=1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
產生開始和停止信號
當然這是主機產生
思考:
只設置了SDA為輸出,SCL不管嗎???可能是SCL始終是輸出,是主機產生的時鍾脈沖。而SDA可能是輸出也可能是輸入。所以需要確保它的狀態。
我覺得 I2C 停止信號應該這樣寫:???
void IIC_Stop(void)
{
SDA_OUT(); //設置SDA線為輸出
IIC_SCL=1; //SCL置高
IIC_SDA=0;
delay_us(4); //保持一段時間
//發送I2C總線結束信號
IIC_SDA=1; //STOP:when CLK is high, DATA change form low to high
delay_us(4); //保持一段時間
}
1
2
3
4
5
6
7
8
9
10
11
//產生IIC起始信號
void IIC_Start(void)
{
SDA_OUT(); //設置SDA線為輸出,只設置了SDA為輸出,SCL不管嗎???
IIC_SDA=1; //SDA置高
IIC_SCL=1; //SCL置高
delay_us(4); //讓數據保持一段時間(電平嘛)傳輸的是TTL電平信號
//SDA由高-->低跳變,SCL保持高電平
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; //SCL置低
IIC_SDA=0; //SDA置低
delay_us(4); //保持一段時間
IIC_SCL=1; //SCL置高
//發送I2C總線結束信號
IIC_SDA=1; //STOP:when CLK is high, DATA change form low to high
delay_us(4); //保持一段時間
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
等待應答信號
當然是發送方等待(發送方可能是主機也可能是從機)
//等待應答信號到來
//返回值:1,接收應答失敗
// 0,接收應答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA設置為輸入
//等待應答的時候把SDA和SCL線都給拉高 ???
IIC_SDA=1; delay_us(1);
IIC_SCL=1; delay_us(1);
while(READ_SDA) //READ_SDA就是PC11(SDA)引腳,當輸入為低時就是(ACK)信號
{
//等待接收方應答
ucErrTime++;
if(ucErrTime>250) //超時傳送失敗
{
IIC_Stop(); //傳送失敗發送停止信號
return 1;
}
}
IIC_SCL=0; //主機時鍾輸出0,為下一步做准備
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
產生或不產生應答
這里應該是針對主機而言的,此時主機是接收方???
思考:
SCL先置低,再置高,再置低,這恰恰就是第 9 個時鍾脈沖
而在 SCL 為高電平這段時間,SDA 要保持低電平或高電平
不可以這樣寫(違背了在SCL高電平期間,SDA要保持穩定):
void IIC_Ack(void)
{
IIC_SCL=0; //SCL置低
SDA_OUT(); //SDA設為輸出
IIC_SCL=1; //SCL置高
delay_us(2); //保持一段時間
IIC_SDA=0; //SDA置低,表示應答(ACK)
delay_us(2); //保持一段時間
IIC_SCL=0; //SCL置低
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//第 9 個時鍾脈沖問題
//產生ACK應答
void IIC_Ack(void)
{
IIC_SCL=0; //SCL置低
SDA_OUT(); //SDA設為輸出
IIC_SDA=0; //SDA置低,表示應答(ACK)
delay_us(2); //保持一段時間
IIC_SCL=1; //SCL置高
delay_us(2); //保持一段時間
IIC_SCL=0; //SCL置低
}
//不產生ACK應答
void IIC_NAck(void)
{
IIC_SCL=0; //SCL置低
SDA_OUT(); //SDA設為輸出
IIC_SDA=1; //SDA置高,表示非應答(NACK)
delay_us(2); //保持一段時間
IIC_SCL=1; //SCL置高
delay_us(2); //保持一段時間
IIC_SCL=0; //SCL置低
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
I2C寫操作
無論讀寫均由主機觸發開始條件和結束條件
如果從機接了主機的中斷線,也只是提醒主機而已,任然需要主機觸發開始條件
思考:
拉低時鍾開始數據傳輸,規定SCL低電平,SDA才允許變化,否則就是開始和停止信號了
對TEA5767這三個延時都是必須的???為啥
SCL同樣的先置低,再置高,最后置低,恰好也是一個時鍾脈沖,並且只有在SCL低電平期間,SDA才傳送數據:驗證了這句話(在 I2C 總線上傳送的每一位數據都有一個時鍾脈沖相對應(或同步控制) ,即在SCL串行時鍾的配合下,在 SDA上逐位地串行傳送每一位數據。數據位的傳輸是邊沿觸發。
如果不能進行位操作,可用如下代碼:
for(t=0;t<8;t++)
{
//規定從高位開始傳輸;
if((txd & 0x80) >> 7)//判斷最高位是0還是1,然后寫入PC11,寫入總線
{
PC |= (1<<11); //PC11輸出1
}
else
{
PC &= ~(1<<11); //PC11輸出0
}
txd <<= 1; //將數據(地址)左移一位
delay_us(2); //對TEA5767這三個延時都是必須的,為啥???
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//IIC發送一個字節
//返回從機有無應答,利用上述的 IIC_Wait_Ack 等待應答函數
//1,有應答
//0,無應答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT(); //SDA設為輸出
//拉低時鍾開始數據傳輸,規定SCL低電平,SDA才允許變化,否則就是開始和停止信號了
IIC_SCL=0;
for(t=0;t<8;t++)
{
//規定從高位開始傳輸;
IIC_SDA = (txd & 0x80) >> 7;
txd <<= 1;
delay_us(2); //對TEA5767這三個延時都是必須的,為啥???
IIC_SCL=1; //SCL置高
delay_us(2);
IIC_SCL=0; //SCL置低
delay_us(2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
I2C讀操作
無論讀寫均由主機觸發開始條件和結束條件
思考:
SCL先置低,然后置高,再置低,也是一個脈沖
然后在SCL為高電平期間讀取SDA的數據,因為SCL高電平期間,SDA數據保持穩定
//讀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; //SCL置低
delay_us(2); //保持
IIC_SCL=1; //SCL置高
receive <<= 1; //等同於乘以2
//先移位再加,不可先加在移位
//主機通過判斷P11輸入引腳的電平高低來接收數據
//高電平就加一,低電平就什么都不做
if(READ_SDA)receive++;
delay_us(1);
}
if (ack)
IIC_Ack(); //發送ACK
else
IIC_NAck(); //發送nACK
return receive;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
對24C02操作
EEPROM(24C02),2就是 2K ,總容量是256(2K/8)個字節
詳細信息請參考 AT2402 芯片手冊
24C02的時序圖
必須知會24C02的時序圖,不同硬件有不同的時序圖
寫時序:
讀時序:
頭文件
#ifndef __24CXX_H
#define __24CXX_H
#include "myiic.h"
//Mini STM32開發板
//24CXX驅動函數(適合24C01~24C16,24C32~256未經過測試!有待驗證!)
//2010/6/10
//V1.2
#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開發板使用的是24c02,所以定義EE_TYPE為AT24C02
#define EE_TYPE AT24C02
u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址讀取一個字節
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite); //指定地址寫入一個字節
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址開始寫入指定長度的數據
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len); //指定地址開始讀取指定長度數據
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite); //從指定地址開始寫入指定長度的數據
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead); //從指定地址開始讀出指定長度的數據
u8 AT24CXX_Check(void); //檢查器件
void AT24CXX_Init(void); //初始化IIC
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
初始化IIC接口
//初始化IIC接口
void AT24CXX_Init(void)
{
IIC_Init();
}
1
2
3
4
5
6
寫數據(一個字節)
//在AT24CXX指定地址寫入一個數據
//WriteAddr :寫入數據的目的地址
//DataToWrite:要寫入的數據
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //發送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//發送高地址
}else
{
IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //發送器件地址0XA0,寫數據
}
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //發送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //發送字節
IIC_Wait_Ack();
IIC_Stop();//產生一個停止條件
delay_ms(10);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
寫數據(長度為Len)
//在AT24CXX里面的指定地址開始寫入長度為Len的數據
//該函數用於寫入16bit或者32bit的數據.
//WriteAddr :開始寫入的地址
//DataToWrite:數據數組首地址
//Len :要寫入數據的長度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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
寫數據(指定長度)
//在AT24CXX里面的指定地址開始寫入指定個數的數據
//WriteAddr :開始寫入的地址 對24c02為0~255
//pBuffer :數據數組首地址
//NumToWrite:要寫入數據的個數
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
讀數據(一個字節)
//在AT24CXX指定地址讀出一個數據
//ReadAddr:開始讀數的地址
//返回值 :讀到的數據
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
if(EE_TYPE>AT24C16)
{
IIC_Send_Byte(0XA0); //發送寫命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//發送高地址
IIC_Wait_Ack();
}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //發送器件地址0XA0,寫數據
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //發送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //進入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//產生一個停止條件
return temp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
讀數據(長度為Len)
//在AT24CXX里面的指定地址開始讀出長度為Len的數據
//該函數用於讀出16bit或者32bit的數據.
//ReadAddr :開始讀出的地址
//返回值 :數據
//Len :要讀出數據的長度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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
讀數據(指定長度)
//在AT24CXX里面的指定地址開始讀出指定個數的數據
//ReadAddr :開始讀出的地址 對24c02為0~255
//pBuffer :數據數組首地址
//NumToRead:要讀出數據的個數
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
檢查AT24CXX是否正常
//檢查AT24CXX是否正常
//這里用了24XX的最后一個地址(255)來存儲標志字.
//如果用其他24C系列,這個地址要修改
//返回1:檢測失敗
//返回0:檢測成功
u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(255);//避免每次開機都寫AT24CXX
if(temp==0X55)return 0;
else//排除第一次初始化的情況
{
AT24CXX_WriteOneByte(255,0X55);
temp=AT24CXX_ReadOneByte(255);
if(temp==0X55)return 0;
}
return 1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
主函數
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "key.h"
#include "24cxx.h"
#include "myiic.h"
//要寫入到24c02的字符串數組
const u8 TEXT_Buffer[]={"MiniSTM32 IIC TEST"};
#define SIZE sizeof(TEXT_Buffer) //獲取字節個數
int main(void)
{
u8 key;
u16 i=0;
u8 datatemp[SIZE];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置中斷優先級分組2
delay_init(); //延時函數初始化
uart_init(9600); //串口初始化為9600
LED_Init(); //初始化與LED連接的硬件接口
LCD_Init();
KEY_Init(); //按鍵初始化
AT24CXX_Init(); //IIC初始化
//用LCD顯示屏顯示出來
POINT_COLOR=RED;//設置字體為紅色
LCD_ShowString(60,50,200,16,16,"Mini STM32");
LCD_ShowString(60,70,200,16,16,"IIC TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2014/3/9");
LCD_ShowString(60,130,200,16,16,"WK_UP:Write KEY0:Read"); //顯示提示信息
while(AT24CXX_Check())//檢測不到24c02
{
LCD_ShowString(60,150,200,16,16,"24C02 Check Failed!");
delay_ms(500);
LCD_ShowString(60,150,200,16,16,"Please Check! ");
delay_ms(500);
LED0=!LED0;//DS0閃爍
}
LCD_ShowString(60,150,200,16,16,"24C02 Ready!");
POINT_COLOR=BLUE;//設置字體為藍色
while(1)
{
key=KEY_Scan(0);
if(key==WKUP_PRES)//WK_UP 按下,寫入24C02
{
LCD_Fill(0,170,239,319,WHITE);//清除半屏
LCD_ShowString(60,170,200,16,16,"Start Write 24C02....");
AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
LCD_ShowString(60,170,200,16,16,"24C02 Write Finished!");//提示傳送完成
}
if(key==KEY0_PRES)//KEY0 按下,讀取字符串並顯示
{
LCD_ShowString(60,170,200,16,16,"Start Read 24C02.... ");
AT24CXX_Read(0,datatemp,SIZE);
LCD_ShowString(60,170,200,16,16,"The Data Readed Is: ");//提示傳送完成
LCD_ShowString(60,190,200,16,16,datatemp);//顯示讀到的字符串
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系統正在運行
i=0;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
實驗結果截圖
實驗結果視頻地址:
視頻地址:http://t.cn/EoZQHMm?m=4368576113101384&u=5955627170
————————————————
版權聲明:本文為CSDN博主「Iem Gnay」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_41995541/article/details/89843330
