软件模拟IIC的全面笔记(已调通)

2023-11-03 00:20

本文主要是介绍软件模拟IIC的全面笔记(已调通),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

@[toc] lib_i2c_simulation

/** @Author: Haiyichen* @Date: 2023-09-21 16:16:16* @LastEditors: Haiyichen* @LastEditTime: 2023-10-31 18:01:10* @Description: Personal notes of i2c-simulation*/

i2c基础

通讯流程

协议

除了文字解释,有用wavedrom简单画了一些各种信号的电平变化过程,但需要支持MPE才能看到,虽然CSDN上的在线编辑器无法渲染出图,但还是放出来了,如果有条件可以在自己的VSCode中配置MPE(markdown preview enhenced)

  1. 开始:SCL为高电平时,SDA从高电平状态切换到低电平状态。
    {signal:[{       name:'SCL',   wave:'p.Pp'},{       name:'data',  wave:"x.=x", data:["Start" ]},{       name:'SDA',   wave:"1.0"}
    ]}
  2. 停止:SCL为高电平时,SDA从低电平状态切换到高电平状态。
    {signal:[{       name:'SCL',   wave:'p.PPp'},{       name:'data',  wave:"x.=x", data:["Stop" ]},{       name:'SDA',   wave:"0.10"}
    ]}
  3. 应答:发送侧发送完8bit数据后,接收侧需要回复一个信号,即第9个SCL时,接收侧将SDA拉低,称作ACK。
    {signal:[{       name:'SCL',   wave:'p.Pp..|Pp'},{       name:'data',  wave:"x.==..|=x", data:["Start", "SlaveAddress","Ack"]},{       name:'SDA',   wave:"0.10.1|0."}
    ]}
  4. 无应答:发送侧发送完8bit数据后,接收侧需要回复一个信号,即第9个SCL时,接收侧将SDA拉高(或叫释放SDA),称作NACK。NACK时同时会引起Master发生RESTART或STOP流程。
    {signal:[
    {       name:'SCL',   wave:'p.Pp..|Pp'},
    {       name:'data',  wave:"x.==..|=x", data:["Start", "SlaveAddress","Nack"]},
    {       name:'SDA',   wave:"0.10.0|1."}
    ]}
  5. 地址命令:i2c的地址是7bit,第8bit是方向位。1代表Read、0代表Write

写流程

  1. Master发起START
  2. Master发送Slave地址(7bit)和W(0:写动作),等待Slave发动应答ACK
  3. Slave发送应答ACK
  4. Master发送寄存器地址(8bit),等待Slave发送应答ACK
  5. Slave发送应答ACK
  6. Master发送写入寄存器的数据(8bit),等待Slave发送应答ACK
  7. Slave发送应答ACK
  8. 6~7可以循环多次,即按顺序写多个寄存器
  9. Master发起STOP

读流程

  1. Master发起START
  2. Master发送Slave地址(7bit)和W(0:写动作),等待Slave发送应答ACK
  3. Slave发送应答ACK
  4. Master发送寄存器地址(8bit),等待Slave发送应答ACK
  5. Slave发送应答ACK
  6. Master发起START
  7. Master发送Slave地址(7bit)和R(1:读动作),等待Slave发送应答ACK
  8. Slave发送应答ACK
  9. Slave发送寄存器里数据(8bit),等待Master发送ACK
  10. Master发送ACK或NACK(出现NACK,后面直接就是STOP流程)
  11. 9~10可以循环多次,即顺序读多个寄存器
  12. Master发起STOP

i2c模拟驱动函数

相关硬件配置

/* IO definition of i2c simulation */
#define I2C_SIMULATION_GPIO_PORT             GPIOA
#define I2C_SIMULATION_SCL_PIN               GPIO_PINS_0
#define I2C_SIMULATION_SDA_PIN               GPIO_PINS_1/* driver definition of i2c simulation for M1 */
#define I2C_SDA_H()        I2C_SIMULATION_GPIO_PORT->scr = I2C_SIMULATION_SDA_PIN;
#define I2C_SDA_L()        I2C_SIMULATION_GPIO_PORT->clr = I2C_SIMULATION_SDA_PIN; 
#define I2C_SCL_H()        I2C_SIMULATION_GPIO_PORT->scr = I2C_SIMULATION_SCL_PIN;
#define I2C_SCL_L()        I2C_SIMULATION_GPIO_PORT->clr = I2C_SIMULATION_SCL_PIN;

关于模拟i2c的IO配置细节

关于SDA引脚是配置成开漏还是推挽,

  • 推挽输出对应->需要切换输入输出模式;
  • 开漏输出对应->不需要切换输入输出模式.
    参考链接已验证

i2c_Delay()

//需要根据主控MCU频率和i的取值,调整i2c_Delay的时长,进而调整SCL的脉宽。(也受编译器“优化等级”影响)
/*** @name   i2c_Delay* @brief  soft delay for i2c clock* @param  none* @retval none*/
static void i2c_Delay(void)
{uint8_t i;/**AT32F425F6P7,*i = 100,SCL = 163.4KHZ,6.1us*i = 75, SCL = 243.9KHZ,4.1us*i = 50, SCL = 312.5kHZ,3.2us*/for(i=0;i<100;i++);
}

hw_i2c_START()

/*** @name   hw_i2c_START* @brief  start signal for i2c simulation* @param  none* @retval none*/void hw_i2c_START(void){I2C_SDA_H();I2C_SCL_H();i2c_Delay();I2C_SDA_L();i2c_Delay();I2C_SCL_L();i2c_Delay();}

hw_i2c_ACK()

/*** @name   hw_i2c_ACK* @brief  ACK signal for i2c simulation* @param  none* @retval none*/
void hw_i2c_ACK(void)
{I2C_SDA_L();i2c_Delay();I2C_SCL_H();i2c_Delay();I2C_SCL_L();i2c_Delay();I2C_SDA_H();}

hw_i2c_WaitAck()

/*** @name   hw_i2c_WaitAck* @brief  Wait ACK signal for i2c simulation* @param  none* @retval uint8_t tempRe:Get slave ack signal or not*/uint8_t hw_i2c_WaitAck(void)
{uint8_t tempRe;I2C_SDA_H();                //MCU(master) set SDA Highi2c_Delay();I2C_SCL_H();                //MCU(master) send a new SCL signal, slave device should return an Ack signali2c_Delay();tempRe = I2C_SDA_READ();    //MCU(master) read SDA state(1 or 0)I2C_SCL_L();i2c_Delay();return tempRe;
}

hw_i2c_NACK()

/*** @name   hw_i2c_NACK* @brief  Send NACK signal to slave for i2c simulation* @param  none* @retval none*/
void hw_i2c_NACK(void)
{I2C_SDA_L();I2C_SCL_H();i2c_Delay();I2C_SDA_H();
}

hw_i2c_STOP()

/*** @name   hw_i2c_STOP* @brief  Send STOP signal to slave for i2c simulation* @param  none* @retval none*/
void hw_i2c_STOP(void)
{I2C_SDA_L();I2C_SCL_H();i2c_Delay();I2C_SDA_H();i2c_Delay();
}

lib_i2c_SendByte()

/*** @name   lib_i2c_SendByte* @brief  Send Byte from master by simulation of i2c* @param  DataByte:data* @retval none*/
void lib_i2c_SendByte(uint8_t DataByte)
{uint8_t i;for ( i = 0; i < 8; i++){if (DataByte & 0x80){I2C_SDA_H();}else{I2C_SDA_L();}i2c_Delay();I2C_SCL_H();i2c_Delay();I2C_SCL_L();if (i == 7){I2C_SDA_H();      //MCU(master) set SDA high}DataByte <<= 1;i2c_Delay();        }
}

lib_i2c_ReadByte()

/*** @name   lib_i2c_ReadByte* @brief  Read Byte from slave device by simulation of i2c* @param  none * @retval tempData:data*/
uint8_t lib_i2c_ReadByte(void)
{uint8_t i;uint8_t	tempData = 0;uint8_t	tempRe = 0;for(i = 0; i < 8; i++){tempData <<=1;I2C_SCL_H();i2c_Delay();tempRe = I2C_SDA_READ();if(tempRe){tempData++;}I2C_SCL_L();i2c_Delay();}return tempData;
}

lib_i2c_ReadMutiBytes()

/*** @name   lib_i2c_ReadMutiBytes* @brief  Read Muti Bytes data from slave device* @param  slave_address* @param  reg_address* @param  pdatabuf* @param  len* @retval tempRe:whether read data successfully or not*/
uint8_t lib_i2c_ReadMutiBytes(uint8_t slave_address, uint8_t reg_address, uint8_t* pdatabuf, uint8_t len)
{uint8_t tempData;		uint8_t tempRe = 0;uint8_t cnt = 0;uint8_t tempaddr_W = slave_address<<1;uint8_t tempaddr_R = tempaddr_W + 1;do{/* 1st:i2c start signal */hw_i2c_START();/* 2nd:write slave device address, bit0 is a read-write control bit, 0 for write, and 1 for read */lib_i2c_SendByte(tempaddr_W);/* 3rd:wait ack from slave device */tempRe = hw_i2c_WaitAck();if (tempRe){							// the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ackbreak;}/* 4th:send target register address */lib_i2c_SendByte(reg_address);/* 5th:wait Ack from slave device */stempRe = hw_i2c_WaitAck();if (tempRe){							// the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ackbreak;}/* 6th:send a start signal to reset i2c bus, and then start to read data from slave device */hw_i2c_START();/* 7th:Send a read command(bit0) for the slave address */lib_i2c_SendByte(tempaddr_R);/* 8th:wait Ack from slave device */tempRe = hw_i2c_WaitAck();if (tempRe){							// the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ackbreak;}			//9th:read data by loopfor(cnt = 0; cnt < len; cnt++){pdatabuf[cnt] = lib_i2c_ReadByte();    //read 1 byte/* After reading each byte, an Ack needs to be sent, except for the last byte, which requires a Nack */if(cnt != len - 1){/* After the middle byte is read, the CPU generates the ACK signal (Drive SDA = 0) */hw_i2c_ACK();}else{/* After reading the last byte, the CPU generates the NACK signal (drive SDA = 1) */hw_i2c_NACK();}}}while(0);/* Send I2C bus stop signal */hw_i2c_STOP();if(tempRe){tempRe = 0;}else{tempRe = 1;}return tempRe;
}

lib_i2c_WriteSingleByte()

/*** @brief  write single byte through the i2c1(For M1) by sofyware simulation.* @param  slave_address: address of tagert slave device* @param  reg_address: address of tagert register* @param  pdata: pointer to the data* @retval tempRe: Write data successfully or not*/
uint8_t lib_i2c_WriteSingleByte(uint16_t slave_address, uint16_t reg_address, uint8_t pdata)
{uint8_t tempRe = 0;uint8_t tempData = pdata;uint16_t tempaddr = slave_address<<1;do{hw_i2c_START();lib_i2c_SendByte(tempaddr);tempRe = hw_i2c_WaitAck();if (tempRe){							// the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ackbreak;}lib_i2c_SendByte(reg_address);tempRe = hw_i2c_WaitAck();if (tempRe){							// the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ackbreak;}lib_i2c_SendByte(tempData);tempRe = hw_i2c_WaitAck();if (tempRe){							// the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ackbreak;}hw_i2c_STOP();} while (0);return tempRe;
}

小结

因为用的芯片硬件IIC的底层官方函数一直卡死跑不通,于是干脆自己整理了一套软件模拟IIC的相关流程和函数,已经在项目中顺利调通了,项目换芯片也经历过不同芯片的移植,也很方便。

这篇关于软件模拟IIC的全面笔记(已调通)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/334302

相关文章

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

软件设计师备考——计算机系统

学习内容源自「软件设计师」 上午题 #1 计算机系统_哔哩哔哩_bilibili 目录 1.1.1 计算机系统硬件基本组成 1.1.2 中央处理单元 1.CPU 的功能 1)运算器 2)控制器 RISC && CISC 流水线控制 存储器  Cache 中断 输入输出IO控制方式 程序查询方式 中断驱动方式 直接存储器方式(DMA)  ​编辑 总线 ​编辑

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点