0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MM32F0140 MCU关于I2C的介绍、配置及实验

海阔天空的专栏 来源:灵动MM32MCU 作者:灵动MM32MCU 2022-05-20 17:08 次阅读

I2C简介

I2C总线是一个两线串行接口,包含串行数据线(SDA)与串行时钟线(SCL),能够在连接到总线的器件间传递信息,每一个连接总线的设备都有独立的地址,主机可以通过该地址选择连接总线的设备并与之通信。I2C通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号,从而进行数据传输,微控制器可通过I2C总线接口实现芯片间串行互联。

I2C的功能框图如图1所示,当I2C采用主模式进行数据传输时,主机先发送从机设备地址与读写位数据,在从机地址匹配时可进行数据传输;I2C采用从模式时,从设备等待接收由主机发送来的地址数据,地址匹配时可进行数据传输。

poYBAGKHWEaAVCkKAADLsTmn1Hk923.png

主机与从机

主机是初始化总线的数据传输并产生允许传输的时钟信号的器件。此时,任何被寻址的器件都被认为是从机,在主模式下只支持一个主机。

设备地址

在进行数据传输前需要发送从设备地址,只有当主机的发送设备地址与从机的设备地址匹配时才能进行数据传输。总线的每个器件都有一个唯一的地址识别,而且都可以作为一个发送或接收器

I2C信号

起始信号

当总线处于空闲状态时,SCL和SDA同时被外部上拉电阻拉为高电平。当主机启动数据传输时,必须先产生一个起始信号。在SCL线是高电平时, SDA线从高电平向低电平切换表示起始信号,如图2所示。在起始信号产生后,总线处于忙碌状态,除当前数据传输的主从设备外,其他I2C器件无法访问总线。

pYYBAGKHWEqAFQ34AACIN5Olxgc566.png

停止信号

停止信号如图2所示,当主机结束传输时要发送停止信号,在SCL线为高电平、SDA线由低电平向高电平切换时,产生停止信号,从而释放总线,总线再次处于空闲状态。

应答信号

I2C具有完善的应答机制,当主机发送完成地址与读写位或者主机发送一个字节的数据到从机上时,从机会回应一个应答信号(ACK),即在第9个SCL时钟周期时拉低SDA,如图3所示。

poYBAGKHWHWAR2NvAABvVwicb2o015.png

非应答信号

如图3所示,当第9个SCL时钟周期时拉高SDA,表示为非应答信号,主机或从机将产生一个停止信号中止传输。

读写位

在传输设备地址时会跟随1bit的读写位数据,该读写位决定当前数据传输方向。如图4所示,起始信号后发送从设备地址及读写位数据,当读写位为1时,执行读操作;读写位为0时,执行写操作。

pYYBAGKHWHuAT43jAAFUGXwXtbk059.png

I2C配置

I2C的配置包括传输速率、从设备地址、数据传输方向及主从模式选择的配置,从而进行数据传输。

工作速率

I2C存在两种工作速率模式:标准模式(数据传输速率为0 ~ 100Kbps),快速模式(数据传输速率最大为400Kbps);可通过I2C控制寄存器(I2C_CR)的SPEED位进行控制,由于I2C从机速率跟随主机速率,因此只需在I2C为主模式下配置工作速率,如图5所示。

poYBAGKHWMaAP71YAACZwGvsds0480.png

地址格式

I2C有两种地址格式:7位地址格式和10位地址格式。

7位地址

如图6所示,在起始条件(S)后发送的一个字节的前7位(A6 ~ A0)为从机地址,最低位(R/W)是数据方向位,当R/W位为0,表示主机写数据到从机,R/W位为1表示主机从从机读数据。

pYYBAGKHWMuABBZGAACu68oR-S4913.png

10位地址

在10位的地址格式中,发送2个字节来传输10位地址。发送的第一个字节的起始位后的5位(bit7:3)用于告示从机接下来是10位的传输,第一个字节的后两位(bit2:1)是从机地址的bit9:8,最低位(bit 0)是数据方向位(R/W)。传输的第二个字节为10位地址的低八位,如图7所示。

poYBAGKHWO2Aarr2AACSVE7LJl4662.png

写操作

I2C进行写操作时,先通过I2C目标地址寄存器(I2C_TAR)配置从设备地址,再配置读写位,令控制数据命令寄存器(I2C_DR)的CMD位为0,所需传输数据放入I2C_DR寄存器的DR位,进行写操作。

读操作

I2C进行读操作时,先由主机写从设备地址及设备内寄存器地址,当从设备地址及设备内寄存器地址发送完成后,将控制数据命令寄存器(I2C_DR)的CMD位为1,进行读操作,读出设备内寄存器地址中的数值。

poYBAGKHWPOAMtkdAABzOhFaN3M167.png

主模式

I2C主模式配置需在I2C未使能状态下进行,将I2C使能寄存器(I2C_IMR)的ENABLE位置0。通过配置控制寄存器(I2C_CR)的SPEED位选择速率模式,MASTER10位选择地址格式,DISSLAVE位为1使从模式禁止,MASTER位为1选择主模式。向目标地址寄存器(I2C_TAR)写入从设备地址,I2C_IMR寄存器的ENABLE位置1,使能I2C。

I2C 接口支持读写的动态切换,当发送数据时,写数据到I2C的RX/TX数据缓冲及命令寄存器(DR)的低字节中,配置CMD位为0产生写操作。接下来的读命令,需要确保CMD位为1。如果发送FIFO为空,I2C模块拉低SCL直到下个命令写入到发送FIFO中。

从模式

I2C做从机时,等待其他主机的访问,只有其他主机发送了I2C从机对应的设备地址,才能进行数据传输。配置I2C为从模式,将I2C使能寄存器(I2C_IMR)的ENABLE位置0,禁止I2C使能,将I2C自身从地址写入从机地址寄存器(I2C_SAR),配置控制寄存器(I2C_CR)的SLAVE10选择寻址格式,DISSLAVE位为0,选择从模式,MASTER位为0,禁止主模式;I2C_IMR寄存器的ENABLE位置1,使能I2C。

当其他主机发送地址与 SAR 寄存器中的从机地址匹配,I2C接口响应发送的地址,并识别传输方向;当I2C产生读请求,从机将数据发送至主机;当主机为写操作时,I2C从机接收到主机的数据并将数据存储在接收缓冲中,软件通过读DR寄存器中的bit7:0来获得接收到的数据。

实验

本实验使用轮询进行数据的写操作与读操作,初始化配置I2C为主模式,从设备地址为0x50,速率为100KHz,使用7位地址格式。I2C初始化后,按下一次任意按键,进行8字节数据的写操作,其中第一字节写入数据设置为设备内的寄存器地址,串口显示写入数据;写操作完成后,再次按下任意按键,进行7位数据的读操作,从设备的寄存器地址中读出相关数据。

启用I2C外设时钟 enable_clock()

实验使用I2C1,串口打印输出结果,串口使用引脚属于GPIOA组,I2C使用引脚属于GPIOB组,因此需要启用I2C1、UART1、GPIOA及GPIOB的外设时钟。

voidenable_clock()
{
/*EnableI2C1clock.*/
RCC->APB1ENR|=RCC_APB1_PERIPH_I2C1;
/*EnableGPIOAclock.*/
RCC->AHB1ENR|=RCC_AHB1_PERIPH_GPIOA;
/*EnableGPIOBclock.*/
RCC->AHB1ENR|=RCC_AHB1_PERIPH_GPIOB;
/*EnableUART1clock.*/
RCC->APB2ENR|=RCC_APB2_PERIPH_UART1;
}

配置引脚 pin_init()

配置I2C1引脚,I2C1_SCL为PB6引脚,I2C1_SDA为PB7引脚,复用通道为AF1;由于实验现象通过串口显示,故配置UART的TX(PA9)与RX(PA10)引脚。

voidpin_init()
{
/*PB6-I2C1_SCL.*/
GPIOB->CRL&=~GPIO_CRL_MODE6_MASK;
GPIOB->CRL&=~GPIO_CRL_CNF6_MASK;
GPIOB->CRL|=GPIO_CRL_MODE6(GPIO_Speed_50MHz);
GPIOB->CRL|=GPIO_CRL_CNF6(GPIO_PinMode_AF_OpenDrain);
GPIOB->AFRL&=~GPIO_AFRL_AFRY_MASK;
GPIOB->AFRL|=(GPIO_AF_1<< GPIO_CRL_MODE6_SHIFT);

/*PB7-I2C1_SDA.*/
GPIOB->CRL&=~GPIO_CRL_MODE7_MASK;
GPIOB->CRL&=~GPIO_CRL_CNF7_MASK;
GPIOB->CRL|=GPIO_CRL_MODE7(GPIO_Speed_50MHz);
GPIOB->CRL |= GPIO_CRL_CNF7(GPIO_PinMode_AF_OpenDrain);
GPIOB->AFRL|=(GPIO_AF_1<< GPIO_CRL_MODE7_SHIFT);

/*PA9-UART_TX.*/
GPIOA->CRH&=~GPIO_CRH_MODE9_MASK;
GPIOA->CRH&=~GPIO_CRH_CNF9_MASK;
GPIOA->CRH|=GPIO_CRH_MODE9(GPIO_Speed_50MHz);
GPIOA->CRH|=GPIO_CRH_CNF9(GPIO_PinMode_AF_PushPull);
GPIOA->AFRH|=(GPIO_AF_1<< GPIO_CRH_MODE9_SHIFT);

/*PA10-UART_RX.*/
GPIOA->CRH&=~GPIO_CRH_MODE10_MASK;
GPIOA->CRH&=~GPIO_CRH_CNF10_MASK;
GPIOA->CRH|=GPIO_CRH_CNF10(GPIO_PinMode_In_Floating);
GPIOA->AFRH|=(GPIO_AF_1<< GPIO_CRH_MODE10_SHIFT);
}

UART初始化 uart_init()

初始化UART,配置时钟频率、波特率、数据长度、停止位、传输模式及是否使用校验。

voiduart_init()
{
/*Clearthecorrespondingbittobeused.*/
UART1->CCR&=~(UART_CCR_PEN_MASK|UART_CCR_PSEL_MASK|UART_CCR_SPB0_MASK|UART_CCR_SPB1_MASK|UART_CCR_CHAR_MASK);
UART1->GCR&=~(UART_GCR_AUTOFLOWEN_MASK|UART_GCR_RXEN_MASK|UART_GCR_TXEN_MASK);
/*WordLength.*/
UART1->CCR|=UART_CCR_CHAR_MASK;
/*XferMode.*/
UART1->GCR|=(UART_XferMode_RxTx<< UART_GCR_RXEN_SHIFT);
/*Setupbaudrate,BOARD_DEBUG_UART_FREQ=48000000u,BOARD_DEBUG_UART_BAUDRATE=9600u.*/
UART1->BRR=(BOARD_DEBUG_UART_FREQ/BOARD_DEBUG_UART_BAUDRATE)/16u;
UART1->FRA=(BOARD_DEBUG_UART_FREQ/BOARD_DEBUG_UART_BAUDRATE)%16u;
/*EnableUART1.*/
UART1->GCR|=UART_GCR_UARTEN_MASK;
}

I2C初始化 i2c_init()

I2C通过操作配置寄存器(I2C_CR)实现初始化,配置I2C为主模式、速率为100KHz、7位地址格式、时钟频率为60000000u,设置从设备地址为0x50,使能I2C。

voidi2c_init()
{
I2C1->ENR&=~I2C_ENR_ENABLE_MASK;
uint32_ttmp=BOARD_I2C_FREQ/I2C_BaudRate_100K;/*BOARD_I2C_FREQ=60000000u,I2C_BaudRate_100K=100000u.*/
I2C1->SSHR=tmp/2u-12u;/*Configurehighlevelcountinnormalspeed.*/
I2C1->SSLR=tmp/2u-1u; /*Configurelowlevelcountinnormalspeed.*/
I2C1->CR=I2C_CR_SPEED(1u);
I2C1->CR&=~I2C_CR_MASTER10_MASK; /*Addressformat.*/
I2C1->CR|=I2C_CR_RESTART_MASK /*Generaterestartsignal.*/
|I2C_CR_DISSLAVE_MASK /*Disableslavemodule.*/
|I2C_CR_REPEN_MASK /*Enablesendingrestartcondition.*/
|I2C_CR_EMPINT_MASK /*Controltx_emptyinterruptgeneration.*/
|I2C_CR_MASTER_MASK; /*Enablemastermodule.*/

I2C1->RXTLR=0u; /*Configurethesendingreceivevalue.*/
I2C1->TXTLR=0u; /*Configurethesendingthresholdvalue.*/

/*Setuptargetaddress.*/
I2C1->TAR=I2C_TAR_ADDR(APP_I2C_TARGET_ADDR);/*APP_I2C_TARGET_ADDR=0x50.*/
I2C1->ENR|=I2C_ENR_ENABLE_MASK; /*EnableI2C.*/
}

I2C写操作 i2c_write()

设置I2C发送数组i2c_tx_buf及发送数据长度i2c_tx_len,将数组i2c_tx_buf的第一个数值设置为寄存器地址,发送8字节数据。数据发送后读取状态寄存器(I2C_SR),等待发送缓冲空,再进行下一次发送;数据传输完成,操作使能寄存器的ABORT位为1,使I2C停止数据传输;读清除TX_ABRT中断寄存器(I2C_TXABRT),将TX FIFO从刷新/复位状态中释放。

voidi2c_write()
{
I2C1->DR=I2C_DR_DAT(i2c_tx_buf[0]);
while(0u==(I2C1->SR&I2C_STATUS_TX_EMPTY))
{
}
for(uint32_ti=1u;i< i2c_tx_len; i++)
{
I2C1->DR=I2C_DR_DAT(i2c_tx_buf[i]);

while(0u==(I2C1->SR&I2C_STATUS_TX_EMPTY)) /*Waittotxfifoempty.*/
{
}
}
I2C1->ENR|=I2C_ENR_ABORT_MASK; /*Prepareforthestop.*/
I2C1->TXABRT; /*Readregistertoreleasetxfifo.*/
while(I2C1->SR&I2C_STATUS_ACTIVE) /*WaittoI2Cnotactive,whichmeansstopistakingeffect.*/
{
}
}

I2C读操作 i2c_read()

设置I2C接收数组i2c_rx_buf及接收长度i2c_rx_len,在进行数据读取前,需要发送从设备地址及寄存器地址,本实验设置发送数组的第一个数据为寄存器地址,发送完成后再将I2C_DR寄存器的读写位(CMD)置1,读取I2C_DR寄存器进行读操作。读操作结束后,操作使能寄存器的ABORT位为1,使I2C停止数据传输。

voidi2c_read()
{
I2C1->DR=I2C_DR_DAT(i2c_tx_buf[0]);
while(0u==(I2C1->SR&I2C_STATUS_TX_EMPTY))
{
}
/*Readdatafromtargetdevice.*/
for(uint32_ti=0u;i< i2c_rx_len; i++)
{
I2C1->DR=I2C_DR_CMD_MASK; /*Swichread-writebittoread,preparetogetdata.*/

while(0u==(I2C1->SR&I2C_STATUS_RX_NOTEMPTY)) /*Waittorxfifonotempty.*/
{
}
i2c_rx_buf[i]=(uint8_t)I2C1->DR;
}
I2C1->ENR|=I2C_ENR_ABORT_MASK; /*Prepareforthestop.*/
I2C1->TXABRT; /*Readregistertoreleasetxfifo.*/
while(I2C1->SR&I2C_STATUS_ACTIVE) /*WaittoI2Cnotactive,whichmeansstopistakingeffect.*/
{
}
}

main()函数

main()函数结合上述操作,串口打印“i2c_master_basic example”,初始化I2C后,将发送数组设置为0到7,按下一次任意按键,进行数据发送,串口显示发送数据;再次按下任意按键,进行数据读取,从寄存器地址中读出数值,设置寄存器地址设置为发送数据的首个数据,串口显示接收数据,实验结果如图9所示。

intmain()
{
enable_clock();
pin_init();
uart_init();
printf("\r\ni2c_master_basicexample\r\n");
i2c_init();
for(uint32_ti=0u;i< i2c_tx_len; i++)
{
i2c_tx_buf[i]=i;
}
while(1)
{
printf("pressanykeytowritei2c-eeprom.\r\n");
getchar();
i2c_write();
printf("writedata:");
for(uint32_ti=0u;i< i2c_tx_len; i++)
{
printf("0x%02X,",i2c_tx_buf[i]);
}
printf("\r\n");

printf("pressanykeytoreadi2c-eeprom.\r\n");
getchar();
i2c_read();
printf("readdatafromdeviceregisteraddress:0x%02X\r\n",i2c_tx_buf[0u]);
printf("readdata:");
for(uint32_ti=0u;i< i2c_rx_len; i++)
{
printf("0x%02X,",i2c_rx_buf[i]);
}
printf("\r\n");
}

pYYBAGKHWbGAeyxrAACu9-GW9Cs322.png

来源: 灵动MM32MCU

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

    关注

    146

    文章

    16949

    浏览量

    350112
  • I2C
    I2C
    +关注

    关注

    28

    文章

    1478

    浏览量

    123176
  • 灵动微
    +关注

    关注

    3

    文章

    174

    浏览量

    22649
  • MM32
    +关注

    关注

    1

    文章

    106

    浏览量

    740
收藏 人收藏

    评论

    相关推荐

    I2CMM32F032/MM32F0140系列MCU上的实现

    概述:I2C通讯接口,是我们日常应用中使用最多的MCU外设。最早在MCU没有硬件I2C之前,都是通过GPIO口模拟I2C的时序来完成
    发表于 09-22 14:22 731次阅读

    基于MM32F0140系列MCU实现UDS Bootloader的设计

    1、使用MM32F0140系列MCU实现UDS Bootloader  MM32F0140 使用高性能的 Arm®Cortex-M0 内核的 32 位微控制器,最高工作频率可达 72MHz,内置
    发表于 09-15 16:35

    灵动微MM32F0140可替换瑞萨单片机LPC11C14

    灵动微 超值型MM32F0140系列MCU是灵动第一款基于12寸晶圆打造的产品系列,其搭载72兆赫兹,Arm Cortex-M0内核,提供最高64KB Flash和8KB SRAM,并集成了性能升级
    发表于 01-12 15:18 707次阅读

    AN0052从MM32F0130移植到MM32F0140(英文版)

    AN0052 从MM32F0130移植到MM32F0140(英文版)
    发表于 02-22 18:43 0次下载
    AN0052从<b class='flag-5'>MM32F</b>0130移植到<b class='flag-5'>MM32F0140</b>(英文版)

    MM32F0140 产品手册(中文版)

    MM32F0140 产品手册(中文版)
    发表于 02-22 18:45 0次下载
    <b class='flag-5'>MM32F0140</b> 产品手册(中文版)

    MM32F0140 用户手册(英文版)

    MM32F0140 用户手册(英文版)
    发表于 02-22 18:46 0次下载
    <b class='flag-5'>MM32F0140</b> 用户手册(英文版)

    MM32F0140 勘误表(中文版)

    MM32F0140 勘误表(中文版)
    发表于 02-22 18:47 0次下载
    <b class='flag-5'>MM32F0140</b> 勘误表(中文版)

    MM32F0140 勘误表(英文版)

    MM32F0140 勘误表(英文版)
    发表于 02-22 18:48 0次下载
    <b class='flag-5'>MM32F0140</b> 勘误表(英文版)

    基于MM32F0140的UDS Bootloader学习笔记

    基于MM32F0140的UDS Bootloader学习笔记
    的头像 发表于 10-30 17:11 691次阅读
    基于<b class='flag-5'>MM32F0140</b>的UDS Bootloader学习笔记

    MM32F0140学习笔记——CRC

    MM32F0140学习笔记——CRC
    的头像 发表于 11-10 18:27 566次阅读
    <b class='flag-5'>MM32F0140</b>学习笔记——CRC

    MM32F0140 FlexCAN一致性测试 (2)

    MM32F0140 FlexCAN一致性测试 (2)
    的头像 发表于 11-10 18:23 634次阅读
    <b class='flag-5'>MM32F0140</b> FlexCAN一致性测试 (<b class='flag-5'>2</b>)

    MM32F0140 SPI学习笔记

    MM32F0140 SPI学习笔记
    的头像 发表于 09-26 16:51 563次阅读
    <b class='flag-5'>MM32F0140</b> SPI学习笔记

    MM32F0140 DMA学习笔记

    MM32F0140 DMA 学习笔记
    的头像 发表于 09-18 16:57 677次阅读
    <b class='flag-5'>MM32F0140</b> DMA学习笔记

    MM32F0140 UART学习笔记

    MM32F0140 UART学习笔记
    的头像 发表于 09-26 16:45 730次阅读
    <b class='flag-5'>MM32F0140</b> UART学习笔记

    MM32F0140 GPIO学习笔记

    MM32F0140 GPIO学习笔记
    的头像 发表于 09-26 16:42 521次阅读
    <b class='flag-5'>MM32F0140</b> GPIO学习笔记