"STM32" IIC communication theory and experiment

I2C two-wire serial bus protocol, which is developed by Philips, is mainly used for the connection between the microcontroller and its peripheral equipment, which is a signal from the data line SDA and SCL line configuration, i.e., may transmit and receive data between MUC and I2C devices, full-duplex signal transmission between I2C and I2C, can achieve high speed ships I2C bus 400kbps. Generally, we also called the TWI.

I2C supports multi-master mode:

I.e. the main line can mount the n I2C peripherals.

For the I2C protocol, in fact, very simple, I do not think so complex, in fact, the level of transformation. We can artificially divided into six parts

Overall timing diagram:

Each state:

  • Idle

SCK I2C bus and the SDA line while the two at a high level when a predetermined idle state of the bus, this is pulled up by the resistor on the bus of the level pulling.

  • Start signal

When the SCL is high, the SDA from the high into low level, and is the start signal. Level enable signal is a timing signal transition, not a level signal.

  • Stop signal

When the SCL is high, the SDA from low to high, that is, the stop signal. Stop signal is also a level transition timing signal, not a signal level.

  • Answer signal

The transmitter transmits each byte (8bit) data, it releases the data line (SDA) during clock (SCL) 9, and then fed back by a receiver a response signal, the response signal is low when the predetermined valid acknowledge bit (ACK: acknowledge bit), indicating that the receiver has successfully received the byte, a response signal is high, a predetermined non-acknowledge bit (NACK: non acknowledgment bit) indicating the receiver does not successfully receive the word section.

For effective feedback acknowledge bit (ACK): the receiver during a low level before the 9th clock pulse the SDA is pulled low, and to ensure a high level of the clock period, SDA is stable low. We mainly look at pictures, see if it is the case.

If the receiver is a master, then after it receives the last byte, transmitting a NACK signal to inform him of the transmitter end of transmission data, and releases the SDA line to the master receiver sends a stop signal.

  • The validity of data

The clock signal (SCL) is high level period, the data line must remain stable only when the clock signal (SCL) low during the high period, the data line to a low level or changes.

数据必须在时钟信号(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;
}

  

 

 

Guess you like

Origin www.cnblogs.com/cniot/p/11325324.html