【Ai-WB2中级篇】I2C通信接口

[复制链接]
查看873 | 回复2 | 2024-9-5 16:11:54 | 显示全部楼层 |阅读模式
I2C (linter-intergrated Circuit)是一种串行通讯总线,使用多主从架构,用来连接低速外围装置。 每个器件都有一个唯一的地址识别,并且都可以作为一个发送器或接收器。每个连接到总线的器件都可以通过唯一的地址和一直存在的主、从机关系用软件设置地址,主机可以作为主机发送器或主机接收器。如果有两个或多个主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏。 BL602/BL604包含一个12C控制器主机,可灵活配置slaveAddr、subAddr以及传输数据,方便与从设备通信,提供2个word深度的fif0,提供中断功能,可搭配DMA使用提高效率,可灵活调整时钟频率。
本文将详细介绍如何使用Ai-WB2的I2C模块。
一:
I2C介绍
BL602芯片的I2C有如下特性:
      · 支持主机模式
      · 支持多主机模式和仲裁功能
      · 时钟频率可灵活调整
      · 最高工作频率为40MHZ

12C时钟设置
I2C的时钟是由bck(bus clock)而来,可以在bck时钟的基础上做分频处理。 寄存器 I2C_PRD_DATA 可以对数据段的时钟做分频处理。i2c模块将数据发送分为4个阶段,每个阶段在寄存器中用单独一个字节来控制,每个阶段的采样个数是可以设置的,4个采样数共同决定了i2c clock的分频系数。比如现在bck是32M,寄存器I2C_PRD_DATA在不做配置默认情况下的值是0x15151515,那么12C的时钟频率为 32M/(15+1)*4)=500K,同理,寄存器I2C_PRD_START和I2C_PRD_STOP也会分别对起始位和停止位的时钟做分频处理。
I2C配置项
      · 读写标志位
      · 从设备地址
      · 从设备寄存器地址
      · 从设备寄存器地址长度
      · 数据(发送时,配置发送的数据;接收时,存储接收到的数据)
      · 数据长度
      · 使能信号

1) 读写标志位
I2C支持发送和接收两种工作状态,寄存器PKTDIR表示发送或者接收状态,设置为0时,表示发送状态,设置为1时,表示接收状态。
2) 从设备地址
每个对接I2C的从设备,都会有唯一设别地址,通常该地址是7位长度,将从设备地址写入寄存器 SLVADDR,I2C在将从设备地址发送出去之前,会自动左移1位,并在最低位补上发送接收方向位。
3) 从设备寄存器地址
从设备青存器地址表示I2C需要对从设备某个寄存器做读写操作的寄存器地址,将从设备寄存器地址写入寄存器 I2C_SUB_ADDR,同时需要将寄存器 SAEN 置1。如果将寄存器 SAEN 置0,那么!2C主机发送时会跳过从设备寄存器地址段。
4) 从设备寄存器地址长度
将从设备寄存器地址长度减1再写入青存器 SABC。
5) 数据
数据部分表示需要发送到从设备的数据,或者需要从从设备接收到的数据。 当I2C发送数据时,需要将数据依次以word为单位写入I2CFIFO,发送数据写FIFO的寄存器地址 I2C_FIFO_WDATA。当I2C接收数据时,需要依次以word为单位从12C_FIFO中将数据读出来,接收数据读FIFO的寄存器地址I2C_FIFO_RDATA。
6) 数据长度
将数据长度减1再写入寄存器PKTLEN。
7) 使能信号
将以上几项配置完成后,再将使能信号寄存器 MEN 写1,就自动启动I2C发送流程了。
当读写标志位配置为0时,12C发送数据,主机发送流程:
      1.  起始位
      2.  (从设备地址左移1位+0)+ ACK
      3.  从设备寄存器地址 +ACK
      4. 1字节数据+ ACK
      5. 1字节数据+ACK
      6.  停止位

当读写标志位配置为1时,I2C接收数据,主机发送流程:
      1. 起始位
      2. (从设备地址左移1位+0)+ ACK
      3. 从设备寄存器地址 +ACK
      4. 起始位
      5. (从设备地址左移1位+1)+ ACK
      6. 1字节数据+ ACK
      7. 1字节数据+ ACK
      8. 停止位

FIFO管理
I2C FIFO深度为2个word,I2C发送和接收可分为RX FIFO和TX FIFO。寄存器 RFICNT 表示RX FIFO中有多少数据(单位word)需要读取。 寄存器 TFICNT 表示TX FIFO中剩余多少空间(单位Word)可供写入。
I2C FIFO状态:
      · RX FIFO underfow:当RX FIFO中的数据被读取完毕或者为空时,继续从RX FIFO中读取数据,寄存器 RFIU 会被置位;
      · RX FIFO overiow:当I2C接收数据直到RX FIFO的2个word被填满后,在没有读取RX FIFO的情况下,12C再次接收到数据,寄存器 RFIO 会被位;
      · TX FIFO underiow:当向TX FIFO中填入的数据大小不满足配置的(2C数据长度 PKTLEN,并且已经没有新数据继续填入TXFIFO中时,寄存器TFIU 会被置位;
      · TX FIFO overfow:当TX FIFO的2个word被填满后,在TXFIFO中的数据没有发出去之前,再次向TXFIFO中填入数据,寄存器 TFIO 会被置位。

DMA配置流程
I2C可以使用DMA进行数据的发送和接收。将 DTEN 置1,则开启DMA发送模式,为I2C分配好通道后,DMA会将数据从存储区传输型12C_FIFO_WDATA寄存器中。将 DREN 置1.则开启DMA接收模式,为I2C分配好通道后,DMA会将 I2C_FIFO_RDATA寄存器中的传输到存储区中。I2C模块搭配使用DMA时,数据部分将由DMA自动完成搬运,不需要CPU再将数据写入I2CTXFIFO或者从I2C RXFIFO中读取数据。
DMA发送流程
      1. 配置读写标志位为0
      2. 配置从设备地址
      3. 配置从设备寄存器地址
      4. 配置从设备寄存器地址长度
      5. 数据长度
      6. 使能信号寄存器置1
      7. 配置DMA transfer size
      8. 配置DMA源地址transfer width
      9. 配置DMA目的地址transfer width(需要注意!2C搭配DMA使用时,目的地址transfer width需要设置为32bits,以word对齐使用)
      10. 配置DMA源地址为存储发送数据的内存地址
      11. 配置DMA目的地址为12CTXFIFO地址,I2C_FIFO_WDATA
      12. 使能DMA

DMA接收流程
      1. 配置读写标志位为1
      2. 配置从设备地址
      3. 配置从设备寄存器地址
      4. 配置从设备寄存器地址长度
      5. 数据长度
      6. 使能信号寄存器置1
      7. 配置DMA transfer size
      8. 配置DMA源地址transfer width(需要注意)2C搭配DMA使用时,源地址transfer width需要设置为32bits,以word对齐使用)
      9. 配置DMA目的地址transfer width
      10. 配置DMA源地址为12C RXFIFO地址,I2C_FIFO_RDATA
      11. 配置DMA目的地址为存储接收数据的内存地址
      12. 使能DMA

中断
I2C包括如下几种中断:
      · I2C_TRANS_END_INT:I2C传输结束中断
      · I2C_TX_FIFO_READY_INT:当I2C TX FIFO有空闲空间可用于填充时,触发中断

      · I2C_RX_FIFO_READY_INT: 当I2C RX FIFO接收到数据时,触发中断
      · I2C_NACK_RECV_INT: 当I2C模块检测到NACK状态,触发中断
      · I2C_ARB_LOST_INT:I2C仲裁丢失中断
      · I2C_FIFO_ERR_INT:I2C FIFO ERROR中断

二:I2C驱动API介绍
I2C的HOSAL层APl在 components/platform/hosal/include/hosal_i2c.h 中定义,常用的AP如下:
· int hosal_i2c_init(hosal_i2c_dev_t *i2c):12C初始化。参数说明如下:
      · i2c:12C设备实例。其定义如下:

  1.   typedef struct {
  2.       uint8_t       port;             /**< @brief i2c 端口 */
  3.       hosal_i2c_config_t  config;     /**< @brief i2c 配置 */
  4.       void         *priv;             /**< @brief 用户自定义数据 */
  5.   } hosal_i2c_dev_t;
复制代码
其中,hosal_i2c_config_t为l2C配置,定义如下:
  1. #define HOSAL_I2C_MODE_MASTER 1     /**< @brief i2c communication is master mode */
  2. #define HOSAL_I2C_MODE_SLAVE  2     /**< @brief i2c communication is slave mode */

  3. typedef struct {
  4.     uint32_t address_width; /**< @brief 地址模式: 7 bit or 10 bit */
  5.     uint32_t freq;          /**< @brief 时钟频率 */
  6.     uint8_t  scl;           /**< @brief i2c clk 引脚 */
  7.     uint8_t  sda;           /**< @brief i2c data 引脚 */
  8.     uint8_t  mode;          /**< @brief master 或 slave 模式 */
  9. } hosal_i2c_config_t;
复制代码
      · 返回值:成功时,返回0;否则,返回非零值。
· int hosal_i2c_master_send(hosal_i2c_dev_t *i2c,uint16_t dev_addr, const uint8_t *data,uint16_t size, uint32_ttimeout):Master模式下发送数据。参数说明如下:
      · i2c:12C设备实例
      · dev_addr:从机设备地址
      · data:需要发送的数据
      · size:发送的数据长度
      · timeout:通信超时时长
      · 返回值:成功时,返回0;否则,返回非零值。

· int hosal_i2c_master_recv(hosal_i2c_dev_t *i2c, uint16_t dev_addr, uint8 t *data,uint16_t size, uint32_t timeout)Master模式下接收数据。参数说明如下:
      · i2c:I2C设备实例
      · dev_addr:从机设备地址
      · data:接收发送的数据
      · size:接收的数据长度
      · timeout:通信超时时长
      · 返回值:成功时,返回0;否则,返回非零值。

· int hosal_i2c_slave_send(hosal_i2c_dev_t *i2c, const uint8_t *data, uint16_t size, uint32_t timeout) : Slave模式下发送数据,参数说明如下:
      · i2c:I2C设备实例
      · data:接收发送的数据
      · size:接收的数据长度
      · timeout:通信超时时长
      · 返回值:成功时,返回0,否则,返回非零值。

· int hosal_i2c_slave_recy(hosal_i2c_dev_t *i2c, uint8_t *data, uint16_t size, uint32_t timeout):
Slave模式下接收数据。参数说明如下:
      · i2c:I2C设备实例
      · data:接收发送的数据
      · size:接收的数据长度
      · timeout:通信超时时长
      · 返回值:成功时,返回0;否则,返回非零值。

· int hosal_i2c_mem_write(hosal_i2c_dev_t *i2c, uint16_t dev_addr, uint32_t mem_addr,uint16_t mem_addr_size, const wint8_t*data,uint16_ t size,uint32_t timeout):I2C mem写数据。参数说明如下:
      · i2c:12C设备实例
      · dev_addr:从机地址
      · mem_addr:从机内存(寄存器)地址
      · mem_addr_size:内存(寄存器)地址长度。定义如下:

  1.   #define HOSAL_I2C_MEM_ADDR_SIZE_8BIT  1 /**< @brief i2c memory address size 8bit */
  2.   #define HOSAL_I2C_MEM_ADDR_SIZE_16BIT 2 /**< @brief i2c memory address size 16bit */
  3.   #define HOSAL_I2C_MEM_ADDR_SIZE_24BIT 3 /**< @brief i2c memory address size 24bit */
  4.   #define HOSAL_I2C_MEM_ADDR_SIZE_32BIT 4 /**< @brief i2c memory address size 32bit */
复制代码
      · data:接收发送的数据
      · size:接收的数据长度
      · timeout:通信超时时长
      · 返回值:成功时,返回0;否则,返回非零值。

· int hosal_i2c_mem_read(hosal_i2c_dev_t *i2c, uint16_t dev_addr, uint32_t mem_addr,uint16_t mem_addr_size, uint8_t *data.uint16_t size,uint32_t timeout):l2C Mem读数据。参数说明如下:
      · i2c:12C设备实例
      · dev_addr:从机地址
      · mem addr:从机内存(寄存器)地址
      · mem_addr_size:内存(青存器)地址长度
      · data:接收发送的数据
      · size:接收的数据长度
      · timeout:通信超时时长
      · 返回值:成功时,返回0;否则,返回非零值。

· int hosal_i2c_finalize(hosal_i2c_dev_t *i2c):销毁l2C设备实例,释放相关资源。参数说明如下:
      · i2c:12C设备实例
      · 返回值:成功时,返回0;否则,返回非零值。

三:I2C使用实例
下面将演示在主机模式下,如何对I2C的设备地址进行扫描。代码如下:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <FreeRTOS.h>
  4. #include <task.h>
  5. #include <stdio.h>
  6. #include <stdbool.h>
  7. #include <blog.h>
  8. #include "hosal_i2c.h"

  9. static hosal_i2c_dev_t i2c0;


  10. void i2c_master_init(void) {
  11.     int ret = -1;
  12.     int i = 0;

  13.     i2c0.port = 0;
  14.     i2c0.config.freq = 100000;                                    /* only support 305Hz~100000Hz */
  15.     i2c0.config.address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT;     /* only support 7bit */
  16.     i2c0.config.mode = HOSAL_I2C_MODE_MASTER;                     /* only support master */
  17.     i2c0.config.scl = 4;
  18.     i2c0.config.sda = 3;

  19.     /* init i2c with the given settings */
  20.     ret = hosal_i2c_init(&i2c0);
  21.     if (ret != 0) {
  22.         hosal_i2c_finalize(&i2c0);
  23.         blog_error("hosal i2c init failed!\r\n");
  24.         return;
  25.     }
  26.     uint8_t tx_data[] = {0x01};
  27.     uint8_t rx_data[1] = {0x00};
  28.     for(uint8_t i = 0;i < 128;i++){
  29.         int ret = hosal_i2c_master_send(&i2c0,i,tx_data,1,100);
  30.         ret = hosal_i2c_master_recv(&i2c0,i,rx_data,1,100);
  31.         if(ret == 0){
  32.             printf("0x%x ",i);
  33.         }else{
  34.             printf(".");
  35.         }
  36.         if(i != 0 && i % 16 == 0){
  37.             printf("\r\n");
  38.         }
  39.     }
  40.     printf("\r\n");
  41.    
  42.     vTaskDelay(500);

  43.     ret = hosal_i2c_finalize(&i2c0);
  44.     if (ret != 0) {
  45.         blog_error("hosal i2c finalize failed!\r\n");
  46.         return;
  47.     }

  48. }


  49. void main(void) {

  50.     printf("start to init i2c master...\r\n");
  51.     i2c_master_init();
  52. }
复制代码
用心做好保姆工作
回复

使用道具 举报

lovzx | 2024-9-5 19:06:24 | 显示全部楼层
学习
回复

使用道具 举报

djy876 | 7 天前 | 显示全部楼层
学习打卡
回复

使用道具 举报

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

本版积分规则