STM32 寄存器配置笔记——I2C 读写AT24C02 EEPROM

2023-12-15 19:18:41

一、简介

? ? ? ? 本文主要介绍STM32F10xx系列如何使用软件模拟I2C总线读写AT24C02的EEPROM数据。

二、概述

? ? ? ? I2C协议是一种用于同步、半双工、串行总线(由单片机时钟线、单数据交换器数据线组成)上的协议。规定了总线空闲状态、起始条件、停止条件、数据有效性、字节格式、响应确认信号、从设备地址选择、数据方向。有主从机之分,主机主控就是掌控单片机时钟信号的一方,并且起始信号和停止信号也由主机发送。现在很多的硬件、传感器等都是用到i2c协议与MCU(stm32)进行通信的。因此i2c还是必不可少的一个重要知识点。

三、I2C协议

? ? ? ? I2C基本读写过程如下:

? ? ? ??

包括:起始信号、停止信号、应答、发送数据等。

? ? ? ? 1)起始信号

? ? ? ? 在SCL为高定平的基础上,SDA由高电平跳变为低电平为起始信号。为一次传输的开始。

? ? ? 2)停止信号

? ? ??在SCL为高定平的基础上,SDA由低电平跳变为高电平为停止信号。为一次传输的结束。

? ? ? ? 3)数据有效性

? ? ? ?在起始信号接收到之后,需将SCL信号拉低准备数据传输。SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL 为低电平时,SDA的数据无效,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。注意每次数据传输都以字节为单位。

? ? ? ? 4)应答

? ? ? ?I2C 的数据和地址传输都带响应。响应包括“应答 (ACK)”和“非应答 (NACK)”两种信号。作为数据接收端时,当设备 (无论主从机) 接收到 I2C 传输的一个字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答 (ACK)”信号,发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答 (NACK)”信号,发送方接收到该信号后会产生一个
停止信号,结束信号传输。传输时主机产生时钟,在第9个时钟时,数据发送端会释放 SDA 的控制权,由数据接收端控制SDA,若 SDA 为高电平,表示非应答信号 (NACK),低电平表示应答信号 (ACK)。
?

四、驱动代码

? ? ? ? 1)IIC驱动

#include "iic_drv.h"
#include "delay.h"

#define IIC_SCL  PCout(12)
#define IIC_SDA  PCout(11)

#define SDA_OUT() {GPIOC->CRH &= 0xFFFF0FFF;\
                 GPIOC->CRH |= 0x00003000;\
                };

#define SDA_IN() {GPIOC->CRH &= 0xFFFF0FFF;\
                 GPIOC->CRH |= 0x00008000;\
                };

void IIC_Init(void)
{
    RCC->APB2ENR |= 1 << 4;           //enable I/O port C clock
    GPIOC->CRH &= 0xFFF00FFF;          //cfg sda scl as output PP speed 50MHZ
	  GPIOC->CRH |= 0x00033000;
	  GPIOC->ODR |= 0x3 << 11;           //cfg sda scl output '1'
}

//start signal SCL=1 SDA change from high to low
void IIC_Start(void)
{
    SDA_OUT();
	  IIC_SDA = 1;
    IIC_SCL = 1;
	  delay_us(4);
	  IIC_SDA = 0;
	  delay_us(4);
	  //IIC_SCL = 0;     //SCL change to low for ready to send data after start signal(SCL = 1,data line stable,data valid)
}

//stop signal SCL=1 SDA change from low to high
void IIC_Stop(void)
{
    SDA_OUT();
	  IIC_SDA = 0;
    IIC_SCL = 1;
	  delay_us(4);
	  IIC_SDA = 1;
	  delay_us(4);
}

//wait ack
//return       0:ACK
//             1:NACK
void IIC_WaitAck(void)
{
	  u8 ack = 0;
	  u16 timeout = 0;
    SDA_IN();
	  IIC_SCL = 1;
	  delay_us(4);
	  do
		{
	      ack = IIC_SDA;
			  timeout++;
			  if (timeout > 250)
				{
				    IIC_Stop();
					  return;
				}
		} while (ack);
}

//send Ack
void IIC_Ack(void)
{
    SDA_OUT();
	  IIC_SDA = 0;
    IIC_SCL = 1;
	  delay_us(4);
	  IIC_SCL = 0;    //release scl
	  delay_us(4);
}

//send NAck
void IIC_NAck(void)
{
    SDA_OUT();
	  IIC_SDA = 1;
    IIC_SCL = 1;
	  delay_us(4);
	  IIC_SCL = 0;    //release scl
	  delay_us(4);
}

void IIC_Send_Byte(u8 dat)
{
	  u8 i;
    SDA_OUT();

	  for (i = 0; i < 8; i++)
	  {
        IIC_SCL = 1;
			  IIC_SDA = (dat >> 7) & 0x01;
			  delay_us(2);
        IIC_SCL = 0;
			  delay_us(2);
	  }
}

u8 IIC_Read_Byte(u8 ack)
{
	  u8 i, receive = 0;
    SDA_IN();

	  for (i = 0; i < 8; i++)
	  {
        IIC_SCL = 1;
			  receive |= IIC_SDA << (7 - i);
			  delay_us(4);
        IIC_SCL = 0;
			  delay_us(4);
	  }
		
		(ack == 0) ? IIC_Ack() : IIC_NAck();
		return receive;
}

? ? ? ? 2)AT24C02驱动

#include "AT24C02.h"
#include "iic_drv.h"
#include "delay.h"

void AT24C02_Init(void)
{
    IIC_Init();
}

u8 AT24C02_ReadOneByte(u16 ReadAddr)
{
    u8 temp = 0;
	  IIC_Start();
	  IIC_Send_Byte(0xA0);
	  IIC_WaitAck();
	  IIC_Send_Byte(ReadAddr%256);
	  IIC_WaitAck();
	  IIC_Start();
	  IIC_Send_Byte(0xA1);
	  IIC_WaitAck();
	  temp = IIC_Read_Byte(1);
	  IIC_Stop();
}

void AT24C02_WriteOneByte(u16 WriteAddr, u8 dat)
{
    IIC_Start();
	  IIC_Send_Byte(0xA0);
	  IIC_WaitAck();
	  IIC_Send_Byte(WriteAddr%256);
	  IIC_WaitAck();
    IIC_Send_Byte(dat);
	  IIC_WaitAck();
	  IIC_Stop();
	  delay_ms(10);
}

u8 AT24C02_Check(void)
{
    u8 temp;
	  temp = AT24C02_ReadOneByte(0xFF);
	  if (temp == 0x55)
			return 0;

		AT24C02_WriteOneByte(0xFF, 0x55);
		temp = AT24C02_ReadOneByte(0xFF);
		if (temp == 0x55)
			return 0;
		
		return 1;
}

void AT24C02_Read(u16 ReadAddr, u8 *pBuf, u16 NumToRead)
{
    while (NumToRead--)
		{
		    *pBuf++ = AT24C02_ReadOneByte(ReadAddr);
			  ReadAddr++;
		}
}

void AT24C02_Write(u16 WriteAddr, u8 *pBuf, u16 NumToWrite)
{
    while (NumToWrite--)
		{
		    AT24C02_WriteOneByte(WriteAddr, *pBuf);
			  WriteAddr++;
			  pBuf++;
		}
}

文章来源:https://blog.csdn.net/wanruiou/article/details/135021573
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。