zoukankan      html  css  js  c++  java
  • 协议— IIC(模拟IIC原理与使用)

    基础认识

    模拟IIC是模拟IIC通信时序,一些单片机有硬件IIC接口,如果没有硬件IIC可以通过普通GPIO模拟得到,这里将介绍如何实现模拟IIC

    数据线:SDA

    时钟线:SCL

    注意:

    1.只允许有一个主设备,总线上可以挂接多个从设备,

    2.总线连线一般不超过2米

    3.两线(SDA,SCL)的总线连接,两条总线都需要1~10K的上拉电阻

    3.每个器件地址唯一(7位地址,最低位0为发送,1为读取),最多127个器件地址,每次通信之前需要先发送地址

    起始/停止信号

    起始信号:

    当时钟线和数据线都为高电平时,IIC总线上的所有从设备都处于空闲状态。当时钟线和数据线都为高电平时,数据线从高电平到低电平的跳动,被定义为起始信号。

     1 //IIC起始信号,SDA下降沿
     2 void iic_start()
     3 {
     4     SDA_OUT_Mode(); //设置SDA为输出
     5     SDA_Control(1);            
     6     SCL_Control(1);
     7     delay_us(4);
     8      SDA_Control(0);//下降沿
     9     delay_us(4);
    10     SCL_Control(0);//准备发送数据
    11 }

    停止信号:

    当时钟线为高电平,数据线为低电平时,数据线从低到高的跳动,被定义为停止信号。

     1 //IIC停止信号,SDA上升沿
     2 void iic_stop()
     3 {
     4     SDA_OUT_Mode();//设置SDA为输出
     5     SCL_Control(0);
     6     SDA_Control(0);
     7      delay_us(4);
     8     SCL_Control(1);
     9     delay_us(4);        
    10        SDA_Control(1);//上升沿
    11 }

    主机收发一个字节

    主机发送数据信号:

    时钟线高电平和数据线为低电平时,当时钟线拉低之后,IIC从设备会收到一个数据0;

    时钟线高电平和数据线为高电平时,当时钟线拉低之后,IIC从设备会收到一个数据1。

    编程思路:因为时钟线为高电平时,数据线是不能动作的,因为有可能会无触发为起始或者停止信号,所以必须先把时钟线拉低后再去改变数据线的电平,然后数据线电平不变的情况下,拉高时钟线然后再拉低时钟线,以发送数据0或1。

     1 //IIC发送一个字节    
     2 void iic_write_byte(u8 c)
     3 { 
     4    u8 i;   
     5    SDA_OUT_Mode();//设置SDA为输出    
     6    SCL_Control(0);//拉低时钟开始数据传输
     7    for(i=0;i<8;i++)
     8    {              
     9       SDA_Control((c&0x80)>>7);//数据变化
    10       c<<=1;       
    11       delay_us(2);
    12       SCL_Control(1);//时钟线产生下降沿
    13       delay_us(2); 
    14       SCL_Control(0);
    15       delay_us(2);
    16     }
    17 }

    应答信号的获取:

    当主设备发送一个字节完成后从设备会产生一个应答信号,读取是否有应答信号可以判断出主设备是否发送发出数据完成,并被从设备接收,但此判断非必须的,编程可以不考虑

     1 //等待应答信号到来
     2 //返回值:1,接收应答失败
     3 //        0,接收应答成功
     4 u8 iic_wait_ack(void)
     5 {
     6     u8 outTime=0;
     7         SDA_OUT_Mode();//设置SDA为输出
     8     SDA_Control(1);delay_us(1); //此条后SDA电平已经无变化
     9     SCL_Control(1);delay_us(1);    
    10         SDA_IN_Mode(); //设置SDA为输入  
    11     while(SDA_Read())
    12     {
    13        outTime++;
    14        if(outTime>250)
    15        {
    16           iic_stop();
    17           return 1;
    18         }
    19     }
    20     SCL_Control(0);//时钟输出0        
    21     return 0;  
    22 }

    主机接收数据信号:

     1 //产生ACK应答
     2 void iic_ack(void)
     3 {
     4     SCL_Control(0);
     5     SDA_OUT_Mode();
     6     SDA_Control(0);
     7     delay_us(2);
     8     SCL_Control(1);
     9     delay_us(2);
    10     SCL_Control(0);
    11 }
    12 //不产生ACK应答            
    13 void iic_nack(void)
    14 {
    15     SCL_Control(0);
    16     SDA_OUT_Mode();
    17     SDA_Control(1);
    18     delay_us(2);
    19     SCL_Control(1);
    20     delay_us(2);
    21     SCL_Control(0);
    22 }    
    23   
    24 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
    25 u8 iic_read_byte(unsigned char ack)
    26 {
    27     unsigned char i,receive=0;
    28     SCL_Control(0);//拉低时钟开始数据传输
    29     SDA_OUT_Mode();//设置SDA为输出
    30     SDA_Control(1); //此条后SDA电平已经无变化
    31     SDA_IN_Mode(); //设置SDA为输入  
    32     for(i=0;i<8;i++ )
    33     {
    34        SCL_Control(1);//锁定数据,让数据不变化
    35        receive<<=1;
    36        if(SDA_Read())receive++;  //读取数据 
    37        delay_us(1); 
    38        SCL_Control(0);//释放锁定,开始下一个数据检测
    39        delay_us(2);
    40    }    
    41    /*
    42    if (!ack) iic_nack();//发送nACK
    43    else  iic_ack(); //发送ACK   
    44    */
    45    return receive;
    46 }

    根据地址收发一个数据

    器件地址(Device Addr):7位,最低位为0表示写数据,最低位为1表示读数据

    数据地址(Register Addr):数据保存的地址

    写:

     读:

     1 //根据地址写数据
     2 void IIC_Write(u8 addr,uint8_t data)
     3 {
     4      iic_start();  //起始信号
     5      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
     6      iic_wait_ack(); //等待应答
     7      iic_write_byte(addr); //发送数据地址
     8      iic_wait_ack(); 
     9      iic_write_byte(data);//发送数据                           
    10      iic_wait_ack();                     
    11      iic_stop();//产生一个停止条件 
    12 }
    13 
    14 //根据地址读取数据
    15 u8 IIC_Read(u8 addr)  //读寄存器或读数据
    16 {    
    17      u8 data;
    18      iic_start();//起始信号  
    19      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
    20      iic_wait_ack();//等待应答
    21      iic_write_byte(addr); //发送数据地址
    22      iic_wait_ack(); 
    23      iic_start();      
    24      iic_write_byte(I2C_SLAVE_ADDRESS7|0x01);//发器件地址,低位为1,表示读
    25      iic_wait_ack();
    26      data=iic_read_byte(0);//读取一个字节
    27      iic_stop();//产生一个停止条件        
    28      return data;
    29 }

    工程整体

    STM8L151K4T6单片机测试工程参考:

      1 #include "stm8l15x.h"
      2 #include "TCA8418.h"
      3 
      4 #define I2C_SLAVE_ADDRESS7     0x68
      5 
      6 //延时函数,大致延时,
      7 //如果需要标准延时,请使用定时器
      8 void delay_us(u8 n)
      9 {
     10   unsigned int x , y;
     11   for(x = n; x > 0; x--);
     12     for(y =30; y > 0 ; y--);
     13 }
     14 
     15 /*
     16 SCL PC1
     17 SDA PC0
     18 */
     19 
     20 #define SCL_PORT   GPIOC  
     21 #define SCL_PIN    GPIO_Pin_1 
     22 
     23 #define SDA_PORT   GPIOC
     24 #define SDA_PIN    GPIO_Pin_0
     25 
     26 //SCL输出电平
     27 void SCL_Control(u8 c){
     28   if(c==0) GPIO_WriteBit(SCL_PORT , SCL_PIN ,RESET);  //低电平
     29   else  GPIO_WriteBit(SCL_PORT , SCL_PIN ,SET);  //高电平
     30 }
     31 //SDA输出电平
     32 void SDA_Control(u8 c){
     33    if(c==0) GPIO_WriteBit(SDA_PORT , SDA_PIN ,RESET);  //低电平
     34    else  GPIO_WriteBit(SDA_PORT , SDA_PIN ,SET);  //高电平
     35 }
     36 //读取SDA的电平
     37 u8 SDA_Read(){
     38   if(GPIO_ReadInputDataBit(SDA_PORT , SDA_PIN)== 0) return 0;
     39   return 1;
     40 }
     41 //设置SDA为输入模式
     42 void SDA_IN_Mode(){
     43    GPIO_Init(SDA_PORT , SDA_PIN , GPIO_Mode_In_PU_No_IT);//输入
     44 }
     45 //设置SDA为输出模式
     46 void SDA_OUT_Mode(){
     47    GPIO_Init(SDA_PORT , SDA_PIN , GPIO_Mode_Out_PP_Low_Fast);//输出
     48 }
     49 
     50 //IIC初始化
     51 void IIC_Init()
     52 {
     53   GPIO_Init(SCL_PORT , SCL_PIN , GPIO_Mode_Out_PP_Low_Fast);//输出
     54   SDA_OUT_Mode();
     55   SDA_Control(1);
     56   SCL_Control(1);
     57 }
     58 
     59 
     60 //IIC起始信号,SDA下降沿
     61 void iic_start()
     62 {
     63     SDA_OUT_Mode(); //设置SDA为输出
     64     SDA_Control(1);            
     65     SCL_Control(1);
     66     delay_us(4);
     67      SDA_Control(0);//下降沿
     68     delay_us(4);
     69     SCL_Control(0);//准备发送数据
     70 }    
     71   
     72 //IIC停止信号,SDA上升沿
     73 void iic_stop()
     74 {
     75     SDA_OUT_Mode();//设置SDA为输出
     76     SCL_Control(0);
     77     SDA_Control(0);
     78      delay_us(4);
     79     SCL_Control(1);
     80     delay_us(4);        
     81        SDA_Control(1);//上升沿
     82 }
     83 
     84 //等待应答信号到来
     85 //返回值:1,接收应答失败
     86 //        0,接收应答成功
     87 u8 iic_wait_ack(void)
     88 {
     89     u8 outTime=0;
     90         SDA_OUT_Mode();//设置SDA为输出
     91     SDA_Control(1);delay_us(1); //此条后SDA电平已经无变化
     92     SCL_Control(1);delay_us(1);    
     93         SDA_IN_Mode(); //设置SDA为输入  
     94     while(SDA_Read())
     95     {
     96        outTime++;
     97        if(outTime>250)
     98        {
     99           iic_stop();
    100           return 1;
    101         }
    102     }
    103     SCL_Control(0);//时钟输出0        
    104     return 0;  
    105 }
    106 //IIC发送一个字节    
    107 void iic_write_byte(u8 c)
    108 { 
    109    u8 i;   
    110    SDA_OUT_Mode();//设置SDA为输出    
    111    SCL_Control(0);//拉低时钟开始数据传输
    112    for(i=0;i<8;i++)
    113    {              
    114       SDA_Control((c&0x80)>>7);//数据变化
    115       c<<=1;       
    116       delay_us(2);
    117       SCL_Control(1);//时钟线产生下降沿
    118       delay_us(2); 
    119       SCL_Control(0);
    120       delay_us(2);
    121     }
    122 }
    123 
    124 //产生ACK应答
    125 void iic_ack(void)
    126 {
    127     SCL_Control(0);
    128     SDA_OUT_Mode();
    129     SDA_Control(0);
    130     delay_us(2);
    131     SCL_Control(1);
    132     delay_us(2);
    133     SCL_Control(0);
    134 }
    135 //不产生ACK应答            
    136 void iic_nack(void)
    137 {
    138     SCL_Control(0);
    139     SDA_OUT_Mode();
    140     SDA_Control(1);
    141     delay_us(2);
    142     SCL_Control(1);
    143     delay_us(2);
    144     SCL_Control(0);
    145 }    
    146   
    147 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
    148 u8 iic_read_byte(unsigned char ack)
    149 {
    150     unsigned char i,receive=0;
    151     SCL_Control(0);//拉低时钟开始数据传输
    152     SDA_OUT_Mode();//设置SDA为输出
    153     SDA_Control(1); //此条后SDA电平已经无变化
    154     SDA_IN_Mode(); //设置SDA为输入  
    155     for(i=0;i<8;i++ )
    156     {
    157        SCL_Control(1);//锁定数据,让数据不变化
    158        receive<<=1;
    159        if(SDA_Read())receive++;  //读取数据 
    160        delay_us(1); 
    161        SCL_Control(0);//释放锁定,开始下一个数据检测
    162        delay_us(2);
    163    }    
    164 
    165    if (!ack) iic_nack();//发送nACK
    166    else  iic_ack(); //发送ACK   
    167    
    168    return receive;
    169 }
    170 
    171 //根据地址写数据
    172 void IIC_Write(u8 addr,u8 data)
    173 {
    174      iic_start();  //起始信号
    175      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
    176      iic_wait_ack(); //等待应答
    177      iic_write_byte(addr); //发送数据地址
    178      iic_wait_ack(); 
    179      iic_write_byte(data);//发送数据                           
    180      iic_wait_ack();                     
    181      iic_stop();//产生一个停止条件 
    182 }
    183 
    184 
    185 //根据地址读取数据
    186 void IIC_Read(u8 addr,u8 *datax) //读寄存器或读数据
    187 {    
    188      iic_start();//起始信号  
    189      iic_write_byte(I2C_SLAVE_ADDRESS7&0xFE);//发器件地址,低位为0,表示写
    190      iic_wait_ack();//等待应答
    191      iic_write_byte(addr); //发送数据地址
    192      iic_wait_ack(); 
    193      iic_start();      
    194      iic_write_byte(I2C_SLAVE_ADDRESS7|0x01);//发器件地址,低位为1,表示读
    195      iic_wait_ack();
    196      *datax =iic_read_byte(0);//读取一个字节
    197      iic_stop();//产生一个停止条件        
    198 }
    199 u8 R_Value=0;
    200 void Test_Check(){
    201     IIC_Init();//IIC初始化
    202     //写读同一个地址,可以判断IIC是否成功
    203     IIC_Write(0x01,0xB2);//写数据到0x01
    204     IIC_Read(0x01,&R_Value);//将0x01的数据读取
    205 }

    https://blog.csdn.net/return_oops/article/details/80965437

    https://www.bilibili.com/video/av73030246

  • 相关阅读:
    VMware虚拟机安装红帽系统无法上网解决办法(转)
    二维指针的malloc内存分配(转)
    c语言中如何通过二级指针来操作二维数组
    如何把一个二维数组的地址赋给一个二维指针?
    二维数组及二维指针的传递及一些思考(转)
    js怎么让时间函数的秒数在页面上显示是变化的
    jquery 实现各种统计图网址
    WEB的相关知识总结
    JS同名方法,
    web components思想如何应用于实际项目
  • 原文地址:https://www.cnblogs.com/dongxiaodong/p/9801684.html
Copyright © 2011-2022 走看看