跟着原子学I2C

I2C通讯

1、IIC总线介绍

集成电路总线,是一种同步串行半双工通信总线。

总线or协议?!

总线是数据传输通道,协议是数据传输规则。

跟着原子学I2C_第1张图片

1、1介绍

a、由时钟线SCL和数据线SDA组成,并且都接上拉电阻,确保总线空闲状态为高电平。

b、总线支持多设备连接,允许多主机存在,每个设备都有一个唯一的地址。

c、连接到总线上的数目受总线的最大电容400pf限制

d、数据传输速率:标准模式100k bit/s 快速模式400k bit/s 高速模式3.4M bit/s。

1、2归纳

三个信号: 起始信号、停止信号、应答信号。

两个注意:数据有效性、数据传输顺序。

一个状态:空闲状态。

起始信号:当SCL为高电平时,SDA从高电平变为低电平。

停止信号:当SCL为高电平时,SDA从低电平变为高电平。

应答信号:上拉电阻影响下SDA默认为高,而从机拉低SDA就是确认收到数据,为ACK,如果没有收到,为NACK。

数据先发送高位,数据以字节(8bit)传输,数据在SCL高电平稳定。

空闲状态:SCL、SDA都是高电平。

1、3跟着正点原子写代码

起始信号

/*SDA、SCL开始都处于高电平,SCL为高电平期间,SDA从高到低跳变*/
void iic_start(void)
{
 IIC_SDA(1);
 IIC_SCL(1);
 IIC_delay();//delay时间看器件
 IIC_SDA(0);
 IIC_delay();
 IIC_SCL(0);//SCL拉低,钳住总线,准备发送/接收数据
 IIC_delay();
}

停止信号

/*SCL为高电平期间,SDA从低电平往高电平跳变*/
void iic_stop(void)
{ 
   IIC_SDA(0);
   IIC_delay();
   IIC_SCL(1);
   IIC_delay();
   IIC_SDA(1);
   IIC_delay();
}

检测应答信号(主机)

//return:  1:fail  0:succeed
uint8_t iic_wait_ack(void) 
{
   IIC_SDA(1);//主机释放SDA线
   IIC_delay();
   IIC_SCL(1);//从机返回ACK
   IIC_delay();
   if(IIC_READ_SDA)//SCL高电平读取SDA状态
   {
      iic_stop();//SDA高电平表示从机NACK
      return 1;
   }
   IIC_SCL(0);
   iic_delay();
   return 0;
}

发送应答信号

void iic_ack(void)
{ 
  IIC_SCL(0);
  IIC_delay();
  IIC_SDA(0);
  IIC_delay();
  IIC_SCL(1);
  IIC_delay();
}

发送非应答信号

void iic_ack(void)
{   
IIC_SCL(0); 
IIC_delay();
IIC_SDA(1);
IIC_delay(); 
IIC_SCL(1); 
IIC_delay();
}
发送1字节数据
void  iic_send_byte(uint8_t  data)
{
  for(uint8_t t=0;t<8;t++)
  {
      IIC_SDA((data&0x80)>>7);
      IIC_delay(); 
      IIC_SCL(1); 
      IIC_delay();
      IIC_SCL(0);
      data<<=1;//左移1位,用于下一次发送   
    }
    IIC_SDA(1);//发送完成,主机释放SDA线
    
}

读取1字节数据

void  iic_read_byte(uint8_t  ack)
{
  uint8_t receive =0;
  for(uint8_t t=0;t<8;t++)
  {
  receive<<1;//高位先输出,先收到的数据位要左移
  IIC_SCL(1);
  IIC_delay();
  if(IIC_READ_SDA)receive++;
  IIC_SCL(0);
  IIC_delay();
  }
  if(!ack)iic_nack();
  else iic_ack();
  return receive;
}

1、4找个NFC芯片测试代码

/**
 * @brief       NFC读取数据
 * @param      NFC读数据
 * @retval     NFC
 */
uint8_t NFC_readData(uint8_t add1,uint8_t add2)
{   
    uint8_t data;
    iic_start();
    iic_send_byte(0xae);
    iic_wait_ack();
    iic_send_byte(add1);
    iic_wait_ack();
    iic_send_byte(add2);
    iic_wait_ack();
    iic_start();
    iic_send_byte(0xaf);
    iic_wait_ack();
    data =iic_read_byte(1);
    iic_nack();
    iic_stop();
    
    return data;

}


/**
 * @brief       NFC写入数据
 * @param      NFC写数据
 * @retval     NFC
 */
void NFC_writeData(uint8_t add1,uint8_t add2,uint8_t data)
{
    
    iic_start();
    iic_send_byte(0xae);
    iic_wait_ack();
    iic_send_byte(add1);
    iic_wait_ack();
    iic_send_byte(add2);
    iic_wait_ack();
    iic_send_byte(data);
    iic_wait_ack();
    iic_stop();  
    
}

以上代码添加到正点原子HAL库I2C实验工程里,具体代码如下:

myiic.c文件

/**
 ****************************************************************************************************
 * @file        myiic.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2020-04-24
 * @brief       IIC 驱动代码
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 STM32F103开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 * 修改说明
 * V1.0 20200424
 * 第一次发布
 *
 ****************************************************************************************************
 */

#include "./BSP/IIC/myiic.h"
#include "./SYSTEM/delay/delay.h"

/**
 * @brief       初始化IIC
 * @param       无
 * @retval      无
 */
void iic_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    IIC_SCL_GPIO_CLK_ENABLE();  /* SCL引脚时钟使能 */
    IIC_SDA_GPIO_CLK_ENABLE();  /* SDA引脚时钟使能 */

    gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;        /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */
    HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct);/* SCL */

    gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;        /* 开漏输出 */
    HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct);/* SDA */
    /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

    iic_stop();     /* 停止总线上所有设备 */
}

/**
 * @brief       IIC延时函数,用于控制IIC读写速度
 * @param       无
 * @retval      无
 */
static void iic_delay(void)
{
    delay_us(2);    /* 2us的延时, 读写速度在250Khz以内 */
}

/**
 * @brief       产生IIC起始信号
 * @param       无
 * @retval      无
 */
void iic_start(void)
{
    IIC_SDA(1);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(0);     /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    iic_delay();
    IIC_SCL(0);     /* 钳住I2C总线,准备发送或接收数据 */
    iic_delay();
}

/**
 * @brief       产生IIC停止信号
 * @param       无
 * @retval      无
 */
void iic_stop(void)
{
    IIC_SDA(0);     /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);     /* 发送I2C总线结束信号 */
    iic_delay();
}

/**
 * @brief       等待应答信号到来
 * @param       无
 * @retval      1,接收应答失败
 *              0,接收应答成功
 */
uint8_t iic_wait_ack(void)
{
    uint8_t waittime = 0;
    uint8_t rack = 0;

    IIC_SDA(1);     /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
    iic_delay();
    IIC_SCL(1);     /* SCL=1, 此时从机可以返回ACK */
    iic_delay();

    while (IIC_READ_SDA)    /* 等待应答 */
    {
        waittime++;

        if (waittime > 250)
        {
            iic_stop();
            rack = 1;
            break;
        }
    }

    IIC_SCL(0);     /* SCL=0, 结束ACK检查 */
    iic_delay();
    return rack;
}

/**
 * @brief       产生ACK应答
 * @param       无
 * @retval      无
 */
void iic_ack(void)
{
    IIC_SDA(0);     /* SCL 0 -> 1  时 SDA = 0,表示应答 */
    iic_delay();
    IIC_SCL(1);     /* 产生一个时钟 */
    iic_delay();
    IIC_SCL(0);
    iic_delay();
    IIC_SDA(1);     /* 主机释放SDA线 */
    iic_delay();
}

/**
 * @brief       不产生ACK应答
 * @param       无
 * @retval      无
 */
void iic_nack(void)
{
    IIC_SDA(1);     /* SCL 0 -> 1  时 SDA = 1,表示不应答 */
    iic_delay();
    IIC_SCL(1);     /* 产生一个时钟 */
    iic_delay();
    IIC_SCL(0);
    iic_delay();
}

/**
 * @brief       IIC发送一个字节
 * @param       data: 要发送的数据
 * @retval      无
 */
void iic_send_byte(uint8_t data)
{
    uint8_t t;
    
    for (t = 0; t < 8; t++)
    {
        IIC_SDA((data & 0x80) >> 7);    /* 高位先发送 */
        iic_delay();
        IIC_SCL(1);
        iic_delay();
        IIC_SCL(0);
        data <<= 1;     /* 左移1位,用于下一次发送 */
    }
    IIC_SDA(1);         /* 发送完成, 主机释放SDA线 */
}

/**
 * @brief       IIC读取一个字节
 * @param       ack:  ack=1时,发送ack; ack=0时,发送nack
 * @retval      接收到的数据
 */
uint8_t iic_read_byte(uint8_t ack)
{
    uint8_t i, receive = 0;

    for (i = 0; i < 8; i++ )    /* 接收1个字节数据 */
    {
        receive <<= 1;  /* 高位先输出,所以先收到的数据位要左移 */
        IIC_SCL(1);
        iic_delay();

        if (IIC_READ_SDA)
        {
            receive++;
        }
        
        IIC_SCL(0);
        iic_delay();
    }

    if (!ack)
    {
        iic_nack();     /* 发送nACK */
    }
    else
    {
        iic_ack();      /* 发送ACK */
    }

    return receive;
}


/**
 * @brief       code卡读取数据
 * @param       code卡读数据
 * @retval      code卡
 */

void code_readData(uint8_t add)
{
    uint8_t data;
    iic_start();
    iic_send_byte(0xa0);
    iic_wait_ack();
    iic_send_byte(add);
    iic_wait_ack();
    iic_send_byte(0xa1);
    iic_wait_ack();
    data =iic_read_byte(1);
    iic_stop();
    
}

/**
 * @brief       NFC读取数据
 * @param      NFC读数据
 * @retval     NFC
 */

uint8_t NFC_readData(uint8_t add1,uint8_t add2)
{   
    uint8_t data;
    iic_start();
    iic_send_byte(0xae);
    iic_wait_ack();
    iic_send_byte(add1);
    iic_wait_ack();
    iic_send_byte(add2);
    iic_wait_ack();
    iic_start();
    iic_send_byte(0xaf);
    iic_wait_ack();
    data =iic_read_byte(1);
    iic_nack();
    iic_stop();
    
    return data;

}


/**
 * @brief       NFC写入数据
 * @param      NFC写数据
 * @retval     NFC
 */
void NFC_writeData(uint8_t add1,uint8_t add2,uint8_t data)
{
    
    iic_start();
    iic_send_byte(0xae);
    iic_wait_ack();
    iic_send_byte(add1);
    iic_wait_ack();
    iic_send_byte(add2);
    iic_wait_ack();
    iic_send_byte(data);
    iic_wait_ack();
    iic_stop();  
    
}









在main.c中调用:



int main(void)
{


    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    usmart_dev.init(72);                        /* 初始化USMART */
    iic_init();

    while (1)
    {
      //  NFC
       uint8_t  data; 
     
      NFC_writeData(0,64,0x88);
       
       NFC_readData(0,64);
        
        delay_ms(1000);
         delay_ms(1000);

    }
}

看一下效果:

写数据效果如下:

跟着原子学I2C_第2张图片

 读数据效果如下:

跟着原子学I2C_第3张图片

 请注意:读数据要注意

先写新地址,在写两个寄存器地址后,在i2cstart一下,发送度数据指令,在读数据!!!

我之前写的读数据代码是这样的(错误代码如下):

/**
 * @brief       NFC读取数据
 * @param      NFC读数据
 * @retval     NFC
 */
uint8_t NFC_readData(uint8_t add1,uint8_t add2)
{   
    uint8_t data;
    iic_start();
    iic_send_byte(0xaf);
    iic_wait_ack();
    iic_send_byte(add1);
    iic_wait_ack();
    iic_send_byte(add2);
    iic_wait_ack();
    iic_send_byte(0xaf);
    iic_wait_ack();
    data =iic_read_byte(1);
    iic_nack();
    iic_stop();
    
    return data;

}

这样波形根本不对,发送芯片地址有回应,发送寄存器地址没有回应。(错误波形如下)

跟着原子学I2C_第4张图片

完整工程已上传到github,注意测试时I2C从机的芯片地址看数据手册。

你可能感兴趣的:(随便写,嵌入式硬件)