13.STM32F407ZGT6-IIC

参考:
1.正点原子
前言:
IIC一般用在比较低速的外围器件上,如EEPROM,ADC采样芯片,等。很基础的一种通信总线,学习总结很有必要。
1.IIC的总线及时序。
2.通过IIC读写24C02这款EEPROM芯片。

36.1 IIC 及 24C02 介绍

36.1.1 IIC 简介

IIC(Inter-Integrated Circuit 内部集成电路)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器以及其外围设备。它是由数据线 SDA 和时钟线 SCL 构成的串行总线,可发送和接收数据,在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送。
IIC 总线有如下特点:
①总线由数据线 SDA 和时钟线 SCL 构成的串行总线,数据线用来传输数据,时钟线用来同步数据收发。
②总线上每一个器件都有一个唯一的地址识别,所以我们只需要知道器件的地址,根据时序就可以实现微控制器与器件之间的通信。
③数据线 SDA 和时钟线 SCL 都是双向线路,都通过一个电流源或上拉电阻连接到正的电压,所以当总线空闲的时候,这两条线路都是高电平。
④总线上数据的传输速率在标准模式下可达 100kbit/s 在快速模式下可达 400kbit/s,在高速模式下可达 3.4Mbit/s。
⑤总线支持设备连接。在使用 IIC 通信总线时,可以有多个具备 IIC 通信能力的设备挂载在上面,同时支持多个主机和多个从机,连接到总线的接口数量只由总线电容 400pF 的限制决定。IIC 总线挂载多个器件的示意图,如下图所示:
在这里插入图片描述

下面来学习 IIC 总线协议,IIC 总线时序图如下所示:
在这里插入图片描述

为了便于大家更好的了解 IIC 协议,我们从起始信号、停止信号、应答信号、数据有效性、数据传输以及空闲状态等 6 个方面讲解,大家需要对应图 36.1.1.2 的标号来理解。
① 起始信号
当 SCL 为高电平期间,SDA 由高到低的跳变。起始信号是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。
② 停止信号
当 SCL 为高电平期间,SDA 由低到高的跳变。停止信号也是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态。
③ 应答信号
发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
观察上图标号③就可以发现,有效应答的要求是从机在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主机,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放 SDA线,以便主机接收器发送一个停止信号。
④ 数据有效性
IIC 总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。数据在 SCL 的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定。
⑤ 数据传输
在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行时钟的配合下,在 SDA 上逐位地串行传送每一位数据。数据位的传输是边沿触发。
⑥ 空闲状态
IIC 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

了解前面的知识后,下面介绍一下 IIC 的基本的读写通讯过程,包括主机写数据到从机即写操作,主机到从机读取数据即读操作。

下面先看一下写操作通讯过程图,见图 36.1.1.3 所示:
在这里插入图片描述

主机首先在 IIC 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地址+0(写操作)组成的 8bit 数据,所有从机接收到该 8bit 数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。注意:IIC 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。

接着讲解一下 IIC 总线的读操作过程,先看一下读操作通讯过程图,见图 36.1.1.4 所示。
在这里插入图片描述

主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的 8bit 数据,从机接收到数据验证是否是自身的地址。 那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回 8bit 数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。
24C02 的数据传输时序是基于 IIC 总线传输时序,下面讲解一下 24C02 的数据传输时序。

36.1.2 24C02 简介

24C02 是一个 2K bit(2048/8=256) 的串行 EEPROM 存储器,内部含有 256 个字节。在 24C02 里面还有一个 8 字节的页写缓冲器。该设备的通信方式 IIC,通过其 SCL 和 SDA 与其他设备通信,芯片的引脚图如图 36.1.2.1 所示。
在这里插入图片描述

上图中有一个 WP,这个是写保护引脚,接高电平只读,接地允许读和写,我们的板子设计是把该引脚接地。每一个设备都有自己的设备地址,24C02 也不例外,但是 24C02 的设备地址是包括不可编程部分和可编程部分,可编程部分是根据上图的硬件引脚 A0、A1 和 A2 所决定。
设备地址最后一位用于设置数据的传输方向,即读操作/写操作,0 是写操作,1 是读操作,具体格式如下图 36.1.2.2 所示:
在这里插入图片描述

根据我们的板子设计,A0、A1 和 A2 均接地处理,所以 24C02 设备的读操作地址为:0xA1(1010 0001);写操作地址为:0xA0(1010 0000)。
在前面已经说过 IIC 总线的基本读写操作,那么我们就可以基于 IIC 总线的时序,理解 24C02 的数据传输时序。
下面把实验中用到的数据传输时序讲解一下,分别是对 24C02 的写时序和读时序。

24C02 写时序图如图 36.1.2.3 所示。
在这里插入图片描述

上图展示的主机向 24C02 写操作时序图,主机在 IIC 总线发送第 1 个字节的数据为 24C02的设备地址 0xA0,用于寻找总线上找到 24C02,在获得 24C02 的应答信号之后,继续发送第 2个字节数据,该字节数据是 24C02 的内存地址,再等到 24C02 的应答信号,主机继续发送第 3字节数据,这里的数据即是写入在第 2 字节内存地址的数据。主机完成写操作后,可以发出停止信号,终止数据传输。
上面的写操作只能单字节写入到 24C02,效率比较低,所以 24C02 有页写入时序,大大提高了写入效率,
下面看一下 24C02 页写时序图,图 36.1.2.4 所示。
在这里插入图片描述

在单字节写时序时,每次写入数据时都需要先写入设备的内存地址才能实现,在页写时序中,只需要告诉 24C02 第一个内存地址 1,后面数据会按照顺序写入到内存地址 2,内存地址 3等,大大节省了通信时间,提高了时效性。因为 24C02 每次只能 8bit 数据,所以它的页大小也就是 1 字节。页写时序的操作方式跟上面的单字节写时序差不多,所以不作过多解释了。参考以上说明去理解页写时序。

下面看一下图 36.1.2.5 关于 24C02 的读时序。
在这里插入图片描述

24C02 读取数据的过程是一个复合的时序,其中包含写时序和读时序。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送 24C02 设备地址 0xA0,获取从机应答信号后,接着发送需要读取的内存地址;在读时序中,起始信号产生后,主机发送 24C02 设备地址 0xA1,获取从机应答信号后,接着从机返回刚刚在写时序中内存地址的数据,以字节为单位传输在总线上,假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,当主机发出的是非应答信号并以停止信号发出为结束,从机就会结束传输。

目前大部分 MCU 都带有 IIC 总线接口,STM32F407 也不例外。但是这里我们不使用STM32F407 的硬件 IIC 来读写 24C02,而是通过软件模拟。ST 为了规避飞利浦 IIC 专利问题,将 STM32 的硬件 IIC 设计的比较复杂,而且稳定性不怎么好,所以这里我们不推荐使用。有兴趣的读者可以自行研究 STM32F407 的硬件 IIC 的使用。
用软件模拟 IIC,最大的好处就是方便移植,同一个代码兼容所有 MCU,任何一个单片机只要有 IO 口,就可以很快的移植过去,而且不需要特定的 IO 口。而硬件 IIC,则换一款 MCU,基本上就得重新移植,这也是我们推荐使用软件模拟 IIC 的另外一个原因。

36.2 硬件设计

1. 例程功能
通过串口指令写数据到24C02 和 通过串口指令读取24C02的数据。
2. 硬件资源
1)EEPROM AT24C02
2)串口 1(PA9/PA10 连接在板载 USB 转串口芯片 CH340 上面)(USMART 使用)
3. 原理图
我们主要来看看 24C02 和开发板的连接,如下图所示:
在这里插入图片描述

24C02 的 SCL 和 SDA 分别连接在 STM32 的 PB8 和 PB9 上。本实验通过软件模拟 IIC 信号建立起与 24C02 的通信,进行数据发送与接收。

36.3 程序设计

IIC 实验中使用的是软件模拟 IIC,所以用到的是 HAL 中 GPIO 相关函数,前面也有介绍到,这里就不做展开了。下面介绍一下使用 IIC 传输数据的配置步骤:

使用 IIC 传输数据的配置步骤
1) 使能 IIC 的 SCL 和 SDA 对应的 GPIO 时钟。
本实验中 IIC 使用的 SCL 和 SDA 分别是 PB8 和 PB9,因此需要先使能 GPIOB 的时钟,代码如下:
__HAL_RCC_GPIOB_CLK_ENABLE(); /* 使能 GPIOB 时钟 */
2) 设置对应 GPIO 工作模式(开漏输出)
本实验 GPIO 使用开漏输出模式(硬件已接外部上拉电阻,对于 F4 以上板子也可以用内部的上拉电阻),通过函数 HAL_GPIO_Init 设置实现。
3) 参考 IIC 总线协议,编写信号函数(起始信号,停止信号,应答信号)
起始信号:SCL 为高电平时,SDA 由高电平向低电平跳变。
停止信号:SCL 为高电平时,SDA 由低电平向高电平跳变。
应答信号:接收到 IC 数据后,向 IC 发出特定的低电平脉冲表示已接收到数据。
4) 编写 IIC 的读写函数
通过参考时序图,在一个时钟周期内发送 1bit 数据或者读取 1bit 数据。读写函数均以一字节数据进行操作。
有了读和写函数,我们就可以对外设进行驱动了。

36.3.1 程序解析

本实验中,我们通过 GPIO 来模拟 IIC,所以不需要使用 Drivers/STM32F4xx_HAL_Driver分组下添加 HAL 库文件支持。实验工程中,我们新增了 myiic.c 和24cxx.c文件。
1. IIC 底层驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。IIC 驱动源码包括两个文件:myiic.c 和 myiic.h。
下面我们直接介绍 IIC 相关的程序,首先先介绍 myiic.h 文件,其定义如下:

#ifndef __MYIIC_H
#define __MYIIC_H

#include "periodic_user.h"
#include "main.h"

/******************************************************************************************/
/* 引脚 定义 */

#define IIC_SCL_GPIO_PORT               EEPROM_IIC_SCL_GPIO_Port
#define IIC_SCL_GPIO_PIN                EEPROM_IIC_SCL_Pin
//#define IIC_SCL_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

#define IIC_SDA_GPIO_PORT               EEPROM_IIC_SDA_GPIO_Port
#define IIC_SDA_GPIO_PIN                EEPROM_IIC_SDA_Pin
//#define IIC_SDA_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

/******************************************************************************************/

/* IO操作 */
#define IIC_SCL(x)        do{
      x ? \
                              HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_RESET); \
                          }while(0)       /* SCL */

#define IIC_SDA(x)        do{
      x ? \
                              HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_RESET); \
                          }while(0)       /* SDA */

#define IIC_READ_SDA     HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) /* 读取SDA */


/* IIC所有操作函数 */
void iic_init(void);            /* 初始化IIC的IO口 */
void iic_start(void);           /* 发送IIC开始信号 */
void iic_stop(void);            /* 发送IIC停止信号 */
void iic_ack(void);             /* IIC发送ACK信号 */
void iic_nack(void);            /* IIC不发送ACK信号 */
uint8_t iic_wait_ack(void);     /* IIC等待ACK信号 */
void    iic_send_byte(uint8_t txd);/* IIC发送一个字节 */
uint8_t iic_read_byte(unsigned char ack);/* IIC读取一个字节 */

#endif

我们通过宏定义标识符的方式去定义 SCL 和 SDA 两个引脚,同时通过宏定义的方式定义了 IIC_SCL() 和 IIC_SDA()设置这两个管脚可以输出 0 或者 1,主要还是通过 HAL 库的 GPIO操作函数实现的。另外方便在 iic 操作函数中调用读取 SDA 管脚的数据,这里直接宏定义IIC_READ_SDA 实现,在后面 iic 模拟信号实现中会频繁调用。
接下来我们看一下 myiic.c 代码中的初始化函数,代码如下:

/**
* @brief 初始化 IIC
* @param 无
* @retval 无
*/
void iic_init(void)
{
   
    // GPIO_InitTypeDef gpio_init_struct;

    // IIC_SCL_GPIO_CLK_ENABLE();  /* SCL引脚时钟使能 */
    // IIC_SDA_GPIO_CLK_ENABLE();  /* SDA引脚时钟使能 */

    // gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;
    // gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;        /* 推挽输出 */
    // gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
    // gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 快速 */
    // HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct);/* SCL */

    // gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;
    // gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;        /* 开漏输出 */
    // HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct);/* SDA */
    /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

    //iic_stop();     /* 停止总线上所有设备 */

    IIC_SDA(1);
    IIC_SCL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值