[蓝桥杯嵌入式有手就行]系列文章
第一章 开发环境的配置
第二章 从点灯开始
第三章 学会看手册
第四章 GPIO(上)
第五章 GPIO(下)
第六章 LCD
第七章 串口
第八章 I2C
第九章 ADC
第十章 TIM(上)
第十一章 TIM(下)
第十二章 RTC
第十三章 14届模拟题1
第十四章 14届模拟题2
第十五章 14届真题解析
未完待续…
文章目录
前言
注:本系列基于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的命令(当然你可以魔改下官方的库文件)。上文已经将两个元件通信流程截图放上来了。比赛忘记了就翻手册,虽然到现在为止,还没考过。