STM32 使用IO口模拟IIC读写AT24C128

STM32 使用IO口模拟IIC读写AT24C128

  • AT24C128简介
    • 使用STM32的IO模拟IIC的时序
      • 头文件
      • c文件
    • PS 扩展内容

AT24C128简介

AT24C128,ATMEL公司出的EEPROM芯片,共有16,384 x 8bit,整个存储器共有256页,每页64BYTES.
使用IIC协议。

使用STM32的IO模拟IIC的时序

头文件

#ifndef EEPROM_H
#define EEPROM_H

#include "Typedef.h"

#define IIC_SDA_PORT    GPIOA
#define IIC_SCL_PORT		GPIOA
#define IIC_SCL_PIN    GPIO_PIN_3
#define IIC_SDA_PIN			GPIO_PIN_4

#define HIGH    GPIO_PIN_SET
#define LOW     GPIO_PIN_RESET
#define PinInput   1
#define PinOutput  0
#define SDA_IN()  {GPIOA->CRL&=0XFFF0FFFF;GPIOA->CRL|=(u32)8<<16;}
#define SDA_OUT() {GPIOA->CRL&=0XFFF0FFFF;GPIOA->CRL|=(u32)3<<16;}


#define GetSDA()         (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4)==GPIO_PIN_SET) //(GPIOA->IDR&((u32)1<<4))
#define SetSDA(state)   HAL_GPIO_WritePin(IIC_SDA_PORT,IIC_SDA_PIN,state)
#define SetSCL(state)   HAL_GPIO_WritePin(IIC_SCL_PORT,IIC_SCL_PIN,state)



#define E_DELAY	2		// 两个EEPROM命令之间的间隔

extern u8 iic_cmd_delay;

extern u8 AT24CXX_ReadOneByte(u32 ReadAddr);
extern void AT24CXX_WriteOneByte(u32 WriteAddr,u8 DataToWrite);
extern void AT24CXX_Read(u32 ReadAddr,u8 *pBuffer,u16 NumToRead);
extern void AT24CXX_Write(u32 WriteAddr,u8 *pBuffer,u16 NumToWrite);
extern void AT24CXX_clr(u32 WriteAddr,u32 NumToWrite);

extern void IIC_Start1(void);
void IIC_Stop(void);
u8 IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_Send_Byte(u8 txd);

void delay_nop(void);
void SetSDADirection(u8 direction);

#endif

c文件


#include "main.h"
#include "eeprom.h"


/*	AT24C128C  organized as 256 pages of 64-bytes each
	AT24C512C, organized as 512 pages of 128 bytes each
	不同的的EEPROM对应的页大小不一样,注意修改
*/

#define PAGE_SIZE	64



u8 iic_cmd_delay=0;

void SetSDADirection(u8 direction)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = IIC_SDA_PIN;
    if(direction)
		{
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
				GPIO_InitStruct.Pull = GPIO_PULLUP;
		}
    else
		{
			GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
			GPIO_InitStruct.Pull = GPIO_NOPULL;
		
		}
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}



// SDA
/***********************iic**************************/
void delay_nop(void)
{
	unsigned int i;
	
	for(i=0;i<50;i++)
		__nop();
	
}
//产生IIC起始信号
void IIC_Start(void)
{
	/*while(iic_cmd_delay < E_DELAY)	// 距上一个命令时间太短
	{
		R_WDT_Restart();
	}*/

	SDA_OUT();//sda线输出
	SetSDA(HIGH);
	SetSCL(HIGH);
	delay_nop();
   SetSDA(LOW);//START:when CLK is high,DATA change form high to low
	delay_nop();
	SetSCL(LOW);//钳住I2C总线,准备发送或接收数据
	

}
void IIC_Start1(void)
{
	SDA_OUT(); //sda线输出
	SetSDA(HIGH);
	delay_nop();
	SetSCL(HIGH);
	delay_nop();
 	SetSDA(LOW);	//START:when CLK is high,DATA change form high to low
	delay_nop();
	SetSCL(LOW);	//钳住I2C总线,准备发送或接收数据
}


//产生IIC停止信号
void IIC_Stop(void)
{
	SetSDA(LOW);
		SDA_OUT();//sda线输出
	SetSCL(LOW);
	delay_nop();
	SetSCL(HIGH);
	delay_nop();
    SetSDA(HIGH);//发送I2C总线结束信号
	delay_nop();

	iic_cmd_delay=0;
}
//等待应答信号到来
//返回值:
//		1,接收应答失败
//       	 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 flag;
		
	SDA_IN(); //SDA设置为输入
	SetSDA(HIGH);
	delay_nop();
	SetSCL(HIGH);
	delay_nop();
	if(GetSDA())
		flag=1;
	else
		flag=0;
	SetSCL(LOW);		//时钟输出0
	return flag;
}
//产生ACK应答
void IIC_Ack(void)
{
	SetSCL(LOW);
		SDA_OUT();
	SetSDA(LOW);
	delay_nop();
	SetSCL(HIGH);
	delay_nop();
	SetSCL(LOW);
}
//不产生ACK应答
void IIC_NAck(void)
{
	SetSCL(LOW);
	SDA_OUT();
	SetSDA(HIGH);
	delay_nop();
	SetSCL(HIGH);
	delay_nop();
	SetSCL(LOW);
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
	u8 t;
	SDA_OUT();
	SetSCL(LOW);//拉低时钟开始数据传输
	for(t=0;t<8;t++)
	{
		if((txd&0x80)>>7)
            SetSDA(HIGH);
        else SetSDA(LOW);
		txd<<=1;
		delay_nop();
		SetSCL(HIGH);
		delay_nop();
		SetSCL(LOW);
	}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(u8 ack)
{
	u8 i,receive=0;
	SDA_IN();
	for(i=0;i<8;i++ )
	{
		SetSCL(LOW);
		delay_nop();
		SetSCL(HIGH);
		delay_nop();
		
		receive<<=1;
		if(GetSDA())
			receive++;
	}
	if (!ack)
	    IIC_NAck();//发送nACK
	else
	    IIC_Ack(); //发送ACK
	return receive;
}
/***********************iic**************************/
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u32 ReadAddr)
{
	u8 temp;
	u8 addrH,addrL;

	addrH=ReadAddr>>8;
	addrL=ReadAddr&0xff;

	IIC_Start();

	IIC_Send_Byte(0xa0);

	IIC_Wait_Ack();
	IIC_Send_Byte(addrH);   //发送高地址
	IIC_Wait_Ack();
   	IIC_Send_Byte(addrL);   //发送低地址
	IIC_Wait_Ack();
	IIC_Start();
	IIC_Send_Byte(0xa1);         //进入接收模式

	IIC_Wait_Ack();
	temp=	IIC_Read_Byte(0);
	IIC_Stop();//产生一个停止条件

	return temp;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr  :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u32 WriteAddr,u8 DataToWrite)
{
	u8 addrH,addrL;

	addrH=WriteAddr>>8;
	addrL=WriteAddr&0xff;

	IIC_Start();

	IIC_Send_Byte(0xa0);

	IIC_Wait_Ack();
	IIC_Send_Byte(addrH);   //发送高地址
	IIC_Wait_Ack();
	IIC_Send_Byte(addrL);   //发送低地址
	IIC_Wait_Ack();
	IIC_Send_Byte(DataToWrite);     //发送字节
	IIC_Wait_Ack();
	IIC_Stop();//产生一个停止条件
}
//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer  :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(u32 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
	u8 addrH,addrL;

	if(NumToRead==0)
		return;

	addrH=ReadAddr>>8;
	addrL=ReadAddr&0xff;

	IIC_Start();

	IIC_Send_Byte(0xa0);//写
	IIC_Wait_Ack();

	IIC_Send_Byte(addrH);   //发送高地址
	IIC_Wait_Ack();
   	IIC_Send_Byte(addrL);   //发送低地址
	IIC_Wait_Ack();

	IIC_Start();
	IIC_Send_Byte(0xa1);  //读       //进入接收模式
	IIC_Wait_Ack();

	NumToRead--;
	while(NumToRead)
	{
		*pBuffer=IIC_Read_Byte(1);
		pBuffer++;
		ReadAddr++;
		NumToRead--;
	}
	*pBuffer=IIC_Read_Byte(0);
	IIC_Stop();//产生一个停止条件
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer   :数据数组首地址
//NumToWrite:要写入数据的个数
// 每128bytes为一个page,写操作不能跨页
void AT24CXX_Write(u32 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
	u8 addrH,addrL;
	u16 lentmp,lentow,lens;
	u8 tmp;

	if(NumToWrite==0)
		return;

	lentmp=NumToWrite;
	while(lentmp)
	{
		tmp=WriteAddr%PAGE_SIZE;
		tmp=PAGE_SIZE-tmp;
		if(tmp>=lentmp)//当页剩余空间大于要写入的长度
		{
			lentow=lentmp;
		}
		else
		{
			lentow=tmp;//写入长度只能是剩余空间长度
		}
		lentmp=lentmp-lentow;//计算要在下一页写入的长度
		lens=lentow;

		addrH=WriteAddr>>8;
		addrL=WriteAddr&0xff;

		IIC_Start();

		IIC_Send_Byte(0xa0);

		IIC_Wait_Ack();
		IIC_Send_Byte(addrH);   //发送高地址
		IIC_Wait_Ack();
		IIC_Send_Byte(addrL);   //发送低地址
		IIC_Wait_Ack();

		while(lentow--)
		{
			IIC_Send_Byte(*pBuffer);
			IIC_Wait_Ack();
			pBuffer++;
		}
		IIC_Stop();//产生一个停止条件

		WriteAddr=WriteAddr+lens;//下一页的地址
	}
}
void AT24CXX_clr(u32 WriteAddr,u32 NumToWrite)
{
	u8 addrH,addrL;
	u16 lentmp,lentow,lens;
	u8 tmp;

	lentmp=NumToWrite;
	while(lentmp)
	{
		tmp=WriteAddr%PAGE_SIZE;
		tmp=PAGE_SIZE-tmp;
		if(tmp>=lentmp)
		{
			lentow=lentmp;
		}
		else
		{
			lentow=tmp;
		}
		lentmp=lentmp-lentow;
		lens=lentow;

		addrH=WriteAddr>>8;
		addrL=WriteAddr&0xff;

		IIC_Start();

		IIC_Send_Byte(0xa0);

		IIC_Wait_Ack();
		IIC_Send_Byte(addrH);   //发送高地址
		IIC_Wait_Ack();
		IIC_Send_Byte(addrL);   //发送低地址
		IIC_Wait_Ack();

		while(lentow--)
		{
			IIC_Send_Byte(0xff);
			IIC_Wait_Ack();
		}
		IIC_Stop();//产生一个停止条件

		WriteAddr=WriteAddr+lens;
	}
}
//----------------------EEPROM---------------------------//
//--------------------------------------------------------EEPROM DATA

PS 扩展内容

本文写的时候是为AT24C128写的,但是也支持其他容量的芯片,比如AT24C02,AT24C256等等。只需要根据对应芯片的页大小修改对应的宏定义就可以了。

你可能感兴趣的:(STM32)