STM32 IIC的学习

IIC
1. IIC的定义及作用?与SPI,USART有何不同?
STM32上有很多的通讯接口,主要用来连接MCU与IC(如下面将要介绍的EEPROM–AT24C02C),及IC与IC之间的通讯。因为对通讯的速度及功能要求不同,主要分为IIC,SPI,USART.
1).IIC (Inter-Integrated Circuit 意为IC之间总线):两线式串行总线,是由数据SDA线和时钟SCL线构成的串行总线,可发送和接收数据。IIC是多主控总线,所以任何一个设备都能像主控器一样工作,并控制总线。 总线上每一个设备都有一个独一无二的地址,根据设备它们自己的能力,它们可以作为发射器或接收器工作。非常适合在器件之间进行近距离、非经常性的数据通信。IIC不可以实现全双工(即异步)。
标准模式 高达 100kHz
快速模式 高达 400kHz
超快速模式 高达 1 MHz
2).SPI(Serial Peripheral Interface:串行外设接口) :主从设备间既可以实现同步通讯也可以异步通讯,SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI),当有多个从设备时,还可以增加一条从设备(SS)选择线。SPI总线可以实现 多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。
速度高达18M bit/s
如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO)或者一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。数据(输入或者输出)在时钟上沿或下沿时改变,在紧接着的下沿或上沿被读取。这样至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。SCL信号线是由主设备控制,从设备不能控制SCL信号线,所以至少有一个主控设备。这样传输的特点:与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。
SPI 通讯接口有:SS( Slave Select),SCK( Serial Clock),MOSI (Master Output, Slave Input),MISO (Master Input, Slave Output)。
3).USART(Universal Asynchronous Receiver Transmitter:通用异步收发器):是用于控制计算机与串行设备的芯片, USART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多。硬件上有两根线,一根用于发送,一根用于接收。有一点要注意的是,它提供了RS-232C数据终端设备接口,这样计算机就可以和调制解调器或其它使用RS-232C接口的串行设备通信了。目前市场上有USB转USART(如CP21xx ),也有USB转RS-232C设备(如PL230x)。其中USART 是TTL 电平3.3v,RS-232C是232电平5v.
显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。
不同之处大概就是上面所示,SPI和UART可以实现全双工,但I2C不行。IIC为两条线(时钟线SCL 和数据线SDA),SPI 为至少3条线,可同步接收和发送,不过只有一条时钟线,如果SPI设备多的话,就还可加入CS片选线选择从机设备。USART

  1. 硬件IIC和模拟IIC的区别?

    硬件IIC指的是MCU上面专有的IIC接口(SDA数据线和SCL时钟线)及在APB1上有IIC的指令寄存器,可以直接配置IIC指令寄存器及连接SDA线及SCL线至需要进行通讯的设备(IC或者EEPROM)进行读写通讯。(在STM32的数据手册上面写的需要配置的就是硬件IIC)
    如下图所示:
    IIIC硬件配置
    模拟IIC与上面的硬件IIC有所不同,指通过通用的GPIO口进行配置与相应的IIC设备(如EEPROM—AT24C02)进行通讯,需要配置两个通用IO口,一个作为SDA线,一个作为SCL线与IC进行通讯,如同下面所示:
    模拟IIC配置连接
    上面图片是模拟IIC使用GPIO口进行配置,在进行IIC的配置前,首先要配置GPIO口。

  2. 模拟IIC的配置&时序图& 协议

    首先要配置GPIO口,现在以电路图的连接为主,PF6设置为SCL & PG7设置为SDA,则 首先需要使能GPIOF,PF6/SCL因为是时钟线,所以只要配置成输出就好了。
    PF7因为是数据GPIO口,数据可以从MCU发送到IC(AT24C02),也可以接收IC(AT24C02)发送的数据。所以需要发送和接收数据,把PF7/SDA设置为输入输出口。
    1)IIC总线协议的时序图:
    IIC总线协议时序图
    1)).看上面的时序图,每个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一个应答位(即一帧共9位)。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断,若未收到应答信号,则判断为受控单元故障。
    2)).IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
    3)).主控器向被控器发送的信息种类有:空闲状态,初始信号,结束信号,7位地址码,读/写控制位,10位地址码,数据字节,应答信号,脉冲时钟。
    被控器向主控器发送的信息种类有:应答信号,数据字节,时钟低电平。
    4)).空闲状态: 时钟线SCL与数据线SDA均为高电平信号。
    初始信号:在空闲状态时,SCL与SDA均为高电平的状态下,SDA数据线由高电平变为低电平。
    结束信号:在SDA数据线和SCL均为低电平的状态下,SDA数据线由低电平变为高电平。
    读写控制位:IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
    应答信号:在发送完8bit 位后,在第九位时钟的时候,由被控器发送一个应答位,主控器接收发送的应答位。当应答位为0时,表示应答。当应答位为1时,表示无应答。
    2).EEPROM—AT24C02的配置
    1)).目前我写的IC/受控器为AT24C02C,该芯片的总容量为256个字节(2kbit),IIC总线上启动信号后,需要发送一个8位设备地址,来使能IIC总线上与设备地址匹配的设备,匹配的设备会有一个“0”(ACK)应答,这样才能进行读写操作。一般所有的EEPROM设备地址前四位是固定的1010,接下来的三位是设备地址(不同容量规则不同),然后最后一位数是读写使能位。AT24C02的设备地址不需要设备地址,所以为000,所以这个芯片的读是0xA1,写是0xA0.
    AT24C02的设备地址
    2)).EEPROM的读协议如下:
    字节写
    页写
    MCU写入AT24C02的流程为:起始信号—->MCU发送设备地址给AT24C02(0xA0)/写—–>MCU接收从AT24C02的应答信号—->MCU发送字节地址给AT24C02(写)(这里有一点很重要,之前一直不明白,就是字节地址都是怎么样写的?一般的数据手册都没有写这个,所以一度很困惑。后来经由大神的解释弄清楚了,因为AT24C02的内存为256个字节,因而0x00~0xFF就代表的是这个范围,所以地址就是这个范围内的。例AT20C128的为16384个字节,为0x0000~0x3FFF. )—–>MCU接收从AT24C02的应答信号—->MCU接收AT24C02发送的数据——>MCU接收从AT24C02的应答信号—–>结束信号
    3)).EEPROM的写协议如下:
    随机读
    连续读
    MCU从AT23C02读的流程为:起始信号—->MCU发送设备地址给AT24C02(0xA0)/写—–>MCU接收从AT24C02的应答信号—->MCU发送字节地址给AT24C02(写)—–>MCU接收从AT24C02的应答信号—->起始信号—–>MCU发送设备地址给AT24C02(0xA1)/读—–>MCU接收AT24C02发送的数据——>MCU接收从AT24C02的应答信号—–>结束信号

  3. 程序

IIC.C

#include <stm32f0xx.h>
#include "IIC.h"
#include "System.h"


void IIC_Init(void)/*initation the SDA=1 & SCL=1*/
{
    //I2C idle status
    SCLEnable; //SCL=1
    SYS_DelayUs(2);
    SDAEnable; //SDA=1
    SYS_DelayUs(2);
  SDA_OUT();
    SCL_OUT();
}

void IIC_Start(void)//Start the signal, when SCL&SDA in the Init status,pull down the SDA from 1 to 0 when SCL=1 to start the signal.
{
    SDA_OUT();
    SDAEnable; //SDA=1
    SCLEnable; //SCL=1
    SYS_DelayUs(2);
    SDAClean; //Pull down SDA from 1 to 0 when SCL=1 to start the signal transfer
    SYS_DelayUs(1);   //Baud Rate
    SCLClean;//SCL=0. The SDA only could valid when SCL=0;
}
void IIC_Stop(void)//Stop the signal,to pull up the SDA from 0 to 1 when SCL=1
{
    SDA_OUT();
    SCLClean; //SCL=0. The SDA only could change when SCL=0;
    SDAClean; //SDA=0
    SYS_DelayUs(2);
    SCLEnable; //SCL=1
    SYS_DelayUs(1);
    SDAEnable; //Pull up the SDA from 0 to 1 when SCL=1 to stop the signal.I2C idle status when SCL=1&SDA=1
    SYS_DelayUs(2);
}


/*TO write the string from MCU to the corresponding IC ,such as 24C02,
The first step to search the Device address(0xa0 write,0xa1 read),wait for the ACK/NoAck,
then search the Word address want to write in the address,wait for ACK/NoAck, then write the string*/
//I2C when transfer the data.The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0.
void IIC_Write(unsigned char String)
{   
    unsigned char i;
    SDA_OUT();
    SCLClean;//The data in SDA could change only when SCL=0
    SYS_DelayUs(1);
    for(i=0;i<8;i++)
    {
        if((String&0x80)==0) SDAClean;
        else SDAEnable;
        String=String<<1;
        SYS_DelayUs(1);
        SCLEnable;//The SDA must stble when SCL=1
        SYS_DelayUs(2);
        SCLClean;
        SYS_DelayUs(1);
    }
}

/*TO Read the string from IC(Such as 24C02) to MCU,
The first step to search the Device address(0xa0 write,0xa1 read),wait for the ACK/NoAck,
then search the Word address want to write in the address,wait for ACK/NoAck, then receive the string*/
//I2C when receive the data from IC. MCU can read it.The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0.
unsigned char IIC_Read(unsigned char ack)
{
    unsigned char i,receive=0;
    SDA_IN();
    for(i=0;i<8;i++)
    {
        SCLClean;
        SYS_DelayUs(2);
        SCLEnable;
        receive <<=1;
        if((GPIOF->IDR&GPIOF_IDR7)==0x80)  receive++;
        SYS_DelayUs(2);
    }
    if(!ack)
        IIC_Ack();
    else
        IIC_NoAck();
    return receive;
}


void IIC_Ack(void)//Response signal,Make sure that when SCL=1,SDA=0.
{
    SCLClean;//SCL=0
    SDA_OUT();
    SDAClean;//SDA=0,The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0.
    SYS_DelayUs(2);
    SCLEnable;//SCL=1
    SYS_DelayUs(2);
    SCLClean;//SCL=0
}

void IIC_NoAck(void)//No Response signal,Make sure that when SCL=1,SDA=1.
{
    SCLClean;//SCL=0
    SDA_OUT();
    SDAEnable;//SDA=1,The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0.
    SYS_DelayUs(2);
    SCLEnable;//SCL=1
    SYS_DelayUs(2);
    SCLClean;//SCL=0
}

unsigned char IIC_Wait_Ack(void)
{
    unsigned char i=0;
    SDA_IN();
    SYS_DelayUs(1);
    SCLEnable; SYS_DelayUs(1);
    while((GPIOF->IDR&GPIOF_IDR7)==0X80)
        {
            i++;
            if(i>250)  
            {
                IIC_Stop();
                return 1;
            }
        }
        SYS_DelayUs(1);
        SCLClean;
        return 0;
}

IIC.h

#if !defined(IIC_H)
#define IIC_H

#define SDAClean                          GPIOF->BSRR=1<<(16+7)  //SDA=0          
#define SDAEnable                         GPIOF->BSRR=1<<(0+7)   //SDA=1
#define SCLClean                          GPIOF->BSRR=1<<(16+6)  //SCL=0
#define SCLEnable                         GPIOF->BSRR=1<<(0+6)   //SCL=1
#define SDA_IN()                          GPIOF->MODER=(GPIOF->MODER&~(3<<(7*2)))|(0<<(7*2))
#define SDA_OUT()                         GPIOF->MODER=(GPIOF->MODER&~(3<<(7*2)))|(1<<(7*2))
#define SCL_OUT()                         GPIOF->MODER=(GPIOF->MODER&~(3<<(6*2)))|(1<<(6*2))
#define GPIOF_IDR7                        ((uint32_t)0x00000080U)


void SystemClock(void);
void IIC_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
void IIC_Write(unsigned char String);
unsigned char IIC_Read(unsigned char ack);
void IIC_Ack(void);
void IIC_NoAck(void);
unsigned char IIC_Wait_Ack(void);
#endif

EEPROM.c

#include <stm32f0xx.h>
#include <String.h>
#include "IIC.h"
#include "EEPROM.h"
#include "System.h"

void EEPROM_Init(void)
{
    IIC_Init();
    return;
}
unsigned long AT24C02_ReadOneByte(unsigned long ReadAddr)
{
    unsigned long temp=0;
    IIC_Start();
    IIC_Write(0XA0);
  IIC_Wait_Ack();
    IIC_Write(ReadAddr);
    IIC_Wait_Ack();
    SYS_DelayUs(2);
    IIC_Start();
    IIC_Write(0XA1);
    IIC_Wait_Ack();
    temp=IIC_Read(0);
    IIC_Stop();
    return temp;
}

void AT24C02_WriteOneByte(unsigned long WriteAddr,unsigned long DataToWrite)
{
    IIC_Start();
    IIC_Write(0XA0);
    IIC_Wait_Ack();
    IIC_Write(WriteAddr);
    IIC_Wait_Ack();
    IIC_Write(DataToWrite);
    IIC_Wait_Ack();
}

void AT24C02_Read(unsigned long ReadAddr,unsigned long *pBuffer,unsigned long NumToRead)
{
    while(NumToRead)
        {
            *pBuffer++=AT24C02_ReadOneByte(ReadAddr++);
            NumToRead--;
        }
}

void AT24C02_Write(unsigned long WriteAddr,unsigned long *pBuffer,unsigned long NumToWrite)
{
    while(NumToWrite--)
        {
            AT24C02_WriteOneByte(WriteAddr,*pBuffer);
            WriteAddr++;
            pBuffer++;
        }
}

EEPROM.h

#if !defined(EEPROM_H)
#define EEPROM_H

#define EEPROM_ADDRESS 0xa0
void EEPROM_Init(void);
void EEPROM_Write(unsigned long Address,const void *pData,unsigned long Length);
void EEPROM_Read(unsigned long Address,unsigned char *ReadData,unsigned long Length);
unsigned long AT24C02_ReadOneByte(unsigned long ReadAddr);
void AT24C02_WriteOneByte(unsigned long WriteAddr,unsigned long DataToWrite);
void AT24C02_Read(unsigned long ReadAddr,unsigned long *pBuffer,unsigned long NumToRead);
void AT24C02_Write(unsigned long WriteAddr,unsigned long *pBuffer,unsigned long NumToWrite);

#endif

在这一定注意SCL时钟线,就像有生命的心脏一样,一定要保持它的跳动,不能有停止中断的时候,因此Delay语句就很重要了,要是SCL时钟的delay恰到好处,所以当有示波器就可以看出写的程序是否是遵循这个道理。如果没有,完成可以根据程序手绘一个,同时根据绘制的时序图可以反映出写的程序是否正确。这个下面是我手绘的版本。这是比较笨的方法,但是很有效果。
write a byte
Read a byte

猜你喜欢

转载自blog.csdn.net/Flylily9997/article/details/70228468