首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > [蓝桥杯嵌入式有手就行]8_I2C
前言
一、内部结构
1、内部框图
2、I2C通信协议
二、I2C模块分析
1、原理图
2、元件介绍
1、24C02(eeprom)
2、MCP4017(可编程电阻)
3、硬件I2C发送与接收
三、代码示例
1、cubemx配置
2、上代码
1、24C02的相关代码
2、MCP4017的相关代码
四、问题
1、PB6和PB7引脚设置
总结
前言
注:本系列基于2023年蓝桥杯实战情况就行编写,所有软件均采用2023年赛点资源包
目前已知stm32 硬件I2C在hal库下存在bug,在I2C通讯过程中,如果进入中断(任意)会导致卡死。近期没有精力去测试各个版本(资料包提供的是1.3的库,但是我的cubemx强制更新1.4,再加上keil g4包版本可能也不一样)。
因此本文基于软件I2C编写(主要是官方给了库文件),等我更新完最近的两个系列文章后,我会去试试硬件I2C,到时会更新在下文或者另出一篇。
一、内部结构
1、内部框图

2、I2C通信协议
I2C总线是一种双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
器件又可以分为主设备和从设备,相当于主人和仆人的关系。在每次通讯时,必须由主设备发送起始信号,每接收一个字节的数据都需要回复一个ack应答信号,在结束后发送结束信号。参看下图。

在同一总线上的每一个从设备必须拥有一个独立的地址,这个地址为8位,因此理论最大连接设备数是256个。采用I2C通讯的设备必须使用开漏输出的形式,以避免多个设备同时发送。在实际使用过程中,每个器件的总线接口都有一定的等效电容,连接数量会受到总线电容负载能力限制(400pF)。

发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
start:SDA(1)➡SCL(1)➡SDA(0)➡SCL(0)
stop:SCL(1)➡SDA(0)➡SCL(1)➡SDA(1)
二、I2C模块分析
1、原理图

2、元件介绍
1、24C02(eeprom)

M24C02-WMN6TP:Internally organized with 32 pages of 8 bytes each,
the 2K requires an 8-bit data word address for random word addressing.
一共可以储存256字节,但是,不能跨页读写(换句话说,要不多次读写,要不依次全读出来挑有用的)。
器件地址:根据手册Device Addressing章节,我们可知其器件地址前4位固定为1010,后三位由A2、A1、A0硬件连接决定,最后一位读取是1,写入是0。在原理图中可见,三个引脚全部接地。因此,我们读取地址是0xa1写入地址是0xa0。

注意:大多数情况下,我们在发送器件地址后,还需要发送页地址来确认需要读取/写入的数据页,然后才是数据传输。如图所示:

当然,也可以按顺序来读写

2、MCP4017(可编程电阻)
地址:参看手册,我们可以知道其读地址为0x5F写地址为0x5E。

我们通过手册可以看到其内部由128个Rs组成一个大电阻。而我们通过指令,可以更改电阻的通断,这样就可以更改总电阻的大小。在板子上的体现就是PB14的电压变化。

那么,电阻如何计算?
我们继续看手册,下图是内部电阻的结构图,我们实际更改的是RW开关的数量。

以下是手册中给出的计算公式。

然而公式中有两个未知数,RAB和RW,我们继续查找手册。此处的Rw应该是为了防止N=0时短路的。

在上面这个表格中,我们可以发现当前器件的RAB为100kΩ。100kΩ/127=Rs=787.402Ω。
于是上式就可以改成Rwb=Rs*N+Rw。但是,Rw还是未知,我们继续查手册。

根据上表,我们得到Rab在100kΩ下,Rw=100Ω。
因此,计算公式就变成了:Rw=787.40*N+100。
3、硬件I2C发送与接收
软件I2C其实就是模仿硬件的形式按照I2C协议发送指定数据。这里在不确定bug是否修复了的情况下,建议还是使用软件模拟。


三、代码示例
1、cubemx配置
这里我们使用软件I2C,直接跳过。我们只需要导入这两个文件,不会的看lcd章节。

2、上代码
1、24C02的相关代码
void write_24c02(unsigned char value)
{
I2CStart();//发送起始信号
I2CSendByte(0xa0);//发送器件写地址
I2CWaitAck();
I2CSendByte(0);//发送页地址
I2CWaitAck();
I2CSendByte(value);//发送数据
I2CWaitAck();
I2CStop();//发送停止信号
}
unsigned char read_24c02(void)
{
unsigned char value;
I2CStart();//发送起始信号
I2CSendByte(0xa0);//发送器件写地址
I2CWaitAck();//等待应答信号
I2CSendByte(0);//发送页地址
I2CWaitAck();
I2CStart();//这个很重要
I2CSendByte(0xa1);//发送器件读地址
I2CWaitAck();
value = I2CReceiveByte();//读数据
I2CSendNotAck();
I2CStop();
return value;
}
2、MCP4017的相关代码
void write_resistor(uint8_t value)
{
I2CStart();
I2CSendByte(0x5E);
I2CWaitAck();
I2CSendByte(value);
I2CWaitAck();
I2CStop();
}
uint8_t read_resistor(void)
{
uint8_t value;
I2CStart();
I2CSendByte(0x5F);
I2CWaitAck();
value = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return value;
}
四、问题
1、PB6和PB7引脚设置
通过阅读i2c_hal.c,我们可以得到
引脚 描述
PB6 SCL,推挽高速输出,带上拉。
PB7 SDA,输入:高速上拉开漏;输出:高速无上下拉开漏输出
总结
以上就是I2C的内容,相比硬件i2c多了发送ack的命令(当然你可以魔改下官方的库文件)。上文已经将两个元件通信流程截图放上来了。比赛忘记了就翻手册,虽然到现在为止,还没考过。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70050091/viewspace-3117700/ ,如需转载,请注明出处,否则将追究法律责任。