STM32F1的I2C模块协议简介

接口/总线/驱动

1139人已加入

描述

I2C是一种多主从的串行通讯协议。STM32F1的I2C模块支持标速(最高100kHz)和高速(最高400kHz)两种工作模式。

一、I2C协议简介

标准的IIC接口有数据线SDA、时钟线SCL两条总线,只能工作于半双工模式,在设计中,对总线的负载电容有一定的要求,具体请查阅元件的技术手册确定。

IIC的通讯时序如图,通讯电平为正逻辑:

SDA

※数据发送的起始和终止信号为:SCL为高时,

起始:SDA下降沿 终止:SDA上升沿

※当总线空闲时,SDA和SCL均为高电平,总线需要外接上拉电阻,阻值5-10k。

※每次发送一字节(8bits)信号,MSB高位先发。

二、I2C通讯时序(使用时以具体被控芯片手册为准)

通讯时序(主机发送若干数据):

※主机引脚配置为open Drain,务必不要内部上拉、下拉

***①保持SCL=1,拉低SDA,产生起始信号。

***②拉低SCL,准备发送数据;

***③将数据高位Shift Out(SDA上),随后拉高SCL。在SCL=1期间SDA电平不能变动。

***④将数据依次输出

***⑤输出最后一位数据且SCL变为低电平后,主机接SDA引脚立即变换为高阻输入模式。

⑥主机向SCL输出一个时钟方波,在方波高位检测从机是否将SDA拉低(即发送ACK);若未收到表明发送未成功。

***⑦当主机检测到SDA被拉低时,表明从机准备好接收下一字节数据。随后主机拉高SCL,从机释放SDA,形成终止信号。若错误,主机也拉高SCL并记录错误。此时SCL=1,SDA=1。***

⑧准备下一次传输。

通讯时序(从机接收若干数据):

①从机引脚均配置为输入模式

***②检测开始信号,随后接收数据(可以通过SCL上升、下降沿触发方式检测校验)

③若接收没有出现错误,从机将SDA配置为开漏,在定义的数据长度(数据位+1ACK)的前一个时钟方波之后(时钟下降沿)拉低SDA发出ACK。若接受错误(例如一个时钟信号上升下降沿SDA数据不同)则不下拉。

④检测到SCL出现上升后,释放SDA。

⑤准备下一次传输。

※IIC数据以1个数据包(主机)+1位ACK(从机)格式传送:

SDA

发送地址的最后一位是数据方向位(R/W位),该位用“0”表示主机发送数据,“1”表示主机接收数据(R)。

当STM32做主机时,在发送地址位后(10位地址模式为首序列最后一位)会根据方向位自动判断进入主发送器模式主接收器模式**。**

三、STM32 LL库IIC驱动

STM32的I2C模块可以配置为从机输出、从机输入、主机输出、主机输入四种模式。

■主模式时,I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。起始条件和停止条件都是在主模式下由软件控制产生。

■从模式时,I2C接口能识别它自己的地址(7位或10位)和广播呼叫地址。软件能够控制开启或禁止广播呼叫地址的识别。

SCL时钟信号、起始终止信号由主机输出应答始终由从机应答 ;从机由主机发送地址选中(主机和从机的区别)。

1.IIC时钟配置:

在I2C_CR2寄存器中Freq[5:0]设定该模块的输入时钟( APH1旁路时钟输入,最高36MHz )。输入时钟的频率至少为:

● 标准模式下为:2MHz ● 快速模式下为:4MHz

若使用CubeMX进行模块的配置,则生成的模块初始化函数中自动设置

2.相关寄存器

①自身地址寄存器(I2C_OAR1):

SDA

SDA

SDA

STM32的IIC模块支持双地址模式,即 可以设置两个自身地址

自身地址寄存器2(I2C_OAR2):

SDA

SDA

*②时钟控制寄存器(I2C_CCR): * (CubeMX自动配置)

在I2C_CR2中设置完成模块输入时钟后对SCL输出时钟进行配置

SDA

SDA

SDA

③控制寄存器(I2C_CR1)

SDA

SDA

SDA

SDA

SDA

④控制寄存器(I2C_CR2)

SDA

SDA

SDA

SDA

⑤数据寄存器(I2C_DR)

SDA

SDA

⑥状态寄存器(I2C_SR1)

SDA

SDA

PEC校验的使用较为复杂,使用时请具体参看手册

SDA

SDA

SDA

SDA

SDA

SDA

对包序号的判断(第几个包?)从起始条件开始计算。

⑦状态寄存器(I2C_SR2)

SDA

SDA

SDA

SDA

SDA

3.传输时序(请参考传输时序进行开发)

** ①主发送器模式**

※7位地址最后一位为R/W位,10位帧头最后一位为R/W,此时设置为"0",由本机发送

SDA

** ②主接收器模式(主模式下起始、终止条件都由本机发出)**

※7位地址最后一位为R/W位,10位帧头最后一位为R/W,此时设置为"1",由本机发送

SDA

** ③从接收器模式**

※地址R/W位非本机发送,由硬件自动比对本机地址判断是否被选中。此时R/W位为"0"

SDA

** ④从发送器模式**

※地址R/W位非本机发送,由硬件自动比对本机地址判断是否被选中。此时R/W位为"0"

SDA

关于主从模式:

*** ■模块默认处于从模式***

*** ■当本机通过START位主动发送起始条件时,进入主模式***

*** ■当主模式最后一字节传输完成后,由本机置STOP位发送停止条件,进入从模式***

*** ■从模式下模块自动检测总线上的电平变化***
*** ■发送/接收模式的选择通过硬件自动判断R/W实现***

■注意发送/接收地址后,读ADDR才能进入主/从模式下一步的数据传输

LL库函数:

1、初始化结构体LL_I2C_InitTypeDef

typedef struct
{
  uint32_t PeripheralMode;/*
  选择模块工作模式,通过LL_I2C_SetMode()实现
  @ref     LL_I2C_MODE_I2C //I2C模式
           LL_I2C_MODE_SMBUS_HOST //
           LL_I2C_MODE_SMBUS_DEVICE //
           LL_I2C_MODE_SMBUS_DEVICE_ARP //
  */
  uint32_t ClockSpeed;/*
  配置时钟频率(< 400kHz(高速);< 100kHz(标准));通过LL_I2C_SetClockPeriod()、LL_I2C_SetDutyCycle()、
  LL_I2C_SetClockSpeedMode()、LL_I2C_ConfigSpeed()实现
    //示例: 若需要100kHz时钟,则输入100000
  */
  uint32_t DutyCycle;/*
  设置高速模式(仅)下信号占空比;通过LL_I2C_SetDutyCycle()实现 
  @ref     LL_I2C_DUTYCYCLE_2 //低:高=2
           LL_I2C_DUTYCYCLE_16_9 //低:高=16:9
 */
  uint32_t OwnAddress1;/*
  设置自身的主地址,通过 LL_I2C_SetOwnAddress1()实现
  //10位模式最大为0x3FF, 7位最大为0x7F
  ※该结构体及模块初始化函数不提供设置第二地址(副地址)的方式
 */
  uint32_t TypeAcknowledge;/*
  配置ACK使能;通过LL_I2C_AcknowledgeNextData()实现
  @ref     LL_I2C_ACK //在接收到一个字节后返回一个应答(匹配的地址或数据)
           LL_I2C_NACK //无应答
  */
  uint32_t OwnAddrSize;/*
  设置自身地址长度;通过LL_I2C_SetOwnAddress1()实现
  ※在双地址模式下只能设置为7位长度
  @ref     LL_I2C_OWNADDRESS1_7BIT //7位长度
           LL_I2C_OWNADDRESS1_10BIT //10位长度
  */
} LL_I2C_InitTypeDef;

2、工作模式设置

void LL_I2C_SetMode(I2C_TypeDef *I2Cx, uint32_t PeripheralMode);/*
设置模块工作模式
@reg       CR1- >SMBUS、SMBTYPE、ENARP
@ref     LL_I2C_MODE_I2C //I2C模式
           LL_I2C_MODE_SMBUS_HOST //
           LL_I2C_MODE_SMBUS_DEVICE //
           LL_I2C_MODE_SMBUS_DEVICE_ARP //
*/
uint32_t LL_I2C_GetMode(I2C_TypeDef *I2Cx);

3、模块开启/关闭函数

void LL_I2C_Enable(I2C_TypeDef *I2Cx);/*
使能(开启)I2C模块
@reg  CR1- >PE
*/
void LL_I2C_Disable(I2C_TypeDef *I2Cx);/*
禁用(关闭)I2C模块
@reg  CR1- >PE
*/
uint32_t LL_I2C_IsEnabled(I2C_TypeDef *I2Cx);/*
检测I2C模块是否开启
@retval: 1//开启
         0//关闭
*/

4.时钟延长控制(ClockStretching)

参考https://blog.csdn.net/happygaohualei/article/details/52864694

通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行。一般情况是从机在发送过程中将SCL接地,主机无法拉高电平后暂停传输,从机处理完成任务后释放SCL,传输继续。 大多数外设不支持时钟延长功能。

※STM32默认允许时钟延长

void LL_I2C_EnableClockStretching(I2C_TypeDef *I2Cx);/*
使能时钟延长。
@reg    CR1- >NOSTRETCH
*/
void LL_I2C_DisableClockStretching(I2C_TypeDef *I2Cx);/*
禁用时钟延长
@reg    CR1- >NOSTRETCH
*/
uint32_t LL_I2C_IsEnabledClockStretching(I2C_TypeDef *I2Cx);/*
检测是否启用时钟延长
@retval     1//启用
*/

5.自身地址设置

地址在从模式下由硬件自动比较

void LL_I2C_SetOwnAddress1(I2C_TypeDef *I2Cx, uint32_t OwnAddress1, uint32_t OwnAddrSize);/*
设置自身主地址,并设置地址模式(7位或10位)
@reg  ADD0    ADD1_7   ADD8_9   ADDMODE 
@param  OWnAddress//自身主地址
        OwnAddrSize//地址模式(7位或10位),@ref    LL_I2C_OWNADDRESS1_7BIT
                                               LL_I2C_OWNADDRESS1_10BIT
※若启用第二地址,设置为7位地址模式
*/
void LL_I2C_EnableOwnAddress2(I2C_TypeDef *I2Cx);/*
双地址模式(启用第二地址),启用后只支持7位地址模式
@reg  OAR2- >ENDUAL
*/
void LL_I2C_SetOwnAddress2(I2C_TypeDef *I2Cx, uint32_t OwnAddress2);/*
设置第二地址(7位)
*/
void LL_I2C_DisableOwnAddress2(I2C_TypeDef *I2Cx);/*
禁用双地址模式
@reg  OAR2- >ENDUAL
*/

6.广播呼叫控制(General Call)

当本机作为从机接收到地址0x00时,将被选中并发ACK(如果使能ACK),0x00即广播地址。

void LL_I2C_EnableGeneralCall(I2C_TypeDef *I2Cx);/*
使能广播呼叫。
@reg    CR1- >ENGC
*/
void LL_I2C_DisableGeneralCall(I2C_TypeDef *I2Cx);/*
禁用广播呼叫.
@reg    CR1- >ENGC
*/
uint32_t LL_I2C_IsEnabledGeneralCall(I2C_TypeDef *I2Cx);/*
检测是否启用了广播呼叫。
@retval  1//启用
*/

7.模块时钟配置

  • 请在模块关闭的状态下配置*
void LL_I2C_SetClockPeriod(I2C_TypeDef *I2Cx, uint32_t ClockPeriod);/*
配置SCL时钟周期;
@reg    CCR- >CCR
@note  具体配置参见MANUAL的CCR寄存器(前有)
*/
uint32_t LL_I2C_GetClockPeriod(I2C_TypeDef *I2Cx);/*
读取CCR- >CCR的值
*/
void LL_I2C_SetClockSpeedMode(I2C_TypeDef *I2Cx, uint32_t ClockSpeedMode);/*
设置速度模式(标准/高速)
@reg    CCR- >FS
@ref      LL_I2C_CLOCK_SPEED_STANDARD_MODE //标准模式(最高100kHz)
          LL_I2C_CLOCK_SPEED_FAST_MODE //高速模式(最高400kHz)
@note  ※高速模式下可设置信号占空比
*/
void LL_I2C_SetPeriphClock(I2C_TypeDef *I2Cx, uint32_t PeriphClock);/*
设置模块的输入时钟(e.g.36Mhz,84Mhz...);单位Hz,函数自动与10MHz对齐;
@reg  CR2- >FREQ
*/
uint32_t LL_I2C_GetPeriphClock(I2C_TypeDef *I2Cx);/*
读取模块的输入时钟(由上一函数设置)
@reg  CR2- >FREQ =@retval
*/
void LL_I2C_SetDutyCycle(I2C_TypeDef *I2Cx, uint32_t DutyCycle);/*
设置信号占空比(※仅高速模式)
@reg            CCR- >DUTY
@ref     LL_I2C_DUTYCYCLE_2 //低:高=2
         LL_I2C_DUTYCYCLE_16_9 //低:高=16:9
*/
uint32_t LL_I2C_GetDutyCycle(I2C_TypeDef *I2Cx);/*
获取信号占空比(仅高速模式);
@reg            CCR- >DUTY
*/
void LL_I2C_SetRiseTime(I2C_TypeDef *I2Cx, uint32_t RiseTime);/*
设置主模式下SCL的最大上升时间。
@reg    TRISE- >TRISE[5:0]
@note 默认值0x02  计算方法请详细参看MANUAL
*/
uint32_t LL_I2C_GetRiseTime(I2C_TypeDef *I2Cx);/*
获取主模式下SCL的最大上升时间。
@retval  =  TRISE[5:0]
*/

时钟配置整合函数(优先使用):

void LL_I2C_ConfigSpeed(I2C_TypeDef I2Cx, uint32_t PeriphClock, uint32_t ClockSpeed,
uint32_t DutyCycle);/

配置输入时钟、时钟周期、占空比(高速模式下)
*/

8.SMbus部分

暂不补充

9.关键控制

①ACK控制(接收时)

void LL_I2C_AcknowledgeNextData(I2C_TypeDef I2Cx, uint32_t TypeAcknowledge);/
配置应答使能(是否接收到一个数据(地址或数据)后返回一个应答)
@reg CR1->ACK
@ref LL_I2C_ACK //返回应答
LL_I2C_NACK //不返回应答
*/

②生成起始条件与终止条件

void LL_I2C_GenerateStartCondition(I2C_TypeDef I2Cx);/
生成起始条件(S),可与SB配合检测起始条件的生成状况
@reg CR1->START
@note 起始条件发送后START位自动清除
*/
void LL_I2C_GenerateStopCondition(I2C_TypeDef I2Cx);/
发送终止条件(P)
@reg CR1->STOP
@note 终止条件发送后STOP位自动清除
*/

③读取/发送操作

uint8_t LL_I2C_ReceiveData8(I2C_TypeDef I2Cx);/
接收8位数据。
读取一次DR,获得8位数据
*/
void LL_I2C_TransmitData8(I2C_TypeDef I2Cx, uint8_t Data);/
发送8位数据。
向DR写入一个字节
*/

I2C中断

(摘自RM008)

SDA

SDA

从中可以看出,I2C模块的中断掩码非常少,一旦开启可靠性将大大降低,因此一般不使用中断功能

中断总共有两个入口,条件检测、中断配置类似UART和SPI

中断掩码控制:

void LL_I2C_EnableIT_BUF(I2C_TypeDef I2Cx);/
置位ITBUFEN
*/
void LL_I2C_DisableIT_BUF(I2C_TypeDef I2Cx);/
清零ITBUFEN
*/

void LL_I2C_EnableIT_ERR(I2C_TypeDef I2Cx);/
置位ITERREN
*/
void LL_I2C_DisableIT_ERR(I2C_TypeDef I2Cx);/
清零ITERREN
*/

void LL_I2C_EnableIT_EVT(I2C_TypeDef I2Cx);/
置位ITEVTEN
*/
void LL_I2C_DisableIT_EVT(I2C_TypeDef I2Cx);/
清零ITEVTEN
*/

I2C状态判断 ※在软件方式使用I2C时非常重要

读取状态位判断的格式为 LL_I2C_IsActiveFlag_XXX()

使用请参见前面的时序图。

DMA控制

I2C模块同样可以配置DMA传输,这通常使用在主模式下传输大量数据的情况。

但LL库使用DMA时容易发生通讯错误,因此不推荐使用。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分