I2C - eeprom_dma

本 demo 主要介绍 I2C 使用 DMA 的方式读写 eeprom。

硬件连接

本 demo 使用到的 gpio 参考 board_i2c0_gpio_init ,将 eeprom 模块与开发板连接,具体引脚连接方式如下表(以BL616为例):

硬件连接

开发板 I2C 引脚

eeprom 模块

SCL(GPIO14)

SCL

SDA(GPIO15)

SDA

GND

GND

VCC

VCC

软件实现

更详细的代码请参考 examples/peripherals/i2c/i2c_eeprom_dma

1board_init();
  • board_init 中会开启 I2C IP 时钟,并选择 I2C 时钟源和分频。

1board_i2c0_gpio_init();
  • 配置相关引脚为 I2C 功能

 1/* Send and receive buffer init */
 2for (size_t i = 0; i < 32; i++) {
 3    ((uint8_t *)send_buffer)[i] = i;
 4    ((uint8_t *)receive_buffer)[i] = 0;
 5}
 6
 7i2c0 = bflb_device_get_by_name("i2c0");
 8
 9bflb_i2c_init(i2c0, 400000);
10bflb_i2c_link_txdma(i2c0, true);
11bflb_i2c_link_rxdma(i2c0, true);
  • 初始化发送和接收 buffer

  • 获取 i2c0 句柄,并初始化 i2c0 频率为 400K

  • bflb_i2c_link_txdma(i2c0, true) 开启 I2C TX DMA 功能

  • bflb_i2c_link_rxdma(i2c0, true) 开启 I2C RX DMA 功能

 1/* Write page 0 */
 2dma0_ch0 = bflb_device_get_by_name("dma0_ch0");
 3
 4struct bflb_dma_channel_config_s tx_config;
 5
 6tx_config.direction = DMA_MEMORY_TO_PERIPH;
 7tx_config.src_req = DMA_REQUEST_NONE;
 8tx_config.dst_req = DMA_REQUEST_I2C0_TX;
 9tx_config.src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
10tx_config.dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
11tx_config.src_burst_count = DMA_BURST_INCR1;
12tx_config.dst_burst_count = DMA_BURST_INCR1;
13tx_config.src_width = DMA_DATA_WIDTH_32BIT;
14tx_config.dst_width = DMA_DATA_WIDTH_32BIT;
15bflb_dma_channel_init(dma0_ch0, &tx_config);
16
17bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL);
  • 对于 TX, DMA 的配置如下:传输方向(direction)为内存到外设(MEMORY_TO_PERIPH),源请求(src_req)为内存,目标请求(dst_req)为 DMA_REQUEST_I2C0_TX

  • 调用 bflb_dma_channel_init(dma0_ch0, &tx_config) 初始化 DMA

  • 调用 bflb_dma_channel_irq_attach(dma0_ch0, dma0_ch0_isr, NULL) 注册 dma 通道 0 中断

 1struct bflb_dma_channel_lli_pool_s tx_llipool[20]; /* max trasnfer size 4064 * 20 */
 2struct bflb_dma_channel_lli_transfer_s tx_transfers[1];
 3tx_transfers[0].src_addr = (uint32_t)send_buffer;
 4tx_transfers[0].dst_addr = (uint32_t)DMA_ADDR_I2C0_TDR;
 5tx_transfers[0].nbytes = 32;
 6bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 20, tx_transfers, 1);
 7
 8msgs[0].addr = 0x50;
 9msgs[0].flags = I2C_M_NOSTOP;
10msgs[0].buffer = subaddr;
11msgs[0].length = 2;
12
13msgs[1].addr = 0x50;
14msgs[1].flags = I2C_M_DMA;
15msgs[1].buffer = NULL;
16msgs[1].length = 32;
17bflb_i2c_transfer(i2c0, msgs, 2);
18
19bflb_dma_channel_start(dma0_ch0);
  • 分配二十块 lli 内存池,最多可以传输 4064 * 20 字节

  • 配置一块内存(tx_transfers)进行传输,源地址(src_addr)为存储发送数据的内存地址(send_buffer),目标地址(dst_addr)为 I2C TX FIFO地址(DMA_ADDR_I2C0_TDR)

  • 调用 bflb_dma_channel_lli_reload(dma0_ch0, tx_llipool, 20, tx_transfers, 1) 初始化

  • 调用 bflb_i2c_transfer(i2c0, msgs, 2) 开启 I2C 传输

  • 调用 bflb_dma_channel_start(dma0_ch0) 启动 DMA 传输

 1/* Read page 0 */
 2dma0_ch1 = bflb_device_get_by_name("dma0_ch1");
 3
 4struct bflb_dma_channel_config_s rx_config;
 5
 6rx_config.direction = DMA_PERIPH_TO_MEMORY;
 7rx_config.src_req = DMA_REQUEST_I2C0_RX;
 8rx_config.dst_req = DMA_REQUEST_NONE;
 9rx_config.src_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
10rx_config.dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
11rx_config.src_burst_count = DMA_BURST_INCR1;
12rx_config.dst_burst_count = DMA_BURST_INCR1;
13rx_config.src_width = DMA_DATA_WIDTH_32BIT;
14rx_config.dst_width = DMA_DATA_WIDTH_32BIT;
15bflb_dma_channel_init(dma0_ch1, &rx_config);
16
17bflb_dma_channel_irq_attach(dma0_ch1, dma0_ch1_isr, NULL);
  • 对于 RX, DMA 的配置如下:传输方向(direction)为外设到内存(PERIPH_TO_MEMORY),源请求(src_req)为 DMA_REQUEST_I2C0_RX ,目标请求(dst_req)为内存

  • 调用 bflb_dma_channel_init(dma0_ch1, &rx_config) 初始化 DMA

  • 调用 bflb_dma_channel_irq_attach(dma0_ch1, dma0_ch1_isr, NULL) 注册 dma 通道 1 中断

 1struct bflb_dma_channel_lli_pool_s rx_llipool[20];
 2struct bflb_dma_channel_lli_transfer_s rx_transfers[1];
 3rx_transfers[0].src_addr = (uint32_t)DMA_ADDR_I2C0_RDR;
 4rx_transfers[0].dst_addr = (uint32_t)receive_buffer;
 5rx_transfers[0].nbytes = 32;
 6
 7bflb_dma_channel_lli_reload(dma0_ch1, rx_llipool, 20, rx_transfers, 1);
 8
 9msgs[1].addr = 0x50;
10msgs[1].flags = I2C_M_DMA | I2C_M_READ;
11msgs[1].buffer = NULL;
12msgs[1].length = 32;
13bflb_i2c_transfer(i2c0, msgs, 2);
14
15bflb_dma_channel_start(dma0_ch1);
  • 分配二十块 lli 内存池,最多可以传输 4064 * 20 字节

  • 配置一块内存(rx_transfers)进行传输,源地址(src_addr)为 I2C RX FIFO地址(DMA_ADDR_I2C0_RDR),目标地址(dst_addr)为存储接收数据的内存地址(receive_buffer)

  • 调用 bflb_dma_channel_lli_reload(dma0_ch1, rx_llipool, 20, rx_transfers, 1) 初始化

  • 调用 bflb_i2c_transfer(i2c0, msgs, 2) 开启 I2C 传输

  • 调用 bflb_dma_channel_start(dma0_ch1) 启动 DMA 传输

1while (dma_tc_flag1 == 0) {
2}
3while ((bflb_i2c_get_intstatus(i2c0) & I2C_INTSTS_END) == 0) {
4}
5bflb_i2c_deinit(i2c0);
  • 数据传输完成后,复位 I2C 模块

1/* Check read data */
2for (uint8_t i = 0; i < 32; i++) {
3    if (((uint8_t *)send_buffer)[i] != ((uint8_t *)receive_buffer)[i]) {
4        printf("check fail, %d write: %02x, read: %02x\r\n", i, ((uint8_t *)send_buffer)[i], ((uint8_t *)receive_buffer)[i]);
5    }
6}
  • 检查发送和读取的数据是否一致

编译和烧录

参考 环境搭建

实验现象

按下 RST 按键,数据传输完成后,打印“write over”,“read over”和“check over”。