Pico使用C/C++选择使用哪个I2C控制器,以及SDA和SCL针脚

news2024/11/25 2:21:50

本文一开始讲述了解决方案,后面是我做的笔记,用来讲述我的发现流程和探究的 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 控制器,那么可以同时使用两套SDASCL针脚,但是要注意必须是I2C0I2C1的针脚,不能是同一个控制器的。

发现历程(选读)

这部分不一定要看。这里记录一下我是怎么知道是这样处理的,顺道了解了一下代码结构和信息传递的流程,万一以后需要就不用花时间翻来翻去了。

第一次尝试

首先分析一下:要定义针脚就要知道针脚这个值是如何被利用的,这样就可以知道如何传递处理这个值了。

一般是在初始化的时候设置使用哪个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_I2CPICO_DEFAULT_I2C_SDA_PINPICO_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))是将i2cPICO_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

可以看到i2c0i2c0_inst的地址,注释说这是I2C HW Block 0的标识符。从上面的

extern i2c_inst_t i2c0_inst;
extern i2c_inst_t i2c1_inst;

可以看到i2c0_insti2c1_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

了解了蛮多知识,也希望能帮到有需要的人~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/937407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

性能测试面试问题,一周拿3个offer不嫌多

性能测试的三个核心原理是什么&#xff1f; 1.基于协议。性能测试的对象是网络分布式架构的软件&#xff0c;而网络分布式架构的核心是网络协议 2.多线程。人的大脑是单线程的&#xff0c;电脑的cpu是多线程的。性能测试就是利用多线程的技术模拟多用户去负载 3.模拟真实场景。…

<C++> STL_容器适配器

1.容器适配器 适配器是一种设计模式&#xff0c;该种模式是将一个类的接口转换成客户希望的另外一个接口。 容器适配器是STL中的一种重要组件&#xff0c;用于提供不同的数据结构接口&#xff0c;以满足特定的需求和限制。容器适配器是基于其他STL容器构建的&#xff0c;通过…

关于类的隐形生成函数

https://www.youtube.com/watch?ve8Cw17p_BiU&listPL5jc9xFGsL8FWtnZBeTqZBbniyw0uHyaH&index6 https://www.youtube.com/watch?vKMSYmY74AEs&listPLE28375D4AC946CC3&index4 如果只有copy asignment operator, 那么default construct will be generated as…

复习之docker部署--项目实战

一、实验环境 1.安装7.6虚拟机 最小化安装&#xff0c;不安装图形&#xff01; 2.封装虚拟机 关闭selinux关闭防火墙关闭networkmanager配置网络&#xff0c;保证可以ssh修改主机名添加双向解析配置7.6网络仓库--安装常用的工具 配置完成后&#xff0c;在真机ssh虚拟机 如果…

webassembly003 GGML Tensor Library part-1

GGML ggml的函数 可以看到官方示例项目仅依赖于#include "ggml/ggml.h"&#xff0c; #include "common.h"&#xff0c;可以阅读ggml.h获取ggml的使用帮助 函数解释注释ggml_tensor多维张量按行主顺序存储。ggml_tensor结构包含每个维度中元素数&#xf…

哈夫曼编码(C++实现)

文章目录 1. 前言2. 固定长度编码3. 哈夫曼编码4. 哈夫曼解码5. 编码特点6. 代码实现7. 总结 1. 前言 在上一篇文章中&#xff0c;介绍了 哈夫曼树的概念及其实现 。 哈夫曼树有什么用途呢&#xff1f; —— 那就是用来创建哈夫曼编码&#xff08;Huffman Coding —— 一种二…

2.神经网络的实现

创建神经网络类 import numpy # scipy.special包含S函数expit(x) import scipy.special # 打包模块 import pickle# 激活函数 def activation_func(x):return scipy.special.expit(x)# 用于创建、 训练和查询3层神经网络 class neuralNetwork:# 初始化神经网络def __init__(se…

JUC的常见类

Callable interfacce 也是一种创建线程的方式 Runnable 能表示一个任务(run方法),返回void Callable 也能表示一个任务(call方法),返回一个具体的值,类型可以通过泛型参数来指定(object) 如果进行多线程操作,如果你只是关心多线程的执行过程,使用Runnable即可,如果是关心多线程…

Lottery抽奖项目第二章第二节:搭建DDD四层结构

搭建DDD四层结构 DDD&#xff1a;Domain Driven Design 描述&#xff1a;基于DDD架构构建&#xff0c;初始化搭建工程结构 本节是陆续搭建系统和编码的开始&#xff0c;我们会优先完成一个基础工程的创建。一般在互联网企业这部分工作可能不需要反复处理&#xff0c;只需要在…

自然语言处理(二):近似训练

近似训练 近似训练&#xff08;Approximate Training&#xff09;是指在机器学习中使用近似的方法来训练模型&#xff0c;以降低计算复杂度或提高训练效率。这种方法通常用于处理大规模数据集或复杂模型&#xff0c;其中精确的训练算法可能过于耗时或计算资源不足。 近似训练…

14张图带你了解Android14中的酷炫的功能

14张图带你了解Android14中的酷炫的功能 在近期的几次更新中&#xff0c;Android系统经历了重要的升级。Android 12通过Material UI改变了外观&#xff0c;使界面更加优化。随后&#xff0c;Android 13在Android 12的基础上进一步提升了用户体验&#xff0c;使系统更加流畅。现…

自己的第一个小程序《我们一起记账吧》

一&#xff0c;想法 为了控制自己不要乱花钱&#xff0c;曾经一段时间每天记账&#xff0c;当时用的是市面上比较受欢迎的一些记账工具&#xff0c;但大多数都是以个人角度来记账的&#xff0c;几乎没有以家庭为单位的多人协同记账类软件&#xff0c;虽然也有多人记账小程序&a…

7.11 SpringBoot实战 全局异常处理 - 深入细节详解

文章目录 前言一、异常分类1.1 业务异常1.2 参数校验异常1.3 通用异常兜底 二、保留异常现场2.1 请求地址2.2 请求header2.3 请求参数body2.4 构建异常上下文消息 最后 前言 全局异常处理, 你真的学会了吗&#xff1f; 学完上文&#xff0c;你有思考和动手实践吗&#xff1f;…

stm32之25.FLASH闪存

打开标准库 源码--- int main(void) {uint32_t d;Led_init();key_init();/* 初始化串口1波特率为115200bps&#xff0c;若发送/接收数据有乱码&#xff0c;请检查PLL */usart1_init(115200);printf("this is flash test\r\n");/* 解锁FLASH&#xff08;闪存&#xf…

腾讯云服务器搭建网站详细教程_2023更新

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网分享使用腾讯云服务器建站教程&#xff0c;新手站长搭…

系统架构设计高级技能 · Web架构

现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 Web架构 一、Web架构介绍1.1 Web架构涉及技术1.2 单台服务…

几个nlp的小任务(生成任务(摘要生成))

几个nlp的小任务生成任务——摘要生成 安装库选择模型加载数据集展示数据集数据预处理 tokenizer注意特殊的 token处理组成预处理函数调用map,对数据集进行预处理微调模型,设置参数设置数据收集器,将处理好的数据喂给模型封装测评方法将参数传给 trainer,开始训练安装库 选…

交叉熵的简单理解:真实分布与非真实分布的交叉,完全对应,熵为0

目录 交叉熵的简单理解&#xff1a;真实分布与非真实分布的交叉&#xff0c;完全对应&#xff0c;熵为0 交叉熵的简单理解&#xff1a;真实分布与非真实分布的交叉&#xff0c;完全对应&#xff0c;熵为0 这个式子就是熵的表达式. 简单来说, 其意义就是在最优化策略下, 猜到颜…

哪些自主品牌「霸榜」30万元向上战场?硬派越野/MPV再助力

占乘用车市场不到20%份额的30万元以上价位&#xff0c;一直以来都是合资品牌的天下。现在&#xff0c;三家中国本土自主品牌已经率先突围。 高工智能汽车研究院监测数据显示&#xff0c;2023年1-7月&#xff0c;理想、比亚迪、蔚来进入30万元以上价位新车交付量TOP10&#xff…