news 2026/4/16 11:00:08

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/4/13 7:02:43

被锁死的想象力:当AI成了科研本身,人类只能是观众

别盯着AI写的烂文案了&#xff01;千亿级投入超越曼哈顿计划&#xff0c;科研速度抛弃人类带宽&#xff0c;一场「里氏10.0级」的震荡已在眼前。这不仅是工具的升级&#xff0c;更是文明断裂的开始。欢迎来到&#xff0c;人类即将无法认知的时代。我们对未来的想象力&#xff0…

作者头像 李华
网站建设 2026/4/13 22:04:51

短视频运营者智能获客全开源SAAS系统源码详解,轻松创建和管理子账户

温馨提示&#xff1a;文末有资源获取方式在数字化营销时代&#xff0c;拥有一套自主可控、功能全面的短视频运营系统&#xff0c;意味着掌握了流量获取与转化的主动权。今天介绍一款专为高效运营与商业变现而生的全开源智能系统源码&#xff0c;它将复杂操作简单化&#xff0c;…

作者头像 李华
网站建设 2026/4/16 10:57:57

【开题答辩全过程】以 基于安卓的景区购票系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/4/15 15:24:48

python基于vue的二手儿童绘本网上商城交易系统设计与实现 7k9k2

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

作者头像 李华
网站建设 2026/4/16 1:51:48

Mac版jdk8如何下载 jdk1.8 (支持M1-M5、intel芯片)

在Mac上下载JDK 8有以下几种方法&#xff1a;方法一&#xff1a;网盘下载&#xff0c;最便捷&#xff0c;推荐&#xff08;事先从Oracle官网下载的&#xff09;复制下方网盘链接&#xff1a;https://pan.quark.cn/s/2efa3d16e97c保存到自己网盘直接下载到本地方法二&#xff1a…

作者头像 李华
网站建设 2026/4/13 16:57:30

python基于Vue的二手书籍交易系统的设计与实现 商家卖家_i5j6h_django Flask pycharm项目

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

作者头像 李华