当前位置:首页 > 单片机 > 单片机
[导读]I2C总线是由NXP(原PHILIPS)公司设计,有十分简洁的物理层定义,其特性如下:只要求两条总线线路:一条串行数据线SDA,一条串行时钟线SCL;每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系

I2C总线是由NXP(原PHILIPS)公司设计,有十分简洁的物理层定义,其特性如下:

只要求两条总线线路:一条串行数据线SDA,一条串行时钟线SCL;

每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系软件设定地址,主机可以作为主机发送器或主机接收器;

它是一个真正的多主机总线,如果两个或更多主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏;

串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s;

连接到相同总线的IC 数量只受到总线的最大电容400pF 限制。

其典型的接口连线如下:

I2C的协议很简单:

数据的有效性

  在传输数据的时候,SDA线必须在时钟的高电平周期保持稳定,SDA的高或低电平状态只有在SCL 线的时钟信号是低电平时才能改变 。

起始和停止条件

  SCL 线是高电平时,SDA 线从高电平向低电平切换,这个情况表示起始条件;

  SCL 线是高电平时,SDA 线由低电平向高电平切换,这个情况表示停止条件。

字节格式

  发送到SDA 线上的每个字节必须为8 位,每次传输可以发送的字节数量不受限制。每个字节后必须处理一个响应位。

应答响应   

数据传输必须带响应,相关的响应时钟脉冲由主机产生。在响应的时钟脉冲期间发送器释放SDA 线(高)。   

在响应的时钟脉冲期间,接收器必须将SDA 线拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。

也就是说主器件发送完一字节数据后要接收一个应答位(低电平),从器件接收完一个字节后要发送一个低电平。

寻址方式(7位地址方式)

  第一个字节的头7 位组成了从机地址,最低位(LSB)是第8 位,它决定了传输的 普通的和带重复开始条件的7位地址格式方向。第一个字节的最低位是

“0”,表示主机会写信息到被选中的从机;

“1”表示主机会向从机读信息。

当发送了一个地址后,系统中的每个器件都在起始条件后将头7 位与它自己的地址比较,如果一样,器件会判定它被主机寻址,至于是从机接收器还是从机发送器,都由R/W 位决定。

仲裁

I2C是所主机总线,每个设备都可以成为主机,但任一时刻只能有一个主机。

stm32至少有一个I2C接口,提供多主机功能,可以实现所有I2C总线的时序、协议、仲裁和定时功能,支持标准和快速传输两种模式,同时与SMBus 2.0兼容。


本实验直接操作寄存器实现对I2C总线结构的EEPROM AT24c02的写入和读取。AT24c02相关操作详见单片机读取EEPROM(AT24C02)。

库函数实现使用stm32的两个I2C模拟I2C设备间的数据收发,并通过串口查看数据交换情况。

直接操作寄存器

首先需要配置I2C接口的时钟,相关寄存器如下:

I2C_CR2寄存器低五位:

FREQ[5:0]:I2C模块时钟频率 ,必须设置正确的输入时钟频率以产生正确的时序,允许的范围在2~36MHz之间:

000000:禁用 000001:禁用 000010:2MHz ... 100100:36MHz 大于100100:禁用。

用于设置I2C设备的输入时钟,本例使用的是PLCK1总线上的时钟所以为36Mhz;

时钟控制寄存器(I2C_CCR)低12位:

CCR[11:0]:快速/标准模式下的时钟控制分频系数(主模式),该分频系数用于设置主模式下的SCL时钟。

在I2C标准模式或SMBus模式下:

Thigh = CCR ×TPCLK1

Tlow = CCR ×TPCLK1

时钟周期为 T = Thigh + Tlow;

例如:在标准模式下,FREQR = 36 即36Mhz,产生200kHz的SCL的频率

时钟控制分频系数 = Freqr /2/f f 为想得到的频率

配置好时钟,还需要配置本机地址,I2C支持7位地址和10位地址,这里用的是7位地址:

自身地址寄存器1(I2C_OAR1)[7:1]:接口地址,地址的7~1位。

其他相关操作参见代码,有详细注释:(system.h 和stm32f10x_it.h等相关代码参照stm32 直接操作寄存器开发环境配置)

User/main.c

#include#include"system.h"#include"usart.h"#include"i2c.h"#defineLED1PAout(4)#defineLED2PAout(5)#defineLED3PAout(6)#defineLED4PAout(7)voidGpio_Init(void);intmain(void){Rcc_Init(9);//系统时钟设置Usart1_Init(72,9600);Nvic_Init(1,0,I2C1_EV_IRQChannel,4);//设置抢占优先级为1,响应优先级为0,中断分组为4Nvic_Init(0,0,I2C1_ER_IRQChannel,4);//设置I2C错误中断抢占优先级为0Gpio_Init();I2c_Init(0x30);//设置I2C1地址为0x30I2c_Start();while(1);}voidGpio_Init(void){RCC->APB2ENR|=1<<2;//使能PORTA时钟RCC->APB2ENR|=1<<3;//使能PORTB时钟;GPIOA->CRL&=0x0000FFFF;//PA0~3设置为浮空输入,PA4~7设置为推挽输出GPIOA->CRL|=0x33334444;GPIOB->CRL&=0x00FFFFFF;//PB6I2C1_SCL,PB7I2C1_SDLGPIOB->CRL|=0xFF000000;//复用开漏输出//USART1串口I/O设置GPIOA->CRH&=0xFFFFF00F;//设置USART1的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入GPIOA->CRH|=0x000008B0;}

User/stm32f10x_it.c

#include"stm32f10x_it.h"#include"system.h"#include"stdio.h"#include"i2c.h"#defineLED1PAout(4)#defineLED2PAout(5)#defineLED3PAout(6)#defineLED4PAout(7)#defineADDRS_R0xA1//读操作地址#defineADDRS_W0xA0//写操作地址u8go=0;//操作步骤标记voidI2C1_EV_IRQHandler(void)//I2C1EventInterrupt{u16clear=0;if(I2C1->SR1&1<<0)//已发送起始条件,写数据寄存器的操作将清除该位{printf("rnI2C1Start..rn");switch(go){case0:{I2c_Write(ADDRS_W);//写入从机地址,写指令操作地址break;}case1:{I2c_Write(ADDRS_W);//写入从机地址,写指令操作地址break;}case2:{I2c_Write(ADDRS_R);//写入从机地址,读数据操作地址break;}}}if(I2C1->SR1&1<<1)//从机地址已发送{printf("rnI2C1hassendaddress..rn");clear=I2C1->SR2;//读取SR2可以清除该位中断switch(go){case0:{I2c_Write(0x01);//写入待写入的EEPROM单元地址break;}case1:{I2c_Write(0x01);//写入待写入的EEPROM单元地址break;}case2:{delay(100000);printf("rnRead0x%XfromAt24c02,Address0x01..rn",I2c_Read());I2c_Stop();break;}}}if(I2C1->SR1&1<<2)//字节发送结束发送地址字节时,不触发此中断{//printf("rnI2C1sendbytesuccess..rn");switch(go){case0:{I2c_Write(0x86);//写入数据printf("rnWrite0x%XtoAt24c02,Address0x01..rn",0x86);//I2c_Stop();delay(10000);go=1;I2c_Start();break;}case1:{delay(10000);go=2;I2c_Start();break;}case2:{break;}}}delay(100000);LED3=1;//I2C1->CR2&=~(1<<9);//事件中断关闭}voidI2C1_ER_IRQHandler(void)//I2C1ErrorInterrupt{delay(100000);LED4=1;if(I2C1->SR1&1<<10)//应答失败{printf("rnACKERROR..rn");I2C1->SR1&=~(1<<10);//清除中断}if(I2C1->SR1&1<<14)//超时{printf("rnTimeout..rn");I2C1->SR1&=~(1<<14);//清除中断}if(I2C1->SR1&1<<11)//过载/欠载{printf("rnOverrun/Underrun..rn");I2C1->SR1&=~(1<<11);//清除中断}if(I2C1->SR1&1<<9)//仲裁丢失{printf("rnArbitrationlost..rn");I2C1->SR1&=~(1<<9);//清除中断}if(I2C1->SR1&1<<8)//总线出错{printf("rnBuserror..rn");I2C1->SR1&=~(1<<8);//清除中断}}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭