这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【分享开发笔记,赚取电动螺丝刀】S32K146 PAL模拟I2C驱动适配

共3条 1/1 1 跳转至

【分享开发笔记,赚取电动螺丝刀】S32K146 PAL模拟I2C驱动适配

工程师
2025-03-05 21:11:01   被打赏 23 分(兑奖)     打赏

简介:

S32K146 的硬件I2C只有一个instance,如果需要硬件设计上使用了多条I2C总线就需要使用GPIO或者FLEXIO 来模拟I2C接口,为了提高上述底层接口的差异S32K1XX的SDK提供了PAL驱动来适配底层的差异,提供了统一的接口供用户使用。S32K1XX 的 PAL(Peripheral Abstract Layer)驱动不仅提高了兼容性,还有提高代码可移植性、简化开发难度、提高代码复用率等。PAL 驱动将能够实现相同功能的外设 PD 组件封装为一个 PAL 功能组件,如 UART_PAL,对应用层提供统一的 API 函数,使得底层驱动代码可以在不同 S32 MCU 硬件平台上通用,屏蔽了底层硬件外设的差别,便于在不同硬件平台间进行移植。

image.png

从上述架构图可以看出PAL 驱动主要来适配底层硬件及平台的差异性,为Application code 提高统一的接口的目的。

适配PAL驱动首先要添加软件I2C算法,然后将软件I2C对接到PAL驱动接口,完成适配。

GPIO 模拟 I2C驱动适配

配置之前我么先使用S32DS 配置模拟IIC功能引脚为GPIO功能

image.png

上面对PAL驱动接口已经有了概要的认识,PAL相当于“承上启下(隔离上层应用与底层硬件)”的作用,我们先适配底层GPIO软件I2C算法,软件模拟I2C的接口实现网上实现的方式也比较多,我们直接使用RT-thread 的软件I2C算法文件,很容易移植到工程中使用,修改后的文件如下:

/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author        Notes
 * 2012-04-25     weety         first version
 */

#include "sw_i2c.h"
#include "utilities.h"
#include <stddef.h>
#include "perf_counter.h"


#define DBG_TAG               "I2C"
#ifdef RT_I2C_BITbus_DEBUG
#define DBG_LVL               DBG_LOG
#else
#define DBG_LVL               DBG_INFO
#endif
#include <rtdbg.h>

#define SET_SDA(bus, val)   bus->set_sda(bus->data, val)
#define SET_SCL(bus, val)   bus->set_scl(bus->data, val)
#define GET_SDA(bus)        bus->get_sda(bus->data)
#define GET_SCL(bus)        bus->get_scl(bus->data)

inline void i2c_delay(struct i2c_bus *bus)
{
    bus->udelay((bus->delay_us + 1) >> 1);
}

inline void i2c_delay2(struct i2c_bus *bus)
{
    bus->udelay(bus->delay_us);
}

#define SDA_L(bus)          SET_SDA(bus, 0)
#define SDA_H(bus)          SET_SDA(bus, 1)
#define SCL_L(bus)          SET_SCL(bus, 0)

/**
 * release scl line, and wait scl line to high.
 */
static int8_t SCL_H(struct i2c_bus *bus)
{
    uint32_t start;

    SET_SCL(bus, 1);

    if (!bus->get_scl)
        goto done;

    start = get_system_ms();
    while (!GET_SCL(bus))
    {
        if ((get_system_ms() - start) > bus->timeout)
            return -RET_ETIMEOUT;
        i2c_delay(bus);
    }
#ifdef RT_I2C_BITbus_DEBUG
    if (get_system_ms() != start)
    {
        LOG_D("wait %ld tick for SCL line to go high",
              get_system_ms() - start);
    }
#endif

done:
    i2c_delay(bus);

    return RET_EOK;
}

static void i2c_start(struct i2c_bus *bus)
{
#ifdef RT_I2C_BITbus_DEBUG
    if (bus->get_scl && !GET_SCL(bus))
    {
        LOG_E("I2C bus error, SCL line low");
    }
    if (bus->get_sda && !GET_SDA(bus))
    {
        LOG_E("I2C bus error, SDA line low");
    }
#endif
    SDA_L(bus);
    i2c_delay(bus);
    SCL_L(bus);
}

static void i2c_restart(struct i2c_bus *bus)
{
    SDA_H(bus);
    SCL_H(bus);
    i2c_delay(bus);
    SDA_L(bus);
    i2c_delay(bus);
    SCL_L(bus);
}

static void i2c_stop(struct i2c_bus *bus)
{
    SDA_L(bus);
    i2c_delay(bus);
    SCL_H(bus);
    i2c_delay(bus);
    SDA_H(bus);
    i2c_delay2(bus);
}

bool i2c_waitack(struct i2c_bus *bus)
{
    bool ack;

    SDA_H(bus);
    i2c_delay(bus);

    if (SCL_H(bus) < 0)
    {
        LOG_W("wait ack timeout");

        return -RET_ETIMEOUT;
    }

    ack = !GET_SDA(bus);    /* ACK : SDA pin is pulled low */
    LOG_D("%s", ack ? "ACK" : "NACK");

    SCL_L(bus);

    return ack;
}

static int32_t i2c_writeb(struct i2c_bus *bus, uint8_t data)
{
    int32_t i;
    uint8_t bit;

    for (i = 7; i >= 0; i--)
    {
        SCL_L(bus);
        bit = (data >> i) & 1;
        SET_SDA(bus, bit);
        i2c_delay(bus);
        if (SCL_H(bus) < 0)
        {
            LOG_D("i2c_writeb: 0x%02x, "
                    "wait scl pin high timeout at bit %d",
                    data, i);

            return -RET_ETIMEOUT;
        }
    }
    SCL_L(bus);
    i2c_delay(bus);

    return i2c_waitack(bus);
}

static int32_t i2c_readb(struct i2c_bus *bus)
{
    uint8_t i;
    uint8_t data = 0;

    SDA_H(bus);
    i2c_delay(bus);
    for (i = 0; i < 8; i++)
    {
        data <<= 1;

        if (SCL_H(bus) < 0)
        {
            LOG_D("i2c_readb: wait scl pin high "
                    "timeout at bit %d", 7 - i);

            return -RET_ETIMEOUT;
        }

        if (GET_SDA(bus))
            data |= 1;
        SCL_L(bus);
        i2c_delay2(bus);
    }

    return data;
}

static size_t i2c_send_bytes(struct i2c_bus *bus,
                                struct rt_i2c_msg        *msg)
{
    int32_t ret;
    size_t bytes = 0;
    const uint8_t *ptr = msg->buf;
    int32_t count = msg->len;
    uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;

    while (count > 0)
    {
        ret = i2c_writeb(bus, *ptr);

        if ((ret > 0) || (ignore_nack && (ret == 0)))
        {
            count --;
            ptr ++;
            bytes ++;
        }
        else if (ret == 0)
        {
            LOG_D("send bytes: NACK.");

            return 0;
        }
        else
        {
            LOG_E("send bytes: error %d", ret);

            return ret;
        }
    }

    return bytes;
}

static int8_t i2c_send_ack_or_nack(struct i2c_bus *bus, int ack)
{
    if (ack)
        SET_SDA(bus, 0);
    i2c_delay(bus);
    if (SCL_H(bus) < 0)
    {
        LOG_E("ACK or NACK timeout.");

        return -RET_ETIMEOUT;
    }
    SCL_L(bus);

    return RET_EOK;
}

static size_t i2c_recv_bytes(struct i2c_bus *bus,
                                struct rt_i2c_msg        *msg)
{
    int32_t val;
    int32_t bytes = 0;   /* actual bytes */
    uint8_t *ptr = msg->buf;
    int32_t count = msg->len;
    const uint32_t flags = msg->flags;

    while (count > 0)
    {
        val = i2c_readb(bus);
        if (val >= 0)
        {
            *ptr = val;
            bytes ++;
        }
        else
        {
            break;
        }

        ptr ++;
        count --;

        LOG_D("recieve bytes: 0x%02x, %s",
                val, (flags & RT_I2C_NO_READ_ACK) ?
                "(No ACK/NACK)" : (count ? "ACK" : "NACK"));

        if (!(flags & RT_I2C_NO_READ_ACK))
        {
            val = i2c_send_ack_or_nack(bus, count);
            if (val < 0)
                return val;
        }
    }

    return bytes;
}

static int32_t i2c_send_address(struct i2c_bus *bus,
                                   uint8_t                addr,
                                   int32_t                retries)
{
    int32_t i;
    int8_t ret = 0;

    for (i = 0; i <= retries; i++)
    {
        ret = i2c_writeb(bus, addr);
        if (ret == 1 || i == retries)
            break;
        LOG_D("send stop condition");
        i2c_stop(bus);
        i2c_delay2(bus);
        LOG_D("send start condition");
        i2c_start(bus);
    }

    return ret;
}

static int8_t i2c_bit_send_address(struct i2c_bus *bus,
                                     struct rt_i2c_msg        *msg)
{
    uint16_t flags = msg->flags;
    uint16_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;

    uint8_t addr1, addr2;
    int32_t retries;
    int8_t ret;

    retries = ignore_nack ? 0 : bus->retries;

    if (flags & RT_I2C_ADDR_10BIT)
    {
        addr1 = 0xf0 | ((msg->addr >> 7) & 0x06);
        addr2 = msg->addr & 0xff;

        LOG_D("addr1: %d, addr2: %d", addr1, addr2);

        ret = i2c_send_address(bus, addr1, retries);
        if ((ret != 1) && !ignore_nack)
        {
            LOG_W("NACK: sending first addr");

            return -RET_EIO;
        }

        ret = i2c_writeb(bus, addr2);
        if ((ret != 1) && !ignore_nack)
        {
            LOG_W("NACK: sending second addr");

            return -RET_EIO;
        }
        if (flags & RT_I2C_RD)
        {
            LOG_D("send repeated start condition");
            i2c_restart(bus);
            addr1 |= 0x01;
            ret = i2c_send_address(bus, addr1, retries);
            if ((ret != 1) && !ignore_nack)
            {
                LOG_E("NACK: sending repeated addr");

                return -RET_EIO;
            }
        }
    }
    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 -RET_EIO;
    }

    return RET_EOK;
}

static size_t i2c_bit_xfer(struct i2c_bus *bus,
                              struct rt_i2c_msg         msgs[],
                              uint32_t               num)
{
    struct rt_i2c_msg *msg;
    int32_t ret;
    uint32_t i;
    uint16_t ignore_nack;

    if((bus->i2c_pin_init_flag == RT_FALSE) && (bus->pin_init != RT_NULL))
    {
        bus->pin_init();
        bus->i2c_pin_init_flag = RT_TRUE;
    }

    if (num == 0) return 0;

    for (i = 0; i < num; i++)
    {
        msg = &msgs[i];
        ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
        if (!(msg->flags & RT_I2C_NO_START))
        {
            if (i)
            {
                i2c_restart(bus);
            }
            else
            {
                LOG_D("send start condition");
                i2c_start(bus);
            }
            ret = i2c_bit_send_address(bus, msg);
            if ((ret != RET_EOK) && !ignore_nack)
            {
                LOG_D("receive NACK from device addr 0x%02x msg %d",
                        msgs[i].addr, i);
                goto out;
            }
        }
        if (msg->flags & RT_I2C_RD)
        {
            ret = i2c_recv_bytes(bus, msg);
            if (ret >= 1)
            {
                LOG_D("read %d byte%s", ret, ret == 1 ? "" : "s");
            }
            if (ret < msg->len)
            {
                if (ret >= 0)
                    ret = -RET_EIO;
                goto out;
            }
        }
        else
        {
            ret = i2c_send_bytes(bus, msg);
            if (ret >= 1)
            {
                LOG_D("write %d byte%s", ret, ret == 1 ? "" : "s");
            }
            if (ret < msg->len)
            {
                if (ret >= 0)
                    ret = -RET_ERROR;
                goto out;
            }
        }
    }
    ret = i;

out:
    if (!(msg->flags & RT_I2C_NO_STOP))
    {
        LOG_D("send stop condition");
        i2c_stop(bus);
    }

    return ret;
}

该算法主要依赖提供操作sda/scl 的接口,及延时的函数指针,延时我们使用perf 提供的delay 函数以及pin 初始化的接口,对于perf 在此不做展开,可以查看此贴有对perf的详细说明(https://forum.eepw.com.cn/thread/386237/1),pin 初始化我们使用默认的PIN 驱动管理来初始化此处不需要配置,配置为MULL即可。

image.png

按照上述结构体,创建I2C 总线的实例,创建上述方法的函数指针,对应代码如下

#include "sdk_project_config.h"
#include "sw_i2c.h"
#include "perf_counter.h"

/********************************************************************************************************
 *                                    Private Type Declarations                                         *
 *******************************************************************************************************/

struct swi2c_io_cfg {
    GPIO_Type * scl_port; /* scl port */
    GPIO_Type * sda_port; /* sda port */
    uint8_t scl_pin;      /* scl pin index */
    uint8_t sda_pin;      /* sda pin index */
};

/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/

static void swi2c_set_sda(void *data, int32_t state)
{
    struct swi2c_io_cfg * cfg = (struct swi2c_io_cfg *)data;
    /* config sda as output */
    PINS_DRV_SetPinDirection(cfg->sda_port,cfg->sda_pin,GPIO_OUTPUT_DIRECTION);

    /* set sda as hight */
    PINS_DRV_WritePin(cfg->sda_port,cfg->sda_pin,state);
}

static void swi2c_set_scl(void *data, int32_t state)
{
    struct swi2c_io_cfg * cfg = (struct swi2c_io_cfg *)data;
    /* config scl as output */
    PINS_DRV_SetPinDirection(cfg->scl_port,cfg->scl_pin,GPIO_OUTPUT_DIRECTION);

    /* set scl as hight */
    PINS_DRV_WritePin(cfg->scl_port,cfg->scl_pin,state);
}

static int32_t swi2c_get_sda(void *data)
{
    struct swi2c_io_cfg * cfg = (struct swi2c_io_cfg *)data;
    /* config sda as input */
    PINS_DRV_SetPinDirection(cfg->sda_port,cfg->sda_pin,GPIO_INPUT_DIRECTION);

    /* get sda pin level*/
    return PINS_DRV_ReadPins(cfg->sda_port) & (0x01 << cfg->sda_pin) ?  1 : 0;
}

static int32_t swi2c_get_scl(void *data)
{
    struct swi2c_io_cfg * cfg = (struct swi2c_io_cfg *)data;
    /* config scl as input */
    PINS_DRV_SetPinDirection(cfg->scl_port,cfg->scl_pin,GPIO_INPUT_DIRECTION);

    /* get scl pin level*/
    return PINS_DRV_ReadPins(cfg->scl_port) & (0x01 << cfg->scl_pin) ?  1 : 0;
}
/********************************************************************************************************
 *                                  Private Variable Definitions                                        *
 *******************************************************************************************************/
static struct swi2c_io_cfg swi2c1pin =
{
    .scl_port = SDA_SW_PORT,
    .sda_port = SCL_SW_PORT,
    .scl_pin = SCL_SW_PIN,
    .sda_pin = SDA_SW_PIN
};

/********************************************************************************************************
 *                                  Global Variable Declarations                                        *
 *******************************************************************************************************/

struct i2c_bus swi2c1_bus =
{
    /* i2c ops config */
    .data = (void *)&swi2c1pin,
    .set_sda = swi2c_set_sda,
    .set_scl = swi2c_set_scl,
    .get_scl = swi2c_get_scl,
    .get_sda = swi2c_get_sda,
    /* delay */
    .udelay = delay_us,
    .delay_us = 5,/* 100k */
    .timeout = 100,/* 80M 100tick = 12.5us */
    .retries = 3,

    /* pin init */
    .pin_init = NULL,
    .i2c_pin_init_flag = true,
};

软件模拟I2C算法适配完成后,先验证下软件模拟I2C适配的是否成功,我们使用总线的接口来探测下I2C总线上的节点信息,添加如下测试代码。

static void i2c_scan1(uint8_t start_addr, uint8_t stop_addr)
{
    printf("    00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
    uint8_t pos = start_addr <= 0xF? 0x00: start_addr & 0xF;
    for(; pos < stop_addr; pos++)
    {
        if((pos & 0xF) == 0)
        {
            printf("\r\n%02X: ", pos);
        }
        if(pos < start_addr)
        {
            printf("   ");
            continue;
        }
        if( i2c1_probe(pos) == 1)
        {
            printf("%02X", pos);
        }
        else
        {
            printf("--");
        }
        printf(" ");
    }
    printf("\r\n");
}

unsigned int  i2c1(char argc,char *argv[])
{
    i2c_scan1(0x00,0x7f);
    return 0;
}

LTSH_FUNCTION_EXPORT(i2c1,"i2c test command");

输入i2c1 命令后,已经嗅探到总线上的节点信息跟,说明总线算法功能正常。

image.png

对接软件I2C至PAL 驱动

上面我们已经完成了PAL 驱动的物理层依赖的接口,我们继续将GPIO模拟的I2C的接口对接到PAL驱动接口即可完成PAL驱动的适配工作。PAL驱动已经定义好了软件框架我么只需要开启定义相应的数据结构和对应的函数。按照PAL 依赖的数据结构,定义如下的数据结构,

#ifndef SWI2C_DRIVER_H
#define SWI2C_DRIVER_H
/********************************************************************************************************
 *                                      Include header files                                            *
 *******************************************************************************************************/
#include <stdint.h>
#include "status.h"
#include <stdbool.h>
#include "device_registers.h"

/********************************************************************************************************
 *                                  Global Types definition                                             *
 *******************************************************************************************************/

typedef struct swi2c_pin
{
    GPIO_Type * port; /* port */
    uint8_t pin;      /* pin index */
} swi2c_pin_t;

typedef struct swi2cswi2c_master_user_config
{
    uint16_t slaveAddress;     /*!< Slave address, 7-bit or 10-bit */
    uint32_t baudRate;

    swi2c_pin_t *sclPin;       /* SCL pin for SWI2C */
    swi2c_pin_t *sdaPin;       /* SDA pin for SWI2C */
    swi2c_pin_t *sdaReadPin;   /* SDA read pin for SWI2C */
    swi2c_pin_t *sclReadPin;   /* SCL read pin for SWI2C */
    uint8_t instIdx;           /* Instance index of the peripheral over which the PAL is used */
} swi2c_master_user_config_t;

typedef struct swi2c_master_state
{
    swi2c_master_user_config_t * swi2c_cfg;     /* swi2c config */
    void * bus;                                 /* swi2c bus */
} swi2c_master_state_t;

#endif /* SWI2C_DRIVER_H */

并实现上述依赖的函数,底层调用对接软件模拟I2C接口函数

/********************************************************************************************************
 *                                      Include header files                                            *
 *******************************************************************************************************/
#include <swi2c_driver.h>
#include "pins_driver.h"
#include "i2c_pal_cfg.h"
#include "swi2c_ops.h"
#include "perf_counter.h"

/********************************************************************************************************
 *                                  Private Variable Definitions                                        *
 *******************************************************************************************************/
static struct i2c_bus swi2c[SWI2C_INSTANCE_COUNT] = {};

/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/

static void swi2c_set_sda(void *data, int32_t state)
{
    swi2c_master_user_config_t * cfg = (swi2c_master_user_config_t *)data;
    /* config sda as output */
    PINS_DRV_SetPinDirection(cfg->sdaPin->port,cfg->sdaPin->pin,GPIO_OUTPUT_DIRECTION);

    /* set sda as hight */
    PINS_DRV_WritePin(cfg->sdaPin->port,cfg->sdaPin->pin,state);
}

static void swi2c_set_scl(void *data, int32_t state)
{
    swi2c_master_user_config_t * cfg = (swi2c_master_user_config_t *)data;
    /* config scl as output */
    PINS_DRV_SetPinDirection(cfg->sclPin->port,cfg->sclPin->pin,GPIO_OUTPUT_DIRECTION);

    /* set scl as hight */
    PINS_DRV_WritePin(cfg->sclPin->port,cfg->sclPin->pin,state);
}

static int32_t swi2c_get_sda(void *data)
{
    swi2c_master_user_config_t * cfg = (swi2c_master_user_config_t *)data;
    /* config sda as input */
    PINS_DRV_SetPinDirection(cfg->sdaPin->port,cfg->sdaPin->pin,GPIO_INPUT_DIRECTION);

    /* get sda pin level*/
    return PINS_DRV_ReadPins(cfg->sdaPin->port) & (0x01 << cfg->sdaPin->pin) ?  1 : 0;
}

static int32_t swi2c_get_scl(void *data)
{
    swi2c_master_user_config_t * cfg = (swi2c_master_user_config_t *)data;
    /* config scl as input */
    PINS_DRV_SetPinDirection(cfg->sclPin->port,cfg->sclPin->pin,GPIO_INPUT_DIRECTION);

    /* get scl pin level*/
    return PINS_DRV_ReadPins(cfg->sclPin->port) & (0x01 << cfg->sclPin->pin) ?  1 : 0;
}

/********************************************************************************************************
 *                                  Global Function Declarations                                        *
 *******************************************************************************************************/

status_t  SWI2C_DRV_MasterInit(swi2c_master_state_t * status,
                               swi2c_master_user_config_t *swi2cConfig)
{
    status_t ret = STATUS_SUCCESS;
    if( swi2cConfig->instIdx < SWI2C_INSTANCE_COUNT)
    {
        status->swi2c_cfg = swi2cConfig;  /* Init software i2c config */
        /* Init swi2c ops */
        swi2c[swi2cConfig->instIdx].get_scl = swi2c_get_scl;
        swi2c[swi2cConfig->instIdx].get_sda = swi2c_get_sda;
        swi2c[swi2cConfig->instIdx].set_scl = swi2c_set_scl;
        swi2c[swi2cConfig->instIdx].set_sda = swi2c_set_sda;
        /* delay */
        swi2c[swi2cConfig->instIdx].udelay = delay_us,
        swi2c[swi2cConfig->instIdx].delay_us = 5,/* 100k */
        swi2c[swi2cConfig->instIdx].timeout = 100,/* 80M 100tick = 12.5us */
        swi2c[swi2cConfig->instIdx].retries = 3,
        /* pin init */
        swi2c[swi2cConfig->instIdx].pin_init = NULL,
        swi2c[swi2cConfig->instIdx].i2c_pin_init_flag = true,
        swi2c[swi2cConfig->instIdx].data = (void *)swi2cConfig;
        status->bus = (void *)&swi2c[swi2cConfig->instIdx];
    }
    else
    {
        ret = STATUS_ERROR;
    }
    return ret;
}


status_t SWI2C_DRV_MasterSendDataBlocking(swi2c_master_state_t * status,
                                    const uint8_t * txBuff,
                                    uint32_t txSize,
                                    bool sendStop)
{
    struct rt_i2c_msg msg;
    int32_t ret = 0;

    msg.addr = status->swi2c_cfg->slaveAddress;
    msg.buf = (uint8_t *)txBuff;
    msg.flags = RT_I2C_WR;
    msg.len = txSize;
    if(sendStop == false)
        msg.flags |= RT_I2C_NO_STOP;


    ret = i2c_bit_xfer((struct i2c_bus *)status->bus,&msg,1);

    return ret < 0 ? STATUS_ERROR : STATUS_SUCCESS;
}


status_t SWI2C_DRV_MasterReceiveDataBlocking(swi2c_master_state_t * status,
                                           uint8_t * rxBuff,
                                           uint32_t rxSize,
                                           bool sendStop)
{
    struct rt_i2c_msg msg;
    int32_t ret = 0;

    msg.addr = status->swi2c_cfg->slaveAddress;
    msg.buf = rxBuff;
    msg.flags = RT_I2C_WR;
    msg.len = rxSize;
    if(sendStop == false)
        msg.flags |= RT_I2C_NO_STOP;


    ret = i2c_bit_xfer((struct i2c_bus *)status->bus,&msg,1);

    return ret < 0 ? STATUS_ERROR : STATUS_SUCCESS;
}


void SWI2C_DRV_MasterSetSlaveAddress(swi2c_master_state_t * status, const uint16_t address)
{
    status->swi2c_cfg->slaveAddress = address;
}

void SWI2C_DRV_SetWaitTimeForSCLTransition(swi2c_master_state_t * status, uint32_t counter)
{
    ((struct i2c_bus *)(status->bus))->delay_us = perfc_convert_ticks_to_us(counter);
}

至此已经适配好了,PAL SWI2C 接口函数,我们添加如下初始化配置结构。

/*******************************************************************************
 * swi2c_pal_config_2 initialization code
 ******************************************************************************/
static const swi2c_pin_t swi2c0_pin_scl = {PTB,9};
static const swi2c_pin_t swi2c0_pin_sda = {PTB,10};

static const extension_swi2c_for_i2c_t extension_swi2c0 = {
  .sclPin = (swi2c_pin_t *)&swi2c0_pin_scl,
  .sdaPin = (swi2c_pin_t *)&swi2c0_pin_sda,
  .sclReadPin = NULL,
  .sdaReadPin = NULL
};

/* PAL instance information */
i2c_instance_t swi2c0_instance = {I2C_INST_TYPE_SWI2C, 0U};

i2c_master_t i2c_pal_swi2c0_MasterConfig0 = {
    .slaveAddress = 50U,
    .is10bitAddr = false,
  .baudRate = 100000UL,
  .dmaChannel1 = 0U,
  .dmaChannel2 = 1U,
  .transferType = I2C_PAL_USING_INTERRUPTS,
  .operatingMode = I2C_PAL_STANDARD_MODE,
  .callback = NULL,
  .callbackParam = NULL,
  .extension = (void *)&extension_swi2c0
};

添加如下的测试代码输入 i2c swi2c  来验证SWI2C 的功能。

void i2c_init(void)
{
    status_t ret;
    /* Init lpi2c master */
    ret = I2C_MasterInit(&lpi2c0_instance,&i2c_pal_lpi2c0_MasterConfig0);

    INT_SYS_SetPriority(LPI2C0_Master_IRQn,2);

    if(ret != STATUS_SUCCESS)
    {
        LOG_E("LPI2c init failed %x.",ret);
    }
    else
    {
        LOG_I("LPI2c init OK.");
    }
    /* Init lpi2c master */
    ret = I2C_MasterInit(&swi2c0_instance,&i2c_pal_swi2c0_MasterConfig0);
    if(ret != STATUS_SUCCESS)
    {
        LOG_E("SWI2c init failed %x.",ret);
    }
    else
    {
        LOG_I("SWI2c init OK.");
    }
}

/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/

static int i2c_probe(i2c_instance_t * instance,char addr)
{
    static uint8_t data;
    status_t retVal,retVal1;
    /* Set i2c slave address */
    I2C_MasterSetSlaveAddress(instance,(uint16_t)addr,false);

    data = 0x00;

    retVal1 = I2C_MasterSendDataBlocking(instance,&data,1,false,100);

    retVal = I2C_MasterReceiveDataBlocking(instance,&data,1,true,100);

    return (retVal == STATUS_SUCCESS && retVal1 == STATUS_SUCCESS) ? 1 : 0 ;
}

static void i2c_scan(i2c_instance_t * instance,uint8_t start_addr, uint8_t stop_addr)
{
    printf("    00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
    uint8_t pos = start_addr <= 0xF? 0x00: start_addr & 0xF;
    for(; pos < stop_addr; pos++)
    {
        if((pos & 0xF) == 0)
        {
            printf("\r\n%02X: ", pos);
        }
        if(pos < start_addr)
        {
            printf("   ");
            continue;
        }
        if( i2c_probe(instance,pos) == 1)
        {
            printf("%02X", pos);
        }
        else
        {
            printf("--");
        }
        printf(" ");
    }
    printf("\r\n");
}

unsigned int  i2c(char argc,char *argv[])
{
    if(strcmp(argv[1],"lpi2c") == 0)
    {
        i2c_scan(&lpi2c0_instance,0x00,0x7f);
    }

    if(strcmp(argv[1],"swi2c") == 0)
    {
        i2c_scan(&swi2c0_instance,0x00,0x7f);
    }
    return 0;
}

LTSH_FUNCTION_EXPORT(i2c,"i2c test command");

运行结果如下,和预期的一致模拟I2C对接PAL成功。

image.png

至此我么已经完成PAL驱动接口来适配软件I2C,这样在应用层调用i2c 使用的统一的接口,实现了应用层和底层硬件的隔离。 


专家
2025-03-09 11:54:01     打赏
2楼

模拟方式下的通讯速度怎么样?通过代码延迟调节吗?


院士
2025-03-11 15:56:01     打赏
3楼

速度肯定不会受到影响,但MCU的效率与性能就影响太大了。


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]