通过 bitbang GPIO来实现i2c总线协议

Linux下通过ethtool ioctl直接读写PHY寄存器:原理、实现与调试实战 在嵌入式Linux和网络设备开发中,深入理解底层硬件是解决复杂问题的关键。MDIO/SMI总线作为管理以太网PHY芯片的标准接口,其协议定义了通过时钟线(MDC)和数据线(MDIO)访问PHY寄存器的帧格式,这是实现网络物理层精细控制的基石。掌握直接寄存器访问技术,对于网络深度调试、性能优化和特殊功能启用具有重要价值,尤其在链路协商异常、信号质量调优等应用场景中不可或缺。本文聚焦于利用Linux内核已完善的mdio_bus框架,通过用户空间程序构造MDIO命令,并借助ethtool工具提供的标准ioctl接 阅读详情

i2c总线可以通过bitbang两个gpio口来实现, bitbang就是位拉高拉低的意思,英文手册中经常出现的一个词。

drivers/i2c/algos/i2c-algo-bit.c, 就是这个i2c的algos程序,通过bitbang SCL 和SDA两个引脚,来实现I2C总线协议。

1. 实现bitbang,  有两个最基本的操作,一个是设置GPIO的输出(高或者低),另一个就是读取GPIO的状态。

因为实现I2C总线协议,必须要有时间来控制SCL的脉冲宽度(这里用的是udelay),所以需要看GPIO口的拉高或者拉低到底有没有实现,就需要读取GPIO的状态来判断。

这里相关的代码如下:

/* --- setting states on the bus with the right timing: --------------- */

#define setsda(adap,val) adap->setsda(adap->data, val)
#define setscl(adap,val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)
设置sda 和scl的输出,读取sda和scl的状态, 这两个函数的具体实现需要用到i2c busses下的一个代码,这里使用的是通用的drivers/i2c/busses/i2c-gpio.c。
在i2c_gpio_probe()函数中,实现了上面几个函数指针的赋值:

static int __init i2c_gpio_probe(struct platform_device *pdev)
{
        struct i2c_gpio_platform_data *pdata;
        struct i2c_algo_bit_data *bit_data;
        struct i2c_adapter *adap;
        int ret;

        pdata = pdev->dev.platform_data;
        if (!pdata)
                return -ENXIO;

        ret = -ENOMEM;
        adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
        if (!adap)
                goto err_alloc_adap;
        bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);
        if (!bit_data)
                goto err_alloc_bit_data;

        gpio_request_mux(pdata->sda_pin, GPIO_MUX_GPIO);
        gpio_request_mux(pdata->scl_pin, GPIO_MUX_GPIO);

        gpio_set_puen(pdata->sda_pin, 1);
        gpio_set_puen(pdata->scl_pin, 1);

        if (pdata->sda_is_open_drain) {
                mxc_set_gpio_direction(pdata->sda_pin, 0);
                bit_data->setsda = i2c_gpio_setsda_val;
        } else {
                mxc_set_gpio_direction(pdata->sda_pin, 1);
                bit_data->setsda = i2c_gpio_setsda_dir;
        }

        if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
                mxc_set_gpio_direction(pdata->scl_pin, 0);
                bit_data->setscl = i2c_gpio_setscl_val;
        } else {
                mxc_set_gpio_direction(pdata->scl_pin, 1);
                bit_data->setscl = i2c_gpio_setscl_dir;
        }

        if (!pdata->scl_is_output_only)
                bit_data->getscl = i2c_gpio_getscl;
        bit_data->getsda = i2c_gpio_getsda;

        if (pdata->udelay)
                bit_data->udelay = pdata->udelay;
        else if (pdata->scl_is_output_only)
                bit_data->udelay = 50;                  /* 10 kHz */
        else
                bit_data->udelay = 5;                   /* 100 kHz */

        if (pdata->timeout)
                bit_data->timeout = pdata->timeout;
        else
                bit_data->timeout = HZ / 10;            /* 100 ms */

        bit_data->data = pdata;

        adap->owner = THIS_MODULE;
        snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
        adap->algo_data = bit_data;
        adap->dev.parent = &pdev->dev;

        ret = i2c_bit_add_bus(adap);
        if (ret)
                goto err_add_bus;

        platform_set_drvdata(pdev, adap);

        dev_info(&pdev->dev, "using pins %x (SDA) and %x (SCL%s)\n",
                 pdata->sda_pin, pdata->scl_pin,
                 pdata->scl_is_output_only
                 ? ", no clock stretching" : "");

        return 0;

err_add_bus:
        mxc_free_gpio(pdata->scl_pin);
err_request_scl:
        mxc_free_gpio(pdata->sda_pin);
err_request_sda:
        kfree(bit_data);
err_alloc_bit_data:
        kfree(adap);
err_alloc_adap:
        return ret;
}
首先是获得GPIO, 把GPIO设成高阻抗状态。

        gpio_request_mux(pdata->sda_pin, GPIO_MUX_GPIO);
        gpio_request_mux(pdata->scl_pin, GPIO_MUX_GPIO);

        gpio_set_puen(pdata->sda_pin, 1);
        gpio_set_puen(pdata->scl_pin, 1);
然后就是开始赋值这四个函数指针:

        if (pdata->sda_is_open_drain) {
                mxc_set_gpio_direction(pdata->sda_pin, 0);
                bit_data->setsda = i2c_gpio_setsda_val;
        } else {
                mxc_set_gpio_direction(pdata->sda_pin, 1);
                bit_data->setsda = i2c_gpio_setsda_dir;
        }

        if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
                mxc_set_gpio_direction(pdata->scl_pin, 0);
                bit_data->setscl = i2c_gpio_setscl_val;
        } else {
                mxc_set_gpio_direction(pdata->scl_pin, 1);
                bit_data->setscl = i2c_gpio_setscl_dir;
        }

        if (!pdata->scl_is_output_only)
                bit_data->getscl = i2c_gpio_getscl;
        bit_data->getsda = i2c_gpio_getsda;
再接下来是设置 udelay 和timeout, 这连个值在bitbang中都会使用到,udelay是用来控制scl的脉冲周期的,timeout是用来控制最长脉冲宽度,比如有时CPU被中断,被别的进程使用,这时bitbang就会有延时,timeout就是用来控制最大的延时的,如果延时超出timeout,就会报错之类的出现,后面会有详细代码说明。

        if (pdata->udelay)
                bit_data->udelay = pdata->udelay;
        else if (pdata->scl_is_output_only)
                bit_data->udelay = 50;                  /* 10 kHz */
        else
                bit_data->udelay = 5;                   /* 100 kHz */

        if (pdata->timeout)
                bit_data->timeout = pdata->timeout;
        else
                bit_data->timeout = HZ / 10;            /* 100 ms */

最后就是i2c adapter 的赋值,并向i2c core 添加i2c adapter:

        bit_data->data = pdata;

        adap->owner = THIS_MODULE;
        snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
        adap->algo_data = bit_data;
        adap->dev.parent = &pdev->dev;

        ret = i2c_bit_add_bus(adap);
        if (ret)
                goto err_add_bus;

        platform_set_drvdata(pdev, adap);
i2c_bit_add_bus() 中可以先测试I2C bus bitbang功能行不行,如果测试失败,就返回错误。如果成功,就添加adapter到i2c core中。
int i2c_bit_add_bus(struct i2c_adapter *adap)
{
        int err;

        err = i2c_bit_prepare_bus(adap);
        if (err)
                return err;

        return i2c_add_adapter(adap);
}
EXPORT_SYMBOL(i2c_bit_add_bus);
i2c_bit_prepare_bus() 就是用来测试i2c最基本的拉低拉高bitbang功能的,并添加几个adapter数据。

/*
 * registering functions to load algorithms at runtime
 */
static int i2c_bit_prepare_
STM407ZET6+OV5640调试心得 最近一个项目用了STMF407ZET6 +OV5640,调试了好几天做些总结; STMF407ZET6 开发板用的是正点原子的开发板,OV5640摄像头用的是秉火摄像头; 这其中有一个大坑,开发板的DCMI接口跟摄像头的接口不一样,要重新用杜邦线进行连接; 本人是在正点原子的“”15,ATK-OV5640摄像头模块“”源码上进行修改的。 阅读详情

相关推荐

【龙印】在龙芯1c上用TM7705+NTC热敏电阻实现温度测量

本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”,Git地址“https://gitee.com/caogos/marlin_ls1c”本文重点放在TM7705和ntc热敏电阻结合实现测量温度上,另外有一篇重点放在TM7705的驱动上。详见《【龙印】龙芯1c上双路16位AD芯片TM7705的linux驱动》http://blog.youkuaiyun.com/caogos/art

勤为本的专栏 4593

【龙印】龙芯1c上双路16位AD芯片TM7705的linux驱动

本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”,git地址“https://gitee.com/caogos/marlin_ls1c”TM7705和热敏电阻一起实现3d打印机的温度测量。本文重点放在tm7705的linux驱动上,关于温度测量后面另外写一篇详细介绍。硬件电路测试用的硬件为“安富莱”推出的TM7705模块。这里着重强调一下TM7705是深圳天微电子的A

勤为本的专栏 6955

BitBang_I2C:一种软件I2C实现,可在任何系统上的任何GPIO引脚上运行

Bit Bang I2C库 版权所有(c)2018 BitBank Software,Inc.由Larry Bank撰写( )项目于2018年12月12日开始 该代码的目的是提供一个简单的C库,该库可以对任何系统上任意2个GPIO引脚上的I2C协议进行位冲击。 除了标准GPIO功能外,I2C协议不需要引脚的任何特殊功能。 我写它的原因是为了轻松访问不一定公开I2C接口的各种微控制器上的I2C设备。 这已在包括AVR,ESP32和nRF5微处理器控制器在内的各种项目中派上了用场。 引脚访问功能可以是本机版本的包装函数(例如,在nRF5 SDK上)。在AVR微型计算机上,digitalWrite / digitalRead / pinMode函数在某种程度上较慢,因为它们会对照表检查引脚编号并执行其他任务。 该库包含加快速度的逻辑。 通过将引脚号指定为端口名称+位,该库将在AVR微控制器上

AI初识—LLM、Ollama、Llama都是些个啥?

LLM指的是大语言模型(Large Language Model),是一种基于深度学习的模型,能够理解和生成自然语言。它通过大量文本数据进行训练,具备了理解上下文、生成连贯文本和回答问题的能力。著名的LLM包括OpenAI的GPT系列和Google的BERT。在AI技术快速发展的背景下,LLM、Ollama和Llama代表了现代自然语言处理的不同方面。LLM为我们提供了强大的语言理解能力,Ollama为模型的部署和管理提供了便利,而Llama则在效率和多样性上不断推进。

小蜗牛的珍贵百宝箱 4629

论文阅读------Exposure Fusion: A Simple and Practical Alternative to High Dynamic Range Photography

Mertens T, Kautz J, Van Reeth F. Exposure fusion: A simple and practical alternative to high dynamic range photography[C]//Computer graphics forum. Oxford, UK: Blackwell Publishing Ltd, 2009, 28(1): 161-171. 摘要 本文提出了一种将包围曝光的序列融合到高质量图像中的技术,而无需先转换为高动态范围(HD.

qq_25283239的博客 3397

python 爬虫 xpath 存储到数据库

参考

我是张先生 1289

Ai数字人直播系统源码部署

虚拟形象定制,直播内容智能生成,多语言支持,智能互动,声音克隆,24 小时不间断直播,多平台直播,智能数据分析,实时情感识别,视频合成,Ai数字人直播系统源码出售,Ai数字人直播系统源码部署实现 AI 数字人 24 小时不间断直播功能是一个较为复杂的任务,涉及到多个方面,如直播流的推送、内容的持续生成、系统的稳定性保障等。以下为你提供一个简化的实现思路和示例代码,借助 Python 结合 FFmpeg 工具来模拟 24 小时不间断直播。

weixin_ggwwsscc的博客 1256

别再只会用555了!手把手教你用运放和RC电路搭一个50Hz正弦波信号源(附Multisim仿真文件)

本文详细介绍了如何使用运放LM358和RC电路搭建一个精准的50Hz正弦波信号源,涵盖文氏电桥核心原理、单电源供电解决方案、Multisim仿真及实物调试技巧。通过实战指南和优化建议,帮助电子爱好者快速掌握低频信号生成技术,适用于各种测试和校准场景。

weixin_34116482的博客 364

[Dify] 如何在Dify中解析上传的TXT文件内容

在Dify平台上构建智能应用时,解析用户上传的.txt文件是关键步骤。

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 2637

12、有杆泵抽油系统:原理、性能与选择

本文全面介绍了有杆泵抽油系统的原理、性能分析和设备选择方法。有杆泵抽油系统广泛应用于石油开采领域,通过地面抽油装置与井下柱塞泵的配合,实现高效、稳定的原油提升。文章详细解析了系统的工作原理、组成结构、光杆运动特性、载荷计算、泵输送能力及功率需求,并提供了抽油装置的选择流程和实际案例分析。此外,还探讨了泵的容积效率影响因素、示功图的作用及系统优化运行的建议,为石油开采工程提供了理论支持和实践指导。

gan8painter的博客 433

【图像超分】论文精读:RepSR: Training Efficient VGG-style Super-Resolution Networks with Structural Re-Paramete

摘要: 本文提出RepSR,一种基于结构重新参数化和批量归一化(BN)的高效VGG式超分辨率网络。针对SR任务中BN易引发伪影的问题,作者通过分析发现训练-测试阶段的不一致性是主因,并提出先使用小批量统计训练、后期切换为总体统计的优化策略。RepSR块设计包含干净的残差路径和带改进BN的扩展-压缩卷积路径,有效平衡了训练非线性与推理效率。实验表明,RepSR在不同模型规模下优于现有重参数化方法,且实际推理速度更快。该工作为SR提供了性能与效率兼优的解决方案,相关代码未开源。 关键词: 超分辨率、结构重参数化

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 304

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: xargs 及其常用的一些命令
下一篇: 应用层, i2c-dev.c,和i2c-core.c的交互
lamdoc
博客等级 码龄18年 155粉丝 655原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值