目录
[4.IIC 外设框图](#4.IIC 外设框图)
1.IIC协议
1、什么是IIC通信?
两个芯片之间应该如何通信?常见的通信协议比如IIC、SPI、usart,这些通信协议就是规范数据传输与交换的规则,IIC(全称Inter -integrated Circuit)是一种串行通信总线协议,目的是用于连接集成电路(IC)之间的通信。IIC由飞利浦公司,也就是现在的NXP半导体开发,是一种双向通信协议,需要两根线,时钟线(SCL)和数据线(SDA),其中 SCL时钟线是由主设备提供时钟信号来同步数据传输,SDA用来传输数据(双向)。
2、IIC硬件电路
- 硬件规定包含:硬件连接、端口输入输出模式等
- 主机对SCL线:任何时候都完全控制,从机任何时候都不能控制SCL线
- 主机对SDA线:空闲状态下,主机可以发起对SDA数据线的控制,只有从机发送数据与从机应答的时候,主机才会转交SDA数据线的控制权给从机。

- 所有设备的SCL和SDA连在一起
- SCL和SDA各添加一个上拉电阻,阻值一般为4.7kΩ
- 设备的SCL和SDA均要配置为开漏输出模式,再通过上拉电阻实现弱上拉
- 开漏加弱上拉模式,同时具备了输出与输入的功能。如果想要输入数据,观察电平变化
- 在开漏模式下,输出高电平就相当于断开引脚
- 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备直接的访问。
- 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定哪个设备占用总线。
- 具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4M/s,但目前大多IIC设备尚不支持高速模式。
IIC高阻态
- 漏极开路(Open Drain)即高阻状态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻
- 高阻状态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状 态外,还有第三种状态------高阻状态的门电路。电路分析时高阻态可做开路理解。
- 我们知道IIC的所有设备是接在一根总线上的,那么我们进行通信的时候往往只是几个设备进行通信,那么这时候其余的空闲设备可能会受到总线干扰,或者干扰到总线, 怎么办呢?
- 为了避免总线信号的混乱,IIC的空闲状态只能有外部上拉, 而此时空闲设备被拉到了高阻态,也就是相当于断路, 整个IIC总线只有开启了的设备才会正常进行通信, 而不会干扰到其他设备。
3、IIC软件协议
软件规定包括:时序的定义、字节的传输、高位或低位先行、一个完整的时序由以下部分组成
IIC时序的基本单元
start起始条件
SCL高电平期间,SDA从高电平切换为低电平,即SCL不动,SDA被拽下来,产生一个下降沿,当从机捕获到SCL高电平,SDA下降沿时,就会自动进行自身的复位,等待主机召唤。

起始信号:当SCL为高电平期间,SDA有高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高电平期间,SDA由低到高的跳变;停止信号也是一种高电平跳变时序信号,而不是一个电平信号
起始信号和停止信号一般由主机产生
stop终止条件
SCL高电平期间,SDA从低电平切换为高电平,一个完整的数据帧总是以起始信号开始,终止信号结束,起始信号与终止信号都是由从机产生,在空闲状态下,从机必须双手放开。

发送一个字节
SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL, 从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许由有数据变化,依次循环这个过程8次,即可发送一个字节。 总结:(SCL)低电平切换(放)数据,高电平读取(读)数据

接收一个字节
SCL低电平期间,从机数据依次放到SDA线上(高位先行),然后主机释放SCL, 主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程八次,即可接收一个字节(主机在接收之前,需要释放 SDA).

数据的有效性
IIC总线进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。SDA数据线在SCL的每个时钟周期传输一位数据。
即:数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
发送应答信号ack
主机在接收一个完整字节之后,在下一个时钟发送一位数据。规则:数据0表示应答,数据1 表示非应答。发送应答信号是为了告诉从机,主机是否还要数据 (命令从机发不发数据)。
目的是为了通知从机是否继续发送数据。

接收应答信号ack
主机在接收一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前需要释放SDA)。规则:数据 0表示应答,数据1表示非应答。
从机告诉主机接收到了主机传递的数据了,主机刚发送一个字节,就立刻说有没有人收到数据(SDA高),如果有接收数据就会立马回应(SDA拉低)。
目的:判断从机是否接收到了主机发送的数据
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位)表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。
如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
2.IIC的时序
1、IIC指定地址写时序


slave address:设备地址 reg address:寄存器地址, 时序:
- 开始条件
- 七位从机地址+1位RW(读:1,写:0)
- 接受从机的应答位(Receive ACK,RA)
- 发送寄存器地址
- 接收从机应答
- 发送一个字节的数据
- 从机应答
- 结束
S:数据由主机传输至从机 P:数据传输结束 SLAVE ADDRESS : 从机地址
起始信号产生后,所有从机就开始接收广播的从机地址信号。IIC总线,每个设备的地址都是唯一的,当主机广播的地址与某个设备的地址相同时,这个设备就被选中了,没被选中的设备讲会忽略之后的数据信号。根据IIC协议,这个从机地址可以是7位或10位。
地址位之后,传输方向选择位,为0:表示数据传输方向是由主机传输至从机,即主机向从机写数据。为1:则相反。从机接收传输方向选择位后,主机或从机会返回一个应答(ACK)或非应答(NACK)信号,只有接收到应答信号后,主机才能继续发送或接收数据。
2、IIC指定地址读时序


- 开始条件
- 七位从机地址+1位RW(读:1,写:0)
- 从机应答
- 发送寄存器地址
- 从机应答
- 当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注 意此时该地址的第8位为1,表明将主机设置成接收模式开始读取数据
- 从机应答
- 读取一个字节
- 主机发送非应答
- 结束
配置方向传输位为"读数据"方向。广播完地址后,接收到应答信号后,从机开始向主机返回数据(DATA),数据包大小也为8位,从机每发送完一个数,都会等待主机的应答信号(ACK),重复这个过程,可以返回N个数据,N没有限制大小。当主机希望停止接收数据时,就向从机返回一个非应答信号(NCAK),则从机自动停止数据传输。

通讯复合格式,该传输过程有两次起始信号(S),在第一次传输过程中,主机通过SLAVE_ADDRESS寻找到从设备后,发送一段"数据",这段数据通常用于表示从设备内部的寄存器或存储器地址;第二次传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
3.硬件IIC和软件IIC
1、硬件IIC
硬件IIC是由STM32内部的硬件模块实现的,使用CPU的时钟信号来控制数据传输和时序, 通信速度较快,可以达到几十MHz的速度。硬件IIC的实现相对简单,无需编写复杂的代码,因此在实现IIC通信时,硬件IIC通常是首选的方式。硬件IIC的主要优点有:
- 速度快、可以实现高速通信
- 实现简单、无需编写复杂时序代码
- 稳定性好、不容易出现通信错误
2、软件IIC
软件IIC是由CPU的GPIO模拟实现的,通过CPU的软件来控制时序和数据传输,通信速度相对较慢,一般在几十kHz到几百kHz之间。软件IIC的实现相对复杂,需要编写复杂的代码, 因此在实现IIC通信时,软件IIC通常是在硬件IIC无法满足需求时才采用的方式。软件IIC的主要优点有:
- 可以实现多路IIC通信,硬件IIC一般只能实现单路通信
- 可以在STM32的任何GPIO上实现IIC通信,相对灵活
- 可以实现任意时序,更加灵活
总的来说,硬件IIC和软件IIC各有优缺点,选择哪种方式要根据具体的应用需求进行选择。如果需要高速通信,建议选择硬件IIC;如果需要多路通信或者灵活的时序控制,建议选择软件IIC。
4.IIC 外设框图

1.I2C架构
通讯引脚-时钟控制逻辑-数据控制逻辑-整体控制逻辑
1.通讯引脚
STM32芯片有多个IIC外设,它们的IIC通讯信号引出到不同的GPIO引脚上,使用时必须配置这些指定的引脚。

2.时钟控制逻辑
SCL线的时钟信号,由IIC接口根据时钟控制寄存器(CCR)控制,控制的参数主要为时钟频率。
-
可选择IIC通讯的"标准/快速"模式,这两个模式分别对应100/400Kbits/s的通讯速率。
-
在快速模式下可选择SCL时钟的占空比,可选T(low)/T(high) = 2或T(low)/T(high)=16/9模式。
-
CCR寄存器中12位的配置因子CCR,它与IIC外设的输入时钟源共用作用,产生SCL时钟。STM32的IIC外设输入时钟源位PCKL1
计算时钟频率
标准模式
T high = CCR *T pckl1,T low= CCR*Tpclk1
快速模式中 Tlow/Tlow =2时:
Thigh = CCR*Tpckl1,T low = 2*low*Tpckl1
快速模式中 Tlow/Tlow =16/9时:
Thigh = 9*CCR*Tpckl1,T low = 16*low*Tpckl1
PCLK1 = 36MHz,想要配置400Kbits/s 方法:
PCLK时钟周期: TPCLK1 = 1/36 000 000
目标SCL时钟周期: TSCL = 1/400 000
SCL时钟周期内的高电平时间: Thigh = TSCL/3
SCL时钟周期内的低电平时间: Tlow = 2*TSCL/3
计算CCR的值 : CCR = THIGH/TPCLK1 = 30
计算出来的值写入到寄存器即可
3.数据控制逻辑
IIC的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC寄存器以及SDA数据线。
当向外发送数据的时候,数据移位寄存器以"数据寄存器"为数据源,把数据一位一位地通过SDA信号线发送出去。
当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到"数据寄存器"中。
发送数据和接收数据的核心部分是数据寄存器和数据移位寄存器
2、发送数据
当我们发送数据时,可以把一个字节的数据写到数据寄存器DR中,当数据移位寄存器没有数据移位时,这个数据寄存器的值会进一步转到移位寄存器里,在移位的过程中,我们就可以直接把下一个数据放到数据寄存器中里等着,一旦前一个数据移位完成,下一个数据就可以无缝衔接继续发送,当数据由数据寄存器转移到数据移位寄存器时,置状态寄存器 TXE(TX empty)位为1,表示发送寄存器为空。
主发送器通讯过程

1.控制产生起始信号(S),当发生起始信号后,它产生事件"EV5",并会对SR1寄存器的 SB 位置1,表示起始信号已经发生。
2.发生设备地址并等待应答信号,若有从机应答,则产生事件 EV6 及 EV8 ,这时SR1寄存器的 ADDR位及 TXE位被置1,ADDR位1 表示地址已经发送,TEX表示数据寄存器为空。
3.往IIC的数据寄存器DR写入要发送的数据,这时TXE位会被置0,表示数据寄存器非空,IIC外设通过SDA信号线一位位把数据发送出去后,又会产生EV8事件,即TXE被置1,重复这个过程,可发送多个字节。
4.发送数据完成后,控制IIC设备产生一个停止信号P,这个时候产生EV2事件,SR1的TEX位及BTF位被置1,表示通讯结束。、
注:SR1(Status Register 1)是I2C主设备(或当前作为主控制器角色的设备)内部的状态寄存器,用于告诉CPU"I2C硬件模块当前正在发生什么事件"。
- 物理主设备:I2C硬件模块
- 逻辑控制者:CPU(执行软件)
- 通信状态:记录在I2C模块的SR1寄存器中,供CPU查询
3、接收数据
输入的数据一位一位的从引脚移入到移位寄存器里,当一个字节的数据收齐之后,数据就会整体从移位寄存器转到数据寄存器,同时置标志位RXNE(RX not empty)为1,表示接收寄存器非空,这时就可以把数据从数据寄存器读出来。在IIC中数据收发是用一组数据寄存器与数据移位寄存器。通过控制寄存器的对应位控制什么时候收什么时候发。
5.硬件IIC配置
1、结构体
cs
typedef struct {
uint32_t I2C_ClockSpeed; /*!< 设置SCL时钟频率,此值要低于400000*/
uint16_t I2C_Mode; /*!< 指定工作模式,可选I2C模式及SMBUS模式 */
uint16_t I2C_DutyCycle; /*指定时钟占空比,可选low/high = 2:1及16:9模式*/
uint16_t I2C_OwnAddress1; /*!< 指定自身的I2C设备地址 */
uint16_t I2C_Ack; /*!< 使能或关闭响应(一般都要使能) */
uint16_t I2C_AcknowledgedAddress; /*!< 指定地址的长度,可为7位及10位 */
} I2C_InitTypeDef;
1.I2C_ClockSpeed
本成员设置的是I2C的传输速率,在调用初始化函数时,函数会根据我们输入的数值经过运算后把时钟因子写入到I2C的时钟控制寄存器CCR。 而我们写入的这个参数值不得高于 400KHz。实际上由于CCR寄存器不能写入小数类型的时钟因子,影响到SCL的实际频率可能会低于本成员设置的参数值, 这时除了通讯稍慢一点以外,不会对I2C的标准通讯造成其它影响。
2.I2C_Mode
本成员是选择I2C的使用方式,有I2C模式(I2C_Mode_I2C)和SMBus主、 从模式 (I2C_Mode_SMBusHost、 I2C_Mode_SMBusDevice ) 。 I2C不需要在此处区分主从模式,直接设置I2C_Mode_I2C即可。
3.I2C_DutyCycle
本成员设置的是I2C的SCL线时钟的占空比。该配置有两个选择, 分别为低电平时间比高电平时间为2:1 ( I2C_DutyCycle_2)和16:9 (I2C_DutyCycle_16_9)。 其实这两个模式的比例差别并不大,一般要求都不会如此严格,这里随便选就可以。
4.I2C_OwnAddress1
本成员配置的是STM32的I2C设备自己的地址,每个连接到I2C总线上的设备都要有一个自己的地址,作为主机也不例外。 地址可设置为7位或10位(受下面 I2C_AcknowledgeAddress成员决定),只要该地址是I2C总线上唯一的即可。 STM32的I2C外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员 I2C_OwnAddress1配置的是默认的、OAR1寄存器存储的地址, 若需要设置第二个地址寄 存器OAR2,可使用I2C_OwnAddress2Config函数来配置,OAR2不支持10位地址,只有7 位。
5.I2C_Ack_Enable
本成员是关于I2C应答设置,设置为使能则可以发送响应信号。本实验配置为允许应答 (I2C_Ack_Enable), 这是绝大多数遵循I2C标准的设备的通讯要求,改为禁止应答 (I2C_Ack_Disable)往往会导致通讯错误。
6.I2C_AcknowledgeAddress
本成员选择I2C的寻址模式是7位还是10位地址。这需要根据实际连接到I2C总线上设备的地址进行选择,这个成员的配置也影响到I2C_OwnAddress1成员, 只有这里设置成10位模式时,I2C_OwnAddress1才支持10位地址。 配置完这些结构体成员值,调用库函数I2C_Init即可把结构体的配置写入到寄存器中
6.软件IIC配置
1、起始信号函数
void My_IIC_Start(void)
{
MyI2C_W_SDA(1); //释放数据线
MyI2C_W_SCL(1); //释放时钟线
MyI2C_W_SDA(0); //下拉数据线
MyI2C_W_SCL(0); //下拉时钟线
}
2、停止信号函数
void My_IIC_Stop(void)
{
MyI2C_W_SDA(0); //下拉数据线
MyI2C_W_SCL(1); //释放时钟线
MyI2C_W_SDA(1); //释放数据线
}
3、IIC发送一个字节函数
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++) //循环发送8次,完成一个字节的发送
{
MyI2C_W_SDA(Byte & (0x80 >> i)); //循环发送8次,完成一个字节的发送
MyI2C_W_SCL(1); //释放时钟线
MyI2C_W_SCL(0); //释放时钟线
}
}
4、IIC接收一个字节函数
5、IIC发送应答信号函数
6、IIC接收应答信号函数
7.滴答定时器
1.系统定时器-SysTick

SysTick : 24位系统定时器,只能递减,存在于内核嵌套在NVIC中。所有的Cortex-M中都有这个系统定时器。
重装载值reload 递减,当递减到0会触发中断并且会有置位countflag标志,VAL表示当前值。
然后reload继续从预设值开始递减,周而复始。


2.计算
T : 一个计数循环的时间,跟reload和 CLK有关
CLK : 72M 或者9 M, 由CTRL寄存器配置
reload : 24位,用户自己配置
T = reload * (1/ CLK)
CLK= 72M, 1us = (72 )* (1/ 72 000 000)
CLK= 72M, 1ms = (72 000 )* (1/ 72 000 000)
时间单位换算: 1s = 1000ms = 1000 000 us = 1000 000 000 ns
3.结构体
typedef struct
{
__IO uint32_t CTRL; //控制及状态寄存器
__IO uint32_t LOAD; //重装载寄存器
__IO uint32_t VAL; //重装载寄存器
__I uint32_t CALIB; //校准寄存器
} SysTick_Type;
4.SysTick中断优先级
①system tick 属于内核的外设,它的中断优先级与片上外设的中断优先级高
②systick 中断优先级配置的是scb->shprx寄存器
外设中断优先级配置的是nvic-> iprx寄存器
有优先级分组
①STM32的外设(内核还是片上)都是使用4个二进制来表示中断优先级
②中断优先级的分组对内核和外设同样适用,只需要将中断优先级的四个位按外设优先级来分组即可,人为的进行分出抢占优先级和子优先级。例举:
1<<4 -1=16 -1= 15 (1 1 1 1 )
前面两位表示抢占优先级 后面两位表示子优先级
3 3
① 编写一个us延时的函数
②编写一个ms延时的函数
cs
#include "stm32f10x.h"
#include "systick.h"
void us_delay( uint32_t us)
{
uint32_t i;
SysTick_Config(72);
for(i = 0;i < us;i++)
{
while( !((SysTick->CTRL) & (1<<16) ) );
}
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
void ms_delay( uint32_t ms)
{
uint32_t i;
SysTick_Config(72000);
for(i = 0;i < ms;i++)
{
while( !((SysTick->CTRL) & (1<<16) ) );
}
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}
注:SysTick_Config()设置自动重装载寄存器的值
SysTick_Config(72);即完成一次循环为1us,完成多少次循环就延时多少秒,通过判断COUNT_FLAG的值来判断一次计数是否完成,完成后,失能系统时钟

