STM32F4-DISCO 学习之I2C高效实现

/ 0评 / 0

之前使用I2C是在CS43L22或者DCMI下用过,不过,实际上,我们用的效率比较低,只是为了实现某些功能而已,实际上并不是需要这样的.先计算一下,I2C的时序,以及事件.

首先读时序,对于I2C,先是读写时序.

  1. 第一步是I2C_GenerateSTART,这个会发生I2C_EVENT_MASTER_MODE_SELECT事件.
  2. 第二步是I2C_Send7bitAddress,发送从机地址,以及读写状态.这时候会发生I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED事件.
  3. 第三步是I2C_SendData,发送的是从寄存器地址,这三步,读写一致,会发生I2C_EVENT_MASTER_BYTE_TRANSMITTED事件.
    1. 针对写入时序,会反复进入I2C_EVENT_MASTER_BYTE_TRANSMITTING,直到发送结束.
    2. 针对读取时序,重新执行第一第二步,然后反复进入I2C_EVENT_MASTER_BYTE_TRANSMITTING.

那么,我们就可以开I2C事件中断,查询我们事件,因为整个I2C生命周期里面,我们就不再干预了,所以,最好是有个类似标志位的东西. 我们需要知道的是,当前I2C时候空闲,在做Read还是Write.目标设备的地址DeviceAddress,目标寄存器地址DeviceRegister,需要传输的长度BufferLen,以及我用到的pBuff,另外再传输过程需要记录,还需要PointerIndex,另外I2C只有事件中断,我们中断逻辑内应该是读写分离的,读时候不写,写时候不读,读取时序其实是写-读,写入时序是写.所以我捏造了两个人工中断.I2C1_RX_IRQHandler和I2C1_TX_IRQHandler.定义结构体.

typedef struct
{
    __IO uint8_t IDLE: 1;
    uint8_t RW: 1;
    uint8_t DeviceAddress;
    uint8_t DeviceRegister;
    volatile uint8_t PointerIndex;
    uint8_t BufferLen;
    volatile uint8_t * pBuff;
    void (* IRQn)();
} I2C1_Hal_Def;

初始化就和普通I2C初始化没什么区别,只需要多加一个中断.

void I2C1_Hal_Init(void)
{
    I2C_InitTypeDef  I2C_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    I2C_Cmd(I2C1, DISABLE);
    I2C_DeInit(I2C1);
    /* Connect I2C_SCL*/
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
    /* Connect I2C_SDA*/
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
    /* Configure I2C pins: SCL and SDA ---------------------------------------*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    /* DISABLE I2C event and buffer interrupt */
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);
    /* I2C configuration -----------------------------------------------------*/
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x32;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 400000;
    I2C_Init(I2C1, &I2C_InitStructure);
    /* Configure and enable I2C interrupt ------------------------------------*/
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    /* Enable I2C ------------------------------------------------------------*/
    I2C_Cmd(I2C1, ENABLE);
}

 但是,写入读出函数,就是需要填充刚才的结构体,然后发送一个Start,马上就能进入中断.

uint32_t I2C1_Hal_Write(uint8_t *buff, uint16_t i2cSaleAddress, uint8_t writeAddress, uint16_t writeLen)
{
    if (I2C_Hal_Status.IDLE != 0)
        return I2C1_FAIL;
    I2C_Hal_Status.IDLE  = 1;
    I2C_Hal_Status.DeviceAddress = i2cSaleAddress;
    I2C_Hal_Status.DeviceRegister = writeAddress;
    I2C_Hal_Status.PointerIndex = 0;
    I2C_Hal_Status.RW = 0;
    I2C_Hal_Status.BufferLen = writeLen;
    I2C_Hal_Status.pBuff = buff;
    I2C_Hal_Status.IRQn = (void ( *)( ))I2C1_TX_IRQHandler;
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);
    I2C_AcknowledgeConfig(I2C1, ENABLE);
    /* Send I2C START condition */
    I2C_GenerateSTART(I2C1, ENABLE);
    return I2C1_OK;
}

 这个函数,执行后马上就返回了, 并不阻塞任何的应用.在Start传输后,会马上进入中断,在中断内进行事务传输.

static void I2C1_TX_IRQHandler(void)
{
    switch (I2C_GetLastEvent(I2C1))
    {
        /* Test on I2Cx EV5 and clear it */
    case I2C_EVENT_MASTER_MODE_SELECT:
        I2C_Send7bitAddress(I2C1, I2C_Hal_Status.DeviceAddress, I2C_Direction_Transmitter);
        break;
        /* Test on I2Cx EV6 and first EV8 and clear them */
    case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
        /* Send the first data */
        I2C_SendData(I2C1, I2C_Hal_Status.DeviceRegister);  /* EV8 just after EV6 */
        break;
    case I2C_EVENT_MASTER_BYTE_TRANSMITTING:
        if((I2C_Hal_Status.PointerIndex < I2C_Hal_Status.BufferLen) && (I2C_Hal_Status.RW == 0))
        {
            /* Transmit buffer data */
            I2C_SendData(I2C1, I2C_Hal_Status.pBuff[I2C_Hal_Status.PointerIndex++]);
        }
        else
        {
            I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);
        }
        break;
        /* Test on I2Cx EV8 and clear it */
    case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
        if (I2C_Hal_Status.RW != 0)
        {
            I2C_Hal_Status.IRQn = (void ( *)(void *))I2C1_RX_IRQHandler;
            I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
            I2C_GenerateSTART(I2C1, ENABLE);
        }
        else
        {
            I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);
            I2C_AcknowledgeConfig(I2C1, DISABLE);
            I2C_GenerateSTOP(I2C1, ENABLE);
            I2C_Hal_Status.IDLE = 0;
        }
        break;
    }
}

 在Read的时候也同理,但是Read函数一样是进入I2C1_TX_IRQHandler,不过,RW位不一样,如果是RW为读,那么,再次发送Start,否则,在传输后,就发送STOP了.当然,既然非阻塞,也就是传说中的异步,那么,必然有异步的查询,查一下东西好了没,于是做一个函数,查BUSY位和用户BUSY.

uint32_t I2C1_Hal_Poll(void)
{
    return (I2C_Hal_Status.IDLE || I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
}

 测试方法:

    I2C1_Hal_Init();
    aBuffer[0] = 0x01;
    for(;;)
    {
        I2C1_Hal_Write(aBuffer, 0xA6, 0x2F, 3);
        while(I2C1_Hal_Poll());
        I2C1_Hal_Read(aBuffer, 0xA6, 0x2F, 3);
        while(I2C1_Hal_Poll());
        I2C1_Hal_Write(aBuffer, 0xA6, 0x2F, 0);
        while(I2C1_Hal_Poll());
        I2C1_Hal_Read(aBuffer, 0xA6, 0x2F, 0);
        while(I2C1_Hal_Poll());
    }

完整C代码:

#include 
#include 
#include "I2C.h"
static void I2C1_RX_IRQHandler(void);
static void I2C1_TX_IRQHandler(void);
static I2C1_Hal_Def I2C_Hal_Status;
void I2C1_Hal_Init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    GPIOB_B->AFRL.AFR6 = 0x04; //AF_I2C
    GPIOB_B->AFRH.AFR9 = 0x04;
    GPIOB_B->MODER.MODER6 = 0x02;
    GPIOB_B->MODER.MODER9 = 0x02;
    GPIOB_B->OSPEEDR.OSPEEDR6 = 0x02;
    GPIOB_B->OSPEEDR.OSPEEDR9 = 0x02;
    GPIOB_B->OTYPER.OT6 = 0x01;
    GPIOB_B->OTYPER.OT9 = 0x01;
    I2C1->CR1 = 0x00000001;
    I2C1->CR2 = 0x0000002A;
    I2C1->CCR = 0x00008023;
    I2C1->TRISE = 0x0000000D;
    /* Configure and enable I2C interrupt ------------------------------------*/
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
uint32_t I2C1_Hal_Write(uint8_t *buff, uint16_t i2cSaleAddress, uint8_t writeAddress, uint8_t writeLen)
{
    if (I2C_Hal_Status.IDLE != 0)
        return I2C1_FAIL;
    I2C_Hal_Status.IDLE  = 1;
    I2C_Hal_Status.DeviceAddress = i2cSaleAddress;
    I2C_Hal_Status.DeviceRegister = writeAddress;
    I2C_Hal_Status.PointerIndex = 0;
    I2C_Hal_Status.RW = 0;
    I2C_Hal_Status.BufferLen = writeLen;
    I2C_Hal_Status.pBuff = buff;
    I2C_Hal_Status.IRQn = (void ( *)( ))I2C1_TX_IRQHandler;
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);
    I2C1_B->CR1.ACK  = 1;
    I2C1_B->CR1.START = 1;
    return I2C1_OK;
}
uint32_t I2C1_Hal_Read(uint8_t *pBuff, uint16_t DeviceAddress, uint8_t DeviceRegister, uint8_t BufferLen)
{
    if (I2C_Hal_Status.IDLE != 0)
        return I2C1_FAIL;
    I2C_Hal_Status.IDLE = 1;
    I2C_Hal_Status.DeviceAddress = DeviceAddress;
    I2C_Hal_Status.DeviceRegister = DeviceRegister;
    I2C_Hal_Status.PointerIndex = 0;
    I2C_Hal_Status.RW = 1;
    I2C_Hal_Status.BufferLen = BufferLen;
    I2C_Hal_Status.pBuff = pBuff;
    I2C_Hal_Status.IRQn = (void ( *)())I2C1_TX_IRQHandler;
    I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, ENABLE);
    I2C1_B->CR1.ACK  = 1;
    I2C1_B->CR1.START = 1;
    return I2C1_OK;
}
uint32_t I2C1_Hal_Poll(void)
{
    return (I2C_Hal_Status.IDLE || I2C1_B->SR2.BUSY );
}
static void I2C1_TX_IRQHandler(void)
{
    switch (I2C_GetLastEvent(I2C1))
    {
        /* Test on I2Cx EV5 and clear it */
    case I2C_EVENT_MASTER_MODE_SELECT:
        I2C_Send7bitAddress(I2C1, I2C_Hal_Status.DeviceAddress, I2C_Direction_Transmitter);
        break;
        /* Test on I2Cx EV6 and first EV8 and clear them */
    case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
        /* Send the first data */
        I2C_SendData(I2C1, I2C_Hal_Status.DeviceRegister);  /* EV8 just after EV6 */
        break;
    case I2C_EVENT_MASTER_BYTE_TRANSMITTING:
        if((I2C_Hal_Status.PointerIndex < I2C_Hal_Status.BufferLen) && (I2C_Hal_Status.RW == 0))
        {
            /* Transmit buffer data */
            I2C_SendData(I2C1, I2C_Hal_Status.pBuff[I2C_Hal_Status.PointerIndex++]);
        }
        else
        {
            I2C_ITConfig(I2C1, I2C_IT_BUF, DISABLE);
        }
        break;
        /* Test on I2Cx EV8 and clear it */
    case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
        if (I2C_Hal_Status.RW != 0)
        {
            I2C_Hal_Status.IRQn = (void ( *)(void *))I2C1_RX_IRQHandler;
            I2C_ITConfig(I2C1, I2C_IT_BUF, ENABLE);
            I2C_GenerateSTART(I2C1, ENABLE);
        }
        else
        {
            I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);
            I2C_AcknowledgeConfig(I2C1, DISABLE);
            I2C_GenerateSTOP(I2C1, ENABLE);
            I2C_Hal_Status.IDLE = 0;
        }
        break;
    }
}
static void I2C1_RX_IRQHandler(void)
{
    switch (I2C_GetLastEvent(I2C1))
    {
        /* Test on I2Cx EV5 and clear it */
    case I2C_EVENT_MASTER_MODE_SELECT:
        /* Send I2Cx slave Address for write */
        I2C_Send7bitAddress(I2C1, I2C_Hal_Status.DeviceAddress, I2C_Direction_Receiver);
        break;
        /* Test on I2Cx EV6 and first EV8 and clear them */
    case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:
        if (I2C_Hal_Status.PointerIndex == (I2C_Hal_Status.BufferLen - 1))
        {
            I2C_AcknowledgeConfig(I2C1, DISABLE);
            I2C_GenerateSTOP(I2C1, ENABLE);
        }
        break;
        /* Test on I2Cx EV2 and clear it */
    case I2C_EVENT_MASTER_BYTE_RECEIVED:
        I2C_Hal_Status.pBuff[I2C_Hal_Status.PointerIndex++] = I2C_ReceiveData(I2C1);
        if (I2C_Hal_Status.PointerIndex == (I2C_Hal_Status.BufferLen - 1))
        {
            I2C_AcknowledgeConfig(I2C1, DISABLE);
            I2C_GenerateSTOP(I2C1, ENABLE);
        }
        else if (I2C_Hal_Status.PointerIndex >= I2C_Hal_Status.BufferLen)
        {
            I2C_Hal_Status.IRQn = (void ( *)(void *))I2C1_TX_IRQHandler;   //???????
            I2C_ITConfig(I2C1, I2C_IT_EVT | I2C_IT_BUF, DISABLE);
            I2C_Hal_Status.IDLE = 0;
        }
        break;
    }
}
void I2C1_EV_IRQHandler(void)
{
    if (I2C_Hal_Status.IRQn)
    {
        I2C_Hal_Status.IRQn();
    }
}

 完整头文件:

#ifndef _USER_I2C_H_
#define _USER_I2C_H_
#include "stm32f4xx.h"
#define I2C1_FAIL 1
#define I2C1_OK 0
typedef struct
{
    __IO uint8_t IDLE: 1;
    uint8_t RW: 1;
    uint8_t DeviceAddress;
    uint8_t DeviceRegister;
    volatile uint8_t PointerIndex;
    uint8_t BufferLen;
    volatile uint8_t * pBuff;
    void (* IRQn)();
} I2C1_Hal_Def;
void I2C1_Hal_Init(void);
uint32_t I2C1_Hal_Write(uint8_t *buff, uint16_t i2cSaleAddress, uint8_t writeAddress, uint8_t writeLen);
uint32_t I2C1_Hal_Read(uint8_t *buff, uint16_t i2cSaleAddress, uint8_t readAddress, uint8_t readLen);
uint32_t I2C1_Hal_Poll(void);
#endif

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注