title: I2C(二) linux2.6

date: 2019/1/28 18:18:42

toc: true

I2C(二) linux2.6

总线驱动

官方例子是drivers\i2c\busses\i2c-s3c2410.c

关键结构

  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
  2. .master_xfer = s3c24xx_i2c_xfer,
  3. .functionality = s3c24xx_i2c_func,
  4. };
  5. static struct s3c24xx_i2c s3c24xx_i2c = {
  6. .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
  7. .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
  8. .tx_setup = 50,
  9. .adap = {
  10. .name = "s3c2410-i2c",
  11. .owner = THIS_MODULE,
  12. .algo = &s3c24xx_i2c_algorithm,
  13. .retries = 2,
  14. .class = I2C_CLASS_HWMON,
  15. },
  16. };
  17. struct i2c_adapter {
  18. struct module *owner;              //所属模块
  19. unsigned int id;                //algorithm的类型,定义于i2c-id.h,
  20. unsigned int class;
  21. const struct i2c_algorithm *algo;     //总线通信方法结构体指针
  22. void *algo_data;             //algorithm数据
  23. struct rt_mutex bus_lock;        //控制并发访问的自旋锁
  24. int timeout;
  25. int retries;                //重试次数
  26. struct device dev;             //适配器设备
  27. int nr;         //存放在i2c_adapter_idr里的位置号
  28. char name[48];              //适配器名称
  29. struct completion dev_released;    //用于同步
  30. struct list_head userspace_clients; //client链表头
  31. };

入口

这里是一个platform总线框架,第一个函数也就是probe

  1. static struct platform_driver s3c2440_i2c_driver = {
  2. .probe = s3c24xx_i2c_probe,
  3. .remove = s3c24xx_i2c_remove,
  4. .resume = s3c24xx_i2c_resume,
  5. .driver = {
  6. .owner = THIS_MODULE,
  7. .name = "s3c2440-i2c",
  8. },
  9. };
  1. static int s3c24xx_i2c_probe(struct platform_device *pdev)
  2. {
  3. struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
  4. struct resource *res;
  5. int ret;
  6. /* find the clock and enable it */
  7. i2c->dev = &pdev->dev;
  8. i2c->clk = clk_get(&pdev->dev, "i2c");
  9. clk_enable(i2c->clk);
  10. /* map the registers */
  11. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  12. i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
  13. pdev->name);
  14. i2c->regs = ioremap(res->start, (res->end-res->start)+1);
  15. // 设置了具体的适配器算法
  16. /* setup info block for the i2c core */
  17. i2c->adap.algo_data = i2c;
  18. i2c->adap.dev.parent = &pdev->dev;
  19. //寄存器 gpio 初始化
  20. /* initialise the i2c controller */
  21. ret = s3c24xx_i2c_init(i2c);
  22. //设置中断函数,中断里执行具体的读写函数
  23. /* find the IRQ for this unit (note, this relies on the init call to
  24. * ensure no current IRQs pending
  25. */
  26. res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  27. ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
  28. pdev->name, i2c);
  29. i2c->irq = res;
  30. //添加adapt
  31. ret = i2c_add_adapter(&i2c->adap);
  32. //pdev.dev.driver_data=i2c
  33. platform_set_drvdata(pdev, i2c);
  34. return 0;
  35. }

i2c_add_adapter

  1. 设置这个具体的适配器相关的信息
  2. 添加新的适配器,这个情况下可能有新的client能够挂接到dev链表
  3. 调用这个i2c_driverattach_adapter
  1. int i2c_add_adapter(struct i2c_adapter *adapter)
  2. {
  3. res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);
  4. //调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体
  5. i2c_register_adapter(adapter)
  6. }
  7. static int i2c_register_adapter(struct i2c_adapter *adap)
  8. {
  9. int res = 0;
  10. struct list_head *item;
  11. struct i2c_driver *driver;
  12. INIT_LIST_HEAD(&adap->clients);
  13. list_add_tail(&adap->list, &adapters);
  14. // 这个parent 在上面的probe 指向了 资源文件指向的platform_device.dev
  15. /* Add the adapter to the driver core.
  16. * If the parent pointer is not set up,
  17. * we add this adapter to the host bus.
  18. */
  19. if (adap->dev.parent == NULL) {
  20. adap->dev.parent = &platform_bus;
  21. pr_debug("I2C adapter driver [%s] forgot to specify "
  22. "physical device\n", adap->name);
  23. }
  24. sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
  25. adap->dev.release = &i2c_adapter_dev_release;
  26. //这里设置了具体的class
  27. adap->dev.class = &i2c_adapter_class;
  28. //注册device 在总线框架中,注册dev 会去寻找driver,执行probe
  29. /* for new style drivers, when registration returns the driver core
  30. * will have called probe() for all matching-but-unbound devices.
  31. */
  32. res = device_register(&adap->dev);
  33. // 扫描静态的单板外设信息,有个全局链表__i2c_board_list 保存着未添加到链表的
  34. // 想要加入到的client的相关信息,这个后续可以看 linux3.4
  35. // 这里就是根据这个新的适配器添加这个client到链表
  36. /* create pre-declared device nodes for new-style drivers */
  37. if (adap->nr < __i2c_first_dynamic_bus_num)
  38. i2c_scan_static_board_info(adap);
  39. // 遍历这个drivers ,去执行实际驱动i2c_driver 的attach_adapter
  40. /* let legacy drivers scan this bus for matching devices */
  41. list_for_each(item,&drivers) {
  42. driver = list_entry(item, struct i2c_driver, list);
  43. if (driver->attach_adapter)
  44. /* We ignore the return code; if it fails, too bad */
  45. driver->attach_adapter(adap);
  46. }
  47. }

最后执行attach_adapter,这个在后面的设备驱动再分析

硬件操作

硬件操作最后归结到接口master_xfer,I2C的传输到最后可以整理为

  1. 开始信号
  2. 结束信号
  3. ACK/NAK
  4. 数据传输1字节,读写是根据起始信号决定

在官方的驱动中,使用中断来传输,传输中使用休眠唤醒

  1. //休眠
  2. timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
  3. //完成后唤醒
  4. wake_up(&i2c->wait);
  5. // probe 时候申请中断
  6. request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);

设备驱动

参考文件driver/i2c/chips/eeprom.c

入口

  1. static int __init eeprom_init(void)
  2. {
  3. return i2c_add_driver(&eeprom_driver);
  4. }
  5. static inline int i2c_add_driver(struct i2c_driver *driver)
  6. {
  7. return i2c_register_driver(THIS_MODULE, driver);
  8. }

注册

注册的时候会加入驱动到链表,最后执行驱动的attach_adapter

  1. int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
  2. {
  3. int res;
  4. /* add the driver to the list of i2c drivers in the driver core */
  5. driver->driver.owner = owner;
  6. driver->driver.bus = &i2c_bus_type;
  7. //注册driver 会去寻找dev,执行probe
  8. /* for new style drivers, when registration returns the driver core
  9. * will have called probe() for all matching-but-unbound devices.
  10. */
  11. res = driver_register(&driver->driver);
  12. //添加链表
  13. list_add_tail(&driver->list,&drivers);
  14. //遍历adapt,执行驱动的attach_adapter
  15. //这个和我们最后注册 adapt的时候,遍历 driver 时是差不多的
  16. /* legacy drivers scan i2c busses directly */
  17. if (driver->attach_adapter) {
  18. struct i2c_adapter *adapter;
  19. list_for_each_entry(adapter, &adapters, list) {
  20. driver->attach_adapter(adapter);
  21. }
  22. }
  23. return 0;
  24. }

attach_adapter

这里看下eeprom的是怎样的

  1. static struct i2c_driver eeprom_driver = {
  2. .driver = {
  3. .name = "eeprom",
  4. },
  5. .id = I2C_DRIVERID_EEPROM,
  6. .attach_adapter = eeprom_attach_adapter,
  7. .detach_client = eeprom_detach_client,
  8. };

i2c_probe总结起来就是 使用adapter先去检测地址addr_data,成功后调用eeprom_detect,挂接client,注册我们实际的字符设备驱动等操作

  1. static int eeprom_attach_adapter(struct i2c_adapter *adapter)
  2. {
  3. return i2c_probe(adapter, &addr_data, eeprom_detect);
  4. }

我们这里的地址有以下几种,依次处理Force,probe,normal_i2c,ignore

  1. struct i2c_client_address_data {
  2. unsigned short *normal_i2c;
  3. unsigned short *probe;
  4. unsigned short *ignore;
  5. unsigned short **forces;
  6. };

具体流程如下

  1. int i2c_probe(struct i2c_adapter *adapter,
  2. struct i2c_client_address_data *address_data,
  3. int (*found_proc) (struct i2c_adapter *, int, int))
  4. {
  5. int i, err;
  6. int adap_id = i2c_adapter_id(adapter);
  7. /* Force entries are done first, and are not affected by ignore
  8. entries */
  9. if (address_data->forces) {
  10. unsigned short **forces = address_data->forces;
  11. int kind;
  12. for (kind = 0; forces[kind]; kind++) {
  13. for (i = 0; forces[kind][i] != I2C_CLIENT_END;
  14. i += 2) {
  15. if (forces[kind][i] == adap_id
  16. || forces[kind][i] == ANY_I2C_BUS) {
  17. dev_dbg(&adapter->dev, "found force "
  18. "parameter for adapter %d, "
  19. "addr 0x%02x, kind %d\n",
  20. adap_id, forces[kind][i + 1],
  21. kind);
  22. err = i2c_probe_address(adapter,
  23. forces[kind][i + 1],
  24. kind, found_proc);
  25. if (err)
  26. return err;
  27. }
  28. }
  29. }
  30. }
  31. /* Stop here if we can't use SMBUS_QUICK */
  32. if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
  33. if (address_data->probe[0] == I2C_CLIENT_END
  34. && address_data->normal_i2c[0] == I2C_CLIENT_END)
  35. return 0;
  36. dev_warn(&adapter->dev, "SMBus Quick command not supported, "
  37. "can't probe for chips\n");
  38. return -1;
  39. }
  40. /* Probe entries are done second, and are not affected by ignore
  41. entries either */
  42. for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
  43. if (address_data->probe[i] == adap_id
  44. || address_data->probe[i] == ANY_I2C_BUS) {
  45. dev_dbg(&adapter->dev, "found probe parameter for "
  46. "adapter %d, addr 0x%02x\n", adap_id,
  47. address_data->probe[i + 1]);
  48. err = i2c_probe_address(adapter,
  49. address_data->probe[i + 1],
  50. -1, found_proc);
  51. if (err)
  52. return err;
  53. }
  54. }
  55. /* Normal entries are done last, unless shadowed by an ignore entry */
  56. for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
  57. int j, ignore;
  58. ignore = 0;
  59. for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
  60. j += 2) {
  61. if ((address_data->ignore[j] == adap_id ||
  62. address_data->ignore[j] == ANY_I2C_BUS)
  63. && address_data->ignore[j + 1]
  64. == address_data->normal_i2c[i]) {
  65. dev_dbg(&adapter->dev, "found ignore "
  66. "parameter for adapter %d, "
  67. "addr 0x%02x\n", adap_id,
  68. address_data->ignore[j + 1]);
  69. ignore = 1;
  70. break;
  71. }
  72. }
  73. if (ignore)
  74. continue;
  75. dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
  76. "addr 0x%02x\n", adap_id,
  77. address_data->normal_i2c[i]);
  78. err = i2c_probe_address(adapter, address_data->normal_i2c[i],
  79. -1, found_proc);
  80. if (err)
  81. return err;
  82. }
  83. return 0;
  84. }

eeprom_detect

我们在这个驱动中的found_proc实际上是eeprom_detect,这里设置client,设置具体的adapt,driver,执行依附

  1. /* This function is called by i2c_probe */
  2. static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
  3. {
  4. struct i2c_client *new_client;
  5. struct eeprom_data *data;
  6. int err = 0;
  7. /* There are three ways we can read the EEPROM data:
  8. (1) I2C block reads (faster, but unsupported by most adapters)
  9. (2) Consecutive byte reads (100% overhead)
  10. (3) Regular byte data reads (200% overhead)
  11. The third method is not implemented by this driver because all
  12. known adapters support at least the second. */
  13. if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
  14. | I2C_FUNC_SMBUS_BYTE))
  15. goto exit;
  16. if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
  17. err = -ENOMEM;
  18. goto exit;
  19. }
  20. // 设置client
  21. new_client = &data->client;
  22. memset(data->data, 0xff, EEPROM_SIZE);
  23. i2c_set_clientdata(new_client, data);
  24. new_client->addr = address;
  25. new_client->adapter = adapter;
  26. new_client->driver = &eeprom_driver;
  27. new_client->flags = 0;
  28. /* Fill in the remaining client fields */
  29. strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);
  30. data->valid = 0;
  31. mutex_init(&data->update_lock);
  32. data->nature = UNKNOWN;
  33. //i2c_client与适配器进行连接
  34. /* Tell the I2C layer a new client has arrived */
  35. if ((err = i2c_attach_client(new_client)))
  36. goto exit_kfree;
  37. // 这个类似字符设备驱动
  38. /* create the sysfs eeprom file */
  39. err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
  40. }

i2c_attach_client

将i2c_client与适配器进行连接

  1. int i2c_attach_client(struct i2c_client *client)
  2. {
  3. struct i2c_adapter *adapter = client->adapter;
  4. int res = 0;
  5. list_add_tail(&client->list,&adapter->clients);
  6. client->usage_count = 0;
  7. client->dev.parent = &client->adapter->dev;
  8. client->dev.bus = &i2c_bus_type;
  9. if (client->driver)
  10. client->dev.driver = &client->driver->driver;
  11. if (client->driver && !is_newstyle_driver(client->driver)) {
  12. client->dev.release = i2c_client_release;
  13. client->dev.uevent_suppress = 1;
  14. } else
  15. client->dev.release = i2c_client_dev_release;
  16. snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
  17. "%d-%04x", i2c_adapter_id(adapter), client->addr);
  18. dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
  19. client->name, client->dev.bus_id);
  20. res = device_register(&client->dev);
  21. if (adapter->client_register) {
  22. if (adapter->client_register(client)) {
  23. dev_dbg(&adapter->dev, "client_register "
  24. "failed for client [%s] at 0x%02x\n",
  25. client->name, client->addr);
  26. }
  27. }
  28. }

设备驱动参考代码

图片来自 https://www.cnblogs.com/lifexy/p/7816324.html

  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/slab.h>
  5. #include <linux/jiffies.h>
  6. #include <linux/i2c.h>
  7. #include <linux/mutex.h>
  8. #include <linux/fs.h>
  9. #include <asm/uaccess.h>
  10. static unsigned short ignore[] = { I2C_CLIENT_END };
  11. static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
  12. /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */
  13. static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
  14. static unsigned short * forces[] = {force_addr, NULL};
  15. static struct i2c_client_address_data addr_data = {
  16. .normal_i2c = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
  17. .probe = ignore,
  18. .ignore = ignore,
  19. //.forces = forces, /* 强制认为存在这个设备 */
  20. };
  21. static struct i2c_driver at24cxx_driver;
  22. static int major;
  23. static struct class *cls;
  24. struct i2c_client *at24cxx_client;
  25. static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
  26. {
  27. unsigned char address;
  28. unsigned char data;
  29. struct i2c_msg msg[2];
  30. int ret;
  31. /* address = buf[0]
  32. * data = buf[1]
  33. */
  34. if (size != 1)
  35. return -EINVAL;
  36. copy_from_user(&address, buf, 1);
  37. /* 数据传输三要素: 源,目的,长度 */
  38. /* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
  39. msg[0].addr = at24cxx_client->addr; /* 目的 */
  40. msg[0].buf = &address; /* 源 */
  41. msg[0].len = 1; /* 地址=1 byte */
  42. msg[0].flags = 0; /* 表示写 */
  43. /* 然后启动读操作 */
  44. msg[1].addr = at24cxx_client->addr; /* 源 */
  45. msg[1].buf = &data; /* 目的 */
  46. msg[1].len = 1; /* 数据=1 byte */
  47. msg[1].flags = I2C_M_RD; /* 表示读 */
  48. ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
  49. if (ret == 2)
  50. {
  51. copy_to_user(buf, &data, 1);
  52. return 1;
  53. }
  54. else
  55. return -EIO;
  56. }
  57. static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
  58. {
  59. unsigned char val[2];
  60. struct i2c_msg msg[1];
  61. int ret;
  62. /* address = buf[0]
  63. * data = buf[1]
  64. */
  65. if (size != 2)
  66. return -EINVAL;
  67. copy_from_user(val, buf, 2);
  68. /* 数据传输三要素: 源,目的,长度 */
  69. msg[0].addr = at24cxx_client->addr; /* 目的 */
  70. msg[0].buf = val; /* 源 */
  71. msg[0].len = 2; /* 地址+数据=2 byte */
  72. msg[0].flags = 0; /* 表示写 */
  73. ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
  74. if (ret == 1)
  75. return 2;
  76. else
  77. return -EIO;
  78. }
  79. static struct file_operations at24cxx_fops = {
  80. .owner = THIS_MODULE,
  81. .read = at24cxx_read,
  82. .write = at24cxx_write,
  83. };
  84. static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
  85. {
  86. printk("at24cxx_detect\n");
  87. /* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
  88. at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
  89. at24cxx_client->addr = address;
  90. at24cxx_client->adapter = adapter;
  91. at24cxx_client->driver = &at24cxx_driver;
  92. strcpy(at24cxx_client->name, "at24cxx");
  93. i2c_attach_client(at24cxx_client);
  94. major = register_chrdev(0, "at24cxx", &at24cxx_fops);
  95. cls = class_create(THIS_MODULE, "at24cxx");
  96. class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
  97. return 0;
  98. }
  99. static int at24cxx_attach(struct i2c_adapter *adapter)
  100. {
  101. return i2c_probe(adapter, &addr_data, at24cxx_detect);
  102. }
  103. static int at24cxx_detach(struct i2c_client *client)
  104. {
  105. printk("at24cxx_detach\n");
  106. class_device_destroy(cls, MKDEV(major, 0));
  107. class_destroy(cls);
  108. unregister_chrdev(major, "at24cxx");
  109. i2c_detach_client(client);
  110. kfree(i2c_get_clientdata(client));
  111. return 0;
  112. }
  113. /* 1. 分配一个i2c_driver结构体 */
  114. /* 2. 设置i2c_driver结构体 */
  115. static struct i2c_driver at24cxx_driver = {
  116. .driver = {
  117. .name = "at24cxx",
  118. },
  119. .attach_adapter = at24cxx_attach,
  120. .detach_client = at24cxx_detach,
  121. };
  122. static int at24cxx_init(void)
  123. {
  124. i2c_add_driver(&at24cxx_driver);
  125. return 0;
  126. }
  127. static void at24cxx_exit(void)
  128. {
  129. i2c_del_driver(&at24cxx_driver);
  130. }
  131. module_init(at24cxx_init);
  132. module_exit(at24cxx_exit);
  133. MODULE_LICENSE("GPL");

I2C(二) linux2.6的更多相关文章

  1. Linux内核调用I2C驱动_驱动嵌套驱动方法

    禁止转载!!!! Linux内核调用I2C驱动_以MPU6050为例 0. 导语 最近一段时间都在恶补数据结构和C++,加上导师的事情比较多,Linux内核驱动的学习进程总是被阻碍.不过,十一假期终于 ...

  2. 【内核】linux2.6版本内核编译配置选项(二)

    目录 Linux2.6版本内核编译配置选项(一):http://infohacker.blog.51cto.com/6751239/1203633 Linux2.6版本内核编译配置选项(二):http ...

  3. linux内核(二)内核移植(DM365-DM368开发攻略——linux-2.6.32的移植)

    一.介绍linux-2.6.32: Linux-2.6.32的网上介绍:增添了虚拟化内存 de-duplicacion.重写了 writeback 代码.改进了 Btrfs 文件系统.添加了 ATI ...

  4. I2C总线之(二)---时序

    一.协议 1.空闲状态 I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态.此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高. ...

  5. EEPROM读写学习笔记与I2C总线(二)

    无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在运行程序才会用到,有些数据体量较大对于获取时效性并不太强,各种各样的数据也就有不同的存储载体,这次在EEPROM ...

  6. ARM-Linux内核移植之(二)——Linux2.6.22内核移植

    平台:mini2440  交叉工具链:arm-linux-gcc-4.3.2 一.内核移植基本知识 移植内核也叫构建BSP(boardsupprot packet).BSP的作用有两个:一是为内核运行 ...

  7. (二十)linux中i2c的ioctl,write,read函数的使用

    一.ioctl函数的使用:原型:struct ioctl(struct file *file,unsigned int cmd,unsigned long arg);cmd有I2C_SLAVE,I2C ...

  8. STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  9. I2C控制器的Verilog建模之二

    前言:接着上一篇的I2C写操作,今天要实现一个I2C的读操作.虽然在ADV7181B配置内部寄存器时没有必要使用到读操作,但是为了进一步确认寄存器是否在I2C写模块下被正确配置,这一步是必不可少的. ...

随机推荐

  1. vue input输入框长度限制

    今天在开发登录页时,需要设置登录输入框的长度,输入类型为number <input type="number" maxlength="11" placeh ...

  2. dotnet core如何编译exe

    dotnet core 有一个转变,他用dll格式来代替exe作为通用执行格式,然后要命令行dotnet yourApp.dll 来运行程序.为了提高逼格,双击可以运行,可以采用以下方案: 方案一 用 ...

  3. jquery实现静态柱形图(写死的数据,只为系统首页UI更美观)

    这段时间比较空闲,便阅读公司做好的项目的源代码,学习学习同事的开发思路. 在项目中使用图表可以很好地提高人机交互的友好度,开发的时候看到项目的首页有两个很小的柱形图,很漂亮,便找到对应的代码看了看,发 ...

  4. Visual Studio插件开发基础

    Visual Studio插件主要有两种:Add-in 和 VSX(Visual Studio eXtensibility) 两者区别可参考这篇文章:Visual Studio Extensions ...

  5. C# 虚拟串口通信

    将主端口COM8拆分成 COM1和COM2两个虚拟端口 COM8接收的消息会传递给COM1和COM2 SerialPort spSend;//spSend,spReceive用虚拟串口连接,它们之间可 ...

  6. c/c++ 模板 类型推断

    模板类型的推断 下面的函数f是个模板函数,typename T.下表是,根据调用测的实参,推断出来的T的类型. 请注意下表的红字部分, f(T&& t)看起来是右值引用,但其实它会根据 ...

  7. jquery监听textarea内容变化

    $('#textarea').bind('input propertychange', function(){ var length = $("#textarea").val(). ...

  8. LeetCode算法题-Longest Word in Dictionary(Java实现)

    这是悦乐书的第303次更新,第322篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第171题(顺位题号是720).给出表示英语词典的字符串单词数组,找到单词中长度最长的单 ...

  9. Jsp的基本知识

    jsp页面的基本组成部分:指令,表达式,小脚本,声明,注释,静态内容. 指令元素有三种: 1.page:eg <%@ page 属性名="属性值" 属性名="属性值 ...

  10. June 28th. 2018, Week 26th. Thursday

    You cannot change the circumstances but you can change yourself. 既然改变不了环境,那就改变自己. From Jim Rohn. Rec ...