STM32软件模拟实现IIC写入和读取AT24C02(STM32CubeMx配置)

这篇具有很好参考价值的文章主要介绍了STM32软件模拟实现IIC写入和读取AT24C02(STM32CubeMx配置)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原理了解

IIC总线协议

IIC:Inter Integrated Circuit,集成电路总线,是一种同步 串行 半双工通信总线。
在使用IIC时分为硬件IIC以及软件IIC,下图为两者的区别:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
在使用IIC前先来了解一下IIC总线结构图,即下图:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
从图中可以看出IIC有两个双向信号线,一根是数据线SDA,一根是时钟线SCL,并且都接上拉电阻,保证总线空闲状态为高电平;同时上面可以挂在多个设备,允许多主机存在,每个设备都有一个唯一的地址。
在使用IIC过程中可以归纳为以下几个比较重要的部分:
三个信号:起始信号、停止信号、应答信号
两个注意:数据有效性、数据传输顺序
一个状态:空闲状态
后续会将这几个部分拆开来看并使用代码进行实现

AT24C02器件

EEPROM是一种掉电后数据不丢失的储存器,常用来存储一些配置信息,在系统重新上电时就可以加载,而AT24C02是一个2K bit的EEPROM存储器,使用IIC通信方式。所以在本实验中将AT24C02作为我们的EEPROM。首先我们先来了解一下如何对AT24C02进行写入和读取,AT24C02的通讯地址具有8位,如下图:模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
前四位是固定死的为1010,后面的A0-A2三个位则为可编程部分,这七个位则构成了我们的设备地址,而最后一位为方向位,当你置1时则为读数据,置0时则为写入数据,那么八位构成我们的通讯地址,此时将A0-A2置0,最后一位也置0,则生成八位数字10100000即0xA0,这个就是我们的写操作地址,同理读操作地址则为0xA1。
完成这两个之后,根据硬件手册的写时序及读时序设置我们的函数就可以实现对EEPROM的操作啦,后续将会结合代码讲解。

STM32CubeMx配置

选好自己的板子型号,根据硬件原理图:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机

选用PB6以及PB7两个IO口进行软件模拟IIC,配置如下:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
PB6设置为推挽输出,用作SCL线,方便输出高低电平对时钟线进行控制,PB7设置为开漏输出,用作SDA线,这里将PB7设置为开漏输出是因为SDA线既要用作输出,也要用作输入(从机应答信号),使用开漏模式则可以解决这个问题,剩下的PE3/4/5则是按键和LED方便验证程序,可根据自身需要进行设置。使用串口通信验证写入和读取,接下来就可以生成代码了。

工程生成及代码编写

工程生成

打开工程后可以看到左边已经生成了我们需要的代码:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
打开gpio.c文件可以看到里面已经初始化好我们刚刚设置IO口:模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机

代码编写

延时函数

在操作IIC的过程中需要对时间进行一定的掌控,包括高电平的稳定,电平跳变的产生,等待EEPROM写入数据,所以第一步我们先实现延时函数方便控制。

delay.c编写
#include "delay.h"

static uint16_t  g_fac_us = 0;      /* us延时倍乘数 */

/**
 * @brief       初始化延迟函数
 * @param       sysclk: 系统时钟频率, 即CPU频率(HCLK)
 * @retval      无
 */
void delay_init(uint16_t sysclk)
{
    SysTick->CTRL = 0;                                          /* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);   /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */
    g_fac_us = sysclk / 8;                                      /* g_fac_us作为1us的基础时基 */
}

/**
 * @brief       延时nus
 * @param       nus: 要延时的us数.
 * @note        注意: nus的值,不要大于1864135us(最大值即2^24 / g_fac_us @g_fac_us = 9)
 * @retval      无
 */
void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = nus * g_fac_us; /* 时间加载 */
    SysTick->VAL = 0x00;            /* 清空计数器 */
    SysTick->CTRL |= 1 << 0 ;       /* 开始倒数 */

    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必须为1, 并等待时间到达 */

    SysTick->CTRL &= ~(1 << 0) ;    /* 关闭SYSTICK */
    SysTick->VAL = 0X00;            /* 清空计数器 */
}

/**
 * @brief       延时nms
 * @param       nms: 要延时的ms数 (0< nms <= 65535)
 * @retval      无
 */
void delay_ms(uint16_t nms)
{
    uint32_t repeat = nms / 1000;   /* 记录超出1000ms的值,即1s */
    uint32_t remain = nms % 1000;   /* 记录未超出1000ms的值 */

    while (repeat)
    {
        delay_us(1000 * 1000);      /* 利用delay_us 实现 1000ms 延时 */
        repeat--;
    }

    if (remain)
    {
        delay_us(remain * 1000);    /* 利用delay_us, 把尾数延时(remain ms)给做了 */
    }
}

delay.h编写
#ifndef __DELAY_H
#define __DELAY_H

#include "main.h"

void delay_init(uint16_t sysclk);
void delay_us(uint32_t nus);
void delay_ms(uint16_t nms);

#endif

IIC函数实现

完成了延时函数,首先再定义一个IIC中独立使用到的delay函数:

/* iic delay函数 */
static void iic_delay(void)
{
    delay_us(2);
}

接下来根据上面所讲的三个信号来看看对应的时序是怎么完成的。

IIC起始信号

首先是起始信号:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
从图中可以看出,起始信号在代码中要做的就三件事,1.保持SCL高电平;2.SDA产生一个下降沿;3.将SCL拉低,代码如下:

/* iic起始信号 */
void iic_start(void)
{
    /* 保持时钟线高电平,数据线产生下降沿 */
    IIC_SDA(1);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(0);
    iic_delay();

    /* 拉低时钟线,准备发送/接收数据 */
    IIC_SCL(0);
    iic_delay();
}
IIC停止信号

再来看看停止信号:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
从图中可以看出,停止信号操作跟起始信号操作大致一样,只不过SDA的下降沿改为了上升沿,代码如下:

/* iic停止信号 */
void iic_stop(void)
{
    /* 保持时钟线高电平,数据线产生上升沿 */
    IIC_SDA(0);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);
    iic_delay();
}
应答信号

最后再来看看应答信号:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
从图中可以看出,在保持SCL高电平的状态下,通过读取SDA线的电平状态来判断从机是否应答,由于SDA默认是为高信号,所以通过从机操作SDA线,将SDA线拉低则视为应答,而不动则视为非应答。实现代码如下:

/* 等待应答信号 */
/* return 0:fail 1:succeed*/
uint8_t iic_wait_ack (void)
{
    IIC_SDA(1);    /* 主机释放SDA线 */
    iic_delay();
    IIC_SCL(1);    /* 拉高SCL等待读取从机应答信号 */
    iic_delay();
    if (IIC_READ_SDA) /* SCL高电平读取SDA状态 */
    {
        iic_stop();     /* SDA高电平表示从机非应答 */
        return 0;
    }
    IIC_SCL(0);         /* SCL低电平表示结束应答检查 */
    iic_delay();
    return 1;
}

同样主机在读取从机发送数据时,也要通知从机是应答还是非应答来选择是否继续接受数据,实现函数如下:

/* 应答信号 */
void iic_ack(void)
{
    IIC_SCL(0);
    iic_delay();
    IIC_SDA (0);  /* 数据线为低电平,表示应答 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
}

/* 非应答信号 */
void iic_nack(void)
{
    IIC_SCL(0);
    iic_delay();
    IIC_SDA(1);  /* 数据线为高电平,表示非应答 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
}

到这里,三个信号我们就解决了。

数据的发送及读取

接下来看一下两个注意,即实现IIC发送函数及读取函数,首先根据前文所说IIC的特性之一是串行,即数据的发送是一位一位进行发送,在这里选择数据的发送顺序为高位在先,实现代码如下:

/* 发送一个字节数据 */
void iic_send_byte(uint8_t data)
{
    for (uint8_t t=0; t<8; t++)
    {
        /* 高位先发 */
        IIC_SDA((data & 0x80) >> 7);
        iic_delay();
        /* 拉高时钟线,稳定数据接收 */
        IIC_SCL (1);
        iic_delay();
        
        IIC_SCL (0);
        data <<= 1;     /* 左移1位, 用于下一次发送 */
    }
    IIC_SDA (1);      /* 发送完成,主机释放SDA线 */ 
}

/* 读取1字节数据 */
/* ack:通知从机是否继续发送。
   0,停止发送;1,继续发送
*/
uint8_t iic_read_byte(uint8_t ack)
{ 
    uint8_t receive = 0 ;
    for (uint8_t t=0; t<8; t++)
    {
        /* 高位先输出,先收到的数据位要左移 */ 
        receive <<= 1;
        IIC_SCL(1);
        iic_delay();
        if (IIC_READ_SDA) 
        {
            receive++;
        }
        IIC_SCL(0);
        iic_delay();
    }
    /* 判断是否继续读取从机数据 */
    if ( !ack ) 
    {
        iic_nack();
    }
    else 
    {
        iic_ack();
    }
    return receive;
}

还有一个状态需要注意的是,空闲状态为高电平,所以在我们操作完SCL以及SDA两条线之后都需要将其置为高电平,代码中已实现。

iic函数头文件

#ifndef __MYIIC_H
#define __MYIIC_H

#include "main.h"
#include "delay.h"

/* 引脚定义 */
#define IIC_SCL_GPIO_PORT GPIOB
#define SCL GPIO_PIN_6

#define IIC_SDA_GPIO_PORT GPIOB
#define SDA GPIO_PIN_7

/* SCL引脚设置宏定义 */
#define IIC_SCL(x) do{x ? \
                      HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, SCL, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, SCL, GPIO_PIN_RESET); \
                     }while(0)

/* SDA引脚设置宏定义 */
#define IIC_SDA(x) do{x ? \
                      HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, SDA, GPIO_PIN_SET) : \
                      HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, SDA, GPIO_PIN_RESET); \
                     }while(0)

#define IIC_READ_SDA HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, GPIO_PIN_7)


void at24c02_write_one_byte(uint8_t addr, uint8_t data);
uint8_t at24c02_read_one_byte(uint8_t addr);

#endif

这样我们的IIC函数就大功告成啦,但是我们只是完成了IIC的基础功能,接下来则是添加写入/读取AT24C02函数啦,即头文件中的:

void at24c02_write_one_byte(uint8_t addr, uint8_t data);
uint8_t at24c02_read_one_byte(uint8_t addr);

AT24C02的写/读函数

AT24C02写函数

先来看看AT24C02支持的写入操作有哪些:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
可以看出,若使用页写模式可能会因为操作不当导致原先的数据被覆盖,所以这里选择字节写模式。
选定好字节写模式后,接来就是了解字节写的时序并进行代码编写:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
从图中我们就可以清晰的看出我们需要操作的步骤,结合IIC实现的函数即可实现对AT24C02的写操作,代码如下:

/* 写入一个字节到从机AT24C02中 */
void at24c02_write_one_byte(uint8_t addr, uint8_t data)
{
    /* 1、发送起始信号 */
    iic_start();
    
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(0xA0);
    
    /* 3、等待应答信号 */
    iic_wait_ack();
    
    /* 4、发送内存地址:0~255 */
    iic_send_byte(addr);
    
    /* 5、等待应答信号 */
    iic_wait_ack();
    
    /* 6、发送写入数据 */
    iic_send_byte(data);
    
    /* 7、等待应答信号 */
    iic_wait_ack();
    
    /* 8、发送停止信号 */
    iic_stop();
    
    /* 等待EEPROM写入完成 */
    delay_ms(10);
}

在这里需要注意,AT24C02的容量为256byte,所以在传输地址参数时范围应当选择0~255,以及EEPROM的写入时间在器件手册中有标明最长是需要5ms时间,所以这里延时10ms确保其写入完成。模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机

AT24C02读函数

接下来来看看读函数的实现,首先了解一下支持的读取操作
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
这里从图中可以看出随机地址读模式是比较自由的,因为可以读取指定地址的数据,所以这里选定随机地址读模式。
接下来同样先查看对应的时序图:
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
可以看出读时序相比较写时序则多了很多步骤,但是实质上也就是将先前编写好的IIC函数按照时序一个一个放进去就好了,代码如下:

/* 往从机AT24C02中读取一个字节 */
uint8_t at24c02_read_one_byte(uint8_t addr)
{
    uint8_t read = 0;
    
    /* 1、发送起始信号 */
    iic_start();
    
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(0xA0);
    
    /* 3、等待应答信号 */
    iic_wait_ack();
    
    /* 4、发送内存地址:0~255 */
    iic_send_byte(addr);
    
    /* 5、等待应答信号 */
    iic_wait_ack();
    
    /* 6、发送起始信号 */
    iic_start();
    
    /* 7、发送通讯地址(读操作地址) */
    iic_send_byte(0xA1);
    
    /* 8、等待应答信号 */
    iic_wait_ack();
    
    /* 9、接收数据并发送非应答(获取该地址即可) */
    read = iic_read_byte(0);
    
    /* 10、发送停止信号 */
    iic_stop();
    
    return read;
}

至此AT24C02的写入和读取函数就完成了,接下来就是验证程序是否能实现所需的功能了。

main函数编写

/* USER CODE BEGIN Includes */
#include "key.h"
#include "myiic.h"
#include "delay.h"
/* USER CODE END Includes */

首先将需要的头文件包含进去,接下来就是功能验证:

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    delay_init(72);
    uint8_t t = 0;
    uint8_t key = 0;
    uint8_t data = 0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      key = key_scan();
      if(key == KEY0_PRESS)
      {
        at24c02_write_one_byte(0, 100);
        printf("write success\r\n");
      }
      
      if(key == KEY1_PRESS)
      {
        data = at24c02_read_one_byte(0);
        printf("data:%d\r\n",data);
      }
      
      /* LED闪烁证明程序正常运行 */
      t++;
      if(t == 20)
      {
          t = 0;
          HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_5);
      }
      delay_ms(10);
  }
  /* USER CODE END 3 */
}

这里通过板子上的两个按键,一个进行写入,一个进行读取,通过串口打印到电脑上观看数据是否成功写入,以及LED灯闪烁证明程序正常运行。

实现效果

模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
可以看到已经能够成功的写入及读取,这里将数据改为50再来看看
模拟iic配置,STM32学习记录,stm32,嵌入式硬件,单片机
修改为50也成功的写入了,证明代码是没有问题的。
至此软件模拟IIC实验就完成了,也希望大家可以指出有不对地方共同学习,谢谢!文章来源地址https://www.toymoban.com/news/detail-759321.html

到了这里,关于STM32软件模拟实现IIC写入和读取AT24C02(STM32CubeMx配置)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 第19讲 IIC(协议简介/读取驱动AT24C02/实验)

    IIC: Inter Integrated Circuit,集成电路总线,是一种 同步 串行 半双工通信协议 ①总线由数据线 SDA 和时钟线 SCL 构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。 ②总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时序就可以

    2024年02月03日
    浏览(38)
  • STM32——AT24C02(EEPROM )

    1、简介 AT24C02是一种2 Kb(256 × 8)串行电子可擦可编程只读存储器(EEPROM)芯片,支持标准I2C总线通信协议。 AT24C02的编程操作是以页为单位完成的,每次最多可编程8个连续字节。具体来说,AT24C02的一页大小为8个字节,每次写入数据时,需要确保写入的数据不跨页。因此,

    2024年04月13日
    浏览(45)
  • STM32速成笔记—EEPROM(AT24C02)

    🎀 文章作者:二土电子 🌸 关注文末公众号获取其他资料和工程文件! 🐸 期待大家一起学习交流! AT24C01/02/04/08/16…是一个1K/2K/4K/8K/16K位电可擦除PROM,内部含有128/256/512/1024/2048个8位字节,AT24C01有一个8字节页写缓冲器,AT24C02/04/08/16有一个16字节页写缓冲器。电压可允许低

    2024年02月11日
    浏览(62)
  • STM32MX配置EEPROM(AT24C02)------保姆级教程

    ———————————————————————————————————— ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子,使用STM32CUBEMX做了很多驱动,包括ADC、UART、RS485、EEPROM(IIC)、FLASH(SPI)等等。

    2024年02月16日
    浏览(53)
  • STM32-I2C通信在AT24C02的应用

    AT24C02是一种失去电源供给后依旧能保持数据的储存器,常用来储存一些配置信息,在系统重新上电之后也可以加载。它的容量是2k bit的EEPROM存储器,采用I2C通信方式。 AT24C02支持两种写操作:字节写操作和页写操作。本实验中我们采用的是字节写操作,就是一个地址一个数据

    2024年02月09日
    浏览(52)
  • 【STM32】AT24C256硬件I2C读写,基于HAL库

    目录 一、简单介绍 二、配置工程 打开CubeMX,配置时钟,调试接口,工程名,目录等 配置iic 配置串口用于显示信息 三、硬件连接 四、代码编写 一、随机写入一个字节 测试代码 波形如下 代码编写 二、连续写入 代码如下 三、随机读取 测试代码 波形如下 代码编写 四、连续

    2024年02月03日
    浏览(51)
  • AT24C32、AT24C64、AT24C128、AT24C256、AT24C512系列EEPROM芯片单片机读写驱动程序

    1.AT24C01/AT24C02系列EEPROM芯片单片机读写驱动程序 2.AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序 3.AT24C32、AT24C64、AT24C128、AT24C256、AT24C512系列EEPROM芯片单片机读写驱动程序 4.x24Cxx系列EEPROM芯片C语言通用读写程序 在前两篇博文中,分别记录了AT24C01、AT24C02,以及AT24C04、

    2024年02月02日
    浏览(50)
  • STM32软件模拟IIC时序实现与EEPROM的通信

                       IIC简介  IIC物理层 用软件模拟IIC时序         一、空闲状态(初始化):SCL 和SDA都保持高电平         二、开始信号 :SCL为高电平期间,SDA由高电平变为低电平。         三、停止信号:SCL为高电平期间,SDA由低电平变为高电平   

    2024年02月09日
    浏览(79)
  • 蓝桥杯嵌入式(G4系列)HAL:IIC通信之AT24C02与MCP4017

    目录 前言: IIC协议简介: 1、起始信号和停止信号: 2、应答信号: 3、读写字节: AT24C02: 字节写操作: 页写操作: 读操作: MCP4017: 写操作: 读操作:         本篇文章主要介绍IIC通信协议,同时给大家介绍一下蓝桥杯嵌入式的模块的AT24C02和MCP4017,此外本篇博客会采

    2023年04月09日
    浏览(91)
  • stm32之软件模拟IIC

            在之前的文章中分析过在52上的IIC时序,也测试过stm32的自带IIC功能,这里大致写下如何模拟stm32上的IIC。实验硬件基于stm32f103c8t6 废话不多说,先直接上代码。 头文件 .m文件  实验用了PB8和PB9,设置的都是开漏输出式,因为在这个模式下有如下说明: 在配置成开

    2024年02月07日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包