秀一个GD32的通用IIC主机驱动

[复制链接]
3422|11
手机看帖
扫描二维码
随时随地手机跟帖
Simon21ic|  楼主 | 2017-1-22 16:56 | 显示全部楼层 |阅读模式
本帖最后由 Simon21ic 于 2017-1-22 17:02 编辑

使用中断的方式,没有用DMA,把阻塞等待的代码减少了最少:
调用方式类似linux的IIC驱动方式,不过简化了非常多,代码也非常简单
不过,这个库是有BUG的,只是在我们目前的项目中,没问题,就懒得改了


#include "app_cfg.h"
#include "app_type.h"
#include "interfaces.h"

#if IFS_I2C_EN

#include "../core.h"
#define IIC_NUM                                2

struct i2c_ctrl_t
{
        uint8_t chip_addr;
        uint8_t msg_len;
        uint8_t msg_prt;
        uint16_t msg_buf_prt;
        struct interface_i2c_msg_t *msg;
        void *param;
        void (*callback)(void *, vsf_err_t);
} static gd32f1x0_i2c[IIC_NUM];

struct i2c_param_t
{
        I2C_TypeDef *reg;
        struct gd32f1x0_afio_pin_t sda[3];
        struct gd32f1x0_afio_pin_t scl[3];
} static const gd32f1x0_i2c_param[IIC_NUM] =
{
#if IIC_NUM >= 1
        {
                I2C1,
                {
                        {0, 10, 4},                // PA10, AF4
                        {1, 7, 1},                // PB7, AF1
                        {1, 9, 1},                // PB9, AF1
                },
                {
                        {0, 9, 4},                // PA9, AF4
                        {1, 6, 1},                // PB6, AF1
                        {1, 8, 1},                // PB8, AF1
                },
        },
#endif
#if IIC_NUM >= 2
        {
                I2C2,
                {
                        {0, 1, 4},                // PA1, AF4
                        {1, 11, 1},                // PB11, AF1
                        {6, 7, -1},                // PF7, default
                },
                {
                        {0, 0, 4},                // PA0, AF4
                        {1, 10, 1},                // PB10, AF1
                        {6, 6, -1},                // PF6, default
                },
        },
#endif
};

// index spec(LSB to MSB):
// 2-bit iic_idx, 3-bit sda_idx, 3-bit scl_idx
vsf_err_t gd32f1x0_i2c_init(uint8_t index)
{
        uint8_t iic_idx = (index & 0x03) >> 0;
        uint8_t sda_idx = (index & 0x1C) >> 2;
        uint8_t scl_idx = (index & 0xE0) >> 5;
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;

        gd32f1x0_afio_config(&gd32f1x0_i2c_param[iic_idx].sda[sda_idx], gd32f1x0_GPIO_AF | gd32f1x0_GPIO_OD);
        gd32f1x0_afio_config(&gd32f1x0_i2c_param[iic_idx].scl[scl_idx], gd32f1x0_GPIO_AF | gd32f1x0_GPIO_OD);
        
        RCC->APB1CCR |= RCC_APB1CCR_I2C1EN << iic_idx;
        reg->CTLR1 = 0;
        return VSFERR_NONE;
}

vsf_err_t gd32f1x0_i2c_fini(uint8_t index)
{
        uint8_t iic_idx = (index & 0x03) >> 0;
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;
        reg->CTLR1 = 0;
        return VSFERR_NONE;
}

vsf_err_t gd32f1x0_i2c_config(uint8_t index, uint16_t kHz)
{
        uint8_t iic_idx = (index & 0x03) >> 0;
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;
        struct gd32f1x0_info_t *info;
        uint16_t iic_clk, iic_freq;
        
        gd32f1x0_interface_get_info(&info);
        iic_clk = info->apb_freq_hz / 1000000;
        iic_clk = min(iic_clk, 36);
        reg->CTLR2 = iic_clk;
        
        if (kHz <= 100)
        {
                // standard mode
                iic_freq = info->apb_freq_hz / (kHz * 2000);
                if (iic_freq < 4)
                        iic_freq = 4;
                reg->RTR = (iic_clk >= 36) ? 36 : iic_clk + 1;
        }
        else
        {
                // fast mode
                iic_freq = info->apb_freq_hz / (kHz * 3000);
                if (!iic_freq)
                        iic_freq = 1;
                reg->RTR = (uint16_t)(((iic_clk * 300) / 1000) + 1);
        }
        reg->CLKR = iic_freq;
        reg->CTLR1 = I2C_CTLR1_I2CEN;
        
        switch (iic_idx)
        {
#if IIC_NUM >= 1
        case 0: NVIC_EnableIRQ(I2C1_EV_IRQn); NVIC_EnableIRQ(I2C1_ER_IRQn); break;
#endif
#if IIC_NUM >= 2
        case 1: NVIC_EnableIRQ(I2C2_EV_IRQn); NVIC_EnableIRQ(I2C2_ER_IRQn); break;
#endif
        }
        reg->CTLR2 |= I2C_CTLR2_EE | I2C_CTLR2_EIE | I2C_CTLR2_BIE;
        return VSFERR_NONE;
}

vsf_err_t gd32f1x0_i2c_config_cb(uint8_t index, void *param,
                                                        void (*callback)(void *, vsf_err_t))
{
        uint8_t iic_idx = (index & 0x03) >> 0;
        gd32f1x0_i2c[iic_idx].param = param;
        gd32f1x0_i2c[iic_idx].callback = callback;
        return VSFERR_NONE;
}

vsf_err_t gd32f1x0_i2c_xfer(uint8_t index, uint16_t chip_addr,
                                                        struct interface_i2c_msg_t *msg, uint8_t msg_len)
{
        uint8_t iic_idx = (index & 0x03) >> 0;
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;

        if ((msg == NULL) || (msg_len == 0))
                return VSFERR_INVALID_PARAMETER;

        gd32f1x0_i2c[iic_idx].chip_addr = chip_addr;
        gd32f1x0_i2c[iic_idx].msg = msg;
        gd32f1x0_i2c[iic_idx].msg_len = msg_len;
        gd32f1x0_i2c[iic_idx].msg_prt = 0;
        gd32f1x0_i2c[iic_idx].msg_buf_prt = 0;

        reg->CTLR1 |= I2C_CTLR1_GENSTA;
        return VSFERR_NONE;
}

static void gd32f1x0_i2c_cb(uint8_t iic_idx, vsf_err_t err)
{
        struct i2c_ctrl_t *ctrl = &gd32f1x0_i2c[iic_idx];
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;
        
        // MUST wait STOP clear, or next START will fail
        reg->CTLR1 |= I2C_CTLR1_GENSTP;
        while (reg->CTLR1 & I2C_CTLR1_GENSTP);
        if (ctrl->callback != NULL)
                ctrl->callback(ctrl->param, err);
}

static void gd32f1x0_i2c_chk_recv_next(uint8_t iic_idx)
{
        struct i2c_ctrl_t *ctrl = &gd32f1x0_i2c[iic_idx];
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;
        struct interface_i2c_msg_t *msg = &ctrl->msg[ctrl->msg_prt];

        if ((msg->flag & I2C_ACKLAST) || (ctrl->msg_buf_prt < msg->len - 1) ||
                ((ctrl->msg_len > ctrl->msg_prt + 1) &&
                        (ctrl->msg[ctrl->msg_prt + 1].flag & (I2C_READ | I2C_NOSTART))))
        {
                reg->CTLR1 |= I2C_CTLR1_ACKEN;
        }
        else
        {
                reg->CTLR1 &= ~I2C_CTLR1_ACKEN;
        }
}

static void gd32f1x0_i2c_err(uint8_t iic_idx)
{
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;
        reg->STR1 &= ~(I2C_STR1_BE | I2C_STR1_LOSTARB | I2C_STR1_AE);
        gd32f1x0_i2c_cb(iic_idx, VSFERR_FAIL);
}

static void gd32f1x0_i2c_int(uint8_t iic_idx)
{
        struct i2c_ctrl_t *ctrl = &gd32f1x0_i2c[iic_idx];
        I2C_TypeDef *reg = gd32f1x0_i2c_param[iic_idx].reg;
        struct interface_i2c_msg_t *msg = &ctrl->msg[ctrl->msg_prt];
        uint16_t status = reg->STR1;

        if (status & I2C_STR1_SBSEND)
        {
                // I2C_STR1_SBSEND is cleared by reading STR1 and writing DTR
                reg->DTR = (ctrl->chip_addr << 1) | ((msg->flag & I2C_READ) ? 1 : 0);
                // can not put in I2C_STR1_ADDSEND, no idea why
                if (msg->flag & I2C_READ)
                {
                        gd32f1x0_i2c_chk_recv_next(iic_idx);
                }
        }
        else if (status & I2C_STR1_ADDSEND)
        {
                // I2C_STR1_ADDSEND is cleared by reading STR1 and reading STR2
                status = reg->STR2;
        }
        else if (status & I2C_STR1_TBE)
        {
                // I2C_STR1_TBE is cleared by writing DTR while sending data
                if (ctrl->msg_buf_prt < msg->len)
                {
                send_data:
                        reg->DTR = msg->buf[ctrl->msg_buf_prt++];
                }
                else if (++ctrl->msg_prt < ctrl->msg_len)
                {
                        ctrl->msg_buf_prt = 0;
                        msg = &ctrl->msg[ctrl->msg_prt];
                        if (!(msg->flag & I2C_READ) && (msg->flag & I2C_NOSTART))
                                goto send_data;
                        else
                                reg->CTLR1 |= I2C_CTLR1_GENSTA;
                }
                else
                {
                        gd32f1x0_i2c_cb(iic_idx, VSFERR_NONE);
                }
        }
        else if (status & I2C_STR1_RBNE)
        {
                msg->buf[ctrl->msg_buf_prt++] = reg->DTR;
                if (ctrl->msg_buf_prt < msg->len)
                {
                recv_data:
                        gd32f1x0_i2c_chk_recv_next(iic_idx);
                }
                else if (++ctrl->msg_prt < ctrl->msg_len)
                {
                        ctrl->msg_buf_prt = 0;
                        msg = &ctrl->msg[ctrl->msg_prt];
                        if ((msg->flag & I2C_READ) && (msg->flag & I2C_NOSTART))
                                goto recv_data;
                        else
                                reg->CTLR1 |= I2C_CTLR1_GENSTA;
                }
                else
                {
                        gd32f1x0_i2c_cb(iic_idx, VSFERR_NONE);
                }
        }
}

#if IIC_NUM >= 1
ROOTFUNC void I2C1_EV_IRQHandler(void)
{
        gd32f1x0_i2c_int(0);
}
ROOTFUNC void I2C1_ER_IRQHandler(void)
{
        gd32f1x0_i2c_err(0);
}
#endif

#if IIC_NUM >= 2
ROOTFUNC void I2C2_EV_IRQHandler(void)
{
        gd32f1x0_i2c_int(1);
}
ROOTFUNC void I2C2_ER_IRQHandler(void)
{
        gd32f1x0_i2c_err(1);
}
#endif
#endif



tongbu2015| | 2017-1-30 18:24 | 显示全部楼层
这个例程的主要实现了什么功能的?

使用特权

评论回复
tongbu2015| | 2017-1-30 18:25 | 显示全部楼层
只是个简单的I2C总线通讯的?

使用特权

评论回复
baimiaocun2015| | 2017-1-31 10:26 | 显示全部楼层
这个程序的分享的比较好

使用特权

评论回复
叶覃| | 2017-1-31 22:42 | 显示全部楼层
代码看上去写的非常好,非常规范。我多会也写的这么牛X就好了。

使用特权

评论回复
Simon21ic|  楼主 | 2017-2-7 22:06 | 显示全部楼层
tongbu2015 发表于 2017-1-30 18:25
只是个简单的I2C总线通讯的?

这个是我们的系统构架下的IIC底层驱动,实现IIC主机功能

使用特权

评论回复
Simon21ic|  楼主 | 2017-2-7 22:09 | 显示全部楼层
叶覃 发表于 2017-1-31 22:42
代码看上去写的非常好,非常规范。我多会也写的这么牛X就好了。

写着写着就发现自己越来越牛了,所有人都这样

使用特权

评论回复
shenmu2012| | 2017-2-10 22:08 | 显示全部楼层
利用中断的方式处理有优势的,,只需要设置好优先级 的

使用特权

评论回复
vivilzb1985| | 2017-2-12 22:04 | 显示全部楼层
IIC的总线数据通讯的还是蛮重要的

使用特权

评论回复
firstblood| | 2017-2-12 22:51 | 显示全部楼层
这个代码的分享的确很给力的,,连通讯的容错功能的都有的

使用特权

评论回复
smilingangel| | 2017-2-12 23:15 | 显示全部楼层
IIC的主机驱动的是比较重要的

使用特权

评论回复
angerbird| | 2017-2-14 23:03 | 显示全部楼层
struct i2c_ctrl_t
{
        uint8_t chip_addr;
        uint8_t msg_len;
        uint8_t msg_prt;
        uint16_t msg_buf_prt;
        struct interface_i2c_msg_t *msg;
        void *param;
        void (*callback)(void *, vsf_err_t);
} static gd32f1x0_i2c[IIC_NUM];

采用结构体设计管理的,非常好

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

266

主题

2597

帖子

104

粉丝