RT-Thread使用——I2C硬件驱动
前言
使用了RT-Thread一段时间了,总体感觉还不错,配置起来挺方便的,资料也挺齐全。但是有个很不爽的地方,I2C还是用软件模拟,太浪费cpu的资源了。捣鼓了几天,通过研究spi驱动框架,仿照着写了一个I2C硬件驱动文件。
- 硬件:STM32F405RGT6芯片
- RT-Thread版本:4.1.0(发行版)
RT-Thread SPI设备组成
RT-Thread配置使用SPI后,工程文件中会在DeviceDrivers分组中添加spi_core.c和spi_dev.c文件,在Drivers分组下会添加drv_spi.c文件。
spi_dev.c文件实现了对SPI的设备抽象操作,如设备初始化,打开,关闭,读,写等操作,spi_core.c文件实现的是对SPI的总线抽象操作,如总线注册,设备挂在,收发等操作。在这两个文件中,其实并不涉及到对SPI硬件本身的操作。对于SPI的硬件操作,是在drv_spi.c文件中实现的,包括硬件初始化,中断处理等,调用了HAL实现。
RT-Thread I2C总线设备组成
RT-Thread在配置使用I2C后,工程文件中会在DeviceDrivers分组中添加i2c_core.c、i2c_dev.c和i2c-bit-ops.c文件,在Drivers分组下会添加drv_soft_i2c.c文件。
与SPI类似的,i2c_dev.c文件实现了对SPI的设备抽象操作,如设备初始化,打开,关闭,读,写等操作,i2c_core.c文件实现的是对SPI的总线抽象操作,如总线注册,收发等操作。比起SPI设备,I2C总线设备的组件文件多了一个i2c-bit-ops.c,用来模拟i2c的时序。drv_soft_i2c.c文件实现的是硬件操作,即初始化gpio,配置i2c参数,初始化互斥锁等。
通过对比发现,I2C的总线设备框架和SPI设备框架基本一样,只是I2C多了一个实现模拟时序的i2c-bit-ops.c文件而已。因此,我们可以依样画葫芦,保留i2c_core.c和i2c_dev.c,重新写一个drv_i2c.c文件,用来调用HAL的I2C相关操作函数,以此来实现在RT-Thread上使用硬件I2C。
RT-Thread 硬件I2C实现
为了更方便地使用I2C,我把ENV跟I2C配置相关的文件修改了,并且添加了几个文件:
- 在bsp\stm32\libraries\HAL_Drivers文件夹下添加两个文件
drv_i2c.h
#ifndef __DRV_I2C_H
#define __DRV_I2C_H
#include <rtthread.h>
#include "rtdevice.h"
#include <rthw.h>
#include <drv_common.h>
#include "drv_dma.h"
#include <drivers/i2c.h>
struct stm32_i2c_config
{
I2C_TypeDef *Instance;
char *bus_name;
struct dma_config *dma_rx, *dma_tx;
};
struct rt_i2c_configuration
{
uint32_t ClockSpeed; /*!< Specifies the clock frequency.
This parameter must be set to a value lower than 400kHz */
uint32_t DutyCycle; /*!< Specifies the I2C fast mode duty cycle.
This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */
uint32_t OwnAddress1; /*!< Specifies the first device own address.
This parameter can be a 7-bit or 10-bit address. */
uint32_t AddressingMode; /*!< Specifies if 7-bit or 10-bit addressing mode is selected.
This parameter can be a value of @ref I2C_addressing_mode */
uint32_t DualAddressMode; /*!< Specifies if dual addressing mode is selected.
This parameter can be a value of @ref I2C_dual_addressing_mode */
uint32_t OwnAddress2; /*!< Specifies the second device own address if dual addressing mode is selected
This parameter can be a 7-bit address. */
uint32_t GeneralCallMode; /*!< Specifies if general call mode is selected.
This parameter can be a value of @ref I2C_general_call_addressing_mode */
uint32_t NoStretchMode; /*!< Specifies if nostretch mode is selected.
This parameter can be a value of @ref I2C_nostretch_mode */
};
/* stm32 i2c dirver class */
struct stm32_i2c
{
I2C_HandleTypeDef handle;
struct stm32_i2c_config *config;
struct rt_i2c_configuration *cfg;
struct
{
DMA_HandleTypeDef handle_rx;
DMA_HandleTypeDef handle_tx;
} dma;
rt_uint8_t i2c_dma_flag;
struct rt_i2c_bus_device i2c_bus;
};
int rt_hw_i2c_init(void);
rt_err_t rt_i2c_master_read(struct rt_i2c_bus_device *bus, uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size)