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
20 Nvic_Init(1,0,I2C1_EV_IRQChannel,4); //设置抢占优先级为1,响应优先级为0,中断分组为4
22 Nvic_Init(0,0,I2C1_ER_IRQChannel,4); //设置I2C错误中断抢占优先级为0
26 I2c_Init(0x30); //设置I2C1地址为0x30
36 RCC->APB2ENR |= 1<<2; //使能PORTA时钟
37 RCC->APB2ENR |= 1<<3; //使能PORTB时钟;
40 GPIOA->CRL &= 0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
41 GPIOA->CRL |= 0x33334444;
44 GPIOB->CRL &= 0x00FFFFFF; //PB6 I2C1_SCL ,PB7 I2C1_SDL
45 GPIOB->CRL |= 0xFF000000; //复用开漏输出
49 GPIOA -> CRH &= 0xFFFFF00F; //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
50 GPIOA -> CRH |= 0x000008B0;
User/stm32f10x_it.c
001 #include "stm32f10x_it.h"
011 #define ADDRS_R 0xA1 //读操作地址
012 #define ADDRS_W 0xA0 //写操作地址
016 void I2C1_EV_IRQHandler(void) //I2C1 Event Interrupt
020 if(I2C1 -> SR1 & 1<<0 ) //已发送起始条件,写数据寄存器的操作将清除该位
022 printf("rn I2C1 Start .. rn");
027 I2c_Write(ADDRS_W); //写入从机地址,写指令操作地址
031 I2c_Write(ADDRS_W); //写入从机地址,写指令操作地址
035 I2c_Write(ADDRS_R); //写入从机地址,读数据操作地址
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。 推荐阅读
1、STM32CubeMX 简介说明STM32CubeMX 是 ST 意法半导体近几年来大力推荐的STM32 芯片图形化配置工具,允许用户使用图形化向导生成C 初始化代码,可以大大减轻开发工作,时间和费用。STM32CubeMX几乎覆盖了STM32 全系列芯片。它具有如下特性:1)直观的选择MCU 型号,可指定系列、封装、外设数量等条件;2)微控制器图形化配置;3)自动处理引脚冲突;4)动态设置时钟树,生成系统时钟配置代码;5)可以动态设置外围和中间件模式和初始化;6)功耗预测;7)C 代码工程生成器覆盖了STM32 微控制器初始化编译软件,如IAR, KEIL,GCC;8)可以独立使用或者作为Eclipse 插件使用
发表于 2019-04-01
1、Code即代码域,它指的是编译器生成的机器指令,这些内容被存储到ROM区。2、RO-dataRead Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在ROM区,因而程序不能修改其内容。例如:C语言中const关键字定义的变量就是典型的RO-data。3、RW-dataRead Write data,即可读写数据域,它指初始化为“非0值”的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。例如:C语言中使用定义的全局变量,且定义时赋予“非0值”给该变量进行初始化。4、ZI-dataZero Initialie data,即0初始化数据
发表于 2019-04-01
本身的长度,比如int类型就能超过32位二进位。3)位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: struct k { int a:1 int :2 /*该2位不能使用*/ int b:3 int c:2 }; 从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。  
发表于 2019-04-01
1、STM32 启动文件与 .sct 文件分析1) 定义STACK段,{NOINIT,读写}:分配一段内存大小为0.5K;2) 定义HEAP段, {NOINIT,读写}:分配一段内存大小为1K;3) 定义RESET段,{DATA,只读}:DCD各种中断向量;4) 定义|.text|段,{CODE,只读}:Reset_Handler函数,函数中最后加载了__main;对剩余的中断函数进行了弱定义;在最后还有一段用户初始化堆栈的代码__user_initial_stackheap。那这些代码都存放在什么位置呢?5) 分析 .sct 文件:分散加载文件(即scatter file,后缀为.scf)。分散加载文件是一个文本文件,通过编写
发表于 2019-04-01
了。startup.s 这个文件并没有所谓的"段拷贝"功能。对含有启动程序来说,"执行地址与加载地址相同"不容易实现:如果执行地址与加载地址相同哪当然不需要做"段拷贝",但是个人理解编译器还会加入"段拷贝"程序(如果用B __main 的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,"执行地址与加载地址相同"就不容易了.因为启动程序是要烧到非易失存储器里,用来在上电执行的,而这个程序必定会有RW段,如果RW放在非易失存储器,如FLASH,那就不好实现RW功能了,因此要给RW移动到能够实现RW功能的存储器,如SRAM
发表于 2019-04-01
__main就好了;c),虽然通过读标准的CMSIS汇编启动代码也可以知道堆栈是怎样初始化的。以LPC54608为例,早期的芯片会有不同的处理方式,比如大家都很熟悉的STM32F103系列,是需要启动代码与分散加载配合完成的。3.1、异常&中断向量表之前我们说这个可以用数组来实现,普通数组肯定不行,因为这些向量的本质是中断服务函数的入口,也就是“函数指针”所以这个数组必须得是函数指针数组:所以我们先声明一个函数指针类型:typedef void ( *__vector )( void );然后定义一个函数指针型数组取名为__vector_table:__vector __vector_table[] = {};  
发表于 2019-04-01