10.STM32F40x IIC介绍及代码配置

一、IIC协议的介绍

(1)IIC总线(Inter Integrated Circuit Bus):是Philips公司推出的串行总线标准(为二线制)。总线上扩展的外围器件及外设接口通过总线寻址,是具备总线仲裁和高低速设备同步等功能的高性能多主机总线。

(2)数据线—SDA   时钟线—SCL

(3)特性:

半双工同步串行通信总线--一问一答

仲裁----主从模式----任何信息都需要主机主动控制

从属设备地址----器件地址,通过地址去找到对应发送数据的设备

(4)IIC:半双工一问一答、一次只能发送1个位数据,发送一个数据包(连续发送多个位-8位)

二、IIC总线工作原理

10.STM32F40x IIC介绍及代码配置_第1张图片

IIC速度慢,IO口就能达到数据传输要求。

结论:IIC的通信速度是比较慢的。

  在STM32中,一般都是使用IO模拟IIC通信,而不使用它的硬件IIC.

三、IIC时序图

10.STM32F40x IIC介绍及代码配置_第2张图片

10.STM32F40x IIC介绍及代码配置_第3张图片

IIC总时序图

(1)空闲状态:SDA 和SCL都为高电平。

(2)起始位(起始条件、起始信号):在SCL为高电平期间,SDA由高变为低电平。

(3)停止位(停止条件、停止信号):在SCL为高电平期间,SDA由低变为高电平。

(4)应答信号:在IIC通信中,每发送或者接收到一个字节后,都会有一个应答信号(非应答信号)

(5)发送过程(发数据):在第9个时钟周期,SCL为高电平期间,读取SDA的电平(低à应答,高à非应答)。

四、STM32F40x IIC框图

10.STM32F40x IIC介绍及代码配置_第4张图片

五、IIC起始信号时序介绍

     起始信号用于开始I2C总线通信。其中,起始信号是在时钟线SCL为高电平期间,数据SDA上高电平向低电平变化的下降沿信号。起始信号出现以后,才可以进行后续的I2C总线寻址或数据传输等。起始信号时序图如下:

  

10.STM32F40x IIC介绍及代码配置_第5张图片

配置代码如下:

//产生IIC起始信号

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总线,准备发送或接收数据
}

六、终止信号时序介绍

终止信号用于终止I2C总线通信。其中,终止信号是在时钟线SCL为高电平期间,数据线SDA上低电平到高电平变化的上升沿信号。终止信号一出现,所有I2C总线操作都结束,并释放总线控制权。终止信号的时序图如下:

10.STM32F40x IIC介绍及代码配置_第6张图片

配置代码如下:

//产生IIC终止信号

void IIC_Stop(void){
    SDA_OUT();//sda线输出
    IIC_SCL=0;
    IIC_SDA=0;
    delay_us(4);
    IIC_SCL=1;
    IIC_SDA=1; //发送I2C总线结束信号
    delay_us(4);
}

七、应答信号时序介绍

应答信号用于表明I2C总线数据传输的结束。 I2C总线数据传送时,一个字节数据传送完毕后都必须由主器件产生应答信号。主器件在第9个时钟位上释放数据总线SDA,使其处于高电平状态,此时从器件输出低电平拉低数据总线SDA为应答信号。应答信号的时序图如下:

10.STM32F40x IIC介绍及代码配置_第7张图片

配置代码如下:

//等待应答
unsigned char IIC_Wait_ACK(void){
    unsigned char ret;  //用于返回值,存储是否应答的变量
    IIC_SCL = 1;  //SCL拉高电平
	IIC_SDA = 1;  //SDA拉高电平
    delay_us(5);
      //读取 SDA 数据线的状态。
    if (SDA_READ() == 0) {   
        ret = 0; // 返回应答
    }
    else{
        ret = 1;   // 返回非应答
}

//产生ACK应答/非应答

void IIC_ACK_NACK(unsigned char ack){
    IIC_SCL = 0;  //SCL拉低电平
	delay_us(5);  
        // 判断发送应答
    if (ack == 0)   {
        IIC_SDA = 0; // 应答
    }
    else {
        IIC_SDA = 1; // 非应答
    }

                                                                                                                                      

八、IIC写操作

写操作分为字节写和页面写两种操作:在字节写模式下,主器件发送起始命令和从器件地址信息 R/W位置零给从器件,在从器件产生应答信号后,主器件发送16的字节地址,主器件在收到从器件的另一个应答信号后,再发送数据到被寻址的存储单元再次应答,并在主器件产生停止信号后,开始内部数据的擦写,在内部擦写过程中从器件不再应答主器件的任何请求。写操作代码如下:

//写操作
void IIC_WriteByte(unsigned char data){
    u8 i = 0;
    //循环发送8位数据
    for (i = 0; i < 8; i++) {
        IIC_SCL = 0;  //SCL拉低电平
            //判断高位先发
        if (data & 0x80)  {
            IIC_SDA = 1;  //发送高位数据
        }
        else{
            IIC_SDA = 0;  // 发送低位数据
        }
        data = data << 1;  //移位数据
        delay_us(5);
        IIC_SCL = 1;   //SCL拉高时钟
        delay_us(5);
    }
    IIC_SCL = 0;  //拉低时钟
    delay_us(1);
}

九、IIC读操作

    有三种基本操作:当前地址读、随机读和顺序读。下图给出的是顺序读的时序图。应当注意的是,为了结束读操作,主机必须在第9个周期间发出停止条件或者在第9个时钟周期内保持SDA为高电平,然后发出停止条件。 配置代码如下:

//读操作
unsigned char IIC_ReadByte(unsigned char ack){

	unsigned char i = 0, data = 0;
    
    // 循环接收数据
    for (i = 0; i < 8; i++) {

        IIC_SCL = 0; // 拉低时钟SCL等待接收数据
        delay_us(5);
        IIC_SCL = 1; // 延时等待数据线的改变
        delay_us(5);
        data <<= 1; // 拉高时钟读取数据
        if (SDA_READ() == 1) {

            data |= 0x01; // 判断读到的数据并保存
        }
       delay_us(5);
       IIC_SCL = 0; // 延时函数
       delay_us(5);
    }
     
    IIC_SCL = 0;  // 拉低时钟SCL
    delay_us(5); 
    IIC_ACK_NACK(ack);  // 发送给从机应答或非应答
    
    return data;
}

十、IIC函数的头文件

(1)myiic.c

#include "myiic.h"
#include "math.h"


/**********
函 数 名 : IIC_Start
功能说明 : 产生IIC起始信号
形    参 : 无
返 回 值 : 无
备    注 : SCL为高电平期间,SDA由高电平向低电平的跳变。
**********/
// 起始函数
void IIC_Start(void)
{
    IIC_SCL = 1;  //SCL拉高电平
    IIC_SDA = 1;  //SDA拉高电平
    delay_us(5);    //时序图中延迟时间要大于4.7us
    IIC_SDA = 0;  //SDA拉低电平
    delay_us(5);   
    IIC_SCL = 0;  //SCL拉低电平
	delay_us(5);    
}

/**********
函 数 名 : IIC_Stop
功能说明 : 产生IIC结束信号
形    参 : 无
返 回 值 : 无
备    注 : SCL为高电平期间,SDA由低电平向高电平的跳变。
**********/
// 停止函数
void IIC_Stop(void)
{
    IIC_SDA = 0;  //SCL拉低电平
	IIC_SDA = 0;  //SDA拉低电平
    delay_us(5);   
    IIC_SCL = 1;  //SCL拉高电平
     delay_us(5);   
    IIC_SDA = 1;  //SDA拉高电平
    delay_us(5);   
}

/**********
函 数 名 : IIC_ACK_NACK
功能说明 : 作为接收方时,每个字节(8bit)传输完成后的下一个时钟信号,发起应答或非应答信号
形    参 : ack:0应答,1非应答
返 回 值 : 无
备    注 : 在SCL为高电平期间,SDA为低,则表示一个应答信号(ACK)
           SDA为高,则表示一个非应答信号(NACK)。
**********/
void IIC_ACK_NACK(unsigned char ack)
{
    IIC_SCL = 0;  //SCL拉低电平
	   delay_us(5);    
    if (ack == 0) // 判断发送应答
    {
        IIC_SDA = 0; // 应答
    }
    else
    {
        IIC_SDA = 1; // 非应答
    }
    delay_us(5);   
    IIC_SCL = 1; // SCL拉高电平
     delay_us(5);   
    IIC_SCL = 0; // SCL拉低电平
}

/**********
函 数 名 : IIC_Wait_ACK
功能说明 : 作为发送方时,检测从机返回的应答信号
形    参 : 无
返 回 值 : 0:应答,1:非应答
备    注 : 接收从机返回的应答
**********/
// 等待应答
unsigned char IIC_Wait_ACK(void)
{
    unsigned char ret;  //用于返回值,存储是否应答的变量
    IIC_SCL = 1;  //SCL拉高电平
	  IIC_SDA = 1;  //SDA拉高电平
     delay_us(5);   
    if (SDA_READ() == 0)  //读取 SDA 数据线的状态。
    {
        ret = 0; // 返回应答
    }
    else
    {
        ret = 1; // 返回非应答
    }
    IIC_SCL = 0;  //SCL拉低电平 
     delay_us(5);   
    return ret;
}

/**********
函 数 名 : IIC_WriteByte
功能说明 : 发送一个字节的数据到IIC总线上
形    参 : data:需要发送的数据
返 回 值 : 无
备    注 : MSB(高位在前)
**********/
void IIC_WriteByte(unsigned char data)
{
    u8 i = 0;
    for (i = 0; i < 8; i++) //循环发送8位数据
    {
        IIC_SCL = 0;  //SCL拉低电平
        if (data & 0x80)  //判断高位先发 
        {
            IIC_SDA = 1;  //发送高位数据
        }
        else
        {
            IIC_SDA = 0;  // 发送低位数据
        }
        data = data << 1;  //移位数据
        delay_us(5);   
        IIC_SCL = 1;   //SCL拉高时钟
         delay_us(5);   
    }
    IIC_SCL = 0;  //拉低时钟
    delay_us(5);   
}

/*********************************************************************************************************
* 函 数 名 : IIC_ReadByte
* 功能说明 : 读取IIC总线上的一个字节
* 形    参 : ack:0应答,1非应答
* 返 回 值 : 读取到的数据
* 备    注 : MSB(高位在前)
*********************************************************************************************************/ 
unsigned char IIC_ReadByte(unsigned char ack)
{
	unsigned char i = 0, data = 0;
    
    // 循环接收数据
    for (i = 0; i < 8; i++) 
	 {
        IIC_SCL = 0; // 拉低时钟SCL等待接收数据
         delay_us(5);   
        IIC_SCL = 1; // 延时等待数据线的改变
         delay_us(5);   
        data <<= 1; // 拉高时钟读取数据
        if (SDA_READ() == 1) 
				{
            data |= 0x01; // 判断读到的数据并保存
        }
         delay_us(5);   
        IIC_SCL = 0; // 延时函数
         delay_us(5);   
    }
     
    IIC_SCL = 0;  // 拉低时钟SCL
    delay_us(5);   
    IIC_ACK_NACK(ack);  // 发送给从机应答或非应答
    
    return data;
}

(2)myiic.h

#ifndef _MYIIC_H_
#define _MYIIC_H_
#include "stm32f4xx.h"
#include "delay.h"
#include "io_bit.h"
#define SDA_IN() {GPIOB->MODER&=~(3<<(7*2));GPIOB->MODER|=0<<7*2;}	//PB7输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(7*2));GPIOB->MODER|=1<<7*2;} //PB7输出模式
#define SDA_READ() (GPIOB->IDR & (1 << 7))   // 读取PB7引脚的状态
#define IIC_SCL    PBout(6) //SCL
#define IIC_SDA    PBout(7) //SDA	 
void IIC_Start(void);
void IIC_Stop(void);
void IIC_ACK_NACK(unsigned char ack);
unsigned char IIC_Wait_ACK(void);
void IIC_WriteByte(unsigned char data);
unsigned char IIC_ReadByte(unsigned char ack);
#endif

十一、总结

IIC时序中的延迟时间会存在误差,如果上述的延迟不准,可用delay_ms函数来进行延迟。

所写文章均为记录个人学习的过程!!!!!

你可能感兴趣的:(STM32F40x,stm32,单片机,嵌入式硬件)