STM32单片机基础13——使用硬件I2C读写EEPROM(AT24C02)

本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件I2C外设读取EEPROM数据(以AT24C02为例)。

1. 准备工作

硬件准备

mark

  • EEPROM

小熊派开发板左边的接口是E53接口,用来连接E53接口的扩展板,每个扩展板都板载了一块EEPROM用来保存信息,如图:

mark

AT24C02的原理图如下(该原理图中有bug,A0的上拉电阻无效,实际A0为低电平):

mark

软件准备

  • 需要安装好Keil - MDK及芯片对应的包,以便编译和下载生成的代码;

Keil MDK和串口助手的安装包都可以关注“小熊派开源社区”微信公众号,在资料教程一栏中可获取安装包。

2.生成MDK工程

选择芯片型号

打开STM32CubeMX,打开MCU选择器:
mark

搜索并选中芯片STM32L431RCT6:
mark

配置时钟源

  • 如果选择使用外部高速时钟(HSE),则需要在System Core中配置RCC;
  • 如果使用默认内部时钟(HSI),这一步可以略过;

这里我都使用外部时钟:
mark

配置串口

小熊派开发板板载ST-Link并且虚拟了一个串口,原理图如下:

mark

这里我将开关拨到AT-MCU模式,使PC的串口与USART1之间连接。

接下来开始配置USART1

mark

配置硬件I2C

首先查看小熊派开发板的原理图,确定EEPROM接在哪个I2C接口上,如图:

mark

接下来开始配置I2C接口1:

mark

配置时钟树

STM32L4的最高主频到80M,所以配置PLL,最后使HCLK = 80Mhz即可:

mark

生成工程设置

mark

代码生成设置

最后设置生成独立的初始化文件:

mark

生成代码

点击GENERATE CODE即可生成MDK-V5工程:

mark

3. 在MDK中编写、编译、下载用户代码

修改I2C初始化代码的小BUG

mark

重定向printf( )函数

参考:重定向printf函数到串口输出的多种方法

编写EEPROM驱动程序

EEPROM的驱动编写篇幅过多,单独分出来一节讲述。

4. AT24C02驱动的编写

确定IIC器件地址

根据AT24C02的 Datasheet 可知AT24C02有2K bit,即256B,分为32页,每页8个字节,结合数据手册和原理图可以得知,板载AT24C02的读地址为0xA2,写地址为0xA3

mark

首先在at24c02_i2c_drv.h中编写AT24C02相关的宏定义:

#define	AT24C02_ADDR_WRITE	0xA0
#define	AT24C02_ADDR_READ	0xA1

然后在at24c02_i2c_drv.c中引入i2c.h,基于HAL提供的硬件IIC操作函数,编写AT24C02的一些底层函数,如下。

任意地址写一个字节

根据AT24C02的数据手册可知,AT24C02写一个字节的格式如下:

mark

编写的函数如下:

/**
 * @brief		AT24C02任意地址写一个字节数据
 * @param		addr —— 写数据的地址(0-255)
 * @param		dat  —— 存放写入数据的地址
 * @retval		成功 —— HAL_OK
*/
uint8_t At24c02_Write_Byte(uint16_t addr, uint8_t* dat)
{
   
	return HAL_I2C_Mem_Write(&hi2c1, AT24C02_ADDR_WRITE, addr, I2C_MEMADD_SIZE_8BIT, dat, 1
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您解答这个问题!以下是使用软件I2C方式驱动AT24C02STM32F103代码: ``` #include "stm32f10x.h" #include "delay.h" #define SDA GPIO_Pin_6 #define SCL GPIO_Pin_7 #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(uint32_t)8<<28;} #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(uint32_t)3<<28;} #define IIC_SCL PBout(7) //SCL #define IIC_SDA PBout(6) //SDA #define READ_SDA PBin(6) //输入SDA void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); IIC_SDA = 1; IIC_SCL = 1; } void IIC_Start(void) { SDA_OUT(); IIC_SDA = 1; IIC_SCL = 1; delay_us(4); IIC_SDA = 0; delay_us(4); IIC_SCL = 0; } void IIC_Stop(void) { SDA_OUT(); IIC_SCL = 0; IIC_SDA = 0; delay_us(4); IIC_SCL = 1; IIC_SDA = 1; delay_us(4); } u8 IIC_Wait_Ack(void) { u8 ucErrTime = 0; SDA_IN(); IIC_SDA = 1; delay_us(4); IIC_SCL = 1; delay_us(4); while (READ_SDA) { ucErrTime++; if (ucErrTime > 250) { IIC_Stop(); return 1; } delay_us(1); } IIC_SCL = 0; return 0; } void IIC_Ack(void) { IIC_SCL = 0; SDA_OUT(); IIC_SDA = 0; delay_us(2); IIC_SCL = 1; delay_us(2); IIC_SCL = 0; } void IIC_NAck(void) { IIC_SCL = 0; SDA_OUT(); IIC_SDA = 1; delay_us(2); IIC_SCL = 1; delay_us(2); IIC_SCL = 0; } void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL = 0; for (t = 0; t < 8; t++) { if ((txd & 0x80) >> 7) IIC_SDA = 1; else IIC_SDA = 0; txd <<= 1; delay_us(2); IIC_SCL = 1; delay_us(2); IIC_SCL = 0; delay_us(2); } } u8 IIC_Read_Byte(unsigned char ack) { unsigned char i, receive = 0; SDA_IN(); for (i = 0; i < 8; i++) { IIC_SCL = 0; delay_us(2); IIC_SCL = 1; receive <<= 1; if (READ_SDA) receive++; delay_us(1); } if (ack) IIC_Ack(); else IIC_NAck(); return receive; } void AT24C02_WriteOneByte(u16 addr, u8 data) { IIC_Start(); IIC_Send_Byte(0xA0); IIC_Wait_Ack(); IIC_Send_Byte(addr >> 8); IIC_Wait_Ack(); IIC_Send_Byte(addr & 0xFF); IIC_Wait_Ack(); IIC_Send_Byte(data); IIC_Wait_Ack(); IIC_Stop(); delay_ms(5); } u8 AT24C02_ReadOneByte(u16 addr) { u8 temp; IIC_Start(); IIC_Send_Byte(0xA0); IIC_Wait_Ack(); IIC_Send_Byte(addr >> 8); IIC_Wait_Ack(); IIC_Send_Byte(addr & 0xFF); IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(0xA1); IIC_Wait_Ack(); temp = IIC_Read_Byte(0); IIC_Stop(); return temp; } int main(void) { IIC_Init(); while (1) { AT24C02_WriteOneByte(0x00, 0x01); // 在地址0x00写入数据0x01 delay_ms(200); AT24C02_WriteOneByte(0x01, 0x02); // 在地址0x01写入数据0x02 delay_ms(200); AT24C02_WriteOneByte(0x02, 0x03); // 在地址0x02写入数据0x03 delay_ms(200); u8 val_0x00 = AT24C02_ReadOneByte(0x00); // 从地址0x00读取一个字节 u8 val_0x01 = AT24C02_ReadOneByte(0x01); // 从地址0x01读取一个字节 u8 val_0x02 = AT24C02_ReadOneByte(0x02); // 从地址0x02读取一个字节 printf("val_0x00=%d, val_0x01=%d, val_0x02=%d\n", val_0x00, val_0x01, val_0x02); // 打印读取的结果 delay_ms(1000); } } ``` 希望能够帮助您!如果您有更多问题,请随时和我联系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值