zoukankan      html  css  js  c++  java
  • 「STM32 」IIC通讯原理及其实验

    I2C两线式串行总线通讯协议,它是由飞利浦开发的,主要用于连接微控制器及其外围设备之间,它是由数据线SDA和信号线SCL构成的,可发送和接收数据即在MUC和I2C设备之间,I2C和I2C之间进行全双工信号传输,高速I2C总线一般可达到400kbps。一般我们也称为TWI接口。

    I2C支持多主机模式:

    即在这个主线上可以挂载n个I2C外设。

    对于I2C协议,其实也很简单,不要想的那么复杂,其实就是电平的变换。我们可以人为的分为6个部分

    整体时序图:

    各状态:

    • 空闲状态

    I2C总线的SCK和SDA两个线同时处于高电平的时候,规定为总线的空闲状态,这个就是由总线上的上拉电阻把电平拉高的。

    • 起始信号

    当SCL为高电平期间,SDA由高电平变成低电平,即为起始信号。启动信号是一种电平跳变时序信号,不是一个电平信号。

    • 停止信号

    当SCL为高电平期间,SDA由低电平变为高电平,即为停止信号。停止信号也是一种电平跳变时序信号,不是一个电平信号。

    • 应答信号

    发送器每发送一个字节(8bit)数据,就在时钟脉冲(SCL)9期间释放数据线(SDA),再由接收器来反馈一个应答信号,应答信号为低电平的时候,规定为有效应答位(ACK:应答位),表明接收器已经成功的接收了该字节,应答信号为高电平时,规定为非应答位(NACK:非应答位),表示接收器没有成功的接收该字节。

    对于反馈有效应答位(ACK):接收器在第9个时钟脉冲之前的低电平期间将SDA拉低,并且保证在该时钟的高电平期间,SDA为稳定的低电平。大家主要看图,看看是不是这样的。

    要是接收器是主控器,那么它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据的发送,并且释放SDA线,以便主控接收器发送一个停止信号。

    • 数据的有效性

    时钟信号(SCL)为高电平期间,数据线上的数据必须保持稳定,只有在时钟信号(SCL)为低电平期间,数据线上的高电平或者低电平才能发生变化。

    数据必须在时钟信号(SCL)的上升沿到来之前就准备好,并且在数据信号的下降沿来到之前必须稳定。

    • 数据的传输

    在SDA上的每一个位的数据的传输都需要一个时钟脉冲,即在SCL串行时钟的配合下,SDA上逐位的串行发送每一位数据。数据位的传输是边沿触发。

    示例代码讲解

    • 初始化IIC

    其实就是对两个线的初始化,我这里使用的是PA6和PA7,开始都设置为输出,中途会改变PA7的输入输出属性,小平头,在空闲状态,我们知道SCL和SDA是被拉高的,所以这个地方我们给高电平。

    • 产生IIC起始信号

    将SDA线设置为输出,然后SDA和SCL都设置为高电平,延迟4us,然后将SDA拉低,延迟4us,最后将SCL拉低。这其实就是协议规定的动作了。

    • 产生IIC停止信号

    同样的道理,和协议的时序保持一致就好了。

    • 等待应答信号到来

    首先我们需要把SDA设置为输入,因为接收方要给发射方返回一个应答信号的。小平头 就是在SCL第9个高电平的时候,释放信号线,先拉高,然后持续等待,是不是有应答信号返回,其实就是返回一个低电平,所以我们一直在检测READ_SDA这个电平,持续一段时间,要是没有返回的话,我们认为超时了,就直接停止协议了,

    • 产生应答信号

    即在第9个时钟周期内,SDA都为低电平,为应答

    • 不产生应答信号

    即在第9个时钟周期内,SDA都为高电平,为不应答

    • IIC发送一个字节

    发送数据,SDA设置为输出。SCL拉低,SDA准备。

    做一个8次循环,拿出1byte的数据,将你发送的数据和0x80作与运算,拿出最高位,然后右移7位,将这个数据放到最低位,这个数据要是1的话,那么SDA输出一个高电平,要是与后的结果为低电平的话,那么SDA输出一个低电平。这都属于准备发送信号阶段。

    然后SCL拉高,完成数据的发送,然后SCL拉低,此时SDA也就可以拉低了,接着开始次高位的传输,直到传输完成。

    • 读取数据

    读取数据,SDA要设置为输入了。SCL开始为低电平,然后SCL为高电平,我们开始读SDA上的数据,然后左移数据,将读取的数据放在低位。然后检测一下有没有应答。

    其实基本思路就是这样了。我把源码附上:

    i2c.h

    #ifndef __MYIIC_H
    #define __MYIIC_H
    #include "sys.h"
    #include "stm32f10x_gpio.h"
    //IO方向设置
    #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
    #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
    //IO操作函数
    #define IIC_SCL PBout(6) //SCL
    #define IIC_SDA PBout(7) //SDA
    #define READ_SDA PBin(7) //输入SDA
    //IIC所有操作函数
    void IIC_Init(void); //初始化IIC的IO口
    void IIC_Start(void); //发送IIC开始信号
    void IIC_Stop(void); //发送IIC停止信号
    void IIC_Send_Byte(u8 txd); //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
    

      

     本文转自小平头电子技术社区,转载请注明出处:https://www.xiaopingtou.cn/article-104115.html

    i2c.c

    #include "myiic.h"
    #include "delay.h"
    //初始化IIC
    void IIC_Init(void)
    {					 
    	GPIO_InitTypeDef GPIO_InitStructure;
    	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );	//使能GPIOB时钟
    	 
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOB, &GPIO_InitStructure);
    	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); 	//PB6,PB7 输出高
    }
    //产生IIC起始信号
    void IIC_Start(void)
    {
    	SDA_OUT(); //sda线输出
    	IIC_SDA=1;	 	 
    	IIC_SCL=1;
    	delay_us(4);
     	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
    	delay_us(4);
    	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
    }	 
    //产生IIC停止信号
    void IIC_Stop(void)
    {
    	SDA_OUT();//sda线输出
    	IIC_SCL=0;
    	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
     	delay_us(4);
    	IIC_SCL=1; 
    	IIC_SDA=1;//发送I2C总线结束信号
    	delay_us(4);							 	
    }
    //等待应答信号到来
    //返回值:1,接收应答失败
    // 0,接收应答成功
    u8 IIC_Wait_Ack(void)
    {
    	u8 ucErrTime=0;
    	SDA_IN(); //SDA设置为输入 
    	IIC_SDA=1;delay_us(1);	 
    	IIC_SCL=1;delay_us(1);	 
    	while(READ_SDA)
    	{
    		ucErrTime++;
    		if(ucErrTime>250)
    		{
    			IIC_Stop();
    			return 1;
    		}
    	}
    	IIC_SCL=0;//时钟输出0 	 
    	return 0; 
    } 
    //产生ACK应答
    void IIC_Ack(void)
    {
    	IIC_SCL=0;
    	SDA_OUT();
    	IIC_SDA=0;
    	delay_us(2);
    	IIC_SCL=1;
    	delay_us(2);
    	IIC_SCL=0;
    }
    //不产生ACK应答		 
    void IIC_NAck(void)
    {
    	IIC_SCL=0;
    	SDA_OUT();
    	IIC_SDA=1;
    	delay_us(2);
    	IIC_SCL=1;
    	delay_us(2);
    	IIC_SCL=0;
    }					 				 
    //IIC发送一个字节
    //返回从机有无应答
    //1,有应答
    //0,无应答			 
    void IIC_Send_Byte(u8 txd)
    { 
     u8 t; 
    	SDA_OUT(); 	 
     IIC_SCL=0;//拉低时钟开始数据传输
     for(t=0;t<8;t++)
     { 
     //IIC_SDA=(txd&0x80)>>7;
    		if((txd&0x80)>>7)
    			IIC_SDA=1;
    		else
    			IIC_SDA=0;
    		txd<<=1; 	 
    		delay_us(2); 
    		IIC_SCL=1;
    		delay_us(2); 
    		IIC_SCL=0;	
    		delay_us(2);
     }	 
    } 	 
    //读1个字节,ack=1时,发送ACK,ack=0,发送nACK 
    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;
    }
    

      

     

     

  • 相关阅读:
    轻量级数据库sqlite的使用
    Integer引发的思考
    css限制显示行数
    数据库 chapter 17 数据仓库与联机分析处理技术
    数据库 chapter 15 对象关系数据库系统
    数据库 chapter 16 XML数据库
    数据库 chapter 14 分布式数据库系统
    数据库 chapter 11 并发控制
    数据库 chapter 12 数据库管理系统
    数据库 chapter 13 数据库技术新发展
  • 原文地址:https://www.cnblogs.com/cniot/p/11325324.html
Copyright © 2011-2022 走看看