首页 > 其他分享 >STM32IO口模拟IIC时序

STM32IO口模拟IIC时序

时间:2023-07-07 09:44:18浏览次数:57  
标签:SCL STM32IO 时序 从机 SDA IIC GPIO void

正点原子IIC讲解:https://www.bilibili.com/video/BV1o8411n7o9/?spm_id_from=333.337.search-card.all.click&vd_source=e35b16eeaf19ae2b23ff9587a735ee20

一、IIC总线

1. 物理层

(1)支持多设备,一个IIC通讯总线中可以连接多个IIC设备,支持多个通讯主机及多个通讯从机;
(2)两条线:双向串行数据线SDA,串行时钟线SCL,数据线用于传输数据,时钟线用于数据收发同步;
(3)每个连接到总线的设备都有一个独立的地址,主机通过这个地址对不同从机进行访问;
(4)总线通过上拉电阻接到电源,当IIC设备空闲时会输出高阻态。
(5)三种传输模式:标准模式(100kbit/s)、快速模式(400kbit/s)、高速模式(3.4Mbit/s)。
(6)连接相同总线的IC数量受总线最大电容400pf的限制。

2. 协议层

(1)起始信号:主机的IIC接口产生的传输,连接到IIC总线上的从机都会接收到这个信号。
(2)从机地址:起始信号产生后,所有从机开始等待主机广播从机地址信号,当广播地址与某个设备地址相同时,该设备就被选中,没有选择的设备会忽略之后的数据信号。IIC协议规定从机地址可以是7位或10位。
(3)传输方向位:地址位之后是传输方向选择位,该位为0时,表示后面数据传输方向是主机写数据到从机;该位为1时,主机读取从机数据。
(4)应答信号:
第一种,从机接收到匹配的地址,从机返回应答信号(ACK),表示找到对应的从设备;
第二种,当从机被寻址后,主机写数据到从机时,从机每收到一个字节的数据都需要给主机一个应答信号(ACK),主机只有接收到应答信号后才能继续发送数据。
(5)停止信号:发送停止信号结束通信。

S:起始信号
SLAVE ADDRESS:从机地址
R/W:传输方向,1读 0写
A:ACK表示应答信号
A非:NACK非应答信号,表示本次通信结束
P:停止信号

阴影部分为主机给从机发的,空白部分为从机给主机发的。

1、IIC主机写数据到从机

2、IIC主机由从机读取数据

3、IIC复合通信

4、IIC通信时序图

(1)起始信号、停止信号

起始信号:SDA、SCL均为高电平,首先处理器让SCL时钟保持高电平,然后让SDA数据信号由高变低就表示一个开始信号。同时IIC总线上的设备检测到这个开始信号就知道处理器要发送数据了,此时总线处于占用状态。

停止信号:处理器让SCL时钟保持高电平,然后让SDA数据信号由低变高就表示一个停止信号。同时IIC总线上的设备检测到这个停止信号就知道处理器已经结束了数据传输,此时总线处于空闲状态。

(2)数据格式/应答

字节从高位到低位传输。发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字
节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。

(3)通信

开始标志(S)发出后,主设备会传送一个7 位的从机地址,并且后面跟着一个第8位,称为Read/Write 位。R/W 位表示主设备是在接收从设备的数据还是在向从设备写数据。然后,主设备释放SDA线,等待从设备的应答信号(ACK)。每个字节的传输都要跟随有一个应答位,应答产生时(把SDA拉低)。从设备将SDA 线拉低并且在SCL 为高电平时保持低。数据传输总是以停止信号标志结束,然后释放通信线路。然而,主设备也可以产生重复的开始信号去操作另一台从设备,而不发出结束标志。综上可知,所有的SDA 信号变化都要在SCL 时钟为低电平时进行,除了开始和结束标志。

二、软件模拟IIC通信

iic.h


#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"

//IO方向设置
#define SDA_IN()  {GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=(u32)3<<28;}

#define IIC_SCL    PAout(4) //SCL
#define IIC_SDA    PAout(5) //输出SDA	 
#define READ_SDA   PAin(5)  //输入SDA 


//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);		   //发送IIC开始信号
void IIC_Stop(void);	  	   //发送IIC停止信号
void IIC_Send_Byte(u8 TX_DATA);		//IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 		   //IIC等待ACK信号
void IIC_Ack(void);		  //IIC发送ACK信号
void IIC_NAck(void);	         //IIC不发送ACK信号

void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);	  


#endif

iic.c

#include "myiic.h"
#include "delay.h"

/*
*@Function  :IIC_Init()
*@brief     :初始化IIC GPIO
*@param     :void
*@retval    : void
*/
void IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	//使能GPIOA时钟
	   
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5;//PA4---SCL PA5---SDA
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_OD ;      //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5); 	       //PA4,PA5 输出高
}

/*
*@Function  :IIC_Start()
*@brief     :产生IIC起始信号
*@param     :void
*@retval    : void
*/
void IIC_Start(void)
{
	SDA_OUT();  //sda线输出

	IIC_SDA=1; //处于空闲状态  	  
	IIC_SCL=1; //处于空闲状态
	delay_us(4);//延时时间看从机设备
 	IIC_SDA=0;//时钟线为高时数据线由高变低 
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	


/*
*@Function  :IIC_Stop()
*@brief     :产生IIC停止信号
*@param     :void
*@retval    : void
*/  
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出

	IIC_SCL=0;
	IIC_SDA=0;
 	delay_us(4);
        IIC_SCL=1;
	IIC_SDA=1;//时钟线为高,数据线由低变高
	delay_us(4);							   	
}

/*
*@Function  :IIC_Ack()
*@brief     :产生ACK应答
*@param     :void
*@retval    : void
*/ 
void IIC_Ack(void)
{
	SDA_OUT();//sda线输出
        IIC_SCL=0;
	IIC_SDA=0;//数据线为低电平表示应答
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}

/*
*@Function  :IIC_NAck()
*@brief     :不产生ACK应答
*@param     :void
*@retval    : void
*/ 		    
void IIC_NAck(void)
{
	SDA_OUT();//sda线输出
        IIC_SCL=0;
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}

/*
*@Function  :IIC_Wait_Ack()
*@brief     :等待应答信号
*@param     :1,接收应答失败    0,接收应答成功
*@retval    : void
*/
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN(); //SDA设置为输入  
	IIC_SDA=1;//释放sda线
        delay_us(1);	   
	IIC_SCL=1;//从机返回ack
        delay_us(1);	 
	while(READ_SDA) //scl为高电平时读取sda
	{
		ucErrTime++;
		if(ucErrTime>250) //超时判断
		{
			IIC_Stop();//sda高电平表示从机无应答
			return 1;//接收应答失败
		}
	}
	IIC_SCL=0;//时钟输出0,结束ack检查 	   
	return 0;  
} 

/*
*@Function  :IIC_Send_Byte()
*@brief     :IIC发送一个字节
*@param     :TX_DATA 要发送的数据
*@retval    : void
*/ 					 				     	  
void IIC_Send_Byte(u8 TX_DATA)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=(TX_DATA&0x80)>>7;//先发高位,与运算取出最高位,移到最后一位
		if((TX_DATA&0x80)>>7)
			IIC_SDA=1;
		else
			IIC_SDA=0;
		TX_DATA <<=1; //左移一位用于下一位发送,最高位已经发送出去	  
		delay_us(2);  
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
}


/*
*@Function  :IIC_Read_Byte()
*@brief     :IIC读1个字节
*@param     :ack=1时,发送ACK,ack=0,发送nACK
*@retval    : 读取的数据
*/ 
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;//先接收的高位,需要将接收的数据左移一位
        if(READ_SDA) receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

标签:SCL,STM32IO,时序,从机,SDA,IIC,GPIO,void
From: https://www.cnblogs.com/xiaohuzaixue/p/17527437.html

相关文章

  • 看懂uml图和时序图
    继承与泛化继承关系:isa关系,两个对象之间可以用isa表示,就是继承关系。泛化:继承非抽象类,带空心箭头的实现表示。实现:继承抽象类,带空心箭头虚线表示。聚合与组合聚合:整体与部分的弱依赖关系,带空心菱形箭头的直线表示。组合:整体与部分的强依赖关系,带实心菱形箭头的直线表示......
  • 软件IIC SDA输入输出
    SDA输入输出模式改变方式一  SCL线可以配置推挽输出,开漏输出(有上拉电压存在)都可,因为不用兼具输入扫描功能;SDA线必须配置开漏输出,电路上要外加上拉电阻,因为要兼具输入扫描功能, 方式二 对应的gpio口配置代码的改变#defineSCCB_SDA_IN(){GPIOG->CRH&=0X......
  • 深入浅出时序数据库之预处理篇——批处理和流处理,用户可定制,但目前流行influxdb没有做
    时序数据是一个写多读少的场景,对时序数据库以及数据存储方面做了论述,数据查询和聚合运算同样是时序数据库必不可少的功能之一。如何支持在秒级对上亿数据的查询分组聚合运算成为了时序数据库产品必须要面对的挑战。 本文会从时序数据库的查询以及聚合运算角度展开,最后会从如何解决......
  • 时序数据库深入浅出之存储篇——本质LSMtree,同时 metric(比如温度)+tags 分片
    什么是时序数据库先来介绍什么是时序数据。时序数据是基于时间的一系列的数据。在有时间的坐标中将这些数据点连成线,往过去看可以做成多纬度报表,揭示其趋势性、规律性、异常性;往未来看可以做大数据分析,机器学习,实现预测和预警。时序数据库就是存放时序数据的数据库,并且需要支持时序......
  • 时序数据库 TDengine 与 DBeaver 达成合作,生态系统再壮大
    众所周知,DBeaver是一个流行的开源数据库管理和SQL客户端工具,为管理和使用各种类型的数据库(包括多个时序数据库)提供强大而灵活的平台。为了让大家在应用上更加便捷,我们与DBeaver达成合作,新发布的DBeaver23.1.1版本正式支持时序数据库(TimeSeriesDatabase)TDengine和全托......
  • 用友:时序数据库要更懂业务场景
    本文来自IT168作者卢敏 时序数据库是针对时间戳或时间序列数据优化的数据库。比如工业企业为了管好工业设备,需要用传感器收集一些带有时间标签的数据,这些数据既要求“超大规模数据瞬时写入”,又要求实现乱序管理。用友网络助理总裁何冠宇表示,时序数据库的技术发展与上层应用的......
  • CNN GRU 注意力 时序预测 基于加注意力机制(CNN-GRU-Attention)的时间序列预测程序,预测
    CNNGRU注意力时序预测基于加注意力机制(CNN-GRU-Attention)的时间序列预测程序,预测精度很高。可用于做风电功率预测,电力负荷预测,交通预测,负荷预测,经济预测,排放预测等标记注释清楚,可直接换数据运行。代码实现训练与测试精度分析。原创文章,转载请说明出处,资料来源:http://imgcs.......
  • RT-Thread 正点原子阿波罗STM32F429IGT6-软件IIC控制I/O扩展模块PCF8574T(踩坑)
    第一步:在RT-ThreadSettings中打开I2C设备驱动,Ctrl+S保存 第二步:在drivers->board.h中进行配置,取消BSP_USING_I2C2的注释,并根据说明定义好引脚; 第三步:对引脚进行初始化,这里可使用CubeMX进行生成; 第四步:根据设备名查找设备 第五步:调用 rt_i2c_transfer 发......
  • 时序数据库 TDengine 与腾讯云多个产品线完成兼容性互认证明
    随着数字经济蓬勃发展,数据成为驱动企业数字化转型的关键生产要素,如何加强对数据资源的治理利用、实现数据洞察、激活数据价值正成为亟待解决的问题。在此背景下,数据库与操作系统、云平台等国产化软件相互结合赋能成为解决问题的思路之一。近日,经过数月努力,涛思数据旗下时序数据库......
  • 31 IIC(九)iic adapter
    代码1iicadapter驱动架构i2cadapter设备是挂载于platformbus整体重点架构如下分配structi2c_adapter*adap=kzalloc(sizeof(structi2c_adapter),GFP_KERNEL);设置adapter->owner=THIS_MODULE;adapter->algo=&i2c_algo;注册ret=i2c_add_adapter(a......