目錄
寫完回頭一看發現字數還不少,如果你覺得文字太枯燥,那麼可以跳到後面程序設計,直接動手做實驗。如果想仔細瞭解關於IIC協議的細節,那麼希望你能慢慢把看完,看完後一定有所收穫。
概述:IIC BUS(Inter Integrated Circuit BUS,內部集成電路總線)是飛利浦公司推出的二線制串行擴展總線;在IIC總線上,只需要兩條線——數據線SDA線和時鐘線SCL;多個器件可連接到同一個IIC總線上,每一個器件有一個唯一的識別地址,可一對多、多對一、一對一通訊;標準模式下傳輸速度爲100Kb/s,快速模式下爲400Kb/s。優點:引腳少,硬件實現簡單,成本低。
關於IIC的接口原理有些書可以描述出好幾頁,但有時候看完了可能未必能懂,雖然看的時候每句話都能理解,但是看完了之後,自己想描述出來卻不知道從何說起,腦海裏只是一些零散的概念。所以下面講述一些關於IIC比較核心的東西。
1.物理層:
- 它是一種支持多個通信主機及多個通信從機的總線。“總線”是指多個設備共用的信號線。
- 一個C總線只使用兩條總線線路;一條雙向串行數據線(SDA),一條串行時鐘線(SCL)。
- 每個連接到總線的設備都有一個獨立的地址,總線上任一設備可以利用這個地址對其他設備進行交互。
- 總線通過上拉電阻接到電源。總線上的任一設備,如果處於空閒狀態時。會輸出高阻態。所以在總線空閒的時候兩條總線都呈現高電平。
- 具有3種傳輸模式,標準模式(100kbps)、快速模式(400kbps)、高速模式(3.4Mbps,目前大多數不支持)。、
- 連接到相同總線的設備數受到總線的最大電容400pF限制。
2.協議層:
C的協議定義了通信的起始和停止信號、數據有效性、響應、仲裁、時鐘同步和地址廣播等環節。
- 通訊的起始和停止信號
因爲設備發送數據的時候是連續字節的,接受端並不能提前知道發送者要發送的總字節,所以接收端是從起始信號開始接收,直到停止信號爲止;而且這兩種信號要與傳輸過程中的任何時間點的時序不能有衝突(獨特性),iic在數據傳輸的過程中也保證了這一點。通訊的起始:當SCL線是高電平時SDA線從高電平向低電平切換。通信的停止:當SCL線是高電平時SDA線由低電平向高電平切換。可看出,這兩種信號跟數據發送過程中的差異,在數據發送過程中,SDA線總是在SCL線在低電平的時候才變換。
- 數據的有效性
iic使用SDA信號線來傳輸數據,使用SCL時鐘線進行數據同步。SDA數據線在SCL時鐘線的每一個週期內傳輸一位,當SCL時鐘線爲高電平時,SDA數據線有效;即當SCL爲高電平,若SDA爲高電平表示數據“1”,若SDA爲低電平表示數據“0”。當SCL時鐘線爲低電平時,SDA數據無線,一般這個時候SDA進行電平切換,爲下一次表示數據進行準備。
- 應答位
從機每接收到一個字節時,都要作出應答;而主機這時候釋放SDA線的控制權,由數據接收端控制SDA,若SDA爲高電平,表示非應答信號,低電平表示應答信號即傳輸成功。
3.數據的傳輸:
由上面介紹的協議基本理解了IIC通信的傳輸原理,在實際應用中,主機設備發送到從機設備的數據包括地址和數據。主機通過地址可找到對應的從機設備,而收到廣播地址的從機做出判決,若地址與自身地址匹配則做出應答,若不匹配則忽略信息。
- 主機向從機寫數據
上面是主機對芯片AT24C02存儲芯片進行寫操作時SDA的數據流,可以很清晰看出所發送的內容。
對照着圖來說,"DEVICE ADDRESS"設備地址可以是7位或10位,圖中所示爲7位,加上R/W傳輸方向位(1爲發送,0位接受)組成一個字節;圖中所有的ACK是等待從機應答位,主機收到了應答後才繼續發送後面的內容,從圖中可明顯看出,主機每發送一個字節都必須等待從機做出一個應答;
圖中的"WORD ADDRESS"指的是主機往AT24C02存儲芯片寫入的首地址,後面DATA就是寫入的內容。不是所有的iic從設備都有這個,比如是音頻的設備那麼就不存在寫入地址。具體是要根據iic從設備的數據手冊上的功能通信協議來定。
- 主機向從機讀數據
上面是主機對芯片AT24C02存儲芯片進行讀操作時SDA的數據流。
對照着圖來說,首先是發送從機地址"DEVICE ADDRESS",接着給從機發送"WORD ADDRESS"是所讀取內容的地址,這是告訴AT24C02接下主機需要的內容,設置好地址之後,接着主機再發送一個起始位,緊接着發送"DEVICE ADDRESS",這時可以注意到傳輸方向是READ(讀),那麼主機釋放SDA線的控制權,有從機給主機發送數據,可以連續發送多個字節數據,當主機期望停止接收時作出一個非應答,那麼從機就停止發送了。
"WORD ADDRESS"不是所有主機向從機進行讀操作時都要發送這個,具體是要看對方是什麼設備,就如上面所說需要依據設備的數據手冊。
個人總結一下,其實IIC是一個非常有意思的通信協議,起始位和停止位之間肯定有一個設備地址。對於一個在總線上的設備,它需要隨時監聽總線上的起始位和停止位,起始位一旦出現就要進行接收數據,不管地址是不是跟自己匹配,接下來都要關注停止位的出現,因爲如果地址跟自己不匹配在停止位出現之前自己是不能佔用總線的,雖說空閒的時候SDA和SCL必定都是高電平,但SDA和SCL都是高電平的時候總線未必空閒。
我們可以直接控制任意兩個引腳,分別用作SCL和SDA,按照上述信號時序要求,就可以實現IIC通信。直接控制引腳需要CPU控制每個時刻的引腳狀態,所以稱之爲“軟件模擬協議”方式。
相對地,還有“硬件協議”方式,STM32的IIC片上外設專門負責實現IIC通信協議,只要配置好該外設,它就會自動根據協議要求產生通信信號,收發數據並緩存起來。CPU只要檢測該外設的狀態和訪問數據寄存器,就能完成數據的收發。這種由硬件外設處理的IIC協議的方式減輕了CPU的工作,且是軟件設計更簡單。
下面分別介紹這兩種方式在STM32上的實現。
4.程序設計
通過查閱AT24C02的數據手冊,該產品屬於2k容量,2k指的是bit,所以等於256字節,需要一個8位數據字進行尋址。另外設備地址可以在數據手冊裏查到。
由上面兩圖得知該設備地址爲0xA0,第0bit是讀寫方向位。 這裏SCL和SDA接了上拉電阻,然後分別接到stm32f103c8t6的PB6、PB7。
- 軟件模式方式
第一步,通過cpu控制io模擬i2c協議,主要實現起始通訊函數,停止通訊函數,發送字節函數和接受字節函數。在控制io是注意加入延時以滿足i2c的時序。
創建<i2c.h>
#ifndef __I2C_H
#define __I2C_H
#include "stm32f10x.h"
#define IIC_NO_ACK 1
#define IIC_ACK 0
#define SCL_CLR() GPIOB->BRR = GPIO_Pin_6
#define SCL_SET() GPIOB->BSRR = GPIO_Pin_6
#define SDA_CLR() GPIOB->BRR = GPIO_Pin_7
#define SDA_SET() GPIOB->BSRR = GPIO_Pin_7
#define SCL_READ() GPIOB->IDR & GPIO_Pin_6
#define SDA_READ() GPIOB->IDR & GPIO_Pin_7
/*SCL時鐘線*/
#define AT24C02_SCL_PIN GPIO_Pin_4
#define AT24C02_SCL_PORT GPIOC
#define AR24C02_SCL_CLK RCC_APB2Periph_GPIOC
/*SDA數據線*/
#define AT24C02_SCL_PIN GPIO_Pin_4
#define AT24C02_SCL_PORT GPIOC
#define AR24C02_SCL_CLK RCC_APB2Periph_GPIOC
void I2C_Configuration(void);
extern void I2C_Start(void); //發送開始信號
extern void I2C_Stop(void); //發送停止信號
extern void I2C_Send_Byte(uint8_t sebyte); // I2C發送一字節數據
extern uint8_t I2C_Recieve_Byte(void); // I2C接收一字節數據
#endif
創建<i2c.c>
#include "i2c.h"
#include "stm32f10x_gpio.h"
static void delay(unsigned char us) //大概延時
{
uint8_t i = 10;
while(us--)
{
while(i--);
}
}
void I2C_Configuration(void) //i2c初始化引腳
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Configure I2C2 pins: PB6->SCL and PB7->SDA */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void I2C_Stop(void) //產生起始信號
{
SDA_CLR(); //信號線置低
delay(1);
SCL_SET(); //時鐘線置高
delay(1);
SDA_SET(); //信號線置高
delay(1);
}
void I2C_Start(void) //產生停止信號
{
SCL_SET(); //時鐘線置高
SDA_SET(); //信號線置高
delay(1);
SDA_CLR(); //信號線置低
delay(1);
SCL_CLR(); //時鐘線置低
delay(1);
}
static unsigned char IIC_Wait_Ack(void) //阻塞等待從機應答
{
unsigned char ack=0;
SCL_CLR(); //時鐘線置低
delay(1);
SDA_SET(); //信號線置高
delay(1);
SCL_SET(); //時鐘線置高
delay(1);
if(SDA_READ()){ //讀取SDA的電平
ack = IIC_NO_ACK; //如果爲1,則從機沒有應答
}
else{
ack = IIC_ACK; //如果爲0,則從機應答
}
SCL_CLR();//時鐘線置低
delay(1);
return ack; //返回讀取到的應答信息
}
void I2C_Send_Byte(uint8_t IIC_Byte)
{
unsigned char i; //定義變量
for(i=0;i<8;i++) //for循環8次
{
SCL_CLR(); //時鐘線置低,爲傳輸數據做準備
delay(1);
if(IIC_Byte & 0x80) //讀取最高位
SDA_SET();
else
SDA_CLR();
IIC_Byte <<= 1; //數據左移1位
delay(1);
SCL_SET(); //時鐘線置高,產生上升沿,把數據發送出去
delay(1);
}
SCL_CLR(); //時鐘線置低
delay(1);
while(IIC_Wait_Ack()); //阻塞從機應答
}
uint8_t I2C_Recieve_Byte(void)
{
uint8_t rebyte=0,i;
SCL_CLR(); //時鐘線置低
delay(1);
for(i=0;i<8;i++){
SCL_SET(); //時鐘線置高
delay(1);
rebyte=rebyte<<1;
if(SDA_READ()){
rebyte |= 0x01;
}
SCL_CLR(); //時鐘線置低
delay(2);
}
SDA_SET();
delay(1);
SCL_SET(); //給從機應答
delay(2);
SCL_CLR(); //時鐘線置低
return rebyte;
}
第二步,AT24C02的數據手冊,通過上面的I2C通訊接口跟AT24C02芯片進行數據交互,包括單字節讀寫,多字節讀寫函數接口的實現。上面已經提到該芯片大小是256字節,所以地址範圍是0x00~0xFF,不可越界。
創建<AT24C02.h>
#ifndef __AT24C02_H
#define __AT24C02_H
#include "stm32f10x.h"
#define AT24C02_I2Cx I2C1 //AT24C02所用的iic外設
#define AT24C02_ADDR 0xA0 //設備地址
extern void AT24C02_Init(void); //初始化
extern uint8_t AT24C02_ReadOneByte(uint8_t ReadAddr); //指定地址讀取一個字節
extern void AT24C02_WriteOneByte(uint8_t WriteAddr,uint8_t DataToWrite); //指定地址寫入一個字節
extern void AT24C02_Write(uint8_t WriteAddr,uint8_t *Buffer,uint16_t Num); //從指定地址開始寫入指定長度的數據
extern void AT24C02_Read(uint8_t ReadAddr,uint8_t *Buffer,uint16_t Num); //從指定地址開始讀取指定長度的數據
#endif
創建<AT24C02.c>
#include "AT24c02.h"
#include "i2c.h"
static void delay(unsigned int us) //大概延時
{
uint8_t i = 10;
while(us--)
{
while(i--);
}
}
void AT24C02_Init(void)
{
I2C_Configuration();
}
void AT24C02_WriteOneByte(uint8_t WriteAddr, uint8_t DataToWrite)//向AT24C02指定的地址寫入一個字節
{
I2C_Start(); //發送起始信號
I2C_Send_Byte(AT24C02_ADDR|0x00); //設備地址且傳輸方向位設置爲0
delay(1);
I2C_Send_Byte(WriteAddr);//發送地址
I2C_Send_Byte(DataToWrite); //發送字節
I2C_Stop();//產生一個停止條件
delay(100);// 這個延時絕對不能去掉
}
uint8_t AT24C02_ReadOneByte(uint8_t ReadAddr) //從AT24C02指定的地址讀取一個字節
{
uint8_t temp=0;
I2C_Start();//發送起始信號
I2C_Send_Byte(AT24C02_ADDR); //設備地址 且傳輸方向位設置爲0
delay(1);
I2C_Send_Byte(ReadAddr); //發送地址
I2C_Start();
I2C_Send_Byte(AT24C02_ADDR|0x01); //設備地址 且傳輸方向位設置爲1
delay(1);
temp=I2C_Recieve_Byte(); //接受一個字節
I2C_Stop();//產生一個停止條件
return temp;
}
void AT24C02_Read(uint8_t ReadAddr,uint8_t *Buffer,uint16_t Num)//從指定地址連續讀取多個字節
{
while(Num)
{
*Buffer++=AT24C02_ReadOneByte(ReadAddr++);
Num--;
}
}
void AT24C02_Write(uint8_t WriteAddr,uint8_t *Buffer,uint16_t Num)//向指定地址連續寫入過個字節
{
while(Num--)
{
AT24C02_WriteOneByte(WriteAddr,*Buffer);
WriteAddr++;
Buffer++;
}
}
第三步 ,配置一個串口輸出,用於打印調試信息,驗證實驗。
創建USART1.h
#ifndef __USART1_INIT_H__
#define __USART1_INIT_H__
#include "stm32f10x.h"
#include <stdio.h>
int fputc(int ch, FILE *f);
void USART1_Configuration(void);//打印輸出串口初始化
void UART_send(uint8_t *Buffer, uint32_t Length);
uint8_t UART_recive(void); //阻塞等待一個數據到來
#endif
創建USART1.c
#include "USART1.h"
void USART1_Configuration(void)//打印輸出串口初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//配置串口1 (USART1) 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置串口1接收終端的優先級
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//配置串口1 發送引腳(PA.09)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口1 接收引腳 (PA.10)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口1工作模式(USART1 mode)配置
USART_InitStructure.USART_BaudRate = 115200;//一般設置爲9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //數據位爲8個字節
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一位停止位
USART_InitStructure.USART_Parity = USART_Parity_No ; //無校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不需要流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收發送模式
USART_Init(USART1, &USART_InitStructure);
//USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啓中斷
USART_Cmd(USART1, ENABLE);//使能串口
USART_ClearFlag(USART1,0x3FF);
}
int fputc(int ch, FILE *f) //重定向c庫裏面的fputc到串口,那麼使用printf時就能將打印的信息從串口發送出去,在PC上同串口助手接收信息
{
while( USART_GetFlagStatus(USART1,USART_FLAG_TXE)!= SET);
//將Printf內容發往串口
USART_SendData(USART1, (unsigned char) ch);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
return (ch);
}
uint8_t UART_recive(void)
{
while(USART_GetFlagStatus(USART1, USART_IT_RXNE) == RESET);
return USART_ReceiveData(USART1);
}
void UART_send(uint8_t *Buffer, uint32_t Length)
{
while(Length != 0)
{
while( USART_GetFlagStatus(USART1,USART_FLAG_TXE)!= SET);
USART_SendData(USART1, (unsigned char) *Buffer);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
Buffer++;
Length--;
}
}
第四步,首先讀取AT24C02內從0x00地址開始連續讀取11個數據並通過串口打印出來,然後再往該地址寫入11個數據,進入while循環後,將從串口接收到的一個字節數據寫入到AT24C02芯片內,並讀取出來再通過串口打印出來。
創建<main.c>
#include "uart.h"
#include "i2c.h"
#include "at24c02.h"
uint8_t data[11],str[12]="hello world";
int main()
{
uint8_t rece_data;
UART_Init(9600); // 初始化串口
I2C_Init(0); // 初始化I2C口
AT24C02_Read(0x00,data,11);//上電讀取AT24C02內地址0~10的數據
UART_send(data,11); //把讀出的數據返回電腦串口
AT24C02_Write(0x00,str,11);
UART_send(data,11); //把讀出的數據返回電腦串口;
while(1)
{
rece_data = UART_recive();// 等待串口發來的數據
AT24C02_WriteOneByte(0x00, rece_data);// 把串口發來的數據寫入AT24C02地址0x00處
rece_data = 0;// rece_data清零
rece_data = AT24C02_ReadOneByte(0x00);// 讀出AT24C02地址0x00處數據,賦予rece_data
UART_send_byte(rece_data);// 把讀出的數據返回電腦串口
}
}
將所有文件加入到工程中:
編譯運行,下載到開發板上,連接上AT24C02芯片,接上串口調試轉接口,那麼就可以觀察實驗了。AT24C02儲存芯片掉電數據不丟失,可以自行適當更改代碼驗證。
- 硬件協議方式
將調用標準庫的函數進行IIC通信,所以將上面的i2c.c、i2c.h移除,只需要更換AT24C02.c文件就行。
創建<AT242C02.c>
#include "AT24c02.h"
static void delay(unsigned int us) //大概延時
{
uint8_t i = 10;
while(us--)
{
while(i--);
}
}
void AT24C02_Init(void)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); //使能iic外設時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO , ENABLE); //使能GPIO時鐘
/* Configure I2C1 pins: PB6->SCL and PB7->SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C1);
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000; /* 100K速度 */
I2C_Cmd(I2C1, ENABLE);
I2C_Init(I2C1, &I2C_InitStructure);
/*允許1字節1應答模式*/
I2C_AcknowledgeConfig(I2C1, ENABLE);
}
static void I2C_AcknowledgePolling(I2C_TypeDef *I2Cx,uint8_t I2C_Addr)
{
vu16 SR1_Tmp;
do
{
I2C_GenerateSTART(AT24C02_I2Cx, ENABLE); /*起始位*/
/*讀SR1*/
SR1_Tmp = I2C_ReadRegister(AT24C02_I2Cx, I2C_Register_SR1);
/*器件地址(寫)*/
I2C_Send7bitAddress(AT24C02_I2Cx, I2C_Addr, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(AT24C02_I2Cx, I2C_Register_SR1) & 0x0002));
I2C_ClearFlag(AT24C02_I2Cx, I2C_FLAG_AF);
I2C_GenerateSTOP(AT24C02_I2Cx, ENABLE); /*停止位*/
}
uint8_t AT24C02_ReadOneByte(uint8_t ReadAddr) //從AT24C02指定的地址讀取一個字節
{
uint8_t recvalue;
while(I2C_GetFlagStatus(AT24C02_I2Cx, I2C_FLAG_BUSY));
/*允許1字節1應答模式*/
I2C_AcknowledgeConfig(AT24C02_I2Cx, ENABLE);
/* 發送起始位 */
I2C_GenerateSTART(AT24C02_I2Cx, ENABLE);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));/*EV5,主模式*/
/*發送器件地址(寫)*/
I2C_Send7bitAddress(AT24C02_I2Cx, AT24C02_ADDR, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/*發送地址*/
I2C_SendData(AT24C02_I2Cx, ReadAddr);
while (!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));/*數據已發送*/
/*起始位*/
I2C_GenerateSTART(AT24C02_I2Cx, ENABLE);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
/*器件讀*/
I2C_Send7bitAddress(AT24C02_I2Cx, AT24C02_ADDR, I2C_Direction_Receiver);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
I2C_AcknowledgeConfig(AT24C02_I2Cx, DISABLE); /* 最後一位後要關閉應答的 */
I2C_GenerateSTOP(AT24C02_I2Cx, ENABLE); /* 發送停止位 */
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)); /* EV7 */
recvalue = I2C_ReceiveData(AT24C02_I2Cx);
/* 再次允許應答模式 */
I2C_AcknowledgeConfig(AT24C02_I2Cx, ENABLE);
return recvalue;
}
void AT24C02_WriteOneByte(uint8_t WriteAddr, uint8_t DataToWrite)
{
/* 起始位 */
I2C_GenerateSTART(AT24C02_I2Cx, ENABLE);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
/* 發送器件地址(寫)*/
I2C_Send7bitAddress(AT24C02_I2Cx, AT24C02_ADDR, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/*發送地址*/
I2C_SendData(AT24C02_I2Cx, WriteAddr);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* 寫一個字節*/
I2C_SendData(AT24C02_I2Cx, DataToWrite);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
/* 停止位*/
I2C_GenerateSTOP(AT24C02_I2Cx, ENABLE);
I2C_AcknowledgePolling(AT24C02_I2Cx,AT24C02_ADDR);
delay(100);
return ;
}
void AT24C02_Write(uint8_t WriteAddr,uint8_t *Buffer,uint16_t Num)//向指定地址連續寫入過個字節
{
while(Num--)
{
AT24C02_WriteOneByte(WriteAddr,*Buffer);
WriteAddr++;
Buffer++;
}
}
void AT24C02_Read(uint8_t ReadAddr,uint8_t *Buffer,uint16_t Num)//從指定地址連續讀取多個字節
{
if(Num==0)
return ;
while(I2C_GetFlagStatus(AT24C02_I2Cx, I2C_FLAG_BUSY));
/*允許1字節1應答模式*/
I2C_AcknowledgeConfig(AT24C02_I2Cx, ENABLE);
/* 發送起始位 */
I2C_GenerateSTART(AT24C02_I2Cx, ENABLE);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));/*EV5,主模式*/
/*發送器件地址(寫)*/
I2C_Send7bitAddress(AT24C02_I2Cx, AT24C02_ADDR, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/*發送地址*/
I2C_SendData(AT24C02_I2Cx, ReadAddr);
while (!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));/*數據已發送*/
/*起始位*/
I2C_GenerateSTART(AT24C02_I2Cx, ENABLE);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
/*器件讀*/
I2C_Send7bitAddress(AT24C02_I2Cx, AT24C02_ADDR, I2C_Direction_Receiver);
while(!I2C_CheckEvent(AT24C02_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
while (Num){
if(Num==1)
{
I2C_AcknowledgeConfig(AT24C02_I2Cx, DISABLE); /* 最後一位後要關閉應答的 */
I2C_GenerateSTOP(AT24C02_I2Cx, ENABLE); /* 發送停止位 */
}
while(!I2C_CheckEvent(AT24C02_I2Cx,I2C_EVENT_MASTER_BYTE_RECEIVED)); /* EV7 */
*Buffer = I2C_ReceiveData(AT24C02_I2Cx);
Buffer++;
/* Decrement the read bytes counter */
Num--;
}
/* 再次允許應答模式 */
I2C_AcknowledgeConfig(AT24C02_I2Cx, ENABLE);
return;
}
更改完成下載調試,同樣跟軟件模擬方式達到一樣的效果。
完結。
<<千里之行,始於足下;九層之臺,始於壘土。>>