news 2026/6/9 15:51:24

IO模拟I2C

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IO模拟I2C

soft_i2c_io.h

#pragmaonce#include<cstdint>class SoftI2cIo{public:virtual~SoftI2cIo()=default;virtualvoidinit()=0;// GPIO初始化/* ===== SCL ===== */virtualvoidscl_low()=0;// 拉低virtualvoidscl_release()=0;// 释放(输入/高阻)virtual boolscl_read()const=0;// 读电平/* ===== SDA ===== */virtualvoidsda_low()=0;virtualvoidsda_release()=0;virtual boolsda_read()const=0;/* ===== 延时 ===== */virtualvoiddelay_us(uint32_tus)=0;};

SoftI2C.h

#pragmaonce#include"soft_i2c_io.h"/* * SoftI2C * 物理假设: * - SDA / SCL 为开漏或三态输出 * - 总线通过外部或 IOB 上拉电阻拉高 */class SoftI2C{public:/* * 构造函数 * @param io GPIO 操作抽象(拉低 / 释放 / 读电平) * @param halfPeriodUs I²C 半周期时间(us) * * 说明: * - 一个完整时钟周期 = 2 * halfPeriodUs * - I²C 速率 ≈ 1 / (2 * halfPeriodUs) */SoftI2C(SoftI2cIo&io,uint32_thalfPeriodUs);/* * 初始化 I²C 总线 * 通常行为: * - SDA、SCL 释放为高电平 * - 保证总线处于 idle 状态 */voidinit();/* * 判断 I²C 总线是否空闲 * @return true : SDA=1 且 SCL=1 * false : 总线被占用或异常 */boolbusIdle()const;/* * 产生 I²C START 条件 * * 时序: * SDA: 1 -> 0 * SCL: 保持为 1 */voidstart();/* * 产生 I²C STOP 条件 * * 时序: * SDA: 0 -> 1 * SCL: 保持为 1 */voidstop();/* * 向总线写入 1 bit * @param bit 要写入的 bit 值 * * 说明: * - bit=0:SDA 拉低 * - bit=1:SDA 释放(由上拉拉高) * - 数据在 SCL 上升沿被从机采样 */voidwriteBit(bool bit);/* * 从总线读取 1 bit * @return 读取到的 SDA 电平 * * 说明: * - 主机释放 SDA * - 在 SCL 高电平期间采样 SDA */boolreadBit();/* * 写一个字节(8 bit)到 I²C 总线 * @param data 要写入的数据 * @return true : 从机返回 ACK * false : 从机返回 NACK * * 时序: * - MSB first * - 第 9 个时钟周期读取 ACK 位 */boolwriteByte(uint8_tdata);/* * 从 I²C 总线读取一个字节 * @param ack true : 读完后发送 ACK * false : 读完后发送 NACK * @return 读取到的数据 */uint8_treadByte(bool ack);/* * 连续写操作 * @param addr7 7 位 I²C 从机地址 * @param buf 写入数据缓冲区 * @param len 写入字节数 * @return true : 写成功(全部 ACK) * false : 中途 NACK */boolwrite(uint8_taddr7,constuint8_t*buf,uint32_tlen);/* * 连续读操作 * @param addr7 7 位 I²C 从机地址 * @param buf 读取数据缓冲区 * @param len 读取字节数 * @return true : 读成功 * false : 地址阶段 NACK */boolread(uint8_taddr7,uint8_t*buf,uint32_tlen);/* ===== 设备探测 ===== *//* * 探测 I²C 设备是否存在 * @param addr7 7 位从机地址 * @return true : 从机 ACK(设备存在) * false : NACK(设备不存在) * * 实现原理: * START * → 发送 addr7 + W * → 检查 ACK * → STOP */boolprobe(uint8_taddr7);private:/* * 半周期延时 * 用于构造 SCL 高 / 低时间 */inlinevoiddelay()const;private:SoftI2cIo&m_io;// GPIO 操作抽象(SDA / SCL)uint32_tm_halfPeriodUs;// I²C 半周期时间(微秒)};

SoftI2C.cpp

#include"SoftI2C.h"SoftI2C::SoftI2C(SoftI2cIo&io,uint32_thalfPeriodUs):m_io(io),m_halfPeriodUs(halfPeriodUs){}voidSoftI2C::init(){m_io.init();}inlinevoidSoftI2C::delay()const{m_io.delay_us(m_halfPeriodUs);}bool SoftI2C::busIdle()const{returnm_io.scl_read()&&m_io.sda_read();}/* ===== START / STOP ===== */voidSoftI2C::start(){m_io.sda_release();m_io.scl_release();delay();// SDA: 1 -> 0 while SCL = 1m_io.sda_low();delay();m_io.scl_low();delay();}voidSoftI2C::stop(){m_io.sda_low();delay();m_io.scl_release();delay();// SDA: 0 -> 1 while SCL = 1m_io.sda_release();delay();}/* ===== BIT ===== */voidSoftI2C::writeBit(bool bit){bit?m_io.sda_release():m_io.sda_low();delay();m_io.scl_release();delay();m_io.scl_low();delay();}bool SoftI2C::readBit(){m_io.sda_release();delay();m_io.scl_release();delay();bool bit=m_io.sda_read();m_io.scl_low();delay();returnbit;}bool SoftI2C::writeByte(uint8_tdata){for(inti=7;i>=0;--i)writeBit(data&(1<<i));// ACK = 0return!readBit();}uint8_tSoftI2C::readByte(bool ack){uint8_tdata=0;for(inti=7;i>=0;--i)if(readBit())data|=(1<<i);writeBit(!ack);// ACK=0, NACK=1returndata;}bool SoftI2C::write(uint8_taddr7,constuint8_t*buf,uint32_tlen){start();if(!writeByte((addr7<<1)|0)){stop();returnfalse;}for(uint32_ti=0;i<len;++i){if(!writeByte(buf[i])){stop();returnfalse;}}stop();returntrue;}bool SoftI2C::read(uint8_taddr7,uint8_t*buf,uint32_tlen){start();if(!writeByte((addr7<<1)|1)){stop();returnfalse;}for(uint32_ti=0;i<len;++i){buf[i]=readByte(i+1<len);// 最后一个 NACK}stop();returntrue;}bool SoftI2C::probe(uint8_taddr7){start();// 只发地址 + Write 位bool ack=writeByte((addr7<<1)|0);stop();returnack;}

pico_soft_i2c_io.h

#pragmaonce#include"soft_i2c_io.h"#include"hardware/gpio.h"#include"hardware/timer.h"/* * PicoSoftI2cIo * RP2040 GPIO 模拟 I2C 的 IO 实现 */class PicoSoftI2cIo:public SoftI2cIo{public:PicoSoftI2cIo(uint scl_pin,uint sda_pin);/* ===== SCL ===== */voidinit()override;voidscl_low()override;voidscl_release()override;boolscl_read()constoverride;/* ===== SDA ===== */voidsda_low()override;voidsda_release()override;boolsda_read()constoverride;/* ===== Delay ===== */voiddelay_us(uint32_tus)override;private:uint m_scl;uint m_sda;};

pico_soft_i2c_io.cpp

#include"pico_soft_i2c_io.h"#include"hardware/gpio.h"#include"hardware/timer.h"PicoSoftI2cIo::PicoSoftI2cIo(uint scl_pin,uint sda_pin):m_scl(scl_pin),m_sda(sda_pin){}voidPicoSoftI2cIo::init(){/* GPIO 初始化 */gpio_init(m_scl);gpio_init(m_sda);/* * 初始状态:释放总线 * I2C = 开漏 + 上拉 */gpio_set_dir(m_scl,GPIO_IN);gpio_set_dir(m_sda,GPIO_IN);}/* ========= SCL ========= */voidPicoSoftI2cIo::scl_low(){gpio_put(m_scl,0);gpio_set_dir(m_scl,GPIO_OUT);}voidPicoSoftI2cIo::scl_release(){gpio_set_dir(m_scl,GPIO_IN);}bool PicoSoftI2cIo::scl_read()const{returngpio_get(m_scl);}/* ========= SDA ========= */voidPicoSoftI2cIo::sda_low(){gpio_put(m_sda,0);gpio_set_dir(m_sda,GPIO_OUT);}voidPicoSoftI2cIo::sda_release(){gpio_set_dir(m_sda,GPIO_IN);}bool PicoSoftI2cIo::sda_read()const{returngpio_get(m_sda);}/* ========= Delay ========= */voidPicoSoftI2cIo::delay_us(uint32_tus){busy_wait_us_32(us);}

main.cpp

#include"pico/stdlib.h"#include"SoftI2C.h"#include"pico_soft_i2c_io.h"#include"stdio.h"#defineSSD1306_I2C_ADDR_u(0x3C)PicoSoftI2cIoi2c_io(5,4);SoftI2Ci2c(i2c_io,2);intmain(){stdio_init_all();i2c.init();while(true){printf("probe: %d\n",i2c.probe(SSD1306_I2C_ADDR));sleep_ms(1000);}}

diagram.json

{"version":1,"author":"wang minglie","editor":"wokwi","parts":[{"type":"wokwi-pi-pico","id":"pico","top":-137.55,"left":22.8,"attrs":{"builder":"pico-sdk"}},{"type":"board-ssd1306","id":"oled1","top":-102.46,"left":221.03,"attrs":{"i2cAddress":"0x3c"}},{"type":"wokwi-vcc","id":"vcc1","top":-220.04,"left":220.8,"attrs":{}},{"type":"wokwi-gnd","id":"gnd1","top":-201.6,"left":345,"attrs":{}}],"connections":[["pico:GP0","$serialMonitor:RX","",[]],["pico:GP1","$serialMonitor:TX","",[]],["oled1:VCC","vcc1:VCC","red",["v-28.8","h-19.05"]],["gnd1:GND","oled1:GND","black",["v-9.6","h-86.4"]],["pico:GP4","oled1:SDA","green",["h-38.4","v-86.4","h297.6","v9.6"]],["pico:GP5","oled1:SCL","green",["h-67.2","v-115.2","h307.2"]]],"dependencies":{}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 6:36:16

新能源汽车车载双向 OBC 的 MATLAB 仿真探索

新能源汽车车载双向OBC&#xff0c;PFC&#xff0c;LLC&#xff0c;V2G 双向 充电桩 电动汽车 车载充电机 充放电机 MATLAB仿真模型 &#xff08;1&#xff09;基于V2G技术的双向AC/DC、DC/DC充放电机MATLAB仿真模型&#xff1b; &#xff08;2&#xff09;前级电路为双向AC/D…

作者头像 李华
网站建设 2026/6/7 15:28:03

python基于Vue的电影院售票选座评价影评系统_5s356_django Flask pycharm项目

目录已开发项目效果实现截图关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;已开发项目效果实现截图 同行可拿货,招校园代理 ,本人源头供货商 python基于Vue的电影院售票选座评价影…

作者头像 李华
网站建设 2026/5/28 8:05:26

华为 ModelEngine Nexent 零代码平台智能体能力深度实践评测

前言 在 AI 技术加速落地的当下&#xff0c;智能体已从概念走向产业实践&#xff0c;但传统开发模式依赖复杂编程与 Prompt 工程&#xff0c;高门槛成为制约其规模化普及的核心瓶颈。而华为 ModelEngine Nexent 作为零代码级智能体开发平台&#xff0c;精准切中这一行业痛点&am…

作者头像 李华
网站建设 2026/5/28 8:05:20

python基于Vue的北京市公交线路管理系统_vi06l_django Flask pycharm项目

目录已开发项目效果实现截图关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;已开发项目效果实现截图 同行可拿货,招校园代理 ,本人源头供货商 python基于Vue的北京市公交线路管理系…

作者头像 李华
网站建设 2026/6/6 8:00:27

目录结构保留的JAVA文件夹上传实现思路

大文件传输系统解决方案 项目背景与需求分析 作为北京某软件公司项目负责人&#xff0c;我们近期面临一个技术挑战&#xff1a;在产品中集成一个高性能、高稳定性的超大文件传输系统。经过详细需求分析&#xff0c;我们确认以下核心需求&#xff1a; 大文件处理能力&#xf…

作者头像 李华
网站建设 2026/6/9 12:30:39

毕设项目 基于机器视觉的驾驶疲劳检测系统(源码+论文)

文章目录 0 前言1 项目运行效果2 课题背景3 Dlib人脸检测与特征提取3.1 简介3.2 Dlib优点 4 疲劳检测算法4.1 眼睛检测算法4.2 打哈欠检测算法4.3 点头检测算法 5 PyQt55.1 简介5.2相关界面代码 6 最后 0 前言 &#x1f525;这两年开始毕业设计和毕业答辩的要求和难度不断提升…

作者头像 李华