您的位置:首页 > 其它

stm32笔记2—读写I2C存储芯片

2015-07-02 22:52 417 查看
一、stm32读写at24c02存储芯片,并通过串口打印。

1、首先简单介绍下AT24Cx系列存储芯片。详细内容需参考相应芯片手册。
AT24C01/02/04/08/16 是低工作电压的 1K/2K/4K/8K/16K 位串行电可擦除只读存储器,内部组织为
128/256/512/1024/2048 个字节,每个字节 8 位。二线串口接口(SCL,SDA)。

2、I2C总线协议介绍。

总线上扩展的外围器件及外设接口通过总线寻址,是具备总线仲裁和高低速设备同步等功能的高性能多主机总线。标准速率为100kbps
3、I2C总线工作原理。

由串行数据线SDA和串行时钟线SCL构成的。数据线SDA可以发送和接收数据。
挂接在总线上所有器件要依靠SDA发送的地址信号寻址,所以不需要片选线。
此地址(7位)由器件地址和引脚地址2部分组成。

I2C总线上数据是伴随着时钟脉冲,一位一位的传送,数据位又高到低传送。
在数据传送时,SDA上数据的改变在时钟线为低电平时完成,而SCL为高电平时,SDA必须保持稳定,否则SDA上的变化会被当作起始或终止信号而致使数据传输停止。

4、

起始信号:SCL保存高电平状态下,SDA由高变低;(变化时的高低电平要保持5us以上)

终止信号:SCL保存高电平状态下,SDA由低到高;
应答信号:接收数据的器件在接收到8位数据后,向发送数据的器件发出特定的低电平脉冲,表示已收到数据。用于表明I2C总线数据传输的结束
非应答信号:发出特定的高电平脉冲。
总线空闲:SCL和SDA都保持高电平。

5、I2C驱动代码如下:(已在24c02/24C08芯片上验证,能够正常读写。)
//芯片1次最多可连续写入字节个数就是每页字节个数,如24c08最多1次写8个字节。
读字节个数则没有限制。
//STM32相应头文件需要根据自己的工程添加!!

#define SCL_H         GPIO_SetBits(GPIOB, GPIO_Pin_10)
#define SCL_L         GPIO_ResetBits(GPIOB, GPIO_Pin_10)

#define SDA_H         GPIO_SetBits(GPIOB, GPIO_Pin_11)
#define SDA_L         GPIO_ResetBits(GPIOB, GPIO_Pin_11)

#define SCL_read      GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)
#define SDA_read      GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)

#define I2C_PageSize  16  //24C02每页8字节  24C08每页16字节

void Delay(u16 speed) 	//1us
{
u16 i;
while(speed!=0)
{
speed--;
for(i=0;i<20;i++);
}
}
void tus(u16 speed) 	//N us
{
Delay(speed);
}

void tms(u16 speed) 	//1ms
{
while(speed!=0)
{
speed--;
Delay(1000);
}
}

void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef  GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

/* Configure I2C1 pins: SCL and SDA */
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void I2C_delay(void)
{
u8 i=100; //这里可以优化速度	,经测试最低到5还能写入
while(i)
{
i--;
}
}

bool I2C_Start(void)
{
SDA_H;
SCL_H;
I2C_delay();
if(!SDA_read)return FALSE;	//SDA线为低电平则总线忙,退出
SDA_L;
I2C_delay();
if(SDA_read) return FALSE;	//SDA线为高电平则总线出错,退出
SDA_L;
I2C_delay();
return TRUE;
}

void I2C_Stop(void)
{
SCL_L;
I2C_delay();
SDA_L;
I2C_delay();
SCL_H;
I2C_delay();
SDA_H;
I2C_delay();
}

void I2C_Ack(void)
{
SCL_L;
I2C_delay();
SDA_L;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}

void I2C_NoAck(void)
{
SCL_L;
I2C_delay();
SDA_H;
I2C_delay();
SCL_H;
I2C_delay();
SCL_L;
I2C_delay();
}

bool I2C_WaitAck(void) 	 //返回为:=1有ACK,=0无ACK
{
SCL_L;
I2C_delay();
SDA_H;
I2C_delay();
SCL_H;
I2C_delay();
if(SDA_read)
{
SCL_L;
return FALSE;
}
SCL_L;
return TRUE;
}

void I2C_SendByte(u8 SendByte) //数据从高位到低位
{
u8 i=8;
while(i--)
{
SCL_L;
I2C_delay();
if(SendByte&0x80)
SDA_H;
else
SDA_L;
SendByte<<=1;
I2C_delay();
SCL_H;
I2C_delay();
}
SCL_L;
}

u8 I2C_ReceiveByte(void)  //数据从高位到低位//
{
u8 i=8;
u8 ReceiveByte=0;

SDA_H;
while(i--)
{
ReceiveByte<<=1;
SCL_L;
I2C_delay();
SCL_H;
I2C_delay();
if(SDA_read)
{
ReceiveByte|=0x01;
}
}
SCL_L;
return ReceiveByte;
}

//写入1字节数据       待写入数据    待写入地址       器件类型(24c16或SD2403)
bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress)
{
if(!I2C_Start())return FALSE;
I2C_SendByte(((WriteAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);
//设置高起始地址+器件地址
if(!I2C_WaitAck()){I2C_Stop(); return FALSE;}
I2C_SendByte((u8)(WriteAddress & 0x00FF));   //设置低起始地址
I2C_WaitAck();
I2C_SendByte(SendByte);
I2C_WaitAck();
I2C_Stop();
//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms)
//Systick_Delay_1ms(10);
tms(10);
return TRUE;
}

//注意不能跨页写
//写入1串数据      待写入数组地址    待写入长度      待写入地址       器件类型(24c16或SD2403)
bool I2C_BufferWrite(u8* pBuffer, u8 length,     u16 WriteAddress, u8 DeviceAddress)
{
if(!I2C_Start())return FALSE;
I2C_SendByte(((WriteAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);
//设置高起始地址+器件地址
if(!I2C_WaitAck()){I2C_Stop(); return FALSE;}
I2C_SendByte((u8)(WriteAddress & 0x00FF));   //设置低起始地址
I2C_WaitAck();

while(length--)
{
I2C_SendByte(* pBuffer);
I2C_WaitAck();
pBuffer++;
}
I2C_Stop();
//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms)
//Systick_Delay_1ms(10);
tms(10);
return TRUE;
}

//跨页写入1串数据  待写入数组地址    待写入长度      待写入地址   器件类型(24c16或SD2403)
void I2C_PageWrite(  u8* pBuffer, u8 length,     u16 WriteAddress, u8 DeviceAddress)
{
u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
Addr  = WriteAddress % I2C_PageSize;      //写入地址是开始页的第几位
count = I2C_PageSize - Addr;	      //在开始页要写入的个数
NumOfPage   =  length / I2C_PageSize;     //要写入的页数
NumOfSingle =  length % I2C_PageSize;     //不足一页的个数

if(Addr == 0)         //写入地址是页的开始
{
if(NumOfPage == 0)  //数据小于一页
{
I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);
//写少于一页的数据
}
else		            //数据大于等于一页
{
while(NumOfPage)//要写入的页数
{
I2C_BufferWrite(pBuffer,I2C_PageSize,WriteAddress,DeviceAddress);//写一页的数据
WriteAddress +=  I2C_PageSize;
pBuffer      +=  I2C_PageSize;
NumOfPage--;
tms(10);
}
if(NumOfSingle!=0)//剩余数据小于一页
{
I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);
//写少于一页的数据
tms(10);
}
}
}

else                  //写入地址不是页的开始
{
if(NumOfPage== 0)   //数据小于一页
{
I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);
//写少于一页的数据
}
else                //数据大于等于一页
{
length       -= count;
NumOfPage     = length / I2C_PageSize;  //重新计算要写入的页数
NumOfSingle   = length % I2C_PageSize;  //重新计算不足一页的个数

if(count != 0)
{
I2C_BufferWrite(pBuffer,count,WriteAddress,DeviceAddress);
//将开始的空间写满一页
WriteAddress += count;
pBuffer      += count;
}

while(NumOfPage--)  //要写入的页数
{
I2C_BufferWrite(pBuffer,I2C_PageSize,WriteAddress,DeviceAddress);//写一页的数据
WriteAddress +=  I2C_PageSize;
pBuffer      +=  I2C_PageSize;
}
if(NumOfSingle != 0)//剩余数据小于一页
{
I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);
//写少于一页的数据
}
}
}
}

//读出1串数据      存放读出数据  待读出长度      待读出地址       器件类型(24c16或SD2403)
bool I2C_ReadByte(u8* pBuffer,   u8 length,     u16 ReadAddress,  u8 DeviceAddress)
{
if(!I2C_Start())return FALSE;
I2C_SendByte(((ReadAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址
if(!I2C_WaitAck()){I2C_Stop(); return FALSE;}
I2C_SendByte((u8)(ReadAddress & 0x00FF));   //设置低起始地址
I2C_WaitAck();
I2C_Start();
I2C_SendByte(((ReadAddress & 0x0700) >>7) | DeviceAddress | 0x0001);
I2C_WaitAck();
while(length)
{
*pBuffer = I2C_ReceiveByte();
if(length == 1)I2C_NoAck();
else I2C_Ack();
pBuffer++;
length--;
}
I2C_Stop();
return TRUE;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  stm32-i2c