Pico使用C/C++选择使用哪个I2C控制器,以及SDA和SCL针脚
本文一开始讲述了解决方案,后面是我做的笔记,用来讲述我的发现流程和探究的 Pico I2C 代码结构。
前提知识
首先要说明一点:Pico 有两个 I2C,也就是两套 SDA 和 SCL。这点你可以在针脚图中名字看出,比如下图的 Pin 4 和 Pin 5是 I2C1 的,而默认的 Pin 6 和 Pin 7 是 I2C0 的。
默认情况下是只开启了第一个 I2C,也就是只有 I2C0 的针脚是可以使用的。如果这种情况下,你哪怕修改了针脚,但不是 I2C0 的,也是不会正常运行的。
如何选择哪个I2C控制器,以及SDA和SCL针脚
在设置之前声明三个变量或宏来方便开发。建议使用宏,这比较符合树莓派的开发风格:
#define I2C i2c0
#define I2C_SDA_PIN 4
#define I2C_SCL_PIN 5
如果宏扩展出错,那么就使用变量。
然后初始化 I2C 的时候来设置使用哪个 I2C 控制器,以及哪个SDA和SCL针脚。下面是设置根据上面的设置,这里使用的是第一个 I2C 控制器,SDA 使用的是 GP4,SCL 使用的是 GP5,频率为1000000
:
i2c_init(I2C, 1000000);
gpio_set_function(I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(I2C_SDA_PIN);
gpio_pull_up(I2C_SCL_PIN);
由于有两个 I2C 控制器,那么可以同时使用两套SDA
和SCL
针脚,但是要注意必须是I2C0
和I2C1
的针脚,不能是同一个控制器的。
发现历程(选读)
这部分不一定要看。这里记录一下我是怎么知道是这样处理的,顺道了解了一下代码结构和信息传递的流程,万一以后需要就不用花时间翻来翻去了。
第一次尝试
首先分析一下:要定义针脚就要知道针脚这个值是如何被利用的,这样就可以知道如何传递处理这个值了。
一般是在初始化的时候设置使用哪个I2C控制器以及SDA和SCL针脚,代码一般如下:
i2c_init(i2c_default, CLK);
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
研究《用C/C++修改I2C默认的SDA和SCL针脚》的时候,我知道了默认针脚是在pico.h
中配置的的,相关值有三个:PICO_DEFAULT_I2C
、PICO_DEFAULT_I2C_SDA_PIN
和PICO_DEFAULT_I2C_SCL_PIN
,那么只要追溯这三个值就行。
但是这样不好找,引用太多了。所以我就尝试了从另一方面先入手:I2C 是通过i2c_init()
函数初始化的,如下:
i2c_init(i2c_default, SSD1306_I2C_CLK);
我需要的只有第一个参数i2c_default
,因为这个参数传递了一些信息,第二个参数uint baudrate
是传递速率的,和针脚无关。
那么i2c_init()
函数的内容是什么呢?知道这个才能知道i2c_default
的类型是什么结构,以及内部进行了什么处理。
i2c_init()
函数声明在pico-sdk/src/rp2_common/hardware_i2c/i2c.c
中,函数参数列表如下:
uint i2c_init(i2c_inst_t *i2c, uint baudrate) {i2c_reset(i2c);i2c_unreset(i2c);i2c->restart_on_next = false;i2c->hw->enable = 0;...// Re-sets i2c->hw->enable upon returning:return i2c_set_baudrate(i2c, baudrate);
}
那这个i2c_inst_t
是个什么数据类型呢?我就继续找它。
在pico-sdk/src/rp2_common/hardware_i2c/include/hardware/i2c.h
的第 52 行可以看到它是i2c_inst
结构体的重命名:
typedef struct i2c_inst i2c_inst_t;
那继续找结构体i2c_inst
,这个结构体就在同一个文件里的第 135 行:
struct i2c_inst {i2c_hw_t *hw;bool restart_on_next;
};
终点还是第一个变量i2c_hw_t *hw
,因为只有它可能会传递针脚的值,那就继续找i2c_hw_t
是什么数据类型。
这个数据类型的声明在pico-sdk/src/rp2040/hardware_structs/include/hardware/structs/i2c.h
中。换句话说,这个文件就是为i2c_hw_t
结构体所准备的:
这个结构体存储了很多 I2C 的信息,但还是没找到针脚的信息,那么我就回到一开始在进行寻找。
第二次尝试
最开始我是寻找了i2c_init()
的第一个参数的类型i2c_inst_t
,收获不大。但是它的值我还没寻找,所以这次从参数值出发i2c_default
,这个值是哪定义的呢?
在刚才发现i2c_inst_t
声明和定义的pico-sdk/src/rp2_common/hardware_i2c/include/hardware/i2c.h
头文件中发现了需要的东西(第 76 行):
#ifdef PICO_DEFAULT_I2C_INSTANCE
#define i2c_default PICO_DEFAULT_I2C_INSTANCE
#endif
这个PICO_DEFAULT_I2C_INSTANCE
是什么呢?往上一瞅就能看到:
#if !defined(PICO_DEFAULT_I2C_INSTANCE) && defined(PICO_DEFAULT_I2C)
#define PICO_DEFAULT_I2C_INSTANCE (__CONCAT(i2c,PICO_DEFAULT_I2C))
#endif
在这里终于看到一个需要的值:PICO_DEFAULT_I2C
,前文可知这个默认为0
。
这里的(__CONCAT(i2c,PICO_DEFAULT_I2C))
是将i2c
和PICO_DEFAULT_I2C
的值连接起来了,默认情况下也就是i2c0
。也就是说,参数i2c_default
就是i2c0
。
这个技巧很不错,但是有些编译器用不了,比如我用 Clang x86_64-apple-darwin21.6.0 就无法扩展
PICO_DEFAULT_I2C
。
再深入一些
但是这里的i2c0
是什么呢?这是个什么类型的数据呢?
还是在pico-sdk/src/rp2_common/hardware_i2c/include/hardware/i2c.h
头文件中(如上图)有这样一段:
#define i2c0 (&i2c0_inst) ///< Identifier for I2C HW Block 0
#define i2c1 (&i2c1_inst) ///< Identifier for I2C HW Block 1
可以看到i2c0
是i2c0_inst
的地址,注释说这是I2C HW Block 0
的标识符。从上面的
extern i2c_inst_t i2c0_inst;
extern i2c_inst_t i2c1_inst;
可以看到i2c0_inst
和i2c1_inst
是外部变量,类型是i2c_inst_t
,这个类型之前我看到了定义的结构体:
struct i2c_inst {i2c_hw_t *hw;bool restart_on_next;
};
那这个i2c0_inst
是在哪声明的?
这部分在pico-sdk/src/rp2_common/hardware_i2c/i2c.c
中声明的:
i2c_inst_t i2c0_inst = {i2c0_hw, false};
i2c_inst_t i2c1_inst = {i2c1_hw, false};
这个i2c0_hw
又是啥呢?在哪定义的呢?
这是在pico-sdk/src/rp2040/hardware_structs/include/hardware/structs/i2c.h
中:
#define i2c0_hw ((i2c_hw_t *)I2C0_BASE)
#define i2c1_hw ((i2c_hw_t *)I2C1_BASE)
i2c0_hw
表示((i2c_hw_t *)I2C0_BASE)
,意思是I2C0_BASE
是个指向i2c_hw_t
的指针,它的内容在pico-sdk/src/rp2040/hardware_regs/include/hardware/regs/addressmap.h
中:
#define I2C0_BASE _u(0x40044000)
#define I2C1_BASE _u(0x40048000)
也就是说I2C0_BASE
就是0x40044000
,而i2c0_hw
的地址就是0x40044000
。
补充一点,这里_()
是无符号整数的意思,定义在pico-sdk/src/rp2040/hardware_regs/include/hardware/platform_defs
:
#ifndef _u
#ifdef __ASSEMBLER__
#define _u(x) x
#else
#define _u(x) x ## u
#endif
#endif
了解了蛮多知识,也希望能帮到有需要的人~
相关文章:

Pico使用C/C++选择使用哪个I2C控制器,以及SDA和SCL针脚
本文一开始讲述了解决方案,后面是我做的笔记,用来讲述我的发现流程和探究的 Pico I2C 代码结构。 前提知识 首先要说明一点:Pico 有两个 I2C,也就是两套 SDA 和 SCL。这点你可以在针脚图中名字看出,比如下图的 Pin 4…...
C++动态内存管理
动态内存 在C/C程序中(线程)栈空间是有限的,大部分变量使用的都是动态分配来的堆内存,这些动态申请来的堆内存是需要开发者通过代码去自行管理的。如何管理好这些动态申请来的内存,是C/C开发中的一个重点难点问题。 m…...

SpringBoot—日志
目录 日志使用日志日志级别设置日志级别设置分组指定日志文件路径日志切割归档使用第三方日志框架log4j2配置文件【分级存储】logback配置文件【分级存储】 实例代码 日志 使用日志 给controller添加日志信息 要给controller类上添加Slf4j注解,然后使用log.info(…...

如何在,Linux中安装Luajit2.*
1.文件下载The LuaJIT Project 2.将下载文件上传到对应的服务器:例如/opt 3.进入对应的文件夹 4.make PREFIX/usr/local,设置安装路径 5.make install,编译安装 6.进入安装目录,cd /usr/local/include/luajit-2.0 7.luajit -v…...

单片机-如何让数码管动态显示
数码管硬件图 1、数码管 连接 74HC245 芯片 单片机IO口输出难稳定,需要数码管与单片机连接需要增加驱动电路, 使用 74HC245 abcdefgDP并联导出 74HC245 对数码管进行驱动,P0 是输出电流 来驱动各个段的 驱动芯片 增加电阻 是为了防止电流…...

在Visual Studio 2017上配置并使用OpenGL
1 在Visual Studio 2017上配置并使用OpenGL 在GLUT - The OpenGL Utility Toolkit:GLUT - The OpenGL Utility Toolkit中点击“GLUT for Microsoft Windows 95 & NT users”,选择“If you want just the GLUT header file, the .LIB, and .DLL file…...

【C++】多态学习
多态 多态的概念与定义多态的概念构成多态的两个条件虚函数与重写重写的两个特例 final 和 override重载、重写(覆盖)、重定义(隐藏)的对比抽象类多态的原理静态绑定与动态绑定 单继承与多继承关系下的虚函数表(派生类)单继承中的虚函数表查看多继承中的虚函数表查看 菱形继承与…...

大数据之Maven
一、Maven的作用 作用一:下载对应的jar包 避免jar包重复下载配置,保证多个工程共用一份jar包。Maven有一个本地仓库,可以通过pom.xml文件来记录jar所在的位置。Maven会自动从远程仓库下载jar包,并且会下载所依赖的其他jar包&…...
自制centos7.9的wsl发行版
自制centos7.9的wsl发行版 参考:https://zhuanlan.zhihu.com/p/482538727 Windows10提供了一个wsl工具用于直接在windows上运行Linux子系统。 CentOS国内镜像下载:https://mirrors.aliyun.com/centos/ 这里选择了7.9.2009版本:https://mirr…...
使用VisualStudio制作上位机(五)
文章目录 使用VisualStudio制作上位机(五)第四部分:GUI界面数据显示使用VisualStudio制作上位机(五) Author:YAL 第四部分:GUI界面数据显示 这一部分,主要实现GUI的界面显示。 上一文已经实现了CAN数据的接收,并将数据更新到数组里。所以在做界面的显示时,只需要在…...

ChatGPT在医疗领域可应用于改善与患者的沟通
注意:本信息仅供参考,发布该内容旨在传递更多信息的目的,并不意味着赞同其观点或证实其说法。 自从ChatGPT在2022年末对公众开放以来,OpenAI的这款生成式AI聊天机器人在医疗领域展示出了巨大潜力。它已经通过了美国医学执照考试&a…...

直播预告|博睿学院第四季即将开讲:博睿数据资深运维团队现身说法!
博睿学院第四季开讲啦!本季博睿学院的课程将于本周四(8月31日)16点正式启动。本季我们邀请到了博睿数据平台支撑中心的四位资深运维专家现身说法,来为我们分享一体化智能可观测平台Bonree ONE的实践干货。 他们,见多识…...

端到端自动驾驶综述
End-to-end Autonomous Driving: Challenges and Frontiers 文章脉路 Introduction 从经典的模块化的方法到端到端方法的一个对比, 讲了各自的优缺点, 模块化的好处是各个模块都有自己明确的优化的目标, 可解释性较强, 且容易debug, 缺点是各个模块优化的目标并不是最终的驾…...
mysql索引、事务、存储引擎
一、索引 索引的概念: 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址)。使用索引后可以不用扫描全表来定位某行的数据,而是…...
【CMU15445】Fall 2019, Project 2: Hash Table 实验记录
目录 实验准备实验测试 实验准备 官方说明:https://15445.courses.cs.cmu.edu/fall2019/project2/ 实验测试 Task 1: mkdir build cd build make hash_table_page_test ./test/hash_table_page_testTask 2: make hash_table_test ./test…...
PMP证书是不是烂大街了?
大家都知道,PMP证书是项目管理领域的金字招牌。近年来,随着项目管理的重要性日益凸显,越来越多的人开始关注和学习PMP证书。无论是企业招聘还是个人职业发展,PMP证书都成为了一张炙手可热的敲门砖。 那么,PMP证书到底…...

Mac下Docker Desktop安装命令行工具、开启本地远程访问
Mac系统下,为了方便在terminal和idea里使用docker,需要安装docker命令行工具,和开启Docker Desktop本地远程访问。 具体方法是在设置-高级下, 1.将勾选的User调整为System,这样不用手动配置PATH即可使用docker命令 …...

Java实现根据商品ID获取京东商品详情数据,1688商品详情接口,1688API接口封装方法
要通过京东的API获取商品详情数据,您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例,展示如何通过京东开放平台API获取商品详情: 首先,确保您已注册成为京东开放平台的开发者,并创建一…...

element-plus指定el-date-picker的弹出框位置
此处记录一下,通过popper-options指定popper出现的位置...

游戏陪玩语音聊天系统3.0商业升级独立版本源码
首发价值29800元的最新商业版游戏陪玩语音聊天系统3.0商业升级独立版本源码 1、增加人气店员轮播 2、优化ui界面丨优化游戏图标展示丨优化分类展示 3、增加动态礼物打赏功能 4、增加礼物墙功能 增加店员满足业绩,才能升级功能 5、增加店员等级不同,…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...