STM32 IIC详解

1、IIC定义

IIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司(后被NXP收购)在八十年代初设计出来的一种简单、双向、二线制、同步串行总线,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。多主多从的通讯协议。

下文将结合NXP官方的IIC手册讲解IIC协议。下载链接见文末。

I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。速率最高400Kbit/s。

在1998年的修订中增加了高速模式,速率高达3.4Mbit/s。(这里不讲,只说快速模式)。

2、IIC协议规范

2.1 SDA和SCL信号

连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能,当总线空

闲时这两条线路都是高电平。SDA 和SCL 都是双向线路都通过一个电流源或上拉电阻连接到正的电源电压。一般情况下我们都采用上拉电阻的方式

2.2 数据有效性

在SCL高电平的时候采样,也就是有效。低电平的时候切换数据

2.3 开始和结束信号

起始条件:SCL线是高电平时,SDA线从高电平向低电平切换。

停止条件:SCL线是高电平时,SDA线从低电平向高电平切换。

动画展示启动信号

代码实现

1void I2C_Start(void) 2{ 3 //IO输出 4 SDA_OUT(); 5 SCL_OUT(); 6 I2C_DELAY(); 7 //IO置高 8 SDA_SET(); 9 SCL_SET(); 10 //延时 11 I2C_DELAY(); 12 //为低 13 SDA_CLR(); 14 I2C_DELAY(); 15 I2C_DELAY(); 16 SCL_CLR(); 17}

结束信号时类似的方式(不是动图)

代码实现

1void I2C_Stop(void) 2{ 3 //IO输出 4 SDA_OUT(); 5 SCL_OUT(); 6 //IO置0 7 SDA_CLR(); 8 SCL_CLR(); 9 I2C_DELAY(); 10 SCL_SET(); 11 //延时 12 I2C_DELAY(); 13 I2C_DELAY(); 14 I2C_DELAY(); 15 //SDA置1 16 SDA_SET(); 17 I2C_DELAY(); 18 I2C_DELAY(); 19}

2.4 字节格式

SDA数据线上的每个字节必须是8位,每次传输的字节数量没有限制。每个字节后必须跟一个响应位(ACK)。首先传输的数据是最高位(MSB),SDA上的数据必须在SCL高电平周期时保持稳定,数据的高低电平翻转变化发生在SCL低电平时期。

每一个字节后面跟着一个ACK,有ACK就可以继续写或读。NACK,就停止

ACK:主机释放总线,传输完字节最后1位后的SCL的高电处,从机拉低电平。

NACK:主机释放总线,传输完字节最后1位后的SCL的高电处,从机无响应,总线为高电平。

动画描述写入的过程

代码实现

1uint8_t I2C_Send_byte(uint8_t data) 2{ 3 uint8_t k; 4 //发送8bit数据 5 for(k=0;k<8;k++){ 6 7 I2C_DELAY(); 8 if(data&0x80){ 9 SDA_SET(); 10 } 11 else{ 12 SDA_CLR(); 13 } 14 data=data<<1; 15 I2C_DELAY(); 16 SCL_SET(); 17 I2C_DELAY(); 18 I2C_DELAY(); 19 SCL_CLR(); 20 } 21 //延时读取ACK响应 22 I2C_DELAY(); 23 SDA_SET(); 24 //置为输入线 25 SDA_IN(); 26 I2C_DELAY(); 27 SCL_SET(); 28 I2C_DELAY(); //这里出现了问题,延时变的无限大 29 //读数据 30 k=SDA_READ(); 31 if(k){ NACK响应 32 return 0; 33 } 34 I2C_DELAY(); 35 SCL_CLR(); 36 I2C_DELAY(); 37 SDA_OUT(); 38 if(k){ NACK响应 39 return 0; 40 } 41 return 1; 42} 43 44uint8_t I2C_Receive_byte(uint8_t flg) 45{ 46 uint8_t k,data; 47 //接收8bit数据 48 //置为输入线 49 50 SDA_IN(); 51 data=0; 52 for(k=0;k<8;k++){ 53 I2C_DELAY(); 54 SCL_SET(); 55 I2C_DELAY(); 56 //读数据 57 data=data |SDA_READ(); 58 data=data<<1; 59 I2C_DELAY(); 60 SCL_CLR(); 61 I2C_DELAY(); 62 } 63 data=data>>1; //往回移动1次 64 //返回ACK响应 65 //置为输出线 66 SDA_OUT(); 67 if(flg){ 68 SDA_SET(); //输出1-NACK 69 }else{ 70 SDA_CLR();//输出0-ACK 71 } 72 I2C_DELAY(); 73 SCL_SET(); 74 I2C_DELAY(); 75 I2C_DELAY(); 76 SCL_CLR(); 77 I2C_DELAY(); 78 SDA_OUT(); 79 //返回读取的数据 80 return (uint8_t)data; 81}

2.5 从机地址和读写位

开始信号—>地址—>读写位—>ACK—>数据—>ACK.............—>停止位

这里只说7位地址,前7位为地址,最后一位为读写位,1表示读操作,0表示写操作

主机发给从机数据,也就是写,没有数据转向时

主机立即读从机数据,从第一个字节

(Combined)综合数据格式

3、计算IIC的频率

通信频率由主机掌控,也就是代码中的延时函数决定的

从上面,我们得知最高速为400Kbit/s。我们设计300Kbit/s。

速率300Kbit/s,对应周期1/300ms=10/3us≈3us,4分频就是3/4us。

我们使用的延时是,1/120MHZ*3*30 =3/4us

也就是频率是300Kbit/s

和SPI类似,时钟下降沿时,数据转换,时钟上升沿时,采样数据。也就是时钟高电平数据有效。

1/*120Mhz时钟时,当ulCount为1时,函数耗时3个时钟,延时=3*1/120us=1/40us*/ 2/* 3SystemCoreClock=120000000 4us级延时,延时n微秒 5SysCtlDelay(n*(SystemCoreClock/3000000)); 6ms级延时,延时n毫秒 7SysCtlDelay(n*(SystemCoreClock/3000)); 8m级延时,延时n秒 9SysCtlDelay(n*(SystemCoreClock/3)); 10*/ 11 12#if defined (__CC_ARM) /*!< ARM Compiler */ 13__asm void 14SysCtlDelay(unsigned long ulCount) 15{ 16 subs r0, #1; 17 bne SysCtlDelay; 18 bx lr; 19} 20#elif defined ( __ICCARM__ ) /*!< IAR Compiler */ 21void 22SysCtlDelay(unsigned long ulCount) 23{ 24 __asm(" subs r0, #1\n" 25 " bne.n SysCtlDelay\n" 26 " bx lr"); 27} 28 29#elif defined (__GNUC__) /*!< GNU Compiler */ 30void __attribute__((naked)) 31SysCtlDelay(unsigned long ulCount) 32{ 33 __asm(" subs r0, #1\n" 34 " bne SysCtlDelay\n" 35 " bx lr"); 36} 37 38#elif defined (__TASKING__) /*!< TASKING Compiler */ 39/*无*/ 40#endif /* __CC_ARM */ 41 42 43/* 44 * @brief SysCtlDelay 45 * @param ulCount 延时值,必须大于0 46 * @retval None 47 */ 48void SysCtlDelay_IIC(unsigned long ulCount) 49{ 50 SysCtlDelay(ulCount); 51} 52 53 54/定义时钟频率,300KHz 55#define I2C_DELAY() SysCtlDelay_IIC(30)

4、PCF8536

4.1 Acknowledge

这个地方能看到关于2.4节关于ACK和NACk的说明

4.2 Addressing

这里直接给出读地址和写地址,也就是最后一位的区别

4.3 读写时序

其实就是按照IIC协议的

读指定器件的指定寄存器

主机设置完寄存器地址之后,再去读写

注意:读取多个字节,最后一个字节的回包应该是NACK

主机立即从机第一个字节读取

注意:读取多个字节,最后一个字节的回包应该是NACK

开源地址:

https://github.com/strongercjd/STM32F207VCT6

点击查看本文所在的专辑,STM32F207教程

资料下载:

https://pan.baidu.com/s/1lRMxv2EnHRUtugIZloMrxg  提取码:npz0

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )