本实验向大家介绍如何使用STM32的IIC硬件接口,实现和24C02之间的双向通信,通过本实验的学习,我们将对IIC通信的过程会有一个详细的了解。
IIC简介
IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及外围设备。它是由数据线SDA和时钟线SCL构成的串行总线,可用于发送和接收数据。在CPU与被控IC之间,IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上,但是目前大多数IIC设备不支持高速IIC。
使用IIC总线通信时,只需要2根通信线,一条双向串行数据线(SDA),一条串行时钟线(SCL)。每个IIC设备都有一个独立的地址,主机可以利用这个地址进行不同设备间的访问。当多主机使用总线时,IIC有一个仲裁机制,会决定由那个主机使用总线。
IIC总线协定如下:
IIC在数据通信过程中共有3种信号,分别是起始信号,停止信号和应答信号。
**起始信号:**时钟线(SCL)保持高电平期间,数据线(SDA)电平从高到低跳变作为I2C总线的起始信号。
**停止信号:**时钟线(SCL)保持高电平期间,数据线(SDA)电平从低到高的跳变作为I2C总线的停止信号
**应答信号:**接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,则判断为受控单元出现故障。
器件简介
本实验所用IIC器件为非易失性存储器EEPROM——AT24C02,容量为256字节,可擦写100万次,掉电数据不会丢失,通常被用来保存一些用户数据。这个系列芯片容量可以达到1Mbit,即128K字节,如AT24C1024.
AT24C02的管脚配置如下,关于这个芯片详细介绍请参看数据手册:
星光STM32F103开发板采用的EEPROM芯片为AT24C02,容量为256字节,这里我们把A0-A2均接地,也就是将AT24C02的地址设为0,写程序时要注意这点,连接到STM32的I2C2,对应管脚PB10,PB11,电路如图所示
main.c
#include "MyIncludes.h"
u16 sys_cnt = 0;
void systick_isr(void)
{
if(sys_cnt < 1000)
{
sys_cnt++;
}
else
{
sys_cnt = 0;
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5);
}
}
u8 Wr_Buf[100];
//写缓冲器
u8 Rd_Buf[100];
//读缓冲器
int main()
{
u16 i;
System_Init();
LED_Init();
SysTick_Init(systick_isr);
USART1_Init(115200,NULL,NULL);
printf("EEPROM ReadWrite Test\n");
while(EEPROM_Init()!=EEPROM_OK)
{
printf("EEPROM Init ERROM\n");
}
//当EEPROM初始化未完成
//输出EEPROM Init ERROM
for(i=0; i<100; i++)
{
Wr_Buf[i] = i+1;
}
EEPROM_Write(Wr_Buf,0,100);
EEPROM_Read(Rd_Buf,0,100);
if(strncmp((char *)Wr_Buf,(char *)Rd_Buf,100) == 0)
{
printf("EEPROM TEST OK\n");
}
else
{
printf("EEPROM TEST ERROR");
}
while(1)
{
}
}
24cxx.h
#ifndef __24CXX_H_
#define __24CXX_H_
#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"
#define EEPROM_MAX_TRIALS ((uint32_t)3000)
//函数的最大试用次数(貌似未用到)
#define EEPROM_OK ((uint8_t)0)
//状态:成功
#define EEPROM_FAIL ((uint8_t)1)
#define EEPROM_TIMEOUT ((uint8_t)2)
#define EEPROM_I2C_ADDRESS ((uint16_t)0xA0)
//EEPROM地址 0xA0
#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
uint8_t EEPROM_Init(void);
//EEPROM初始化
uint32_t EEPROM_Read(uint8_t* pBuffer,uint16_t ReadAddr,uint16_t NumToRead);
//指定位置读出指定个数的数据(数据数组首地址,开
//始读出的地址,要读出数据的个数);
uint32_t EEPROM_Write(uint8_t* pBuffer,uint16_t WriteAddr,uint16_t NumToWrite);
//在指定位置写入指定个数的数据,(数据数组首地址
//开始写入地址,要写入数据个数)
void EEPROM_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);
//在指定地址写入长度为Len的数据(开始写入的地址
//,数据数组首地址,要写入数据的长度)
uint32_t EEPROM_ReadLenByte(u16 ReadAddr,u8 Len);
//在指定地址读出长度为Len的数据(开始读出的地址
//要读的长度)
#endif
24cxx.c
#include "24cxx.h"
#include "iic.h"
I2C_HandleTypeDef hI2cEEpromHandler;
void EEPROM_IO_Init(void)
//EEPROM的I2c接口初始化
{
I2C2_Init(&hI2cEEpromHandler);
}
HAL_StatusTypeDef EEPROM_IO_WriteData(uint16_t DevAddress,uint16_t MemAddress,uint8_t* pBuffer,uint32_t BufferSize)
//向EEPROM写入数据(器件地址,器件寄存器地址,代写入数据,写入长度)
{
return (I2C2_WriteMultiple(&hI2cEEpromHandler,DevAddress,MemAddress,I2C_MEMADD_SIZE_8BIT,pBuffer,BufferSize));
//I2C写操作(I2chandler,器件地址,器件寄存器地址
//一次发送数据大小,待写入的数据,写入长度)
}
HAL_StatusTypeDef EEPROM_IO_ReadData(uint16_t DevAddress,uint16_t MemAddress,uint8_t* pBuffer,uint32_t BufferSize)
//从EEPROM读取数据(器件地址,器件寄存器地址,待
//读取数据,待读取长度)
{
return (I2C2_ReadMultiple(&hI2cEEpromHandler,DevAddress,MemAddress,I2C_MEMADD_SIZE_8BIT,pBuffer,BufferSize));
//I2C读操作(i2c handler,器件地址,器件寄存
//器地址,一次发送数据大小8位,待写入数据,长度)
}
HAL_StatusTypeDef EEPROM_IO_IsDeviceReady(uint16_t DevAddress,uint32_t Trials)
//检查EEPROM是否准备就绪通信(器件地址,检测最大次数)
{
return(I2C2_IsDeviceReady(&hI2cEEpromHandler,DevAddress,Trials));
//判断器件是否就绪(I2C Hanler,器件地址
//检测最大次数)
}
__IO uint16_t EEPROMAddress = 0;
//EEPROMAddress 地址声明
uint8_t EEPROM_Init(void)
{
EEPROM_IO_Init();
//初始化I2C总线
EEPROMAddress = EEPROM_I2C_ADDRESS;
//EEPROMAddress = 0xA0
if(EEPROM_IO_IsDeviceReady(EEPROMAddress,EEPROM_MAX_TRIALS) != HAL_OK)
//检查EEPROM是否准备就绪通信
{
return EEPROM_FAIL;
//返回失败
}
return EEPROM_OK;
//返回成功
}
uint32_t EEPROM_WaitEepromStandbyState(void)
//等待EEPROM就绪
{
if(EEPROM_IO_IsDeviceReady(EEPROMAddress,EEPROM_MAX_TRIALS) != HAL_OK)
//检查EEPROM是否准备就绪(器件地址,检测最大次数)
{
return EEPROM_TIMEOUT;
}
return EEPROM_OK;
}
//实验未用到
uint8_t EEPROM_ReadOneByte(uint16_t ReadAddr)
//在AT24C02中读出指定个个数数据
{
uint8_t ReadVal = 0;
if(EEPROM_IO_ReadData(EEPROMAddress,ReadAddr,&ReadVal,1) != HAL_OK)
//从EEPROM中读取数据(器件地址,开始读取地址,待写入的数据,写入长度)
{
ReadVal = 0xFF;
}
return ReadVal;
}
uint32_t EEPROM_Read(uint8_t* pBuffer,uint16_t ReadAddr,uint16_t NumToRead)
//EEPROM读数据 缓冲区首地址,要读的地址,要读的个数
{
uint32_t buffersize = NumToRead;
if(EEPROM_IO_ReadData(EEPROMAddress,ReadAddr,pBuffer,buffersize) != HAL_OK)
//从EEPROM读取数据(器件地址,器件寄存器地
//址,待读取数据,待读入长度)
{
return EEPROM_FAIL;
}
return EEPROM_OK;
}
uint32_t EEPROM_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
//向EEPROM写入一字节数据
//(写入的地址,待写入的数据)
{
uint32_t status = EEPROM_OK;
if(EEPROM_IO_WriteData(EEPROMAddress,WriteAddr,&DataToWrite,1) != HAL_OK)
//向EEPROM写入数据(器件地址,器件寄存器地址,
//代写入数据,写入长度)
{
status = EEPROM_FAIL;
}
if(EEPROM_WaitEepromStandbyState() != EEPROM_OK)
//等待EEPROM就绪
{
return EEPROM_FAIL;
}
return status;
}
uint32_t EEPROM_Write(uint8_t *pBuffer,uint16_t WriteAddr,uint16_t NumToWrite)
//在EEPROM里面的指定地址开始写入指定个数的数据
//(数据数组首地址,开始写入的地址,要写入数据的个数)
{
uint32_t status = EEPROM_OK;
while(NumToWrite--)
{
status = EEPROM_WriteOneByte(WriteAddr,*pBuffer);
//向EEPROM写入一字节数据
if(status != EEPROM_OK)
{
return status;
}
WriteAddr++;
//要写入地址自加
pBuffer++;
//缓冲地址自加
}
return EEPROM_OK;
}
//实验未用到
void EEPROM_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
//向EEPROM里面的指定地址开始写入长度为Len的数据
{
u8 t;
for(t=0;t<Len;t++)
{
EEPROM_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
//每次写入一个(开始地址,待写入的数据)
}
}
uint32_t EEPROM_ReadLenByte(u16 ReadAddr, u8 Len)
//读取指定长度为Len的数据。
{
u8 t;
u32 temp=0;
for(t=0;t<Len;t++)
{
temp <<= 8;
temp += EEPROM_ReadOneByte(ReadAddr+Len-t-1);
//读取指定个数数据(地址)
}
return temp;
}