当前位置: 首页 > news >正文

【STM32】IIC

超级常见的外设通信方式,一般叫做I方C。

大部分图片来源:正点原子HAL库课程

 专栏目录:记录自己的嵌入式学习之路-CSDN博客


目录

1    基本概念

1.1    总线结构

1.2    IIC协议

1.3    软件模拟IIC逻辑

2    AT24C02

2.1    设备地址与通信地址

2.2    读写操作模式

2.3    写时序

2.4    读时序

2.5    驱动步骤(例程中使用的是软件IIC)

2.6    SDA的GPIO使用开漏输出的原因

3    硬件IIC

3.1    概述

3.2    配置

3.3    注意事项

4    关于AT24C04、AT24C08、AT24C16


1    基本概念

IIC:Inter Integrated Circuit,集成电路总线,是一种同步、串行、半双工通信总线。

1.1    总线结构

  • 一般来说IIC总线接3个设备就差不多了。
  • IIC的时钟信号是由主机发出的。

1.2    IIC协议


  • 主机通过SDA传输数据时,数据在SCL为高电平时有效。
  • 在主机传输完毕一个字节的数据后,其需要释放SDA,使得从机可以通过拉低SDA来应答确认数据收到。

1.3    软件模拟IIC逻辑

由于初期ST官方说自己的IIC有问题,口碑没做上来,导致大家都使用软件IIC代替其内部的硬件IIC实现。但其实硬件IIC应该没啥大问题,详见:

关于STM32的I2C(IIC)问题的讨论 (stmicroelectronics.cn)




2    AT24C02

EEPROM是一种掉电后数据不丢失的储存器,常用来存储一些配置信息,在系统重新上电时就可以加载。AT24C02是一个2K bit的EEPROM存储器,使用IIC通信方式。

2.1    设备地址与通信地址

  • 通讯地址指定了操作的写和读;
  • 而设备地址不包括读写位;
  • 编程时需要发送出去的是通讯地址,不是设备地址;
  • 对于4K、8K、16K的存储芯片,由于IIC发送一个字节是8位,而4K是9位,因此需要在通讯地址处借1位进行数据发送,8K和16K也是以此类推。但是这样一来通讯地址的可用枚举就变少了,因为A0、A1、A2的位置被拿去做内存扩展了。最终的结果就是,IIC总线上可搭载的该类设备的数量会大打折扣,如16K的就只能搭载一个了。

2.2    读写操作模式

  • 写操作
    • AT24C02支持字节写模式和页写模式,其实也并不是什么字节写,实际上就是在IIC主机发送了数据地址信号后到发送停止信号前,最多可以写到页尾,仅此而已;
    • 其本质就是:收到每个数据字后,数据字地址的低三位(1K/2K)或四位(4K、8K、16K)在内部递增。较高的数据字地址位不递增,保留存储器页面行位置。当内部生成的字地址到达页面边界时,随后的字节被放置在同一页面的开头。如果超过八个(1K/2K)或十六个(4K、8K、16K)数据字传输到EEPROM,数据字地址将“翻转”,先前的数据将被覆盖。
    • 字节写模式就是一个地址一个数据进行写入;
    • 页写模式就是连续写入数据。只需要写一个地址,连续写入数据时地址会自增,但存在页的限制,超出一页时,超出数据覆盖原先写入的数据。但读会自动翻页。因此,若需要使用页写模式,就需要手动在写完一页后进行翻页。
  • 读操作
    • AT24C02支持当前地址读模式,随机地址读模式和顺序读模式;
    • 当前读模式是基于上一次读/写操作的最后位置继续读出数据;
    • 随机地址读模式是指定地址读出数据;
    • 顺序读模式是连续读出数据。

2.3    写时序

2.4    读时序

2.5    驱动步骤(例程中使用的是软件IIC)


2.6    SDA的GPIO使用开漏输出的原因


3    硬件IIC

(正点原子没讲硬件IIC,这个是参考野火的HAL库教程的)

STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。

3.1    概述

STM32F103具有2个I2C总线接口,能够工作于多主模式或从模式,支持标准(100kHz)和快速模式(400kHz)。I2C接口支持7位或10位寻址,7位从模式时支持双从地址寻址。内置了硬件CRC发生器/校验器。它们可以使用DMA操作并支持SMBus总线2.0版/PMBus总线。

  • 时钟
    • STM32F103的两个IIC接口外设都挂载在APB1时钟总线上;
    • 为了产生正确的时序,必须在I2C_CR2寄存器中设定该模块的输入时钟。输入时钟的频率必须至少是(但APB1一般都会满足吧):
      • 标准模式下为:2MHz;
      • 快速模式下为:4MHz;

3.2    配置

由于正点原子和野火都没讲硬件IIC的配置,因此此处的配置步骤是我通过网络上的CubeMX教程总结来的(没错,就连正点原子的CubeMX教程都是用模拟IIC的)。

  • CubeMX方式
    • 根据需要控制的IIC外部原件连接的GPIO其对应的IIC外设进行使能,例如正点原子F103板子上的AT24C02接在PB6、PB7上,因此是I2C1:
    • 配置IIC的设置,对于AT24C02,除了速率外基本不需要变:
    • 在高速模式下,有一个Fast Mode Duty,是用来调节高速模式下的占空比的,据野火教程说两个选项没太大差别,一般使用可以随便选;
  • HAL库函数手动编写方式
    • 初始化IIC:HAL_I2C_Init
    • 初始化时钟和GPIO:HAL_I2C_MspInit
  • 在需要的位置使用HAL_I2C_Mem_Read或者HAL_I2C_Mem_Write进行读写。这两个函数直接可以一次性指定目标从机地址、寄存器地址、要写入寄存器的数据等,超级方便:
      • hi2c:IIC句柄;
      • DevAddress:从机通讯读地址(注意是通讯地址,不是设备地址,分读写地址不同那个);
      • MemAddress:操作的从机寄存器地址;
      • MemAddSize:从机寄存器数据宽度;
      • pData:准备写入的数据的地址或指针;
      • Size:准备写入的数据的字节数;
      • Timeout:超时时间;
      • pData:返回的数据将要写入的缓存地址或指针(一般为变量地址或数组地址);
      • 其余与上一个类似;
  • 除了上述两个函数外,还有两个函数可以实现IIC通信:HAL_I2C_Master_Receive和HAL_I2C_Master_Transmit。这两个只能同时发送从机地址和数据,适合写入和读取那种从机内无需寄存器地址就能读到数据的从机,如传感器之类的。
    • 有博主提出使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。我没有试验过,不知道对不对。

3.3    注意事项

  • 在使用HAL_I2C_Mem_Write的过程中,需要注意AT24C02一页只有8个字节的数据,所以一次Write的长度最多是8,要在循环写32次才能写完整片。循环过程记住要改变MemAddress(每次+8);
  • 在Write的循环中,请务必务必在每次Write后delay一段时间,具体的时间可根据EEPROM手册上说的时间设置,最好设置大一点;
  • 在读写过程中设置的超时时间也应设置大一点;
  • 不过在HAL_I2C_Mem_Read过程中就不需要分页,直接能读256个字节;

4    关于AT24C04、AT24C08、AT24C16

前面提到,这几个性高的通讯地址中有一定的位数是寄存器地址的借位:

由于其数据地址的借位,在发送通讯地址的时候,要结合数据地址对通讯地址进行处理。这篇文章讲得很好:

AT24C04、AT24C08、AT24C16系列EEPROM芯片单片机读写驱动程序-CSDN博客

这里截取一下其对写入的处理:

其中,第一个IIC_WriteByte,实际上是将设备地址DEV_ADDR和写地址WRITE_CMD(即0)或操作组成通讯的写地址;其次,对数据地址进行右移8位,并与0x07进行与运算,实际上就是取出数据地址中高8位的低3位,即通讯地址中的P2、P1、P0(若有的话对应位就是1,所以用或运算很安全)。然后还对取出的低三位进行了左移1位运算,再与通讯地址做与运算,这是因为通讯地址的最低位是读/写设置位。

第二个IIC_WriteByte就是将剩下的8位数据地址也发过去,发完就可以开始发数据了。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • es集群详解
  • IDEA导入第三方jar包, 并在Maven中打包该jar包
  • 医疗数字化转型数据中台架构方案(一)
  • vim 简易配置
  • 【视频讲解】SMOTEBoost、RBBoost和RUSBoost不平衡数据集的集成分类酵母数据集、治癌候选药物|数据分享...
  • 【奇某信-注册/登录安全分析报告】
  • 哪些领域最适合采用音视频私有化解决方案?
  • Python 数据分析笔记— Numpy 基本操作
  • 公司新招了个字节拿36K的人,让我见识到了什么才是测试扛把子......
  • 经验笔记:状态机与下推自动机的理解与应用场景
  • 【Linux篇】环境变量
  • linux关闭热点模式,设置开机自启动wifi模式
  • @ohos.systemParameterEnhance系统参数接口调用:控制设备硬件(执行shell命令方式)
  • 【论文阅读】ColabFold: making protein folding accessible to all
  • 初识Vue.js:从零开始构建你的第一个Vue项目
  • ES6指北【2】—— 箭头函数
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Android优雅地处理按钮重复点击
  • C++入门教程(10):for 语句
  • IDEA 插件开发入门教程
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • jdbc就是这么简单
  • js递归,无限分级树形折叠菜单
  • node学习系列之简单文件上传
  • sessionStorage和localStorage
  • SpriteKit 技巧之添加背景图片
  • ucore操作系统实验笔记 - 重新理解中断
  • 缓存与缓冲
  • 开源地图数据可视化库——mapnik
  • 两列自适应布局方案整理
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 浅谈Golang中select的用法
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 实战|智能家居行业移动应用性能分析
  • mysql面试题分组并合并列
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​Spring Boot 分片上传文件
  • ​浅谈 Linux 中的 core dump 分析方法
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #{}和${}的区别是什么 -- java面试
  • #Spring-boot高级
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • $$$$GB2312-80区位编码表$$$$
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (rabbitmq的高级特性)消息可靠性
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (南京观海微电子)——COF介绍
  • (算法)硬币问题
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • (一)UDP基本编程步骤
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转载)hibernate缓存
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .“空心村”成因分析及解决对策122344
  • .Net Core中的内存缓存实现——Redis及MemoryCache(2个可选)方案的实现