591|3

1867

帖子

3

资源

版主

【AT-START-F425测评】解读I2C i2c_application.c [复制链接]

【前言】 准备使用AT32F425的i2c外设,查看AT32F425_Finrmware_Library_V2.0.1下面的i2c_apllication_library下的对i2c进行了中间件的封装,我是第一次看到官方对i2c进行二次封装的程序,但是他的注释都是英文的,这本着入门学习的着度来解读一下这个文档。

【目的】通过这个文档的学习,通过使用中间件的使用来驱动AH10温湿度计。

i2c_application.h

/** @defgroup I2C_library_event_check_flag  //定义事件检查标志
  * @{
  */

#define I2C_EVENT_CHECK_NONE             ((uint32_t)0x00000000)    /*!<不进行标志检查 */
#define I2C_EVENT_CHECK_ACKFAIL          ((uint32_t)0x00000001)    /*!< 检查ACK失败 */
#define I2C_EVENT_CHECK_STOP             ((uint32_t)0x00000002)    /*!< 检查STOP信号 */

 

/** @defgroup I2C_library_transmission_mode  //工作的模式 定义了主从发送与接收模式,DMA主机发送与接收模式
  * @{
  */

typedef enum 
{
  I2C_INT_MA_TX = 0,  
  I2C_INT_MA_RX,
  I2C_INT_SLA_TX,
  I2C_INT_SLA_RX,
  I2C_DMA_MA_TX,
  I2C_DMA_MA_RX,
  I2C_DMA_SLA_TX,
  I2C_DMA_SLA_RX,  
} i2c_mode_type;
 

/** @defgroup I2C_library_status_code  //工作状态模式 定义了正与错误的状态码,
  * @{
  */

typedef enum 
{
  I2C_OK = 0,          /*!< no error */
  I2C_ERR_STEP_1,      /*!< step 1 error */
  I2C_ERR_STEP_2,      /*!< step 2 error */
  I2C_ERR_STEP_3,      /*!< step 3 error */
  I2C_ERR_STEP_4,      /*!< step 4 error */
  I2C_ERR_STEP_5,      /*!< step 5 error */
  I2C_ERR_STEP_6,      /*!< step 6 error */
  I2C_ERR_STEP_7,      /*!< step 7 error */
  I2C_ERR_STEP_8,      /*!< step 8 error */
  I2C_ERR_STEP_9,      /*!< step 9 error */
  I2C_ERR_STEP_10,     /*!< step 10 error */
  I2C_ERR_STEP_11,     /*!< step 11 error */
  I2C_ERR_STEP_12,     /*!< step 12 error */
  I2C_ERR_TCRLD,       /*!< tcrld error */
  I2C_ERR_TDC,         /*!< tdc error */
  I2C_ERR_ADDR,        /*!< addr error */
  I2C_ERR_STOP,        /*!< stop error */
  I2C_ERR_ACKFAIL,     /*!< ackfail error */
  I2C_ERR_TIMEOUT,     /*!< timeout error */
  I2C_ERR_INTERRUPT,   /*!< interrupt error */
} i2c_status_type;

 

/** @defgroup I2C_library_handler
  * @{
  */

typedef struct 
{
  i2c_type                               *i2cx;                   /*!< i2c registers base address    i2c寄存器地址  */
  uint8_t                                *pbuff;                  /*!< pointer to i2c transfer buffer    传输数据的数据指针  */
  __IO uint16_t                          psize;                   /*!< i2c transfer size       需要传输的数据大小        */
  __IO uint16_t                          pcount;                  /*!< i2c transfer counter        传输计数    */
  __IO uint32_t                          mode;                    /*!< i2c communication mode     传输工作模式     */
  __IO uint32_t                          status;                  /*!< i2c communication status    通信状态   */
  __IO i2c_status_type                   error_code;              /*!< i2c error code         错误代码         */
  dma_channel_type                       *dma_tx_channel;         /*!< dma transmit channel      DMA发送传输通道      */
  dma_channel_type                       *dma_rx_channel;         /*!< dma receive channel       DMA接收传输通道      */
  dma_init_type                          dma_init_struct;         /*!< dma init parameters       DMA初始参数状态      */
} i2c_handle_type;  //i2c句柄类型

 

《i2c_application.c》

 

/**
  * @brief get the dma transfer direction flag through the channel  通过通首获取DMA传输方向的标志
  */
#define DMA_GET_REQUEST(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)hi2c->dma_tx_channel)) ? I2C_DMA_REQUEST_TX : I2C_DMA_REQUEST_RX)

 

/**
  * @brief get the dma transfer complete flag through the channel  //通过通道获取DMA传输完成的标志
  */
#define DMA_GET_TC_FLAG(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL1))? DMA1_FDT1_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL2))? DMA1_FDT2_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL3))? DMA1_FDT3_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL4))? DMA1_FDT4_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL5))? DMA1_FDT5_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL6))? DMA1_FDT6_FLAG : \
                                                         DMA1_FDT7_FLAG)

 

/**
  * @brief get the dma half transfer flag through the channel  通过通道获取dma半传输标志
  */
#define DMA_GET_HT_FLAG(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL1))? DMA1_HDT1_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL2))? DMA1_HDT2_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL3))? DMA1_HDT3_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL4))? DMA1_HDT4_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL5))? DMA1_HDT5_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL6))? DMA1_HDT6_FLAG : \
                                                         DMA1_HDT7_FLAG)

 

/**
  * @brief get the dma transfer error flag through the channel  通过通道获取dma传输错误标志
  */
#define DMA_GET_TERR_FLAG(DMA_CHANNEL) \
(((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL1))? DMA1_DTERR1_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL2))? DMA1_DTERR2_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL3))? DMA1_DTERR3_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL4))? DMA1_DTERR4_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL5))? DMA1_DTERR5_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL6))? DMA1_DTERR6_FLAG : \
 ((uint32_t)(DMA_CHANNEL) == ((uint32_t)DMA1_CHANNEL7))? DMA1_DTERR7_FLAG : \
                                                         DMA1_DTERR7_FLAG)

 

/**
  * @brief i2c transmission status  传输状态
  */
#define I2C_START                        0
#define I2C_END                          1

 

/**

其实函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。

加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数,。

  * @brief  initializes peripherals used by the i2c.  初始化i2c使用的外围设备。注意:这个函数在主函数或者其他的函数上要自己定义。
  * @param  none
  * @retval none
  */
__WEAK void i2c_lowlevel_init(i2c_handle_type* hi2c)
{
  
}

/** 
  * @brief  i2c peripheral initialization.  设备初始化函数
  * @param  hi2c: the handle points to the operation information. 数参为:指向hi2c 结构体指针。
  * @retval none.
  */
void i2c_config(i2c_handle_type* hi2c)

  /* reset i2c peripheral  初始化外设 */
  i2c_reset(hi2c->i2cx);
  /* i2c peripheral initialization   初始化i2c 定义 i2c寄存器地址、 传输数据的数据指针、 需要传输的数据大小   等等 hi2c可以事先定义,也可以在i2c_lowlevel_init里面重新指定参数*/   
  i2c_lowlevel_init(hi2c);
  /* i2c peripheral enable    使能i2c外设 */   
  i2c_enable(hi2c->i2cx, TRUE);
}

 

/** 
  * @brief  refresh i2c register.  刷新i2c寄存器。
  * @param  hi2c: the handle points to the operation information. 参数为(i2c_handle_type结构指针 个人理解为替换了i2c外设或者是传输模式变换了,重定传输参数后重新配置参数
  * @retval none.
  */
void i2c_refresh_txdt_register(i2c_handle_type* hi2c)
{
  /* clear tdis flag */
  if (i2c_flag_get(hi2c->i2cx, I2C_TDIS_FLAG) != RESET)
  {
    hi2c->i2cx->txdt = 0x00;
  }

  /* refresh txdt register*/
  if (i2c_flag_get(hi2c->i2cx, I2C_TDBE_FLAG) == RESET)
  {
    hi2c->i2cx->sts_bit.tdbe = 1;
  }
}

/** 
  * @brief  reset ctrl2 register. 重置ctrl2寄存器。主机发送时,要先设置I2C_CTRL2_CNT = N。
  * @param  hi2c: the handle points to the operation information.
  * @retval none.
  */
void i2c_reset_ctrl2_register(i2c_handle_type* hi2c)
{
  hi2c->i2cx->ctrl2_bit.saddr   = 0;    
  hi2c->i2cx->ctrl2_bit.readh10 = 0; 
  hi2c->i2cx->ctrl2_bit.cnt     = 0; 
  hi2c->i2cx->ctrl2_bit.rlden   = 0; 
  hi2c->i2cx->ctrl2_bit.dir     = 0; 
}

/** 
  * @brief  wait for the transfer to end.  等待数据传输结果
  * @param  hi2c: the handle points to the operation information. 指向外设的的结构体
  * @param  timeout: maximum waiting time.  超时时间   如果用硬件I2C,如果不设置超时,就可以阻塞在等那里
  * @retval i2c status.  返回状态
  */
i2c_status_type i2c_wait_end(i2c_handle_type* hi2c, uint32_t timeout)
{
  while(hi2c->status != I2C_END)
  {
    /* check timeout */
    if((timeout--) == 0)
    {
      return I2C_ERR_TIMEOUT;
    }
  }

  if(hi2c->error_code != I2C_OK)
  {
    return hi2c->error_code;
  }
  return I2C_OK;
}

 

/** 
  * @brief  wait for the flag。等待传输标志
  * @param  hi2c: the handle points to the operation information. 指向外设的结构体
  * @param  flag: flag to wait.  需要等待的标志
  * @param  status: status to wait.  需要等待的状态
  * @param  event_check: flag to check while waiting for the flag.  检测的事件标志,只能选择下三个标志
  *         parameter as following values:    
  *         - I2C_EVENT_CHECK_NONE  
  *         - I2C_EVENT_CHECK_ACKFAIL   
  *         - I2C_EVENT_CHECK_STOP
  * @param  timeout: maximum waiting time.  超时时间
  * @retval i2c status.  返回状态
  */
i2c_status_type i2c_wait_flag(i2c_handle_type* hi2c, uint32_t flag, flag_status status, uint32_t event_check, uint32_t timeout)
{
  while(i2c_flag_get(hi2c->i2cx, flag) == status)
  {
    /* check the ack fail flag */
    if(event_check & I2C_EVENT_CHECK_ACKFAIL)
    {
      if(hi2c->i2cx->sts & I2C_ACKFAIL_FLAG)
      {
        /* clear ack fail flag */
        i2c_flag_clear(hi2c->i2cx, I2C_ACKFAIL_FLAG);
        hi2c->error_code = I2C_ERR_ACKFAIL;
        return I2C_ERR_ACKFAIL;
      }
    }

    /* check the stop flag */
    if(event_check & I2C_EVENT_CHECK_STOP)
    {
      if(hi2c->i2cx->sts & I2C_STOPF_FLAG)
      {
        /* clear stop flag */
        i2c_flag_clear(hi2c->i2cx, I2C_STOPF_FLAG);

        i2c_reset_ctrl2_register(hi2c);
        hi2c->error_code = I2C_ERR_STOP; 
        return I2C_ERR_STOP;
      }    
    }

    /* check timeout */
    if((timeout--) == 0)
    {
      hi2c->error_code = I2C_ERR_TIMEOUT; 
      return I2C_ERR_TIMEOUT;
    }
  }
  return I2C_OK;
}

 

/** 
  * @brief  dma transfer cofiguration. //DMA传输配置
  * @param  hi2c: the handle points to the operation information.  外设结构构
  * @param  dma_channelx: dma channel to be cofigured.   dma传输通道
  * @param  pdata: data buffer.  传输数发送或接收地址
  * @param  size: data size.   数据长度
  * @retval none.
  */
void i2c_dma_config(i2c_handle_type* hi2c, dma_channel_type* dma_channel, uint8_t* pdata, uint16_t size)

  /* disable the dma channel */
  dma_channel_enable(dma_channel, FALSE);
  
  /* disable the transfer complete interrupt   先关dma传输中断*/
  dma_interrupt_enable(dma_channel, DMA_FDT_INT, FALSE);  
  
  /* configure the dma channel with the buffer address and the buffer size  配置dma 通道、传输方向 、地址、等 */
  hi2c->dma_init_struct.memory_base_addr     = (uint32_t)pdata; 
  hi2c->dma_init_struct.direction            = (dma_channel == hi2c->dma_tx_channel) ? DMA_DIR_MEMORY_TO_PERIPHERAL : DMA_DIR_PERIPHERAL_TO_MEMORY;
  hi2c->dma_init_struct.peripheral_base_addr = (dma_channel == hi2c->dma_tx_channel) ? (uint32_t)&hi2c->i2cx->txdt : (uint32_t)&hi2c->i2cx->rxdt;
  hi2c->dma_init_struct.buffer_size          = (uint32_t)size;  
  dma_init(dma_channel, &hi2c->dma_init_struct);

  /* enable the transfer complete interrupt  开启dma传输中断 */
  dma_interrupt_enable(dma_channel, DMA_FDT_INT, TRUE);
  /* enable the dma channel   使能dma 通道*/
  dma_channel_enable(dma_channel, TRUE);
}

 

/** 
  * @brief  start transfer in poll mode or interrupt mode. 在轮询模式或中断模式下启动传输  
  * @param  hi2c: the handle points to the operation information. 外设结构体
  * @param  address: slave address.  从机地址
  * @param  start_stop: config gen start condition mode. 启动条件 只能选择下面三个参数
  *         parameter as following values:
  *         - I2C_WITHOUT_START: transfer data without start condition. 不需要起始信号
  *         - I2C_GEN_START_READ: read data and generate start.读取数据并启动START信号
  *         - I2C_GEN_START_WRITE: send data and generate start. 发送数据并启动START信号
  * @retval i2c status.  返回状态
  */
void i2c_start_transfer(i2c_handle_type* hi2c, uint16_t address, i2c_start_stop_mode_type start_stop)
{
  if (hi2c->pcount > MAX_TRANSFER_CNT)
  {
    hi2c->psize = MAX_TRANSFER_CNT;
    i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_RELOAD_MODE, start_stop);
  }
  else
  {
    hi2c->psize = hi2c->pcount;
    i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_AUTO_STOP_MODE, start_stop);
  }
}

 

/** 
  * @brief  start transfer in dma mode. 在dma 模式下开启数据传输
  * @param  hi2c: the handle points to the operation information.

   这里他漏了一个注释参数,就是传输通道
  * @param  address: slave address.  从机地址
  * @param  start_stop: config gen start condition mode. 选择以下三个参数
  *         parameter as following values:
  *         - I2C_WITHOUT_START: transfer data without start condition.
  *         - I2C_GEN_START_READ: read data and generate start.
  *         - I2C_GEN_START_WRITE: send data and generate start.
  * @retval i2c status.返回状态  
  */
void i2c_start_transfer_dma(i2c_handle_type* hi2c, dma_channel_type* dma_channelx, uint16_t address, i2c_start_stop_mode_type start_stop)
{
  if (hi2c->pcount > MAX_TRANSFER_CNT)
  {
    hi2c->psize = MAX_TRANSFER_CNT;
    /* config dma */
    i2c_dma_config(hi2c, dma_channelx, hi2c->pbuff, hi2c->psize);      
    i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_RELOAD_MODE, start_stop);
  }
  else
  {
    hi2c->psize = hi2c->pcount;
    /* config dma */
    i2c_dma_config(hi2c, dma_channelx, hi2c->pbuff, hi2c->psize);   
    i2c_transmit_set(hi2c->i2cx, address, hi2c->psize, I2C_AUTO_STOP_MODE, start_stop);
  }
}

 

/** 
  * @brief  the master transmits data through polling mode.  主机通过轮询模式传输数据。
  * @param  hi2c: the handle points to the operation information.  外设结构体
  * @param  address: slave address.  从机地址
  * @param  pdata: data buffer.  数据缓冲区
  * @param  size: data size.  数据长度
  * @param  timeout: maximum waiting time. 最长等待时间
  * @retval i2c status.  返回状态
  */
i2c_status_type i2c_master_transmit(i2c_handle_type* hi2c, uint16_t address, uint8_t* pdata, uint16_t size, uint32_t timeout)
{  
  /* initialization parameters */
  hi2c->pbuff = pdata;
  hi2c->pcount = size;

  hi2c->error_code = I2C_OK;
  /* wait for the busy falg to be reset   等总线由忙转为可发送状态*/
  if (i2c_wait_flag(hi2c, I2C_BUSYF_FLAG, SET, I2C_EVENT_CHECK_NONE, timeout) != I2C_OK)
  {
    return I2C_ERR_STEP_1;  //如果超时了,返回状态1
  }

  /* start transfer   开始传输 */
  i2c_start_transfer(hi2c, address, I2C_GEN_START_WRITE);

  while (hi2c->pcount > 0)
  {
    /* wait for the tdis falg to be set  等待数据可传输 */
    if(i2c_wait_flag(hi2c, I2C_TDIS_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
    {
      return I2C_ERR_STEP_2;  //如果超时返回状态2
    }
    /* send data */
    i2c_data_send(hi2c->i2cx, *hi2c->pbuff++);
    hi2c->psize--;    
    hi2c->pcount--;

    if ((hi2c->psize == 0) && (hi2c->pcount != 0))
    {
      /* wait for the tcrld falg to be set  等待从机返回状态 */
      if (i2c_wait_flag(hi2c, I2C_TCRLD_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
      {
        return I2C_ERR_STEP_3; 返回错误状态3
      }
      /* continue transfer  由于还要传输数据 不等待START状态 */
      i2c_start_transfer(hi2c, address, I2C_WITHOUT_START);
    }
  }

  /* wait for the stop falg to be set  数据传输结束 等待STOP信号  */
  if(i2c_wait_flag(hi2c, I2C_STOPF_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
  {
    return I2C_ERR_STEP_4;  返回错误状态 未等到STOP信号
  }

  /* clear stop flag  清除STOP状态 */
  i2c_flag_clear(hi2c->i2cx, I2C_STOPF_FLAG);

  /* reset ctrl2 register  重置外设寄存器 */
  i2c_reset_ctrl2_register(hi2c);

  return I2C_OK;
}
 

/** 
  * @brief  the master receive data through polling mode. 主机通过轮询模式接收数据
  * @param  hi2c: the handle points to the operation information.
  * @param  address: slave address.
  * @param  pdata: data buffer.
  * @param  size: data size.
  * @param  timeout: maximum waiting time.
  * @retval i2c status.
  */
i2c_status_type i2c_master_receive(i2c_handle_type* hi2c, uint16_t address, uint8_t* pdata, uint16_t size, uint32_t timeout)
{  
  /* initialization parameters */
  hi2c->pbuff = pdata;
  hi2c->pcount = size;

  hi2c->error_code = I2C_OK;
   
  /* wait for the busy falg to be reset   等待总结的空闲*/
  if (i2c_wait_flag(hi2c, I2C_BUSYF_FLAG, SET, I2C_EVENT_CHECK_NONE, timeout) != I2C_OK)
  {
    return I2C_ERR_STEP_1;
  }

  /* start transfer   开始传输*/
  i2c_start_transfer(hi2c, address, I2C_GEN_START_READ);

  while (hi2c->pcount > 0)
  {
    /* wait for the rdbf falg to be set 等 I2C_STS_RDBF  寄存器为1  */
    if(i2c_wait_flag(hi2c, I2C_RDBF_FLAG, RESET, I2C_EVENT_CHECK_ACKFAIL, timeout) != I2C_OK)
    {
      return I2C_ERR_STEP_2;
    }

    /* read data */
    (*hi2c->pbuff++) = i2c_data_receive(hi2c->i2cx);
    hi2c->pcount--;    
    hi2c->psize--;

    if ((hi2c->psize == 0) && (hi2c->pcount != 0))
    {
      /* wait for the tcrld falg to be set 等待 I2C_STS_TCRLD 寄存器为1 */
      if (i2c_wait_flag(hi2c, I2C_TCRLD_FLAG, RESET, I2C_EVENT_CHECK_NONE, timeout) != I2C_OK)
      {
        return I2C_ERR_STEP_3;
      }
      
      /* continue transfer  继续接收 不待待START信号 */
      i2c_start_transfer(hi2c, address, I2C_WITHOUT_START);
    }
  }

 

抄了好久的作业,读到这里基本上用的函数也差不多了。我怕抄太多了把网站的空间给占完了。

 

【楼下将我的I2C驱动AH10】展示。

 


回复

5139

帖子

236

资源

管理员

“我怕抄太多了把网站的空间给占完了。”
——什么意思?


回复

5139

帖子

236

资源

管理员

涉及到代码的地方,可以用编辑器的 代码插入 功能,会更好一些

点评

谢谢管理员的提醒,下次注意。  详情 回复 发表于 2022-3-28 16:55

回复

1867

帖子

3

资源

版主

nmg 发表于 2022-3-28 16:34 涉及到代码的地方,可以用编辑器的 代码插入 功能,会更好一些

谢谢管理员的提醒,下次注意。


回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

最新文章 更多>>
    关闭
    站长推荐上一条 1/10 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表