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

  • 相关阅读:
    HDU 1863 畅通工程(Kruskal)
    HDU 1879 继续畅通工程(Kruskra)
    HDU 1102 Constructing Roads(Kruskal)
    POJ 3150 Cellular Automaton(矩阵快速幂)
    POJ 3070 Fibonacci(矩阵快速幂)
    ZOJ 1648 Circuit Board(计算几何)
    ZOJ 3498 Javabeans
    ZOJ 3490 String Successor(模拟)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
  • 原文地址:https://www.cnblogs.com/dongxiaodong/p/9801684.html
Copyright © 2011-2022 走看看