开发者

C语言软件iic虚拟总线中间层设计详解

开发者 https://www.devze.com 2023-01-31 11:14 出处:网络 作者: MacRsh
目录简介IIC-协议接线方式总线工作本质虚拟总线(中间层)设计使用示例简介
目录
  • 简介
  • IIC-协议
    • 接线方式
    • 总线
    • 工作本质
  • 虚拟总线(中间层)设计
    • 使用示例

      简介

      mr-soft-iic 模块为 mr-library 项目下的可裁剪模块,以C语言编写,可快速移植到各种平台(主要以嵌入式mcu为主)。 mr-soft-iic 模块通过 io 模拟实现 iic 协议。

      IIC-协议

      SPI 一般为一主多从设计。由2根线组成:CLK(时钟)、SDA(数据)。

      接线方式

      主机从机
      CLKCLK
      SDASDA

      主机从机一 一对应相接。

      总线

      IIC 通过地址开发者_C入门码识别设备,一条 IIC 总线最多支持挂载127个设备,通常速率为100kbit/s400kbit/s,由于SDA设置为开漏模式,因此双向通信时需外置上拉电阻。

      工作本质

      C语言软件iic虚拟总线中间层设计详解

      我们可以看到CLKSDA都有一个上拉电阻,而CLKSDA都为开漏(OUT-OD)下,所以只有一个下拉的MOS可以控制,因此只有下拉高阻状态。当高阻状态时线上电平因为上拉电阻的存在所以为VCC,而下拉状态时,因为MOS对地,所以线上电平为GND。当主机主动控制时,就可以完成写入的操作,当需要读取时,只需置高阻,等待从机主动拉低,即可完成双向通信。

      虚拟总线(中间层)设计

      首先 IIC 总线的CLKSDA 这2条线是不会变动的,所以我们可以把这部分单独设计为iic-bus,IIC总线需要知道当前有哪个设备拥有IIC总线的使用权,为了防止出现抢占还需要配置一个互斥锁。

      struct mr_soft_iic_bus
      {
        void (*set_clk)(mr_uint8_t level);		// 操作 CLK 的函数指针
        void (*set_sda)(mr_uint8_t level);		// 操作 SDA 的函数指针
        mr_uint8_t (*get_sda)(void);				// 读取 SDA 的函数指针
        struct mr_soft_iic *owner;					// 当前该总线的所有者
        mr_uint8_t lock;									// 互斥锁
      };
      

      IIC设备唯一独有的只有设备addr,所以我们把这部分定义为iic-device。IIC设备还需要知道自己归属于哪条IIC总线。

      struct mr_soft_iic
      {
        mr_uint8_t addr;		// 设备地址
        struct mr_soft_iic_bus *bus;		// 该设备归属的总线
      };
      

      当创建了一条iic-bus,一个iic-device后我们需要一个挂载函数,即将iic-device挂载到iic-bus

      void mr_soft_iic_attach(struct mr_soft_iic *iic, struct mr_soft_iic_bus *iic_bus)
      {
        iic->bus = iic_buwww.devze.coms;
      }
      

      那么由于是虚拟总线设计,当我们要开始传输前需要先去获取总线。

      mr_err_t mr_soft_iic_bus_take(struct mr_soft_iic *iic)
      {
        mr_uint8_t iic_bus_lock;
        mr_base_t level;
        /* check iic-bus owner */
        if(iic->bus->owner != iic)
        {
          /* check mutex lock */
          do {
            iic_bus_lock = iic->bus->lock;
          } while (iic_bus_lock != MR_UNLOCK);
          /* lock mutex lock */
          iic->bus->lock = MR_LOCK;
          /* exchange iic-bus owner */
          iic->bus->owner = iic;
        }
        else
        {
          /* lock mutex lock */
          iic->bus->lock = MR_LOCK;
        }
        return MR_EOK;
      }
      

      当我们使用完毕后需要释放总线

      mr_err_t mr_soft_iic_bus_release(struct mr_soft_iic *iic)
      {
        /* check spi-bus owner */
        if(iic->bus->owner == iic)
        {
          iic->bus->lock = MR_UNLOCK;
          return MR_EOK;
        }
        return -MR_ERROR;
      }
      

      到此其实虚拟总线已经设计完毕,设备需要使用仅需通过挂载 、获取、释放 三步操作即可,其余操作交由中间层处理。 为调用接口的统一,设计iic-msg

      struct mr_soft_iic_msg
      {
        mr_uint8_t read_write;		// 读写模式:IIC_WR/ IIC_RD
        mr_uint8_t addr;		// 写入/读取 地址							
        mr_uint8_t *buffer;		// 数据地址
        mr_size_t size;		// 数据数量
      };
      

      然后通过transfer函数统一调用接口。

      mr_err_t mr_soft_iic_transfer(struct mr_soft_iic *iic, struct mr_soft_iic_msg msg)
      {
        mr_err_t ret;
        /* check msg */
        if(msg.read_write > IIC_RD)
          return -MR_ERROR;
        if(msg.addr == MR_NULL)
          return -MR_EINVAL;
        if(msg.buffer == MR_NULL || msg.size == MR_NULL)
          return MR_EOK;
        /* take iic-bus */
        ret = mr_soft_iic_bus_take(iic);
        if(ret != MR_EOK)
          return ret;
        /* send iic device and register address */
        mr_soft_iic_bus_start(iic->bus);
        mr_soft_iic_bus_send(iic->bus, iic->addr << 1);
        mr_soft_iic_bus_send(iic->bus, msg.addr);
        if(msg.read_write == IIC_WR)
        {
          /* send iic start and device write cmd */
          mr_soft_iic_bus_start(iic->bus);
          mr_soft_iic_bus_send(iic->bus, iic->addr <编程;< 1);
          /* send */
          while(msg.si编程客栈ze)
          {
            mr_soft_iic_bus_send(iic->bus,*msg.buffer);
            ++ msg.buffer;
            -- msg.size;
          }
          /* send iic stop */
          mr_soft_iic_bus_stop(iic->bus);
        }
        else
        {
          /* send iic start and device write cmd */
          mr_soft_iic_bus_start(iic->bus);
          mr_soft_iic_bus_send(iic->bus, iic->addr << 1 | 0x01);
          /* receive */
          while(msg.size)
          {
            *msg.buffer = mr_soft_iic_bus_receive(iic->bus, (msg.size == 0));
            ++ msg.buffer;
            -- msg.siz编程客栈e;
          }
          /* send iic stop */
          mr_soft_iic_bus_stop(iic->bus);
        }
        /* release iic-bus */
        mr_soft_iic_bus_release(iic);
        return MR_EOK;
      }
      

      使用示例

      /* -------------------- 配置 -------------------- */
      /* 创建一条 iic 总线 */
      struct mr_soft_iic_bus iic_bus;
      /* 适配 iic 总线接口 */
      void set_clk(mr_uint8_t level)
      {
        GPIO_WriteBit(GPIOA,GPIO_Pin_0,level);
      }
      void set_sda(mr_uint8_t level)
      {
        GPIO_WriteBit(GPIOA,GPIO_Pin_1,level);
      }
      mr_uint8_t get_sda(void)
      {
        return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
      }
      /* 配置 iic 总线 */
      struct mr_soft_iic_bus iic_bus;
      iic_bus.set_clk = set_clk;
      iic_bus.set_sda = set_sda;
      iic_bus.get_sda = get_sda;
      iic_bus.lock = MR_UNLOCK;
      iic_bus.owner = MR_NULL;
      /* 创建一个 iic 设备 */
      struct mr_soft_iic iic_device;
      /* 配置 iic 设备 */
      iic_device.addr = 0x31;         // iic 设备地址
      /* -------------------- 使用 -------------------- */
      int main(void)
      {
          /* 需要发送的数据 */
          mr_uint8_t buffer[10]={0,1,2,3,4,5,6,7,8,9};
          /* 初始化 gpio */
          GPIO_InitTypeDef GPIO_InitStructure = {0};
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
          GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
          GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
          GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
          GPIO_Init(GPIOA, &GPIO_InitStructure);
          /* 挂载 iic 设备到 iic 总线 */
          mr_soft_iic_attach(&iic_device,&iic_bus);
          /* 创建 iic 消息 */
          struct mr_soft_iic_msg iic_msg;
          iic_msg.addr = 0x55;            // 写入/读取 地址
          iic_msg.buffer = buffer;        // 数据地址
          iic_msg.size = 10;              // 数据数量
          iic_msg.read_write = IIC_WR;    // 只写模式
          /* 发送消息 */
          mr_soft_iic_transfer(&iic_device,iic_msg);
      }
      

      剩余底层代码位于开源代码中,请下载开源代码。

      开源代码仓库链接 gitee.com/chen-fanyi/…

      路径:master/mr-library/ device / mr_soft_iic

      请仔细阅读README.md !!!http://www.devze.com!!

      以上就是C语言软件iic虚拟总线中间层设计详解的详细内容,更多关于C语言软件iic虚拟总线中间层的资料请关注我们其它相关文章!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号