news 2026/6/15 17:34:28

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/15 15:04:13

WSL2安装配置

WSL2安装配置安装默认phplaravel启动环境知乎文章&#xff1a;https://zhuanlan.zhihu.com/p/1961718875029758545 踩坑(装得慢)之后流程&#xff1a; 打开windows powershell 不是 cmdInvoke-WebRequest -Uri https://wslstorestorage.blob.core.windows.net/wslblob/wsl_up…

作者头像 李华
网站建设 2026/6/15 13:22:49

产品为王!用友HR SaaS斩获2025数字人力资源科技最佳产品奖

近期&#xff0c;备受行业瞩目的2025数字人力资源科技奖&#xff08;Digital HRTech Awards&#xff09;正式揭晓&#xff0c;用友HR SaaS成功斩获“2025数字人力资源科技最佳产品奖”。本奖项聚焦人力资源科技的创新价值与实践成效&#xff0c;是行业内极具权威性的专业评选。…

作者头像 李华
网站建设 2026/6/15 12:17:22

中国AI的致命短板:超越“追赶”,构建文明级战略操作系统

中国AI的致命短板&#xff1a;超越“追赶”&#xff0c;构建文明级战略操作系统 埃里克施密特的发言&#xff0c;一针见血地指出了中美AI竞赛中一个常被忽视的结构性差距&#xff1a;资本深度与创新生态的鸿沟。他揭示的并非单纯的技术落后&#xff0c;而是支撑技术爆炸的底层…

作者头像 李华
网站建设 2026/6/15 12:17:13

AI Agent 工程失效的确定性来源与 EDCA 控制解法(工程向)

本文默认读者具备 Agent 系统工程背景&#xff0c;默认读者有EDCA 体系概念 不讨论模型能力&#xff0c;不讨论 Prompt 技巧。1. Agent 失效不是“不聪明”&#xff0c;而是控制面缺失在真实工程中&#xff0c;以下问题高度重复出现&#xff1a;长任务中决策不可预测多 Agent 协…

作者头像 李华
网站建设 2026/6/15 13:26:01

如何在CSDN创作一篇98分高质量的技术博客?

如何在CSDN创作一篇98分高质量的技术博客&#xff1f; 摘要 在CSDN上创作一篇技术博客&#xff0c;如何才能达到98分的高质量&#xff1f;这不仅仅是关于内容本身的深度与广度&#xff0c;还包括文章的结构、排版、互动性等多个方面。本文将介绍如何从各个方面提升你的技术博…

作者头像 李华