STM32——IIC知识总结及实战

1 IIC概念及结构体

IIC:Inter Integrated Circuit,集成电路总线,是一种同步 串行 半双工通信总线。
在这里插入图片描述
结构图

在这里插入图片描述

2 IIC协议时序

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

3 起止信号 停止信号 应答信号

在这里插入图片描述
起止信号:
void iic_start(void)
{ /* SCL为高电平期间, SDA从高电平往低电平跳变*/
IIC_SDA ( 1 );
IIC_SCL ( 1 );
iic_delay( );
IIC_SDA ( 0 );
iic_delay( );
IIC_SCL ( 0 );
iic_delay( ); /* 钳住总线, 准备发送/接收数据 /
}
停止信号:
void iic_stop(void)
{ /
SCL为高电平期间, SDA从低电平往高电平跳变*/
IIC_SDA ( 0 );
iic_delay( );
IIC_SCL ( 1 );
iic_delay( );
IIC_SDA ( 1 ); /* 发送总线停止信号*/
iic_delay( );
}
主设备检测应答信号:主设备在释放 SDA 后,通过读取 SDA 的电平来检测从设备的应答信号。如果 SDA 保持低电平,表示从设备发送了应答信号,即 ACK。如果 SDA 为高电平,表示从设备未发送应答信号,即 NACK(无应答)。
uint8_t iic_wait_ack (void) /* return 1:fail 0:succeed*/
{
IIC_SDA (1); /* 主机释放SDA线 /
iic_delay( );
IIC_SCL (1); /
从机返回ACK*/
iic_delay( );
if ( IIC_READ_SDA ) /* SCL高电平读取SDA状态*/
{
iic_stop(); /* SDA高电平表示从机nack /
return 1;
}
IIC_SCL(0); /
SCL低电平表示结束ACK检查 /
iic_delay( );
return 0;
}
应答信号:
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( );
}

写一个字节:
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线 /
}
读取一个字节:
uint8_t iic_read_byte (uint8_t ack) /
1:ack 0:nack*/
{
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;

5.AT24C02写时序

写时序在这里插入图片描述
主机首先在 IIC 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的
数据。主机接着发送从机地址+0(写操作)组成的 8bit 数据,所有从机接收到该 8bit 数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。
在这里插入图片描述
主机向从机读取数据的操作,一开始的操作与写操作有点相似,观察两个图也可以发现,
都是由主机发出起始信号,接着发送从机地址+1(读操作)组成的 8bit 数据,从机接收到数据验证是否是自身的地址。 那么在验证是自己的设备地址后,从机就会发出应答信号,并向主机返回 8bit 数据,发送完之后从机就会等待主机的应答信号。假如主机一直返回应答信号,那么从机可以一直发送数据,也就是图中的(n byte + 应答信号)情况,直到主机发出非应答信号,从机才会停止发送数据。

6 T24C02模块与各开发板引脚硬件连接说明

在这里插入图片描述
在这里插入图片描述
根据我们的板子设计,A0、A1 和 A2 均接地处理,所以 24C02 设备的读操作地址为:0xA1;写操作地址为:0xA0。设备地址最后一位用于设置数据的传输方向,即读操作/写操作,0 是写操作,1 是读操作

7配置步骤

在这里插入图片描述

8 实战

8.1myiic.c

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

//使用 IIC 传输数据的配置步骤:
//1) 使能 IIC 的 SCL 和 SDA 对应的 GPIO 时钟。
//本实验中 IIC 使用的 SCL 和 SDA 分别是 PB8 和 PB9,因此需要先使能 GPIOB 的时钟
//2.2) 设置对应 GPIO 工作模式(开漏输出)
//本实验 GPIO 使用开漏输出模式(硬件已接外部上拉电阻,对于 F4 以上板子也可以用内部
//的上拉电阻),通过函数 HAL_GPIO_Init 设置实现。
//3) 参考 IIC 总线协议,编写信号函数(起始信号,停止信号,应答信号)
//起始信号:SCL 为高电平时,SDA 由高电平向低电平跳变。
//停止信号:SCL 为高电平时,SDA 由低电平向高电平跳变。
//应答信号:接收到 IC 数据后,向 IC 发出特定的低电平脉冲表示已接收到数据。
//4) 编写 IIC 的读写函数
/**
 * @brief       初始化IIC
 * @param       无
 * @retval      无
 */
 //2.2) 设置对应 GPIO 工作模式(开漏输出)
//本实验 GPIO 使用开漏输出模式(硬件已接外部上拉电阻,对于 F4 以上板子也可以用内部
//的上拉电阻),通过函数 HAL_GPIO_Init 设置实现。
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();     /* 停止总线上所有设备 */
}

/**
 * @brief       IIC延时函数,用于控制IIC读写速度
 * @param       无
 * @retval      无
 */
static void iic_delay(void)
{
   
    delay_us(2);    /* 2us的延时, 读写速度在250Khz以内 */
}

//3) 参考 IIC 总线协议,编写信号函数(起始信号,停止信号,应答信号)
/**
 * @brief       产生IIC起始信号
 * @param       无
 * @retval      无
 */
//起始信号:SCL 为高电平时,SDA 由高电平向低电平跳变。
void iic_start(void)
{
   
    IIC_SDA(1);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(0);     /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    iic_delay();
    IIC_SCL(0);     /* 钳住I2C总线,准备发送或接收数据 */
    iic_delay();
}

/**
 * @brief       产生IIC停止信号
 * @param       无
 * @retval      无
 */
//停止信号:SCL 为高电平时,SDA 由低电平向高电平跳变。
void iic_stop(void)
{
   
    IIC_SDA(0);     /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);     /* 发送I2C总线结束信号 */
    iic_delay();
}

//应答信号:接收到 IC 数据后,向 IC 发出特定的低电平脉冲表示已接收到数据。
/**
 * @brief       等待应答信号到来
 * @param       无
 * @retval      1,接收应答失败
 *              0,接收应答成功
 */
uint8_t iic_wait_ack(void)
{
   
    uint8_t waittime = 0;
    uint8_t rack = 0;

    IIC_SDA(1);     /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_libinc++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值