zoukankan      html  css  js  c++  java
  • RT-thread 设备驱动组件之IIC总线设备

          本文主要介绍RT-thread中IIC总线设备驱动,涉及到的主要文件有:驱动框架文件(i2c_core.c,i2c_dev.c,i2c-bit-ops.c,i2c_dev.h,i2c.h);底层硬件驱动文件(i2c_soft.c,i2c_soft.h)。这里的i2c_soft.c和i2c_soft.h是指利用MCU的GPIO口模拟IIC总线时序,而不是利用MCU的硬件IIC接口。应用IIC总线设备驱动时,需要在rtconfig.h中添加宏定义#define RT_USING_I2C。若使用GPIO口模拟IIC总线,则还需要添加宏定义#define RT_USING_I2C_BITOPS。

    一、IIC总线设备驱动框架

    先看i2c.h中定义的一些数据结构:

    #define RT_I2C_WR                0x0000
    #define RT_I2C_RD               (1u << 0)
    #define RT_I2C_ADDR_10BIT       (1u << 2)  /* this is a ten bit chip address */
    #define RT_I2C_NO_START         (1u << 4)
    #define RT_I2C_IGNORE_NACK      (1u << 5)
    #define RT_I2C_NO_READ_ACK      (1u << 6)  /* when I2C reading, we do not ACK */
    
    struct rt_i2c_msg
    {
        rt_uint16_t addr;
        rt_uint16_t flags;
        rt_uint16_t len;
        rt_uint8_t  *buf;
    };
    
    struct rt_i2c_bus_device;
    
    struct rt_i2c_bus_device_ops
    {
        rt_size_t (*master_xfer)(struct rt_i2c_bus_device *bus,
                                 struct rt_i2c_msg msgs[],
                                 rt_uint32_t num);
        rt_size_t (*slave_xfer)(struct rt_i2c_bus_device *bus,
                                struct rt_i2c_msg msgs[],
                                rt_uint32_t num);
        rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus,
                                    rt_uint32_t,
                                    rt_uint32_t);
    };
    
    /*for i2c bus driver*/
    struct rt_i2c_bus_device
    {
        struct rt_device parent;
        const struct rt_i2c_bus_device_ops *ops;
        rt_uint16_t  flags;
        rt_uint16_t  addr;
        struct rt_mutex lock;
        rt_uint32_t  timeout;
        rt_uint32_t  retries;
        void *priv;
    };

    i2c_dev.h中相关数据结构(struct rt_i2c_priv_data用于i2c_bus_device_control()函数中RT_I2C_DEV_CTRL_RW控制标志):

    #define RT_I2C_DEV_CTRL_10BIT        0x20
    #define RT_I2C_DEV_CTRL_ADDR         0x21
    #define RT_I2C_DEV_CTRL_TIMEOUT      0x22
    #define RT_I2C_DEV_CTRL_RW           0x23
    
    struct rt_i2c_priv_data
    {
        struct rt_i2c_msg  *msgs;
        rt_size_t  number;
    };

    i2c-bit-ops.h中主要定义了模拟IIC总线时序时需要的数据结构:

    struct rt_i2c_bit_ops
    {
        void *data;            /* private data for lowlevel routines */
        void (*set_sda)(void *data, rt_int32_t state);
        void (*set_scl)(void *data, rt_int32_t state);
        rt_int32_t (*get_sda)(void *data);
        rt_int32_t (*get_scl)(void *data);
    
        void (*udelay)(rt_uint32_t us);
    
        rt_uint32_t delay_us;  /* scl and sda line delay */
        rt_uint32_t timeout;   /* in tick */
    };

    在i2c_dev.c主要实现IIC设备驱动统一接口函数:i2c_bus_device_read(),i2c_bus_device_write(),i2c_bus_device_control()以及rt_i2c_bus_device_device_init()。

    rt_err_t rt_i2c_bus_device_device_init(struct rt_i2c_bus_device *bus,
                                           const char               *name)
    {
        struct rt_device *device;
        RT_ASSERT(bus != RT_NULL);
    
        device = &bus->parent;
    
        device->user_data = bus;
    
        /* set device type */
        device->type    = RT_Device_Class_I2CBUS;
        /* initialize device interface */
        device->init    = RT_NULL;
        device->open    = RT_NULL;
        device->close   = RT_NULL;
        device->read    = i2c_bus_device_read;
        device->write   = i2c_bus_device_write;
        device->control = i2c_bus_device_control;
    
        /* register to device manager */
        rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
    
        return RT_EOK;
    }

    i2c_core.c中实现IIC总线设备注册,以及使用IIC总线进行数据传输,如:rt_i2c_transfer(),rt_i2c_master_send(),rt_i2c_master_recv()。

    rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,
                                        const char               *bus_name)
    {
        rt_err_t res = RT_EOK;
    
        rt_mutex_init(&bus->lock, "i2c_bus_lock", RT_IPC_FLAG_FIFO);
    
        if (bus->timeout == 0) bus->timeout = RT_TICK_PER_SECOND;
    
        res = rt_i2c_bus_device_device_init(bus, bus_name);
    
        i2c_dbg("I2C bus [%s] registered
    ", bus_name);
    
        return res;
    }

    i2c-bit-ops.c中主要实现了利用GPIO模拟IIC总线时序的相关接口函数,如:i2c_start(),i2c_restart(),i2c_stop(),i2c_waitack(),i2c_writeb(),i2c_readb(),i2c_send_bytes(),i2c_send_ack_or_nack(),i2c_recv_bytes(),i2c_send_address(),i2c_bit_send_address()等。并且实现了i2c_bit_xfer():

    static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops =
    {
        i2c_bit_xfer,
        RT_NULL,
        RT_NULL
    };
    rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus,
                                const char               *bus_name)
    {
        bus->ops = &i2c_bit_bus_ops;
    
        return rt_i2c_bus_device_register(bus, bus_name);
    }

    二、底层硬件驱动

    本文采用的是模拟IIC,即用GPIO口模拟IIC时序。在i2c_soft.c中主要实现struct rt_i2c_bit_ops中的指针函数:

    void stm32_set_sda(void *data, rt_int32_t state)
    {
        if(state == 1)
            GPIO_SetBits(I2C1_GPIO , I2C1_GPIO_SDA);   //GPIOB->BSRRL = I2C1_GPIO_SDA
        else if(state == 0)
            GPIO_ResetBits(I2C1_GPIO , I2C1_GPIO_SDA); //GPIOB->BSRRH = I2C1_GPIO_SDA
    }
    
    void stm32_set_scl(void *data, rt_int32_t state)
    {
        if(state == 1)
            GPIO_SetBits(I2C1_GPIO , I2C1_GPIO_SCL);   //GPIOB->BSRRL = I2C1_GPIO_SCL
        else if(state == 0)
            GPIO_ResetBits(I2C1_GPIO , I2C1_GPIO_SCL); //GPIOB->BSRRH = I2C1_GPIO_SCL
    }
    
    rt_int32_t stm32_get_sda(void *data)
    {
        return (rt_int32_t)GPIO_ReadInputDataBit(I2C1_GPIO , I2C1_GPIO_SDA);//return(GPIOB->IDR  & I2C1_GPIO_SDA)
    }
    
    rt_int32_t stm32_get_scl(void *data)
    {
        return (rt_int32_t)GPIO_ReadInputDataBit(I2C1_GPIO , I2C1_GPIO_SCL);//return(GPIOB->IDR  & I2C1_GPIO_SCL)
    }
    
    void stm32_udelay(rt_uint32_t us)
    {
        rt_uint32_t delta;
        /* ¼ÆËãusÑÓʱËùÐè¼ÆÊýÖµ£»sysTick->LOAD=21000, RT_TICK_PER_SECOND=1000 */
        us = us * (SysTick->LOAD/(1000000/RT_TICK_PER_SECOND));
        /* »ñÈ¡µ±Ç°àÖ઼ÆÊýÖµ */
        delta = SysTick->VAL;
        /* ÑÓʱus */
        while (delta - SysTick->VAL< us);
    }
    
    void stm32_mdelay(rt_uint32_t ms)
    {
          stm32_udelay(ms * 1000);
    }
    
    static const struct  rt_i2c_bit_ops stm32_i2c_bit_ops =
    {
        (void*)0xaa,     //no use in set_sda,set_scl,get_sda,get_scl
        stm32_set_sda,
        stm32_set_scl,
        stm32_get_sda,
        stm32_get_scl,
        stm32_udelay,
        20, 
        5
    };

    最后,实现IIC总线硬件初始化(包括RCC时钟配置和GPIO配置,最重要的是将stm32_i2c_bit_ops初始化为IIC总线设备结构体的priv变量,即stm32_i2c.priv = (void *)&stm32_i2c_bit_ops):

    int rt_hw_i2c_init(void)
    {
        static struct rt_i2c_bus_device stm32_i2c;//"static" add by me. It must be add "static", or it will be hard fault
        
        RCC_Configuration();
        GPIO_Configuration();
        
        rt_memset((void *)&stm32_i2c, 0, sizeof(struct rt_i2c_bus_device));
        stm32_i2c.priv = (void *)&stm32_i2c_bit_ops;
        rt_i2c_bit_add_bus(&stm32_i2c, "i2c1");   
            
        return 0;
    }
    INIT_BOARD_EXPORT(rt_hw_i2c_init);//rt_hw_i2c_init will be called in rt_components_board_init()

    三、IIC总线设备初始化

    这里以cs43l22数字音频放大器为例:

    static rt_err_t cs43l22_init(const char * i2c_bus_name)
    {
        i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(i2c_bus_name);
        if(i2c_bus == RT_NULL)
        {    
         rt_kprintf("
    i2c_bus %s for cs43l22 not found!
    ", i2c_bus_name);
         return -RT_ENOSYS;
      }
        
        /* oflag has no meaning for spi device , so set to RT_NULL */
        if(rt_device_open(&i2c_bus->parent, RT_NULL) != RT_EOK)
        {
             rt_kprintf("
    i2c_bus %s for cs43l22 opened failed!
    ", i2c_bus_name);
             return -RT_EEMPTY;
        }
        
        EVAL_AUDIO_Init(OUTPUT_DEVICE_AUTO, volume, I2S_AudioFreq_48k); 
        
        /* it must be at the back of EVAL_AUDIO_Init, which reset the cs43l22 */
      uint8_t chip_id = Codec_ReadRegister(i2c_bus, 0x01);
        rt_kprintf("(chip_id of cs43l22 is 0x%02x)", chip_id);
        
        return 0;
    }
    
    int rt_cs43l22_init(void)
    {
        rt_sem_init(&sem_cs43l22, "cs43l22", 1, RT_IPC_FLAG_FIFO);
        
        cs43l22_init("i2c1");
        
        return 0;
    }
    INIT_APP_EXPORT(rt_cs43l22_init);

    注意事项:

    1、在应用IIC总线设备驱动时,需要用到rt_device_read或rt_device_write,因此在初始化函数中需要调用rt_device_open将IIC总线设备打开。

    2、下面利用rt_device_read和rt_device_write操作寄存器(每一次调用rt_device_read或rt_device_write都包括了i2c_start,i2c_bit_send_address,i2c_recv_bytes/i2c_send_bytes,i2c_stop这4个步骤):

    static uint32_t Codec_WriteRegister(struct rt_i2c_bus_device * i2c_bus, uint8_t RegisterAddr, uint8_t RegisterValue)
    {
      uint32_t result = 0;
        
      rt_uint16_t flags = 0x00;
      rt_uint16_t DevAddr = (rt_uint16_t)CODEC_ADDRESS >> 1;
      rt_off_t pos = (rt_off_t)((flags << 16) | DevAddr);
        
      rt_uint8_t buffer[2];
      buffer[0] = RegisterAddr;
      buffer[1] = RegisterValue;
        
      rt_device_write(&i2c_bus->parent, pos, buffer, sizeof(buffer));
        
    #ifdef VERIFY_WRITTENDATA
      /* Verify that the data has been correctly written */  
      result = (Codec_ReadRegister(i2c_bus, RegisterAddr) == RegisterValue)? 0:1;
        if(result == 0)
            rt_kprintf("
    the reg 0x%02x verify passed
    ",RegisterAddr);
        else
            rt_kprintf("
    the reg 0x%02x verify failed
    ",RegisterAddr);
    #endif /* VERIFY_WRITTENDATA */
    
      /* Return the verifying value: 0 (Passed) or 1 (Failed) */
      return result;  
    }
    static uint8_t Codec_ReadRegister(struct rt_i2c_bus_device * i2c_bus, uint8_t RegisterAddr)
    {    
      rt_uint16_t flags = 0x00;
      rt_uint16_t DevAddr = (rt_uint16_t)CODEC_ADDRESS >> 1;
      rt_off_t pos = (rt_off_t)((flags << 16) | DevAddr);
    
      rt_uint8_t buffer;
      buffer = RegisterAddr;
        
      rt_device_write(&i2c_bus->parent, pos, &buffer, 1);    
      rt_device_read(&i2c_bus->parent, pos, &buffer, 1);
      
      /* Return the byte read from Codec */
      return buffer;
    }

    在上面两个函数中,有符号整型32位pos的高16位表示flags,低16位表示IIC器件地址。flags取值如i2c.h文件中宏定义所示。

    这里说明一个问题:在rt_i2c_master_send和rt_i2c_master_recv函数中均有“msg.flags = flags & RT_I2C_ADDR_10BIT;”这一语句,该句用于标志IIC器件地址是否为10位地址,但是这条语句会将其他预置好的标志全部清除,如RT_I2C_NO_START,RT_I2C_IGNORE_NACK或RT_I2C_NO_READ_ACK。所以,在一般情况下,flags标志只能事先预置RT_I2C_ADDR_10BIT,若IIC器件地址为7位,则直接设置flags为0。

    3、根据i2c_bit_send_address()函数中:

     else
        {
            /* 7-bit addr */
            addr1 = msg->addr << 1;
            if (flags & RT_I2C_RD)
                addr1 |= 1;
            ret = i2c_send_address(bus, addr1, retries);
            if ((ret != 1) && !ignore_nack)
                return -RT_EIO;
        }

    可得,若IIC器件地址为7位,则pos低16位所表示的地址值DevAddr不包括读写标志位(最低位)。而cs43l22数据手册中的8位地址值包含了读写标志位,因此设置DevAddr为CODEC_ADDRESS >> 1。

  • 相关阅读:
    CodeForces 734F Anton and School
    CodeForces 733F Drivers Dissatisfaction
    CodeForces 733C Epidemic in Monstropolis
    ZOJ 3498 Javabeans
    ZOJ 3497 Mistwald
    ZOJ 3495 Lego Bricks
    CodeForces 732F Tourist Reform
    CodeForces 732E Sockets
    CodeForces 731E Funny Game
    CodeForces 731D 80-th Level Archeology
  • 原文地址:https://www.cnblogs.com/King-Gentleman/p/4658615.html
Copyright © 2011-2022 走看看