STM32-I2C通信在AT24C02的应用

AT24C02是一种失去电源供给后依旧能保持数据的储存器,常用来储存一些配置信息,在系统重新上电之后也可以加载。它的容量是2k bit的EEPROM存储器,采用I2C通信方式。

AT24C02支持两种写操作:字节写操作和页写操作。本实验中我们采用的是字节写操作,就是一个地址一个数据这样进行数据写入。页写模式就是连续写入数据,只需写入一个地址,连续写入数据的时候,地址会自动后移,但有页限制,超出一页的时候,超出数据会覆盖原先写入的数据。

AT24C02支持三种读操作:当前地址读操作,随机地址读操作和顺序读操作模式。

当前地址读模式是基于上一次读/写操作的最后位置继续读出数据。随机地址读模式是指定地址读出数据。顺序读操作模式就是连续读出数据,会自动翻页。

注意到我们采用的STM32F1系列里,SCL时钟线为PB6,SDA数据线为PB7。本次实验中,时钟线我们就正常的设置为推挽输入,数据线则设置为开漏模式(因为我们引入了外部上拉电阻,提供稳定的空闲高电平,而且我们的数据线既要作为输出,又要作为输入,用开漏输出模式,能很好地实现输入输出共用,避免频繁IO模式切换带来的麻烦)。编写代码时,注意完成发送的时候,主机要释放SDA。

在开漏模式下,MCU读取IDR状态寄存器,来获取引脚高低电平。

接下来编写我们的实验代码:

首先编写我们的函数头文件iic.c:

#include "./BSP/IIC/iic.h"
#include "./SYSTEM/delay/delay.h"

void iic_init(void){

    GPIO_InitTypeDef gpio_init_struct = {0};

    IIC_SCL_GPIO_CLK_ENABLE();
    IIC_SDA_GPIO_CLK_ENABLE();

    //SCL设置
    gpio_init_strcut.Pin = IIC_SCL_GPIO_PIN;
    gpio_init_strcut.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_strcut.Pull = GPIO_PULLUP;
    gpio_init_strcut.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(IIC_GPIO_PORT, &gpio_init_strcut); //IIC_GPIO_PORT就是GPIOB

    //SCL设置
    gpio_init_strcut.Pin = IIC_SDA_GPIO_PIN;
    gpio_init_strcut.Mode = GPIO_MODE_OUTPUT_OD;
    HAL_GPIO_Init(IIC_GPIO_PORT, &gpio_init_strcut);
}

static void iic_delay(void){
    delay_ms(2);
}

void iic_start(void){
    //SCL为高电平时,SDA从高电平向低电平跳变,制造起始信号
    IIC_SDA(1);
    IIC_SCL(1);
    delay_ms();
    IIC_SDA(0);
    delay_ms();
    IIC_SCL(0);
    delay_ms();//钳住总线,准备发送或者接受数据
}
 
void iic_stop(void){
    //SCL为高电平的适合,SDA从低电平向高电平跳变,制造停止信号
    IIC_SDA(0);
    delay_ms();
    IIC_SCL(1);
    delay_ms();
    IIC_SDA(1);
    delay_ms();
}

uint8_t iic_wait_ack(void){ //主机检测应答信号
 
    IIC_SDA(1); //主机释放SDA线
    delay_ms();
    IIC_SCL(1); //从机返回ACK
    delay_ms();
 
    if(IIC_READ_SDA){ //SDA高电平表示读取到高电平,从机未作出应答,由于上拉电阻有高电平阻塞效果,表示读取到非应答信号
        iic_stop();
        return 1;
    }
 
    IIC_SCL(0);
    delay_ms();
    return 0;
}
 
void iic_ack(void){ //从机发送低电平应答信号,将主机发送的高电平信号导入至此,使主机的数据线检测函数得到的是低电平,继续要求数据
 
    IIC_SCL(0);
    delay_ms();
    IIC_SDA(0);
    delay_ms();
    IIC_SCL(1);
    delay_ms();
}
 
void iic_nack(void){ //从机发送非应答信号,不再要求数据
 
    IIC_SCL(0);
    delay_ms();
    IIC_SDA(1);
    delay_ms();
    IIC_SCL(1);
    delay_ms();
}

void iic_send_byte(uint8_t data){
    for(uint8_t t = 0; t < 8; t++){
        //高位先发
        IIC_SDA((data & 0x80) >> 7);
        IIC_SCL(1);
        delay_ms();
        IIC_SCL(0);
        data <<= 1; //左移一位,进行下一次发送
    }
    IIC_SDA(1); //完成后,释放数据线,进行应答检测
}
 
uint8_t iic_read_byte(uint8_t ack){
    uint8_t receive = 0;
 
    for(uint8_t i = 0; t < 8; t++){
        //高位先被输入,左移以腾空右边的位置给下一低位数据
        receive <<= 1;
        IIC_SCL(1);
        delay_ms();
        if(IIC_READ_SDA) receive++;
        IIC_SCL(0);
        delay_ms();
    }

    if(!ack) iic_nack();
    else iic_ack();
    return receive;
}

接下来编写函数头文件iic.h:

#ifndef __IIC_H
#define __IIC_H

void iic_init(void);
static void iic_delay(void);
void iic_start(void);
void iic_stop(void);
uint8_t iic_wait_ack(void);
void iic_ack(void);
void iic_nack(void);
void iic_send_byte(uint8_t data);
uint8_t iic_read_byte(uint8_t ack);

#endif

接下来再编写存储器的函数文件24cxx.c:

#include "./BSP/IIC/iic.h"
#include "./BSP/24CXX/24cxx.h"
#include "./SYSTEM/delay/delay.h"

void at24c02_init(void){
    iic_init();
}

void at24c02_write_byte(uint8_t addr, uint8_t data){
    iic_start();
    iic_send_byte(0xA0); //发送通讯地址(写地址操作)
    iic_wait_ack();
    iic_send_byte(addr);
    iic_wait_ack();
    iic_send_byte(data);
    iic_wait_ack();
    iic_stop();

    delay_ms(10);
}

uint8_t at24c02_read_byte(uint8_t addr){
    uint8_t rec = 0;

    iic_start();
    iic_send_byte(0xA0); //发送通讯地址(写地址操作)
    iic_wait_ack();
    iic_send_byte(addr);
    iic_wait_ack();
    iic_start();
    iic_send_byte(0xA1); //发送通讯地址(写地址操作)
    iic_wait_ack();
    iic_send_byte(addr);
    iic_wait_ack();
    rec = iic_read_byte(0);
    iic_stop();

    return rec;
}

接下来编写存储器函数文件的头文件:24cxx.h:

#ifndef __24CXX_H
#define __24CXX_H

void at24c02_init(void);
void at24c02_write_byte(uint8_t addr, uint8_t data);
uint8_t at24c02_read_byte(uint8_t addr);

#endif

到这里我们的示例代码便编写完成了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/30616.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构算法 -分而治之算法

引言 坤坤是一个养鸡场的员工&#xff0c;他非常热爱他的工作&#xff0c;并且总是努力提高他的专业技能。有一天&#xff0c;养鸡场接到了一项任务&#xff1a;在短时间内处理一批大量的鸡。 这批鸡数量非常大&#xff0c;比普通的数量要多得多&#xff0c;坤坤意识到他们需…

C++核心编程——详解函数模板

纵有疾风起&#xff0c;人生不言弃。本文篇幅较长&#xff0c;如有错误请不吝赐教&#xff0c;感谢支持。 &#x1f4ac;文章目录 一.模板的基础知识①为什么有模板&#xff1f;②初识模板 二.函数模板①函数模板的定义②函数模板的使用③函数模板实例化1️⃣隐式实例化2️⃣显…

泰坦陨落2找不到msvcr120.dll的解决方法

msvcr120.dll是的动态链接库文件之一。它在Windows操作系统中发挥着重要的作用&#xff0c;它提供了应用程序所需的各种功能和方法。 该文件返回编译后的代码所需的支持库。msvcr120.dll包含用于C / C编译器生成的应用程序所需的重要功能&#xff0c;包括数学函数&#xff0c;…

C++学习 数据类型

数据类型存在的意义&#xff1a; 给变量分配合适的内存空间&#xff0c;避免资源浪费。 整型&#xff1a; 整型变量表示的是整数类型的数据 long类型 在 windows 中4字节 linux 中 32位4字节 64位8字节&#xff0c;占用空间的不同&#xff0c;可以表示的取值范围就越广&…

python:基础知识—流程控制—函数与模块—数据结构—类与GUI和Turtle—异常处理与文件,概括全书(上万字最详细版)

这里是一张夜景&#xff0c;给大家放松一下。 !&#xff01;无锡南长街 文章目录 模块一&#xff1a;基础知识1、python语言2、常见数字类型3、字符串4、数字类型转换5、标识符命名6、常见关键字7、运算符与表达式&#xff08;1&#xff09;算术运算符&#xff08;2&#xff09…

ZooKeeper的集群部署和启动与关闭

ZooKeeper是一个分布式应用程序协调服务。一个ZooKeeper集群可以存在多个Follower和Observer服务器&#xff0c;但只允许存在一台Leader服务器。如果Leader服务器宕机&#xff0c;那么ZooKeeper集群的其它服务器会投票选举出一个新的Leader服务器&#xff0c;为防止投票数不过半…

Elasticsearch:部署 ELSER - Elastic Learned Sparse EncoderR

警告&#xff1a;此功能处于技术预览阶段&#xff0c;可能会在未来的版本中更改或删除。 Elastic 将尽最大努力修复任何问题&#xff0c;但技术预览中的功能不受官方 GA 功能的支持 SLA 约束。 Elastic Learned Sparse EncodeR - 或 ELSER - 是由 Elastic 训练的检索模型&#…

浅谈中移链中插件的功能及使用

中移链是在满足我国信息化监管需求、合规可控的前提下&#xff0c;打造的中国移动区块链服务平台。它允许使用插件来扩展其功能&#xff0c;以适应各种不同的使用场景。 什么是中移链插件呢&#xff1f;如果把中移链比作一个操作系统&#xff0c;那么插件就类比于操作系统上的…

工作流引擎Flowable

这里写目录标题 1.Flowable基础1.1 入门学习 2.流程图设计器2.1 FlowableUI2.1.1 绘制流程图 1.Flowable基础 官方手册 1.1 入门学习 一、依赖 <dependencies><dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</…

Android系统安全 — 6.2 Ethernet安卓架构

1. Android Ethernet架构介绍 整个Ethernet系统架构如下图所示&#xff1a; 以太网服务&#xff08;EthernetService&#xff09;的启动与注册流程&#xff1b;应用层调用使能ethernet功能的方法流程来分析&#xff0c;从应用层如何将指令一步一步传到底层kernel&#xff1b;…

阿里巴巴开源的Spring Cloud Alibaba手册在GitHub上火了

“微服务架构经验你有吗&#xff1f;” 前段时间一个朋友去面试&#xff0c;阿里面试官一句话问倒了他。实际上&#xff0c;不在BAT这样的大厂工作&#xff0c;是很难接触到支撑千亿级流量微服务架构项目的。但也正是这种难得&#xff0c;让各个大厂都抢着要这样的人才&#x…

docker-harbor私有仓库

一、Harbor概述 1、Harbor的概念 • Harbor是VMware公司开源的企业级Docker Registry项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的Docker Registry服务 • Harbor以 Docker 公司开源的Registry 为基础&#xff0c;提供了图形管理UI、基于角色的访问控制(Role Base…

华为流程体系:IPD流程框架(限制版)

目录 前言 详细内容 专栏列表 CSDN学院课程地址 前言 今天主要来谈谈 IPD 体系的主体框架所涉及的一些相关内容。 其实关于 IPD 体系&#xff0c;我在之前的文章或课程中都有过不同程度的讲解。 但是&#xff0c;由于这个体系所涉及的面是非常广泛的。 这个时候就必须通…

sql总理论加实践

一、基础查询 DQL语法 SELECT 字段列表 1.设置别名 SELECT 字段1[别名],字段二[别名]......FROM 表名 WHERE 条件列表; 2.查询多个字段 SELECT 字段1,字段2,字段3......FROM 表名; SELECT * FROM 表名; 3.去除重复记录 DISTANT FROM 表明列表 WHERE 条件列表(条件…

利用etcd实现分布式锁

python etcd3模块的lock使用 观察lock的加解锁影响 在python中已经自带了分布式锁的实现方式&#xff0c;下面我们尝试一下加锁与解锁的流程 在运行该demo同时也对lock对应的key进行watch&#xff0c;观察其变化&#xff0c;注意python-etcd3在实现分布式锁的时候&#xff0…

【Python爬虫开发基础⑤】HTML概述与基本标签详解

专栏&#xff1a;python网络爬虫从基础到实战 欢迎订阅&#xff01;近期还会不断更新~ 往期推荐&#xff1a; 【Python爬虫开发基础①】Python基础&#xff08;变量及其命名规范&#xff09; 【Python爬虫开发基础②】Python基础&#xff08;正则表达式&#xff09; 【Python爬…

探秘华为交换机:端口类型全解析

在下列情况下&#xff0c;判断的一般方法是什么&#xff1f; 1.交换机某个端口下的用户丢包。 2.交换机下的所有用户都在丢失数据包。 3、用户反映网速缓慢 检查网络电缆&#xff0c;重做水晶头&#xff0c;检查用户的计算机网卡&#xff0c;并重新启动交换机。 这几种做法都能…

Flink 学习五 Flink 时间语义

Flink 学习五 Flink 时间语义 1.时间语义 在流式计算中.时间是一个影响计算结果非常重要的因素! (窗口函数,定时器等) Flink 可以根据不同的时间概念处理数据。 处理时间: process time System.currentTimeMillis()是指执行相应操作的机器系统时间&#xff08;也称为纪元时间…

Docker安全

一、Docker 容器与虚拟机的区别 1、隔离与共享 • 虚拟机通过添加 Hypervisor 层&#xff0c;虚拟出网卡、内存、CPU 等虚拟硬件&#xff0c;再在其上建立虚拟机&#xff0c;每个虚拟机都有自己的系统内核 • Docker容器则是通过隔离的方式&#xff0c;将文件系统、进程、设…

Redis入门 - Lua脚本

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis入门 - Lua脚本 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-scription.html Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。 …