• 登录 | 注册

小雷的学习空间

用硬件包围软件 最终实现软硬通吃

  • 目录视图
  • 摘要视图
  • 订阅

Linux I2C驱动完全分析(二)

标签: clinuxstructalgorithmtable
2011-05-01 18:17 14323人阅读 评论(25) 收藏 举报

 分类:
Linux设备驱动程序第三版学习笔记(17) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

博主按:大热的天,刚刚负重从五道口走到石板房,大约4公里吧。终于让我找了一个咖啡屋休息一下,继续写这篇驱动分析。单身的生活就是这样无聊啊。 不发牢骚了,活出个样儿来给自己看!千难万险脚下踩,啥也难不倒咱!继续整!~

先说一下,本文中有个疑惑,一直没有搞懂,写在这里,望高人指点一二,不胜感激!

#define I2C_M_NOSTART  0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK  0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */

这里I2C_FUNC_PROTOCOL_MANGLING 是什么意思?为什么定义这些东东?看了注释也不太理解。求解释!

3. I2C总线驱动代码分析

   s3c2440的总线驱动代码在i2c-s3c2410.c中。照例先从init看起。

[c-sharp] view plaincopy
  1. static int __init i2c_adap_s3c_init(void)
  2. {
  3. return platform_driver_register(&s3c24xx_i2c_driver);
  4. }

在init中只是调用了平台驱动注册函数注册了一个i2c的平台驱动s3c24xx_i2c_driver。这个驱动是一个platform_driver的结构体变量。注意这里不是i2c_driver结构体,因为i2c_driver是对设备的驱动,而这里对控制器的驱动要使用platform_driver

[c-sharp] view plaincopy
  1. static struct platform_driver s3c24xx_i2c_driver = {
  2. .probe      = s3c24xx_i2c_probe,
  3. .remove     = s3c24xx_i2c_remove,
  4. .suspend_late   = s3c24xx_i2c_suspend_late,
  5. .resume     = s3c24xx_i2c_resume,
  6. .id_table   = s3c24xx_driver_ids,
  7. .driver     = {
  8. .owner  = THIS_MODULE,
  9. .name   = "s3c-i2c",
  10. },
  11. };

同样的,重要的函数还是那几个:probe,remove,suspend_late,resume。再加上一个id_table和device_driver结构体变量。

下面逐个分析:

* probe函数

当调用platform_driver_register函数注册platform_driver结构体时,probe指针指向的s3c24xx_i2c_probe函数将会被调用。这部分详细解释参考本博客另一篇文章《S3C2410看门狗驱动分析》。细心的朋友可能会发现,在s3c24xx_i2c_driver中,驱动的名字是"s3c-i2c",而在板文件中可以看到,设备的名字是"s3c2410-i2c",这两个名字不一样,那驱动和设备是如何match的呢?答案就在于id_table。这个id_table包含了驱动所支持的设备ID表。在match的时候,判断这个表中的名字是不是和设备一致,一致则match成功。这也是为什么一个驱动可以同时match成功多个设备的原因。如果只是靠platform_driver-->driver中的名字来匹配的话,那么驱动和设备只能是一对一的关系了。

[c-sharp] view plaincopy
  1. static struct platform_device_id s3c24xx_driver_ids[] = {
  2. {
  3. .name       = "s3c2410-i2c",
  4. .driver_data    = TYPE_S3C2410,
  5. }, {
  6. .name       = "s3c2440-i2c",
  7. .driver_data    = TYPE_S3C2440,
  8. }, { },
  9. };

扯远了,还是看看probe的代码吧~

[c-sharp] view plaincopy
  1. static int s3c24xx_i2c_probe(struct platform_device *pdev)
  2. {
  3. struct s3c24xx_i2c *i2c;
  4. struct s3c2410_platform_i2c *pdata;
  5. struct resource *res;
  6. int ret;
  7. pdata = pdev->dev.platform_data;
  8. if (!pdata) {
  9. dev_err(&pdev->dev, "no platform data/n");
  10. return -EINVAL;
  11. }
  12. //给s3c24xx_i2c结构体申请空间
  13. i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
  14. if (!i2c) {
  15. dev_err(&pdev->dev, "no memory for state/n");
  16. return -ENOMEM;
  17. }
  18. //填充s3c24xx_i2c结构体中各项,包括名称、所有者、算法、所属class等等
  19. strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
  20. i2c->adap.owner   = THIS_MODULE;
  21. i2c->adap.algo    = &s3c24xx_i2c_algorithm; //这个下面会重点介绍
  22. i2c->adap.retries = 2;
  23. i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
  24. i2c->tx_setup     = 50;
  25. spin_lock_init(&i2c->lock);
  26. init_waitqueue_head(&i2c->wait);
  27. /* find the clock and enable it */
  28. // 找到i2c始终并且使能它
  29. i2c->dev = &pdev->dev;
  30. i2c->clk = clk_get(&pdev->dev, "i2c");
  31. if (IS_ERR(i2c->clk)) {
  32. dev_err(&pdev->dev, "cannot get clock/n");
  33. ret = -ENOENT;
  34. goto err_noclk;
  35. }
  36. dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);
  37. clk_enable(i2c->clk);
  38. /* map the registers */
  39. /*映射寄存器*/
  40. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  41. if (res == NULL) {
  42. dev_err(&pdev->dev, "cannot find IO resource/n");
  43. ret = -ENOENT;
  44. goto err_clk;
  45. }
  46. i2c->ioarea = request_mem_region(res->start, resource_size(res),
  47. pdev->name);
  48. if (i2c->ioarea == NULL) {
  49. dev_err(&pdev->dev, "cannot request IO/n");
  50. ret = -ENXIO;
  51. goto err_clk;
  52. }
  53. i2c->regs = ioremap(res->start, resource_size(res));
  54. if (i2c->regs == NULL) {
  55. dev_err(&pdev->dev, "cannot map IO/n");
  56. ret = -ENXIO;
  57. goto err_ioarea;
  58. }
  59. dev_dbg(&pdev->dev, "registers %p (%p, %p)/n",
  60. i2c->regs, i2c->ioarea, res);
  61. /* setup info block for the i2c core */
  62. i2c->adap.algo_data = i2c;
  63. i2c->adap.dev.parent = &pdev->dev;
  64. /* initialise the i2c controller */
  65. /*s3c24xx_i2c结构体变量i2c的必要的信息都填充完了以后,开始进行初始化*/
  66. ret = s3c24xx_i2c_init(i2c);
  67. if (ret != 0)
  68. goto err_iomap;
  69. /* find the IRQ for this unit (note, this relies on the init call to
  70. * ensure no current IRQs pending
  71. */
  72. //接下来申请中断
  73. i2c->irq = ret = platform_get_irq(pdev, 0);
  74. if (ret <= 0) {
  75. dev_err(&pdev->dev, "cannot find IRQ/n");
  76. goto err_iomap;
  77. }
  78. ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
  79. dev_name(&pdev->dev), i2c);
  80. if (ret != 0) {
  81. dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq);
  82. goto err_iomap;
  83. }
  84. ret = s3c24xx_i2c_register_cpufreq(i2c);
  85. if (ret < 0) {
  86. dev_err(&pdev->dev, "failed to register cpufreq notifier/n");
  87. goto err_irq;
  88. }
  89. /* Note, previous versions of the driver used i2c_add_adapter()
  90. * to add the bus at any number. We now pass the bus number via
  91. * the platform data, so if unset it will now default to always
  92. * being bus 0.
  93. */
  94. i2c->adap.nr = pdata->bus_num;
  95. //看到了吧?下面调用了i2c-core中的i2c_add_adapter函数来添加一个i2c控制器
  96. //i2c_add_numbered_adapter和i2c_add_adapter的区别在于前者用来添加一个在CPU内
  97. //部集成的适配器,而后者用来添加一个CPU外部的适配器。显然这里应该用前者。
  98. ret = i2c_add_numbered_adapter(&i2c->adap);
  99. if (ret < 0) {
  100. dev_err(&pdev->dev, "failed to add bus to i2c core/n");
  101. goto err_cpufreq;
  102. }
  103. platform_set_drvdata(pdev, i2c);
  104. dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev));
  105. return 0;
  106. err_cpufreq:
  107. s3c24xx_i2c_deregister_cpufreq(i2c);
  108. err_irq:
  109. free_irq(i2c->irq, i2c);
  110. err_iomap:
  111. iounmap(i2c->regs);
  112. err_ioarea:
  113. release_resource(i2c->ioarea);
  114. kfree(i2c->ioarea);
  115. err_clk:
  116. clk_disable(i2c->clk);
  117. clk_put(i2c->clk);
  118. err_noclk:
  119. kfree(i2c);
  120. return ret;
  121. }

*remove函数

这是和probe相反的一个函数,在i2c_adap_s3c_exit时调用。主要功能是注销适配器,释放中断,释放内存区域,禁止始终等等。看到上边代码中的err_的各个部分了吧? remove是它们的汇总。

*suspend函数和resume函数

把这两个放一起说吧,挂起和恢复函数。挂起时保存状态并置标志位,恢复时重新初始化i2c适配器并置标志位。

Algorithm

哎呀我去,终于到这了。憋得我难受啊。这里要重点介绍一下,不仅要知其然,还要知其所以然,这样我们以后自己写驱动的时候就有把握了。

[c-sharp] view plaincopy
  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
  2. .master_xfer        = s3c24xx_i2c_xfer,
  3. .functionality      = s3c24xx_i2c_func,
  4. };

这里实现的就是这个s3c24xx_i2c_xfer。这个是控制器能不能动作的关键,缺了这个,控制器就是废铜烂铁。

[c-sharp] view plaincopy
  1. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
  2. struct i2c_msg *msgs, int num)
  3. {
  4. struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
  5. int retry;
  6. int ret;
  7. for (retry = 0; retry < adap->retries; retry++) {
  8. ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
  9. if (ret != -EAGAIN)
  10. return ret;
  11. dev_dbg(i2c->dev, "Retrying transmission (%d)/n", retry);
  12. udelay(100);
  13. }
  14. return -EREMOTEIO;
  15. }

完成任务的函数是s3c24xx_i2c_doxfer(),源码清单如下,

[c-sharp] view plaincopy
  1. static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
  2. struct i2c_msg *msgs, int num)
  3. {
  4. unsigned long timeout;
  5. int ret;
  6. if (i2c->suspended)
  7. return -EIO;
  8. ret = s3c24xx_i2c_set_master(i2c);
  9. if (ret != 0) {
  10. dev_err(i2c->dev, "cannot get bus (error %d)/n", ret);
  11. ret = -EAGAIN;
  12. goto out;
  13. }
  14. spin_lock_irq(&i2c->lock);
  15. i2c->msg     = msgs;
  16. i2c->msg_num = num;
  17. i2c->msg_ptr = 0;
  18. i2c->msg_idx = 0;
  19. i2c->state   = STATE_START;
  20. s3c24xx_i2c_enable_irq(i2c);
  21. s3c24xx_i2c_message_start(i2c, msgs);
  22. spin_unlock_irq(&i2c->lock);
  23. timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
  24. ret = i2c->msg_idx;
  25. /* having these next two as dev_err() makes life very
  26. * noisy when doing an i2cdetect */
  27. if (timeout == 0)
  28. dev_dbg(i2c->dev, "timeout/n");
  29. else if (ret != num)
  30. dev_dbg(i2c->dev, "incomplete xfer (%d)/n", ret);
  31. /* ensure the stop has been through the bus */
  32. msleep(1);
  33. out:
  34. return ret;
  35. }

上面代码可以分成几个部分来看:

* s3c24xx_i2c_set_master() 这个函数每隔1ms查看一次i2c总线状态,timeout是400ms,如果在这期间总线状态不忙,则返回零。否则返回-ETIMEDOUT

* 将要发送的消息和其他信息付给i2c->msg和其他变量,并将状态设置为STATE_START

* s3c24xx_i2c_enable_irq() 使能中断

* s3c24xx_i2c_message_start() 重中之重啊。在看代码之前先来看看2440的datasheet上是怎么说的吧。

代码清单如下:

[c-sharp] view plaincopy
  1. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
  2. struct i2c_msg *msg)
  3. {
  4. unsigned int addr = (msg->addr & 0x7f) << 1;
  5. unsigned long stat;
  6. unsigned long iiccon;
  7. stat = 0;
  8. stat |=  S3C2410_IICSTAT_TXRXEN;
  9. if (msg->flags & I2C_M_RD) {//如果是read data, from slave to master     stat |= S3C2410_IICSTAT_MASTER_RX;
  10. addr |= 1;
  11. } else
  12. stat |= S3C2410_IICSTAT_MASTER_TX;
  13. if (msg->flags & I2C_M_REV_DIR_ADDR)
  14. addr ^= 1;
  15. /* todo - check for wether ack wanted or not */
  16. s3c24xx_i2c_enable_ack(i2c);
  17. iiccon = readl(i2c->regs + S3C2410_IICCON);
  18. writel(stat, i2c->regs + S3C2410_IICSTAT);
  19. dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS/n", stat, addr);
  20. writeb(addr, i2c->regs + S3C2410_IICDS);
  21. /* delay here to ensure the data byte has gotten onto the bus
  22. * before the transaction is started */
  23. ndelay(i2c->tx_setup);
  24. dev_dbg(i2c->dev, "iiccon, %08lx/n", iiccon);
  25. writel(iiccon, i2c->regs + S3C2410_IICCON);
  26. stat |= S3C2410_IICSTAT_START;
  27. writel(stat, i2c->regs + S3C2410_IICSTAT);
  28. }

(今天没写完啊,明天继续~)

  • 上一篇Linux I2C驱动完全分析(一)
  • 下一篇基于Qt的GPS导航系统软件源代码
0
0

我的同类文章

Linux设备驱动程序第三版学习笔记(17)

主题推荐
c语言linux生活
猜你在找
查看评论
25楼 terry_wang2003 2015-09-20 18:54发表 [回复]
非常感谢,让我耳目一新, 后续三 还有么?
24楼 Healthy_蓝 2014-11-25 09:38发表 [回复]
楼主啊,你倒是继续写撒...这葵花宝典看着难受啊...走火入魔中.....
23楼 guaiguaixiangai 2014-11-24 22:51发表 [回复]
里面提到timeout = 400ms 是在哪里赋值的,在哪里看到的呀?
22楼 sdkhy0808 2014-07-05 13:21发表 [回复]
我也期待着继续写下去呢
21楼 sdkhy0808 2014-07-05 13:20发表 [回复]
转了哈
20楼 sdkhy0808 2014-07-05 13:20发表 [回复]
赞!
19楼 云在青天水在瓶-_- 2013-08-10 00:33发表 [回复]
先说一下,本文中有个疑惑,一直没有搞懂
这几个宏定义是GPIO模拟i2c总线算法实现用的
18楼 yuelengloulan 2013-06-17 14:36发表 [回复]
楼主文章很精简清晰,怎么不继续写了啊
17楼 tian1112yong 2013-05-27 17:27发表 [回复]
后续呢,楼主。
16楼 TC_CT 2013-04-09 11:57发表 [回复]
博主居然没有坚持写完,唉,失望了
15楼 刘二傻 2013-04-01 17:36发表 [回复]
怎么没有下面了吗?
14楼 kcchao 2012-11-05 17:40发表 [回复]
For I2C_FUNC_PROTOCOL_MANGLING, you can see the explanation in http://www.kernel.org/doc/Documentation/i2c/i2c-protocol
13楼 way 2012-08-02 23:44发表 [回复]
写得很好,把总线驱动分析得不错,要是把设备驱动的几种写法都分析一下,那i2c驱动就算很完美了,不过现在的方法,不管是写总线驱动,还是设备驱动,都够了,不过三星公司的总线驱动太复杂
12楼 bbs598598 2012-07-31 15:56发表 [回复]
顶一个,翘首等待
11楼 Ugly_Jorney 2012-07-24 14:13发表 [回复]
顶起!
10楼 zgwstar 2012-07-20 20:06发表 [回复]
太佩服楼主了 向楼主学习
9楼 jackylongchen 2012-06-15 16:19发表 [回复]
来学习
8楼 maybe2101 2012-04-21 15:27发表 [回复]
难道不会出后续了么?%>_<%
7楼 yhl2007kaka 2011-11-23 11:15发表 [回复]
支持楼主!写的确实非常好!
6楼 junziyou1324325 2011-10-28 10:39发表 [回复]
太期盼了!!楼主说的再详细一点吧!!
5楼 烂笔头 2011-10-11 09:54发表 [回复]
楼主,请问一下如果从机是单片机而不是一个芯片应该怎么写驱动啊?
4楼 laobenzhuang1 2011-09-02 23:23发表 [回复]
等待三、四的出现,楼主写的太好了
3楼 lwc92008 2011-08-10 12:30发表 [回复]
关于I2C_M_NOSTART之类的困惑可以在
Document/i2c/i2c-protocol
文件找到说明。
2楼 yanyang_031 2011-07-27 15:19发表 [回复]
翘首等待三、四的出现,楼主写的太好了~~
1楼 Quietly 2011-06-30 17:53发表 [回复]
[e03]
您还没有登录,请[登录]或[注册]
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStackVPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQuery BIHTML5 Spring Apache .NET API HTML SDK IIS Fedora XML LBS Unity SplashtopUML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTC coremailOPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide Maemo Compuware大数据 aptech Perl Tornado Ruby Hibernate ThinkPHP HBase Pure Solr AngularCloud Foundry Redis Scala Django Bootstrap

  • 个人资料
  •  
    小雷总
     
    • 访问:227557次
    • 积分:2684
    • 等级: 
    • 排名:第7731名
    • 原创:44篇
    • 转载:46篇
    • 译文:1篇
    • 评论:146条
  • 文章搜索
  • 文章分类
  • 和小雷一起学开发(1)
  • C++/C#(9)
  • CPLD(0)
  • DSP(0)
  • linux学习(19)
  • Linux设备驱动程序第三版学习笔记(18)
  • qpeGPS(0)
  • Qt学习(10)
  • TX-2440A开发板(2)
  • 各种算法研究(1)
  • 嵌入式系统移植(0)
  • 市场营销(3)
  • 心情文章(5)
  • 成长中的研发经理(10)
  • 源代码(1)
  • 电路设计(7)
  • 视频技术(1)
  • 项目管理(0)
  • 企业管理(1)
  • 文章存档
    • 2015年08月(1)
    • 2015年01月(1)
    • 2014年07月(1)
    • 2014年05月(1)
    • 2014年04月(1)
      展开
  • 阅读排行
  • Linux I2C驱动完全分析(一)(27925)
  • Linux DM9000网卡驱动程序完全分析(27116)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(23994)
  • Linux I2C驱动完全分析(二)(14318)
  • Qt函数之QPainter::drawImage(7520)
  • 不懂技术 如何管理好研发部门?(6097)
  • Linux驱动开发环境配置(内核源码树构造)(5082)
  • 加了醋的啤酒(4937)
  • C# winform DataGridView 操作大全(4751)
  • 基于Qt的GPS导航系统软件源代码(4551)
  • 评论排行
  • Linux DM9000网卡驱动程序完全分析(53)
  • Linux I2C驱动完全分析(二)(25)
  • 基于Qt的GPS导航系统软件源代码(10)
  • Linux I2C驱动完全分析(一)(10)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(8)
  • 周立功:我的成功可以复制(5)
  • 基于Qt的GPS导航系统(4)
  • v4l2驱动编写篇(4)
  • Linux内核sk_buff的结构分析(3)
  • S3C2410看门狗驱动分析(3)
  • 最新评论
  • Linux I2C驱动完全分析(二)

    terry_wang2003: 非常感谢,让我耳目一新, 后续三 还有么?

  • Linux DM9000网卡驱动程序完全分析

    No威_: 发错了,不好意思

  • Linux DM9000网卡驱动程序完全分析

    No威_: 都是内核里面的。。。

  • C# winform DataGridView 操作大全

    youshuai168: Thanks 这个比较有用

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的,为什么32位系统就是28,64位系统就是32?多谢指教。。

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的?为啥32位系统就是28,64位系统就是32?多谢指教。。

  • 周立功:我的成功可以复制

    小雷总: @phker:你是做什么行业的?

  • 周立功:我的成功可以复制

    小雷总: @phker:我在技术上是挺顽固的,“顽固”我当做褒义词来听。哈哈哈

  • 周立功:我的成功可以复制

    走错路的程序员: 说真的,你老不要生气.我看到你照片的第一印象认为你是那种专注技术方向的技术顽固.不好沟通的人.看过你...

  • 周立功:我的成功可以复制

    走错路的程序员: 它山之石可以攻玉,减少“阶段0”的开发 注重核心技术,其余的外包这两点我深有体会啊.

公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈
网站客服杂志客服微博客服webmaster@csdn.net400-600-2320|北京创新乐知信息技术有限公司 版权所有|江苏乐知网络技术有限公司 提供商务支持
京 ICP 证 09002463 号|Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

  • 登录 | 注册

小雷的学习空间

用硬件包围软件 最终实现软硬通吃

  • 目录视图
  • 摘要视图
  • 订阅

Linux I2C驱动完全分析(二)

标签: clinuxstructalgorithmtable
2011-05-01 18:17 14323人阅读 评论(25) 收藏 举报

 分类:
Linux设备驱动程序第三版学习笔记(17) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

博主按:大热的天,刚刚负重从五道口走到石板房,大约4公里吧。终于让我找了一个咖啡屋休息一下,继续写这篇驱动分析。单身的生活就是这样无聊啊。 不发牢骚了,活出个样儿来给自己看!千难万险脚下踩,啥也难不倒咱!继续整!~

先说一下,本文中有个疑惑,一直没有搞懂,写在这里,望高人指点一二,不胜感激!

#define I2C_M_NOSTART  0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK  0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */

这里I2C_FUNC_PROTOCOL_MANGLING 是什么意思?为什么定义这些东东?看了注释也不太理解。求解释!

3. I2C总线驱动代码分析

   s3c2440的总线驱动代码在i2c-s3c2410.c中。照例先从init看起。

[c-sharp] view plaincopy
  1. static int __init i2c_adap_s3c_init(void)
  2. {
  3. return platform_driver_register(&s3c24xx_i2c_driver);
  4. }

在init中只是调用了平台驱动注册函数注册了一个i2c的平台驱动s3c24xx_i2c_driver。这个驱动是一个platform_driver的结构体变量。注意这里不是i2c_driver结构体,因为i2c_driver是对设备的驱动,而这里对控制器的驱动要使用platform_driver

[c-sharp] view plaincopy
  1. static struct platform_driver s3c24xx_i2c_driver = {
  2. .probe      = s3c24xx_i2c_probe,
  3. .remove     = s3c24xx_i2c_remove,
  4. .suspend_late   = s3c24xx_i2c_suspend_late,
  5. .resume     = s3c24xx_i2c_resume,
  6. .id_table   = s3c24xx_driver_ids,
  7. .driver     = {
  8. .owner  = THIS_MODULE,
  9. .name   = "s3c-i2c",
  10. },
  11. };

同样的,重要的函数还是那几个:probe,remove,suspend_late,resume。再加上一个id_table和device_driver结构体变量。

下面逐个分析:

* probe函数

当调用platform_driver_register函数注册platform_driver结构体时,probe指针指向的s3c24xx_i2c_probe函数将会被调用。这部分详细解释参考本博客另一篇文章《S3C2410看门狗驱动分析》。细心的朋友可能会发现,在s3c24xx_i2c_driver中,驱动的名字是"s3c-i2c",而在板文件中可以看到,设备的名字是"s3c2410-i2c",这两个名字不一样,那驱动和设备是如何match的呢?答案就在于id_table。这个id_table包含了驱动所支持的设备ID表。在match的时候,判断这个表中的名字是不是和设备一致,一致则match成功。这也是为什么一个驱动可以同时match成功多个设备的原因。如果只是靠platform_driver-->driver中的名字来匹配的话,那么驱动和设备只能是一对一的关系了。

[c-sharp] view plaincopy
  1. static struct platform_device_id s3c24xx_driver_ids[] = {
  2. {
  3. .name       = "s3c2410-i2c",
  4. .driver_data    = TYPE_S3C2410,
  5. }, {
  6. .name       = "s3c2440-i2c",
  7. .driver_data    = TYPE_S3C2440,
  8. }, { },
  9. };

扯远了,还是看看probe的代码吧~

[c-sharp] view plaincopy
  1. static int s3c24xx_i2c_probe(struct platform_device *pdev)
  2. {
  3. struct s3c24xx_i2c *i2c;
  4. struct s3c2410_platform_i2c *pdata;
  5. struct resource *res;
  6. int ret;
  7. pdata = pdev->dev.platform_data;
  8. if (!pdata) {
  9. dev_err(&pdev->dev, "no platform data/n");
  10. return -EINVAL;
  11. }
  12. //给s3c24xx_i2c结构体申请空间
  13. i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
  14. if (!i2c) {
  15. dev_err(&pdev->dev, "no memory for state/n");
  16. return -ENOMEM;
  17. }
  18. //填充s3c24xx_i2c结构体中各项,包括名称、所有者、算法、所属class等等
  19. strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
  20. i2c->adap.owner   = THIS_MODULE;
  21. i2c->adap.algo    = &s3c24xx_i2c_algorithm; //这个下面会重点介绍
  22. i2c->adap.retries = 2;
  23. i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
  24. i2c->tx_setup     = 50;
  25. spin_lock_init(&i2c->lock);
  26. init_waitqueue_head(&i2c->wait);
  27. /* find the clock and enable it */
  28. // 找到i2c始终并且使能它
  29. i2c->dev = &pdev->dev;
  30. i2c->clk = clk_get(&pdev->dev, "i2c");
  31. if (IS_ERR(i2c->clk)) {
  32. dev_err(&pdev->dev, "cannot get clock/n");
  33. ret = -ENOENT;
  34. goto err_noclk;
  35. }
  36. dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);
  37. clk_enable(i2c->clk);
  38. /* map the registers */
  39. /*映射寄存器*/
  40. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  41. if (res == NULL) {
  42. dev_err(&pdev->dev, "cannot find IO resource/n");
  43. ret = -ENOENT;
  44. goto err_clk;
  45. }
  46. i2c->ioarea = request_mem_region(res->start, resource_size(res),
  47. pdev->name);
  48. if (i2c->ioarea == NULL) {
  49. dev_err(&pdev->dev, "cannot request IO/n");
  50. ret = -ENXIO;
  51. goto err_clk;
  52. }
  53. i2c->regs = ioremap(res->start, resource_size(res));
  54. if (i2c->regs == NULL) {
  55. dev_err(&pdev->dev, "cannot map IO/n");
  56. ret = -ENXIO;
  57. goto err_ioarea;
  58. }
  59. dev_dbg(&pdev->dev, "registers %p (%p, %p)/n",
  60. i2c->regs, i2c->ioarea, res);
  61. /* setup info block for the i2c core */
  62. i2c->adap.algo_data = i2c;
  63. i2c->adap.dev.parent = &pdev->dev;
  64. /* initialise the i2c controller */
  65. /*s3c24xx_i2c结构体变量i2c的必要的信息都填充完了以后,开始进行初始化*/
  66. ret = s3c24xx_i2c_init(i2c);
  67. if (ret != 0)
  68. goto err_iomap;
  69. /* find the IRQ for this unit (note, this relies on the init call to
  70. * ensure no current IRQs pending
  71. */
  72. //接下来申请中断
  73. i2c->irq = ret = platform_get_irq(pdev, 0);
  74. if (ret <= 0) {
  75. dev_err(&pdev->dev, "cannot find IRQ/n");
  76. goto err_iomap;
  77. }
  78. ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
  79. dev_name(&pdev->dev), i2c);
  80. if (ret != 0) {
  81. dev_err(&pdev->dev, "cannot claim IRQ %d/n", i2c->irq);
  82. goto err_iomap;
  83. }
  84. ret = s3c24xx_i2c_register_cpufreq(i2c);
  85. if (ret < 0) {
  86. dev_err(&pdev->dev, "failed to register cpufreq notifier/n");
  87. goto err_irq;
  88. }
  89. /* Note, previous versions of the driver used i2c_add_adapter()
  90. * to add the bus at any number. We now pass the bus number via
  91. * the platform data, so if unset it will now default to always
  92. * being bus 0.
  93. */
  94. i2c->adap.nr = pdata->bus_num;
  95. //看到了吧?下面调用了i2c-core中的i2c_add_adapter函数来添加一个i2c控制器
  96. //i2c_add_numbered_adapter和i2c_add_adapter的区别在于前者用来添加一个在CPU内
  97. //部集成的适配器,而后者用来添加一个CPU外部的适配器。显然这里应该用前者。
  98. ret = i2c_add_numbered_adapter(&i2c->adap);
  99. if (ret < 0) {
  100. dev_err(&pdev->dev, "failed to add bus to i2c core/n");
  101. goto err_cpufreq;
  102. }
  103. platform_set_drvdata(pdev, i2c);
  104. dev_info(&pdev->dev, "%s: S3C I2C adapter/n", dev_name(&i2c->adap.dev));
  105. return 0;
  106. err_cpufreq:
  107. s3c24xx_i2c_deregister_cpufreq(i2c);
  108. err_irq:
  109. free_irq(i2c->irq, i2c);
  110. err_iomap:
  111. iounmap(i2c->regs);
  112. err_ioarea:
  113. release_resource(i2c->ioarea);
  114. kfree(i2c->ioarea);
  115. err_clk:
  116. clk_disable(i2c->clk);
  117. clk_put(i2c->clk);
  118. err_noclk:
  119. kfree(i2c);
  120. return ret;
  121. }

*remove函数

这是和probe相反的一个函数,在i2c_adap_s3c_exit时调用。主要功能是注销适配器,释放中断,释放内存区域,禁止始终等等。看到上边代码中的err_的各个部分了吧? remove是它们的汇总。

*suspend函数和resume函数

把这两个放一起说吧,挂起和恢复函数。挂起时保存状态并置标志位,恢复时重新初始化i2c适配器并置标志位。

Algorithm

哎呀我去,终于到这了。憋得我难受啊。这里要重点介绍一下,不仅要知其然,还要知其所以然,这样我们以后自己写驱动的时候就有把握了。

[c-sharp] view plaincopy
  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
  2. .master_xfer        = s3c24xx_i2c_xfer,
  3. .functionality      = s3c24xx_i2c_func,
  4. };

这里实现的就是这个s3c24xx_i2c_xfer。这个是控制器能不能动作的关键,缺了这个,控制器就是废铜烂铁。

[c-sharp] view plaincopy
  1. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
  2. struct i2c_msg *msgs, int num)
  3. {
  4. struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
  5. int retry;
  6. int ret;
  7. for (retry = 0; retry < adap->retries; retry++) {
  8. ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
  9. if (ret != -EAGAIN)
  10. return ret;
  11. dev_dbg(i2c->dev, "Retrying transmission (%d)/n", retry);
  12. udelay(100);
  13. }
  14. return -EREMOTEIO;
  15. }

完成任务的函数是s3c24xx_i2c_doxfer(),源码清单如下,

[c-sharp] view plaincopy
  1. static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
  2. struct i2c_msg *msgs, int num)
  3. {
  4. unsigned long timeout;
  5. int ret;
  6. if (i2c->suspended)
  7. return -EIO;
  8. ret = s3c24xx_i2c_set_master(i2c);
  9. if (ret != 0) {
  10. dev_err(i2c->dev, "cannot get bus (error %d)/n", ret);
  11. ret = -EAGAIN;
  12. goto out;
  13. }
  14. spin_lock_irq(&i2c->lock);
  15. i2c->msg     = msgs;
  16. i2c->msg_num = num;
  17. i2c->msg_ptr = 0;
  18. i2c->msg_idx = 0;
  19. i2c->state   = STATE_START;
  20. s3c24xx_i2c_enable_irq(i2c);
  21. s3c24xx_i2c_message_start(i2c, msgs);
  22. spin_unlock_irq(&i2c->lock);
  23. timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
  24. ret = i2c->msg_idx;
  25. /* having these next two as dev_err() makes life very
  26. * noisy when doing an i2cdetect */
  27. if (timeout == 0)
  28. dev_dbg(i2c->dev, "timeout/n");
  29. else if (ret != num)
  30. dev_dbg(i2c->dev, "incomplete xfer (%d)/n", ret);
  31. /* ensure the stop has been through the bus */
  32. msleep(1);
  33. out:
  34. return ret;
  35. }

上面代码可以分成几个部分来看:

* s3c24xx_i2c_set_master() 这个函数每隔1ms查看一次i2c总线状态,timeout是400ms,如果在这期间总线状态不忙,则返回零。否则返回-ETIMEDOUT

* 将要发送的消息和其他信息付给i2c->msg和其他变量,并将状态设置为STATE_START

* s3c24xx_i2c_enable_irq() 使能中断

* s3c24xx_i2c_message_start() 重中之重啊。在看代码之前先来看看2440的datasheet上是怎么说的吧。

代码清单如下:

[c-sharp] view plaincopy
  1. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
  2. struct i2c_msg *msg)
  3. {
  4. unsigned int addr = (msg->addr & 0x7f) << 1;
  5. unsigned long stat;
  6. unsigned long iiccon;
  7. stat = 0;
  8. stat |=  S3C2410_IICSTAT_TXRXEN;
  9. if (msg->flags & I2C_M_RD) {//如果是read data, from slave to master     stat |= S3C2410_IICSTAT_MASTER_RX;
  10. addr |= 1;
  11. } else
  12. stat |= S3C2410_IICSTAT_MASTER_TX;
  13. if (msg->flags & I2C_M_REV_DIR_ADDR)
  14. addr ^= 1;
  15. /* todo - check for wether ack wanted or not */
  16. s3c24xx_i2c_enable_ack(i2c);
  17. iiccon = readl(i2c->regs + S3C2410_IICCON);
  18. writel(stat, i2c->regs + S3C2410_IICSTAT);
  19. dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS/n", stat, addr);
  20. writeb(addr, i2c->regs + S3C2410_IICDS);
  21. /* delay here to ensure the data byte has gotten onto the bus
  22. * before the transaction is started */
  23. ndelay(i2c->tx_setup);
  24. dev_dbg(i2c->dev, "iiccon, %08lx/n", iiccon);
  25. writel(iiccon, i2c->regs + S3C2410_IICCON);
  26. stat |= S3C2410_IICSTAT_START;
  27. writel(stat, i2c->regs + S3C2410_IICSTAT);
  28. }

(今天没写完啊,明天继续~)

  • 上一篇Linux I2C驱动完全分析(一)
  • 下一篇基于Qt的GPS导航系统软件源代码
0
0

我的同类文章

Linux设备驱动程序第三版学习笔记(17)

主题推荐
c语言linux生活
猜你在找
查看评论
25楼 terry_wang2003 2015-09-20 18:54发表 [回复]
非常感谢,让我耳目一新, 后续三 还有么?
24楼 Healthy_蓝 2014-11-25 09:38发表 [回复]
楼主啊,你倒是继续写撒...这葵花宝典看着难受啊...走火入魔中.....
23楼 guaiguaixiangai 2014-11-24 22:51发表 [回复]
里面提到timeout = 400ms 是在哪里赋值的,在哪里看到的呀?
22楼 sdkhy0808 2014-07-05 13:21发表 [回复]
我也期待着继续写下去呢
21楼 sdkhy0808 2014-07-05 13:20发表 [回复]
转了哈
20楼 sdkhy0808 2014-07-05 13:20发表 [回复]
赞!
19楼 云在青天水在瓶-_- 2013-08-10 00:33发表 [回复]
先说一下,本文中有个疑惑,一直没有搞懂
这几个宏定义是GPIO模拟i2c总线算法实现用的
18楼 yuelengloulan 2013-06-17 14:36发表 [回复]
楼主文章很精简清晰,怎么不继续写了啊
17楼 tian1112yong 2013-05-27 17:27发表 [回复]
后续呢,楼主。
16楼 TC_CT 2013-04-09 11:57发表 [回复]
博主居然没有坚持写完,唉,失望了
15楼 刘二傻 2013-04-01 17:36发表 [回复]
怎么没有下面了吗?
14楼 kcchao 2012-11-05 17:40发表 [回复]
For I2C_FUNC_PROTOCOL_MANGLING, you can see the explanation in http://www.kernel.org/doc/Documentation/i2c/i2c-protocol
13楼 way 2012-08-02 23:44发表 [回复]
写得很好,把总线驱动分析得不错,要是把设备驱动的几种写法都分析一下,那i2c驱动就算很完美了,不过现在的方法,不管是写总线驱动,还是设备驱动,都够了,不过三星公司的总线驱动太复杂
12楼 bbs598598 2012-07-31 15:56发表 [回复]
顶一个,翘首等待
11楼 Ugly_Jorney 2012-07-24 14:13发表 [回复]
顶起!
10楼 zgwstar 2012-07-20 20:06发表 [回复]
太佩服楼主了 向楼主学习
9楼 jackylongchen 2012-06-15 16:19发表 [回复]
来学习
8楼 maybe2101 2012-04-21 15:27发表 [回复]
难道不会出后续了么?%>_<%
7楼 yhl2007kaka 2011-11-23 11:15发表 [回复]
支持楼主!写的确实非常好!
6楼 junziyou1324325 2011-10-28 10:39发表 [回复]
太期盼了!!楼主说的再详细一点吧!!
5楼 烂笔头 2011-10-11 09:54发表 [回复]
楼主,请问一下如果从机是单片机而不是一个芯片应该怎么写驱动啊?
4楼 laobenzhuang1 2011-09-02 23:23发表 [回复]
等待三、四的出现,楼主写的太好了
3楼 lwc92008 2011-08-10 12:30发表 [回复]
关于I2C_M_NOSTART之类的困惑可以在
Document/i2c/i2c-protocol
文件找到说明。
2楼 yanyang_031 2011-07-27 15:19发表 [回复]
翘首等待三、四的出现,楼主写的太好了~~
1楼 Quietly 2011-06-30 17:53发表 [回复]
[e03]
您还没有登录,请[登录]或[注册]
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStackVPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQuery BIHTML5 Spring Apache .NET API HTML SDK IIS Fedora XML LBS Unity SplashtopUML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTC coremailOPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide Maemo Compuware大数据 aptech Perl Tornado Ruby Hibernate ThinkPHP HBase Pure Solr AngularCloud Foundry Redis Scala Django Bootstrap

  • 个人资料
  •  
    小雷总
     
    • 访问:227557次
    • 积分:2684
    • 等级: 
    • 排名:第7731名
    • 原创:44篇
    • 转载:46篇
    • 译文:1篇
    • 评论:146条
  • 文章搜索
  • 文章分类
  • 和小雷一起学开发(1)
  • C++/C#(9)
  • CPLD(0)
  • DSP(0)
  • linux学习(19)
  • Linux设备驱动程序第三版学习笔记(18)
  • qpeGPS(0)
  • Qt学习(10)
  • TX-2440A开发板(2)
  • 各种算法研究(1)
  • 嵌入式系统移植(0)
  • 市场营销(3)
  • 心情文章(5)
  • 成长中的研发经理(10)
  • 源代码(1)
  • 电路设计(7)
  • 视频技术(1)
  • 项目管理(0)
  • 企业管理(1)
  • 文章存档
    • 2015年08月(1)
    • 2015年01月(1)
    • 2014年07月(1)
    • 2014年05月(1)
    • 2014年04月(1)
      展开
  • 阅读排行
  • Linux I2C驱动完全分析(一)(27925)
  • Linux DM9000网卡驱动程序完全分析(27116)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(23994)
  • Linux I2C驱动完全分析(二)(14318)
  • Qt函数之QPainter::drawImage(7520)
  • 不懂技术 如何管理好研发部门?(6097)
  • Linux驱动开发环境配置(内核源码树构造)(5082)
  • 加了醋的啤酒(4937)
  • C# winform DataGridView 操作大全(4751)
  • 基于Qt的GPS导航系统软件源代码(4551)
  • 评论排行
  • Linux DM9000网卡驱动程序完全分析(53)
  • Linux I2C驱动完全分析(二)(25)
  • 基于Qt的GPS导航系统软件源代码(10)
  • Linux I2C驱动完全分析(一)(10)
  • 安装tslib中遇到的错误:./autogen.sh: 4: autoreconf: not found(8)
  • 周立功:我的成功可以复制(5)
  • 基于Qt的GPS导航系统(4)
  • v4l2驱动编写篇(4)
  • Linux内核sk_buff的结构分析(3)
  • S3C2410看门狗驱动分析(3)
  • 最新评论
  • Linux I2C驱动完全分析(二)

    terry_wang2003: 非常感谢,让我耳目一新, 后续三 还有么?

  • Linux DM9000网卡驱动程序完全分析

    No威_: 发错了,不好意思

  • Linux DM9000网卡驱动程序完全分析

    No威_: 都是内核里面的。。。

  • C# winform DataGridView 操作大全

    youshuai168: Thanks 这个比较有用

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的,为什么32位系统就是28,64位系统就是32?多谢指教。。

  • 关于SetupDiEnumDeviceInfo枚举设备返回false问题的解决办法

    KiteRunner1992: 您好,请问cbsize是怎么计算的?为啥32位系统就是28,64位系统就是32?多谢指教。。

  • 周立功:我的成功可以复制

    小雷总: @phker:你是做什么行业的?

  • 周立功:我的成功可以复制

    小雷总: @phker:我在技术上是挺顽固的,“顽固”我当做褒义词来听。哈哈哈

  • 周立功:我的成功可以复制

    走错路的程序员: 说真的,你老不要生气.我看到你照片的第一印象认为你是那种专注技术方向的技术顽固.不好沟通的人.看过你...

  • 周立功:我的成功可以复制

    走错路的程序员: 它山之石可以攻玉,减少“阶段0”的开发 注重核心技术,其余的外包这两点我深有体会啊.

公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈
网站客服杂志客服微博客服webmaster@csdn.net400-600-2320|北京创新乐知信息技术有限公司 版权所有|江苏乐知网络技术有限公司 提供商务支持
京 ICP 证 09002463 号|Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

I2C驱动分析,好文!!相关推荐

  1. i2c驱动架构 davinc dm368 i2c驱动分析

    预备知识 在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c).标题党请见谅! 其实i2c接口非常的简单,即 ...

  2. 基于framebuffer(fb)的驱动分析

    基于framebuffer的驱动分析 framebuffer帧缓冲(简称fb)是linux内核中用代码虚拟出的一个设备,是一个platform类型设备,设备文件位于/dev/fb* 在嵌入式系统中一般 ...

  3. asoc linux设备驱动_Linux驱动分析之I2C设备

    内核:4.20 芯片:HYM8563 RTC 下面的代码分析主要都在注释中,会按照驱动中函数的执行顺序分析. (1) 加载和卸载函数 static const struct i2c_device_id ...

  4. linux i2c核心,总线与设备驱动,Linux2.6.37 I2C驱动框架分析(一)

    最近工作中又使用到了I2C,所以借S3C2440开发板GT2440为硬件平台温习一遍I2C驱动体系. linux内核中IIC驱动的体系框架 linux内核中IIC部分驱动代码位于:/drivers/i ...

  5. 【驱动】linux下I2C驱动架构全面分析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

  6. Linux I2C子系统分析-I2C设备驱动

    接下来以一个实际的例子来看I2C设备驱动,就以drivers/i2c/i2c-dev.c为例. 先看它的初始化和注销函数 [cpp] view plaincopy static int __init  ...

  7. Linux I2C子系统分析-I2C总线驱动

    在drivers/i2c/busses下包含各种I2C总线驱动,如S3C2440的I2C总线驱动i2c-s3c2410.c,使用GPIO模拟I2C总线的驱动i2c-gpio.c,这里只分析i2c-gp ...

  8. 获取另一个驱动的设备结构体_《rt-thread驱动框架分析》-i2c驱动

    驱动分析 I2C设备驱动框架图: 我们先RT-Thread的I2C框架图(这是我自己理解的框架图,如果不对的地方,请指出): 上图是我分析的RTT的I2C框架图.主要分为三层,驱动层-核心层-设备层. ...

  9. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的I2C驱动架构分析

    PowerPC + Linux2.6.25平台下的I2C驱动架构分析 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人b ...

最新文章

  1. 实战Transformer在NLP和医学图像分割领域的应用
  2. 在你的Fckeditor安装Google map plugin
  3. 【BC】如何将自定义的区域菜单添加到系统默认的菜单中
  4. 最短路径问题(Floyd算法)
  5. centos升级gcc
  6. JSTL/EL表达式/Struts2标签/OGNL表达式
  7. Hi3798M V200 SDK文档介绍
  8. CTS(14)---Android O CTS 测试之Media相关测试小结(二)
  9. Wix 3.0正式发布
  10. 禁止双击、拖动listctrl列头
  11. html5 序列帧播放器,Pdplayer 64位版(序列帧动画播放器)V1.0.7.33 免费版
  12. 指纹算法 c语言,指纹识别算法研究
  13. linux宝塔怎么添加二级域名,如何绑定二级域名使用宝塔面板?
  14. 百度小程序模板制作_百度小程序模板平台
  15. BDD ROBDD
  16. 目标检测YOLO系列------YOLO简介
  17. 基于SpringBoot 适合学习的开源社区平台
  18. 有没有测试女生暗恋的软件,教你一个小技巧就可以测试女生是否喜欢你!
  19. 论文《基于深度学习的表面缺陷检测方法综述》学习笔记
  20. [BUUCTF-pwn] wdb_2020_1st_boom1

热门文章

  1. 使用puppeteer 进行批量网页截图
  2. 【科研利器】Zotero 配合 ipad,读文献效率翻倍!
  3. 魔兽世界lua笔记(2、编写一个打断喊话插件)
  4. 随手记投资安全吗?随手记所合作的广发银行成功入围首批银行存管白名单。
  5. 【Phaser】Phaser监听键盘WASD按下事件
  6. 图的基本概念以及常见公式汇总
  7. Android Log和Slog类详解--Android 12
  8. 积分商城运营中,签到任务以及兑换任务的制定要点
  9. unity实现单个打字输入效果
  10. 互联网晚报 | 2月7日 星期一 | 中国女足亚洲杯夺冠;瑞幸咖啡已履行1.8亿美元民事罚款;快狗打车通过港交所上市聆讯...