STM32之I/O模拟IIC通讯协议-----学习篇


先说说废话,第一篇博客文章,文中有不足之处或者有错误的地方还望指正。

IIC 总线(AT24C02)

一、先了解了解IIC总线协议基本知识

1.IIC协议是二线制,信号线包含SDA和SCL,且信号线是双向的,开路结构,需要通过上拉电阻到VCC,具体的电阻值影响的是信号反应速度和驱动能力。

2.IC传输时,要保持SCL为高电平不变,SDA保持稳定;IIC开始的条件:SCL保持高电平,SDA从高电平跳跃到低电平,IIC停止通讯的条件:SCL保持高电平,SDA从低电平跳跃到高电平。

3.串行的8位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s(一般不用考虑)。

单片机一般最高使用到400KBit/s(具体查看数据手册可知),可知IIC的时钟周期可以设置为:2.5us - 10us。


二、以下是此次学习IIC通讯协议,调试过程中一些要点

1.只有当SCL为高电平时,SDA线上的数据才有效,且在SCL电平升高前,SDA电平要保持稳定。

2.SCL,SDA为高电平时,总线为空闲状态。

3.在主机读数据前,应把SDA拉高,释放SDA,交由从机控制。

4.要注意每种功能实现时总线的时序性(比如:start -> write - ack)。

5.要注意写数据周期不能超过write cycle time(byte or page),查看数据手册可知。

6.在写完数据后读取数据时,需做延时,否则会读取不到数据(具体时间和设置时钟周期有关)。

7.在接收ACK时,应先释放SDA,在SCL为高电平时接收(与第3点一样)。

三、调试过程:先看数据手册时序图

1.启动、停止IIC

启动过程:拉高SCL,SDA  (延时)-->拉低SDA(延时) -->拉低SCL(延时),最后拉低SCL是为了控制总线为忙状态

停止过程:拉高SDA(延时) -->拉高SCL(延时) -->拉高SDA(延时)

void IIC_START(void)
{
    SDA_H;
    SCL_H;
    delay_us(5);
    SDA_L;
    delay_us(5);
    SCL_L;     //控制总线为忙状态
    delay_us(5);
}
void IIC_STOP(void)
{
//    SCL_L;//
    SDA_L;
    delay_us(5);
    SCL_H;
    delay_us(5);
    SDA_H;
    delay_us(5);
}

2.写数据、读数据

这里需要特别注意,读数据时,需先将SDA拉高,释放控制权,交由从机控制。

写数据:先将数据准备好(SDA拉高、或拉低)-->拉高SCL(延时)(此时从机读取SDA的电平) -->拉低SCL(延时)(等待下一位数据,即DATA CHANG)

读数据:拉高SDA(延时) -->拉高SCL(延时) -->读取数据 -->拉低SCL(延时)(等待下一位数据,即DATA CHANG)

void IIC_Write_Byte(u8 ucdata)
{
    u8 i = 8;
//    SCL_L;//  
    while(i--) {
        if(ucdata & 0x80) {  // 1.先准备数据,从机在SCL为高电平时读入数据
            SDA_H;
        }
        else {
            SDA_L;
        }
        delay_us(5);
        SCL_H;              // 2.拉高SCL,从机读取稳定数据
        delay_us(5);
        SCL_L;              // 3.拉低SCL,等待下一位数据
        ucdata <<= 1;
    }
}
u8 IIC_Read_Byte(void)
{
    u8 i = 8,
       ucdata = 0x00;
//    SCL_L;// 
    SDA_H;             // 1.先拉高SDA,释放SDA总线
    delay_us(5);
    while(i--) {
        SCL_H;         // 2.再拉高SCL,从机在SCL为高电平时输出稳定的数据
        delay_us(5);
        ucdata <<= 1;
        if(Read_SDA) {  //读数据
            ucdata = ucdata | 0x01;
        }
        SCL_L;        // 3.拉低SCL,等待下一位数据
        delay_us(5);
    }
    return ucdata;
}

3.获取应答、回复应答

在读数据或写数据,在第9个时钟周期回复应答或获取应答,即读取8bit数据或写8bit数据后应答。其实这里与读数据,写数据是一样的,只是这个过程只有一个周期。

回复应答(写数据),获取应答(读数据)。

u8 IIC_Get_ACK(void)
{
    u8 wait_tiem,ack;
    ack = 0;
    SDA_H;           // 1.先拉高SDA,释放SDA控制,交给从机控制
    delay_us(5);
    SCL_H;           // 2.再拉高SCL,从机输出稳定数据
    delay_us(5);
    while(Read_SDA) { //主机读取数据
        wait_tiem++;
        if(wait_tiem > 10) {
            ack = 1;
            break;
        }
    }
    delay_us(5);
    SCL_L;         // 3.拉低SCL,完成一个时钟周期
    return ack;
}
void IIC_Set_ACK(u8 ack)
{
    if(ack) {
        SDA_H;   // 1.先准备数据
    }
    else {
        SDA_L;
    }  
//    SCL_L;
    delay_us(5);
    SCL_H;       // 2.拉高SCL,从机读取稳定的数据
    delay_us(5);
    SCL_L;       // 3.拉低SCL,完成一个时钟周期
}

IIC总线协议底层程序完成,接下来是写芯片应用程序,读写过程一定要严格按照芯片数据手册的时序。

4.应用(AC24C02)

①.首先要知道设备地址,数据存储地址(个人理解)。AT24C02芯片是一个2K 大小的EEPROM存储芯片,2 * 1024 = 2048 字节,总共256个数据。

设备地址由固定的“1010” + 三位硬件选择位(与硬件连接有关) + 一位读/写命令位;

数据存储地址:0 - 255(0x00 - 0xFF)。

②写数据,可以单次写8位(字节写),或多次写8位(页写);1K/2K的EEPROM的芯片以8位数据(即一个字节)为一页。

byte write:启动 --> 写设备地址+写命令 -->获取应答(ack) -->写位置地址 -->获取应答 -->写数据 -->获取应答 --> 停止

page write:与byte write 过程一样,写完一页数据获取到从机应答后继续写数据,直到写完最后一个数据获取到应答后发送停止。

/***************************
**函数名称:IIC_Write_Data
**参   数:DeviceAddr:设备地址        
            WordAddr:数据存储地址
            *Data   :需要写的数据
            num     :写个数    
**作   用:随机写数据,返回值为1:写数据成功;返回值为0:写数据失败  
**日   期:2020.2.17
**作   者:Yangyang
****************************/
u8 IIC_Write_Data(u8 DeviceAddr,u16 WordAddr,u8 *Data,u8 num)
{
    u8 AddrTemp,
       i;
    IIC_START();                       //启动
    IIC_Write_Byte(DeviceAddr & 0xfe); //设备地址 + 写命令
    if(IIC_Get_ACK()) {
        IIC_STOP();
        return 0;
    }
    AddrTemp = (WordAddr & 0xff);   //存储数据地址
    IIC_Write_Byte(AddrTemp);
    if(IIC_Get_ACK()) {
        IIC_STOP();
        return 0;
    }
    for(i = 0; i < num; i++) {
        IIC_Write_Byte(*(Data + i));//写数据
        if(IIC_Get_ACK()) {
            IIC_STOP();
            return 0;
        }
    }
    IIC_STOP();
    delay_ms(3);//最少需延时2ms,在写完数据后读数据,不延时会读取数据失败(也可以函数外部延时)
    return 1;
}

③读数据,读数据可分为当前地址读数据、随机地址读数据和顺序地址读数据(此处就不说当前地址读数据,基本不用)

随机读数据:启动 --> 先写设备地址 + 写命令 -->获取应答 -->  写读取数据的位置 -->获取应答 --> 再启动 --> 写设备地址 + 读命令 --> 获取应答 --> 读取数据 --> 回复不应答 --> 停止

顺序读数据:读取方式与随机读数据相似,在读取数据后是回复应答,当读到最后一个数据后再回复不应答:....读取数据n-1 --> 回复应答 -->读取数据n --> 回复不应答 --> 停止

/***************************
**函数名称:IIC_Read_Data
**参   数:DeviceAddr: 设备地址    
            WordAddr: 数据存储地址
            *Data   : 读取的数据
            num     : 读取个数
**作   用:随机读取数据,顺序读取数据,返回值为1:读取数据成功;返回值为0:读取数据失败
**日   期:2020.2.17
**作   者:Yangyang
****************************/
u8 IIC_Read_Data(u8 DeviceAddr,u16 WordAddr,u8 *Data,u8 num)
{
    u8 AddrTemp,
       i;
    IIC_START();                      //启动
    IIC_Write_Byte(DeviceAddr & 0xfe);    //设备地址 + 写命令
    if(IIC_Get_ACK()) {                    
         IIC_STOP();                       //Internally organized with 32 pages of 8 byteseach,
         return 0;                         //the 2K requires an 8-bit data word address for random word addressing.
    }                                       //此说明在数据手册可知
    AddrTemp = (WordAddr & 0xff);    //地址可取0 - 255
    IIC_Write_Byte(AddrTemp);
    if(IIC_Get_ACK()) {
        IIC_STOP();
        return 0;
    }
    delay_us(100);
    IIC_START();                          //再启动
    IIC_Write_Byte(DeviceAddr | 0x01);   //设备地址 + 读命令
    if(IIC_Get_ACK()) {                  //
        IIC_STOP();
        return 0;
    }
    for(i = 0; i < num; i++) {
        *(Data + i) = IIC_Read_Byte();  //读数据
        if(i < num - 1) {
            IIC_Set_ACK(0);            //接收N-1个数据后应该从机
        }
        else {                         //接收最后一个数据时不应答从机
            IIC_Set_ACK(1);
        }
    }
    IIC_STOP();                       //停止
    return 1;
}

④测试

使用硬件:STM32F103 + AT24C02

#define at24c02_byte_num 8
u8 IIC_ref_data[at24c02_byte_num] = {0};
u8 IIC_ref_data2[at24c02_byte_num] = {0};
u8 IIC_write_data[at24c02_byte_num] = {0x20,0x20,0x02,0x29,0x21,0x51,0x55,0x55}

u8 Test_IIC(void)
{
    u8 i = 0;
    IIC_Write_Data(0xa0,0,IIC_write_data,at24c02_byte_num);
//    delay_ms(3);
    i = IIC_Read_Data(0xa0,0,IIC_ref_data2,at24c02_byte_num);
    return i;
}

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM