欢迎光临散文网 会员登陆 & 注册

AT32学习笔记-I2C_DMA.md

2022-11-27 09:14 作者:繁花cloud  | 我要投稿

# bsp初始化

```c

// 设置中断优先级组,抢占优先级4响应优先级0

nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

// 时钟初始化

system_clock_config();

// bsp初始化(串口打印,延迟)

at32_board_init();

```

# 初始化I2C

## 常量定义

```c

// 超时时间

#define I2C_TIMEOUT                      0xFFFFFFFF


// I2C速度,主机地址

#define I2Cx_SPEED                       100000

#define I2Cx_ADDRESS                     0xA0


// I2C端口(I2C1) ,外设时钟CRM_I2C1_PERIPH_CLOCK

#define I2Cx_PORT                        I2C1

#define I2Cx_CLK                         CRM_I2C1_PERIPH_CLOCK


// SCL

#define I2Cx_SCL_PIN                     GPIO_PINS_6

#define I2Cx_SCL_GPIO_PORT               GPIOB

#define I2Cx_SCL_GPIO_CLK                CRM_GPIOB_PERIPH_CLOCK


// SDA

#define I2Cx_SDA_PIN                     GPIO_PINS_7

#define I2Cx_SDA_GPIO_PORT               GPIOB

#define I2Cx_SDA_GPIO_CLK                CRM_GPIOB_PERIPH_CLOCK


// DMA外设时钟,TX通道,TX中断,详见手册

#define I2Cx_DMA_CLK                     CRM_DMA1_PERIPH_CLOCK

#define I2Cx_DMA_TX_CHANNEL              DMA1_CHANNEL6

#define I2Cx_DMA_TX_IRQn                 DMA1_Channel6_IRQn


// RX通道,RX中断

#define I2Cx_DMA_RX_CHANNEL              DMA1_CHANNEL7

#define I2Cx_DMA_RX_IRQn                 DMA1_Channel7_IRQn


// I2C中断

#define I2Cx_EVT_IRQn                    I2C1_EVT_IRQn

#define I2Cx_ERR_IRQn                    I2C1_ERR_IRQn

```

## 初始化

```c

// 创建结构体

i2c_status_type i2c_status;

// 设置为常量中的I2C1

hi2cx.i2cx = I2Cx_PORT;

// 再去调用库文件中的配置

i2c_config(&hi2cx);

```

### 内部实现

会先重置I2C外设,然后再去调用用户自己定义的i2c_lowlevel_init  

```c

/**

  * @brief  i2c peripheral initialization.

  * @param  hi2c: the handle points to the operation information.

  * @retval none.

  */

void i2c_config(i2c_handle_type* hi2c)

{

  /* reset i2c peripheral */

  i2c_reset(hi2c->i2cx);


  /* i2c peripheral initialization */

  i2c_lowlevel_init(hi2c);


  /* i2c peripheral enable */

  i2c_enable(hi2c->i2cx, TRUE);

}

```

## 定义i2c_lowlevel_init函数,初始化

```c

void i2c_lowlevel_init(i2c_handle_type* hi2c)

{

    // 先去初始化GPIO

  gpio_init_type gpio_initstructure;

    // 这里判断一下是不是要初始化我们的I2C外设,这个函数初始化其他的I2C的时候也会调用,所以要判断,I2Cx_PORT为我们设置的常量I2C1

  if(hi2c->i2cx == I2Cx_PORT)

  {

    // 开启I2C外设时钟

    /* i2c periph clock enable */

    crm_periph_clock_enable(I2Cx_CLK, TRUE);

    // 然后开启两个GPIO的时钟

    crm_periph_clock_enable(I2Cx_SCL_GPIO_CLK, TRUE);

    crm_periph_clock_enable(I2Cx_SDA_GPIO_CLK, TRUE);


    // 开始配置GPIO

    /* gpio configuration */

    // GPIO输出模式,开漏输出

    // 可选 GPIO_OUTPUT_PUSH_PULL GPIO_OUTPUT_OPEN_DRAIN

    gpio_initstructure.gpio_out_type       = GPIO_OUTPUT_OPEN_DRAIN;

    // GPIO上拉,开启内部上拉,这样就不用外部上拉电阻了

    gpio_initstructure.gpio_pull           = GPIO_PULL_UP;

    // GPIO模式改为IOMUX,AT32特有,内部会自动处理输入输出模式

    gpio_initstructure.gpio_mode           = GPIO_MODE_MUX;

    // 驱动强度,选择强驱动就行

    gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;


    // 设置GPIO PIN

    /* configure i2c pins: scl */

    gpio_initstructure.gpio_pins = I2Cx_SCL_PIN;

    // 开始初始化GPIO

    gpio_init(I2Cx_SCL_GPIO_PORT, &gpio_initstructure);


    /* configure i2c pins: sda */

    gpio_initstructure.gpio_pins = I2Cx_SDA_PIN;

    // 开始初始化GPIO

    gpio_init(I2Cx_SDA_GPIO_PORT, &gpio_initstructure);


    // 这里先开启一下DMA的TX RX中断,后面等待发送完成会用到

    /* configure and enable i2c dma channel interrupt */

    nvic_irq_enable(I2Cx_DMA_TX_IRQn, 0, 0);

    nvic_irq_enable(I2Cx_DMA_RX_IRQn, 0, 0);


    // 开启DMA外设时钟

    /* i2c dma tx and rx channels configuration */

    /* enable the dma clock */

    crm_periph_clock_enable(I2Cx_DMA_CLK, TRUE);


    // 先给I2C设置好DMA通道

    hi2c->dma_tx_channel = I2Cx_DMA_TX_CHANNEL;

    hi2c->dma_rx_channel = I2Cx_DMA_RX_CHANNEL;


    // 然后重置DMA通道

    /* i2c dma channel configuration */

    dma_reset(hi2c->dma_tx_channel);

    dma_reset(hi2c->dma_rx_channel);


    // 给DMA初始化结构体

    dma_default_para_init(&hi2c->dma_init_struct);

    // 外设地址自动增加,关闭

    hi2c->dma_init_struct.peripheral_inc_enable    = FALSE;

    // 内存地址自动增加,开启

    hi2c->dma_init_struct.memory_inc_enable        = TRUE;

    // 外设数据宽度,I2C每次为8位

    hi2c->dma_init_struct.peripheral_data_width    = DMA_PERIPHERAL_DATA_WIDTH_BYTE;

    // 内存中的数据宽度,和上方的一样

    hi2c->dma_init_struct.memory_data_width        = DMA_MEMORY_DATA_WIDTH_BYTE;

    // 循环模式,开启后会一直发送

    hi2c->dma_init_struct.loop_mode_enable         = FALSE;

    // 优先级,设置低

    hi2c->dma_init_struct.priority                 = DMA_PRIORITY_LOW;

    // DMA的方向,内存至外设

    hi2c->dma_init_struct.direction                = DMA_DIR_MEMORY_TO_PERIPHERAL;


    // 初始化DMA

    dma_init(hi2c->dma_tx_channel, &hi2c->dma_init_struct);

    dma_init(hi2c->dma_rx_channel, &hi2c->dma_init_struct);


    // 初始化I2C

    // 第二个参数用于设置在快速模式下(Fast Speed Mode)的时钟线SCL高低电平宽度比值,当第三个参数speed>100000时,该参数有效,当speed<=100000时,该参数无效,此时可以填任意值

    // I2C_FSMODE_DUTY_2_1:低电平宽度:低电平宽度=2:1

    // I2C_FSMODE_DUTY_16_9:低电平宽度:低电平宽度=16:9

    i2c_init(hi2c->i2cx, I2C_FSMODE_DUTY_2_1, I2Cx_SPEED);


    // 这个貌似是ARM系列的一个特性,主机和从机是一份代码,要设置我们自己的地址,做主机的时候随意即可

    i2c_own_address1_set(hi2c->i2cx, I2C_ADDRESS_MODE_7BIT, I2Cx_ADDRESS);

  }

}


```

# 添加中断处理函数

at32f413_int.c

```c

// 一定要加这一段,要不然不能用

#include "i2c_application.h"


extern i2c_handle_type hi2cx;


// 定义常量

#define I2Cx_DMA_TX_IRQHandler           DMA1_Channel6_IRQHandler

#define I2Cx_DMA_RX_IRQHandler           DMA1_Channel7_IRQHandler

#define I2Cx_EVT_IRQHandler              I2C1_EVT_IRQHandler

#define I2Cx_ERR_IRQHandler              I2C1_ERR_IRQHandler


// 这里调用了库文件中的irq处理函数,为了配合后面等待传送结束的等待

/**

  * @brief  this function handles dma interrupt request.

  * @param  none

  * @retval none

  */

void I2Cx_DMA_RX_IRQHandler(void)

{

  i2c_dma_rx_irq_handler(&hi2cx);

}


/**

  * @brief  this function handles dma interrupt request.

  * @param  none

  * @retval none

  */

void I2Cx_DMA_TX_IRQHandler(void)

{

  i2c_dma_tx_irq_handler(&hi2cx);

}

```


# DMA发送

## 函数原型

```c

/**

  * @brief  the master transmits data through dma 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_dma(i2c_handle_type* hi2c, uint16_t address, uint8_t* pdata, uint16_t size, uint32_t timeout)

```

## 使用方法

```c

// 先去定义一个I2C_status 变量,这是一个enum

i2c_status_type i2c_status;

// i2c_master_transmit_dma 发送,同时赋值+判断

// 0x44为从机地址,记得左移一位留给控制位设置发送还是接收

if((i2c_status = i2c_master_transmit_dma(&hi2cx, 0x44 << 1, tx_buf1, 2, I2C_TIMEOUT)) != I2C_OK)

{

    error_handler(i2c_status);

}

```

```c

// 让DMA发送后,我们要等待发送完成,要不然不知道从机是不是真的处理完了我们发送的数据

/* wait for the communication to end */

if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)

{

    error_handler(i2c_status);

}

```


# DMA接收

## 函数原型

```c

/**

  * @brief  the master receive data through dma 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_dma(i2c_handle_type* hi2c, uint16_t address, uint8_t* pdata, uint16_t size, uint32_t timeout)

```

## 使用方法

```c

// 开始接收,一般我们要先发送I2C命令才能开始接收

if((i2c_status = i2c_master_receive_dma(&hi2cx, 0x44<<1, rx_buf1, 6, I2C_TIMEOUT)) != I2C_OK)

{

    error_handler(i2c_status);

}


// 等待接收完成

/* wait for the communication to end */

if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)

{

    error_handler(i2c_status);

}

```


# DEMO

## SHT30读取

```c

hi2cx.i2cx = I2Cx_PORT;

i2c_config(&hi2cx);

// 0X30,0XA2 首先对芯片重置

uint8_t tx_buf1[2] = {0x30,0xa2};

/* start the request reception process */

if((i2c_status = i2c_master_transmit_dma(&hi2cx, 0x44 << 1, tx_buf1, 2, I2C_TIMEOUT)) != I2C_OK)

{

    error_handler(i2c_status);

}

/* wait for the communication to end */

if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)

{

    error_handler(i2c_status);

}

// 按照数据手册要求去等待初始化完成

delay_ms(10); // RST delay


while(1)

{

    // 开始读取,首先去发送读取命令

    tx_buf1[0] = 0x24;

    tx_buf1[1] = 0x00;

    if((i2c_status = i2c_master_transmit_dma(&hi2cx, 0x44 << 1, tx_buf1, 2, I2C_TIMEOUT)) != I2C_OK)

    {

        error_handler(i2c_status);

    }


    /* wait for the communication to end */

    if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)

    {

        error_handler(i2c_status);

    }

    

    // 等待芯片完成温度读取和转换

    delay_ms(20);

    /* start the request reception process */

    uint8_t rx_buf1[6] = {0};

    if((i2c_status = i2c_master_receive_dma(&hi2cx, 0x44<<1, rx_buf1, 6, I2C_TIMEOUT)) != I2C_OK)

    {

        error_handler(i2c_status);

    }


    /* wait for the communication to end */

    if(i2c_wait_end(&hi2cx, I2C_TIMEOUT) != I2C_OK)

    {

        error_handler(i2c_status);

    }

    

    // 打印一下原始值

    printf("%X,%X,%X,%X,%X,%X\r\n",rx_buf1[0],rx_buf1[1],rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5]);

    uint16_t temperature_raw = rx_buf1[0] << 8 | rx_buf1[1];

    uint16_t humidity_raw = rx_buf1[3] << 8 | rx_buf1[4];

    

    // 转换

    float temperature = (((float)temperature_raw / 65535.0) * 175) - 45;

    float humidity = ((float)humidity_raw / 65535.0) * 100;

    

    // 打印温度

    printf("temperature = %.2fC,humidity = %.2f%% \r\n",temperature,humidity);

    

    // 延迟

    delay_ms(1000);

}

```


AT32学习笔记-I2C_DMA.md的评论 (共 条)

分享到微博请遵守国家法律