当前位置: 首页 > news >正文

【每周分享】以ADC芯片为例讲解易于开发调试的驱动初始化代

我们在使用ADC芯片时,往往都需要编写基于MCU和C语言的ADC芯片驱动代码,通过驱动代码以SPI总线等方式对ADC芯片的寄存器进行相关读写操作,并且读取ADC的采样数据。大部分ADC芯片的总线接口方式都是SPI,如下图所示:

在驱动代码中,ADC芯片的初始化代码是非常重要的,即对ADC芯片的寄存器进行相关配置的代码,那究竟通过C语言以什么样的方式编写初始化代码会比较好呢?会便于后面调试修改呢?会增加代码可读性呢?接下来我就以TI的ADC芯片ADS1262为例向大家展示一下我这边的初始化代码及相关宏定义及枚举操作等。不过在这之前,我们先来看下下面的初始化代码:

即将寄存器值reg_data写入相应的ADC寄存器中,如果后面需要通过修改寄存器值进行芯片调试,估计还得去打开芯片手册看下每个寄存器的位定义,然后计算一下十六进制值,最后再修改reg_data的值去更新寄存器。所以,这种代码操作方式的效率是不是比较低?而且还容易出错,即使你把魔法数字改成宏定义方式也无济于事。

接下来看看我实现的这种方式。

我们先来看看ADC芯片的寄存器,主要讲解如下6个寄存器的操作,包括如下:

要想在代码调试时快速且精准的进行寄存器值修改,那就是要在代码中明确现在需要修改的是哪个寄存器的哪个位配置,以及可以配置哪些值,因此代码的相关名称怎么命名和C语言的相关技术如何使用就显得尤为重要了。因此,我们就需要通过枚举,宏定义和位运算符等操作进行初始化代码的编写。

我们先用枚举方式定义好寄存器地址(写寄存器值即向寄存器地址写入数据),代码如下:

复制

  1. /*ADC register address define*/
  2. typedef enum
  3. {
  4. ID_REG = 0x00, //decvice identification register
  5. POWER_REG = 0x01, //power register
  6. INTERFACE_REG = 0x02, //interface register
  7. MODE0_REG = 0x03, //mode0 register
  8. MODE1_REG = 0x04, //mode1 register
  9. MODE2_REG = 0x05, //mode2 register
  10. INPMUX_REG = 0x06, //input multiplexer register
  11. OFCAL0_REG = 0x07, //offset calibration register 0
  12. OFCAL1_REG = 0x08, //offset calibration register 1
  13. OFCAL2_REG = 0x09, //offset calibration register 2
  14. FSCAL0_REG = 0x0A, //full-scale calibration register 0
  15. FSCAL1_REG = 0x0B, //full-scale calibration register 1
  16. FSCAL2_REG = 0x0C, //full-scale calibration register 2
  17. IDACMUX_REG = 0x0D, //IDAC multiplexer register
  18. IDACMAG_REG = 0x0E, //IDAC magnitude register
  19. REFMUX_REG = 0x0F, //reference multiplexer register
  20. TDACP_REG = 0x10, //TDACP control register
  21. TDACN_REG = 0x11, //TDACN control register
  22. GPIOCON_REG = 0x12, //GPIO Connection register
  23. GPIODIR_REG = 0x13, //GPIO direction register
  24. GPIODAT_REG = 0x14, //GPIO data register
  25. ADC2CFG_REG = 0x15, //ADC2 configuration register
  26. ADC2MUX_REG = 0x16, //ADC2 input multiplexer register
  27. ADC2OFC0_REG = 0x17, //ADC2 offset calibration register 0
  28. ADC2OFC1_REG = 0x18, //ADC2 offset calibration register 1
  29. ADC2FSC0_REG = 0x19, //ADC2 full-scale calibration register 0
  30. ADC2FSC1_REG = 0x1A, //ADC2 full-scale calibration register 1
  31. }ads1262_reg_addr_e;

寄存器的名称命名方式可以采用“寄存器名称_REG”的方式,并在每个寄存器后面加上注释,备注一下寄存器的含义。

然后用枚举和宏定义方式定义好每个寄存器,比如以下定义INTERFACE寄存器的代码:

复制

  1. /*interface register*/
  2. #define ADS1262_INTERFACE_REG_TIMEOUT(x) (((x) & 0x01) << 3) //RW
  3. typedef enum {
  4. TIMEOUT_CONFIG_DISABLE = 0, //default
  5. TIMEOUT_CONFIG_ENABLE = 1,
  6. }ads1262_inerface_reg_timeout_config_e;
  7. #define ADS1262_INTERFACE_REG_STATUS(x) (((x) & 0x01) << 2) //RW
  8. typedef enum {
  9. STATUS_CONFIG_DISABLE = 0,
  10. STATUS_CONFIG_ENABLE = 1, //default
  11. }ads1262_inerface_reg_status_config_e;
  12. #define ADS1262_INTERFACE_REG_CRC(x) ((x) & 0x03) //RW
  13. typedef enum {
  14. CRC_CONFIG_DISABLE = 0,
  15. CRC_CONFIG_CHECKSUM_MODE = 1, //default
  16. CRC_CONFIG_CRC_MODE = 2,
  17. }ads1262_inerface_reg_crc_config_e;

寄存器位的名称命名方式可以采用“ADC芯片名称_寄存器名称_REG_位名称”或者“寄存器名称_REG_位名称”,寄存器位值的名称命名方式可以采用“位名称_CONFIG_位值”,位值枚举的名称命名方式可以采用小写的“ADC芯片名称_寄存器名称_reg_位名称_config_e”或者“寄存器名称_reg_位名称_config_e”。

而宏定义里面的x的值即对应位值枚举里面的值,直接修改即可,比如:

ADS1262_INTERFACE_REG_TIMEOUT(TIMEOUT_CONFIG_DISABLE)或者

ADS1262_INTERFACE_REG_TIMEOUT(TIMEOUT_CONFIG_ENABLE)。

其他5个寄存器的定义代码如下所示:

复制

  1. /*power register*/
  2. #define ADS1262_POWER_REG_RESET(x) (((x) & 0x01) << 4) //RW
  3. typedef enum {
  4. RESET_CONFIG_CLEAR = 1,
  5. }ads1262_power_reg_reset_config_e;
  6. #define ADS1262_POWER_REG_VBIAS(x) (((x) & 0x01) << 1) //RW
  7. typedef enum {
  8. VBIAS_CONFIG_DISABLE = 0, //default
  9. VBIAS_CONFIG_ENABLE = 1,
  10. }ads1262_power_reg_vbias_config_e;
  11. #define ADS1262_POWER_REG_INTREF(x) ((x) & 0x01) //RW
  12. typedef enum {
  13. INTREF_CONFIG_DISABLE = 0,
  14. INTREF_CONFIG_ENABLE = 1, //default
  15. }ads1262_power_reg_intref_config_e;
  16. /*mode0 register*/
  17. #define ADS1262_MODE0_REG_REFREV(x) (((x) & 0x01) << 7) //RW
  18. typedef enum {
  19. REFREV_CONFIG_NORMAL_POLARITY = 0, //default
  20. REFREV_CONFIG_REVERSE_POLARITY = 1,
  21. }ads1262_mode0_reg_refrev_config_e;
  22. #define ADS1262_MODE0_REG_RUNMODE(x) (((x) & 0x01) << 6) //RW
  23. typedef enum {
  24. RUNMODE_CONFIG_CONTINUOUS_CONVERSION = 0, //default
  25. RUNMODE_CONFIG_PULSE_CONVERSION = 1,
  26. }ads1262_mode0_reg_runmode_config_e;
  27. #define ADS1262_MODE0_REG_CHOP(x) (((x) & 0x03) << 4) //RW
  28. typedef enum {
  29. CHOP_CONFIG_DISABLE = 0, //default
  30. CHOP_CONFIG_INPUT_CHOP_ENABLE = 1,
  31. CHOP_CONFIG_IDAC_ROTATION_ENABLE = 2,
  32. CHOP_CONFIG_ALL_ENABLE = 3,
  33. }ads1262_mode0_reg_chop_config_e;
  34. #define ADS1262_MODE0_REG_DELAY(x) ((x) & 0x0F) //RW
  35. typedef enum {
  36. DELAY_CONFIG_NO_DELAY = 0, //default
  37. DELAY_CONFIG_8_7US = 1, //8.7us
  38. DELAY_CONFIG_17US = 2, //17us
  39. DELAY_CONFIG_35US = 3, //35us
  40. DELAY_CONFIG_69US = 4,
  41. DELAY_CONFIG_139US = 5,
  42. DELAY_CONFIG_278US = 6,
  43. DELAY_CONFIG_555US = 7,
  44. DELAY_CONFIG_1_1MS = 8, // 1.1ms
  45. DELAY_CONFIG_2_2MS = 9,
  46. DELAY_CONFIG_4_4MS = 10,
  47. DELAY_CONFIG_8_8MS = 11,
  48. }ads1262_mode0_reg_delay_config_e;
  49. /*mode1 register*/
  50. #define ADS1262_MODE1_REG_FILTER(x) (((x) & 0x07) << 5) //RW
  51. typedef enum {
  52. FILTER_CONFIG_SINC1_MODE = 0,
  53. FILTER_CONFIG_SINC2_MODE = 1,
  54. FILTER_CONFIG_SINC3_MODE = 2,
  55. FILTER_CONFIG_SINC4_MODE = 3,
  56. FILTER_CONFIG_FIR_MODE = 4, //default
  57. }ads1262_mode1_reg_filter_config_e;
  58. #define ADS1262_MODE1_REG_SBADC(x) (((x) & 0x07) << 4) //RW
  59. typedef enum {
  60. SBADC_CONFIG_CONNECT_ADC1 = 0, //default
  61. SBADC_CONFIG_CONNECT_ADC2 = 1,
  62. }ads1262_mode1_reg_sbadc_config_e;
  63. #define ADS1262_MODE1_REG_SBPOL(x) (((x) & 0x07) << 3) //RW
  64. typedef enum {
  65. SBPOL_CONFIG_PULLUP_MODE = 0, //default
  66. SBPOL_CONFIG_PULLDOWN_MODE = 1,
  67. }ads1262_mode1_reg_sbpol_config_e;
  68. #define ADS1262_MODE1_REG_SBMAG(x) ((x) & 0x07) //RW
  69. typedef enum {
  70. SBMAG_CONFIG_NO = 0, //default
  71. SBMAG_CONFIG_0_5UA = 1,
  72. SBMAG_CONFIG_2UA = 2,
  73. SBMAG_CONFIG_10UA = 3,
  74. SBMAG_CONFIG_50UA = 4,
  75. SBMAG_CONFIG_200UA = 5,
  76. SBMAG_CONFIG_10M = 6, // 10MΩresistor
  77. }ads1262_mode1_reg_sbmag_config_e;
  78. /*mode2 register*/
  79. #define ADS1262_MODE2_REG_BYPASS(x) (((x) & 0x07) << 7) //RW
  80. typedef enum {
  81. BYPASS_CONFIG_PGA_ENABLE = 0, //default
  82. BYPASS_CONFIG_PGA_BYPASS = 1,
  83. }ads1262_mode2_reg_bypass_config_e;
  84. #define ADS1262_MODE2_REG_GAIN(x) (((x) & 0x07) << 4) //RW
  85. typedef enum {
  86. GAIN_CONFIG_1V = 0, //default,1V/V
  87. GAIN_CONFIG_2V = 1,
  88. GAIN_CONFIG_4V = 2,
  89. GAIN_CONFIG_8V = 3,
  90. GAIN_CONFIG_16V = 4,
  91. GAIN_CONFIG_32V = 5,
  92. }ads1262_mode2_reg_gain_config_e;
  93. #define ADS1262_MODE2_REG_DR(x) ((x) & 0x0F) //RW
  94. typedef enum {
  95. DR_CONFIG_2_5SPS = 0, //2.5 SPS, data rate
  96. DR_CONFIG_5SPS = 1,
  97. DR_CONFIG_10SPS = 2,
  98. DR_CONFIG_16_6SPS = 3,
  99. DR_CONFIG_20SPS = 4, //default
  100. DR_CONFIG_50SPS = 5,
  101. DR_CONFIG_60SPS = 6,
  102. DR_CONFIG_100SPS = 7,
  103. DR_CONFIG_400SPS = 8,
  104. DR_CONFIG_1200SPS = 9,
  105. DR_CONFIG_2400SPS = 10,
  106. DR_CONFIG_4800SPS = 11,
  107. DR_CONFIG_7200SPS = 12,
  108. DR_CONFIG_14400SPS = 13,
  109. DR_CONFIG_19200SPS = 14,
  110. DR_CONFIG_38400SPS = 15,
  111. }ads1262_mode2_reg_dr_config_e;
  112. /*input multiplexer register*/
  113. #define ADS1262_INPMUX_REG_MUXP(x) (((x) & 0x0F) << 4) //RW
  114. typedef enum {
  115. MUXP_CONFIG_AIN0 = 0, //default
  116. MUXP_CONFIG_AIN1 = 1,
  117. MUXP_CONFIG_AIN2 = 2,
  118. MUXP_CONFIG_AIN3 = 3,
  119. MUXP_CONFIG_AIN4 = 4,
  120. MUXP_CONFIG_AIN5 = 5,
  121. MUXP_CONFIG_AIN6 = 6,
  122. MUXP_CONFIG_AIN7 = 7,
  123. MUXP_CONFIG_AIN8 = 8,
  124. MUXP_CONFIG_AIN9 = 9,
  125. MUXP_CONFIG_AINCOM = 10,
  126. MUXP_CONFIG_TSMP = 11,
  127. MUXP_CONFIG_APSMP = 12,
  128. MUXP_CONFIG_DPSMP = 13,
  129. MUXP_CONFIG_TTSP = 14,
  130. MUXP_CONFIG_FLOAT = 15,
  131. }ads1262_inpmux_reg_muxp_config_e;
  132. #define ADS1262_INPMUX_REG_MUXN(x) ((x) & 0x0F) //RW
  133. typedef enum {
  134. MUXN_CONFIG_AIN0 = 0,
  135. MUXN_CONFIG_AIN1 = 1, //default
  136. MUXN_CONFIG_AIN2 = 2,
  137. MUXN_CONFIG_AIN3 = 3,
  138. MUXN_CONFIG_AIN4 = 4,
  139. MUXN_CONFIG_AIN5 = 5,
  140. MUXN_CONFIG_AIN6 = 6,
  141. MUXN_CONFIG_AIN7 = 7,
  142. MUXN_CONFIG_AIN8 = 8,
  143. MUXN_CONFIG_AIN9 = 9,
  144. MUXN_CONFIG_AINCOM = 10,
  145. MUXN_CONFIG_TSMN = 11,
  146. MUXN_CONFIG_APSMN = 12,
  147. MUXN_CONFIG_DPSMN = 13,
  148. MUXN_CONFIG_TTSN = 14,
  149. MUXN_CONFIG_FLOAT = 15,
  150. }ads1262_inpmux_reg_muxn_config_e;

以上的代码都写在ADS1262.h文件里面,即ADC芯片驱动代码的头文件可以用“ADC芯片名称.h”或者“drv_ADC芯片名称.h”来命名。

接下来就是编写初始化代码接口了,对应写在ADS1262.c里面,同样地,ADC芯片驱动代码的源文件可以用“ADC芯片名称.c”或者“drv_ADC芯片名称.c”来命名。初始化函数接口可以用“ADC芯片名称_init”来命名。

以下即为ads1262_init函数接口,实现对ADC芯片的初始化配置操作:

复制

  1. void ads1262_init(void)
  2. {
  3. uint8_t write_byte = 0x00;
  4. ads1262_cs_set(ADS1262_CS_HIGH);
  5. //config power register
  6. write_byte = ADS1262_POWER_REG_RESET(RESET_CONFIG_CLEAR) \
  7. | ADS1262_POWER_REG_VBIAS(VBIAS_CONFIG_ENABLE) \
  8. | ADS1262_POWER_REG_INTREF(INTREF_CONFIG_ENABLE);
  9. ads1262_write_register(POWER_REG, write_byte);
  10. //config interface register
  11. write_byte = ADS1262_INTERFACE_REG_TIMEOUT(TIMEOUT_CONFIG_DISABLE) \
  12. | ADS1262_INTERFACE_REG_STATUS(STATUS_CONFIG_ENABLE) \
  13. | ADS1262_INTERFACE_REG_CRC(CRC_CONFIG_DISABLE);
  14. ads1262_write_register(INTERFACE_REG, write_byte);
  15. //config mode0 register
  16. write_byte = ADS1262_MODE0_REG_REFREV(REFREV_CONFIG_NORMAL_POLARITY) \
  17. | ADS1262_MODE0_REG_RUNMODE(RUNMODE_CONFIG_CONTINUOUS_CONVERSION) \
  18. | ADS1262_MODE0_REG_CHOP(CHOP_CONFIG_DISABLE) \
  19. | ADS1262_MODE0_REG_DELAY(DELAY_CONFIG_NO_DELAY);
  20. ads1262_write_register(MODE0_REG, write_byte);
  21. //config mode1 register
  22. write_byte = ADS1262_MODE1_REG_FILTER(FILTER_CONFIG_FIR_MODE) \
  23. | ADS1262_MODE1_REG_SBADC(SBADC_CONFIG_CONNECT_ADC1) \
  24. | ADS1262_MODE1_REG_SBPOL(SBPOL_CONFIG_PULLUP_MODE) \
  25. | ADS1262_MODE1_REG_SBMAG(SBMAG_CONFIG_0_5UA);
  26. ads1262_write_register(MODE1_REG, write_byte);
  27. //config mode2 register
  28. write_byte = ADS1262_MODE2_REG_BYPASS(BYPASS_CONFIG_PGA_ENABLE) \
  29. | ADS1262_MODE2_REG_GAIN(GAIN_CONFIG_1V)\
  30. | ADS1262_MODE2_REG_DR(DR_CONFIG_7200SPS);
  31. ads1262_write_register(MODE2_REG, write_byte);
  32. //config input multiplexer register
  33. write_byte = ADS1262_INPMUX_REG_MUXP(MUXP_CONFIG_AIN0) \
  34. | ADS1262_INPMUX_REG_MUXN(MUXN_CONFIG_AIN1);
  35. ads1262_write_register(INPMUX_REG, write_byte);
  36. ads1262_cs_set(ADS1262_CS_HIGH);
  37. }

通过以上的基于位或运算符的代码操作,后面如果需要修改某个寄存器的某个位值,直接用枚举值复制粘贴进行替换即可,而且可以保证准确无误。

虽然一开始对照寄存器进行代码编写时会比较繁琐费时,但这是事半功倍的操作,代码的可读性和可维护性是比较好的。

以上是个人见解,有更好的方式也欢迎分享,比如使用表驱动法等也是非常好的,方法很多,归根结底,目的是增加可读性和可维护性。

另外,对于其他的外置芯片驱动初始化代码,包括DAC芯片和EEPROM芯片等,同样可以以这种方式实现一劳永逸的操作。

---------------------
作者:dffzh
链接:https://bbs.21ic.com/icview-3475456-1-1.html?_dsign=9d19c4c8
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

http://www.cnnetsun.cn/news/1910462.html

相关文章:

  • 微信小程序/UniApp蓝牙开发:如何优雅地封装一个可复用的蓝牙通信库(Vue3 Composition API)
  • B站会员购抢票神器:新手也能轻松掌握的自动化购票工具
  • 群辉NAS+Docker实战:容器魔方部署与内网穿透远程管理指南
  • 2024 MCM数学建模美赛A题实战复盘:七鳃鳗性别比例生态模型构建与Logistic模型应用 | 思路解析(一)
  • 如何免费下载Sketchfab 3D模型:Firefox用户脚本完整指南
  • 用嘎嘎降AI处理英文论文全流程:从上传到Turnitin验证教程
  • DeerFlow任务调度:并行处理多个独立研究请求
  • 视频PPT提取神器:3分钟教会你从视频中智能抓取幻灯片内容
  • 5个策略完全解锁Cursor Pro功能:技术实现与最佳实践
  • Windows Subsystem for Android终极指南:在Windows 11上无缝运行Android应用
  • SITS2026核心演讲深度拆解(多模态审核黄金三角模型:视觉-语音-语义协同决策框架)
  • 别再让示波器背锅了!实测开关电源炸机,手把手教你用隔离变压器避坑
  • 成为测试开发工程师的学习路径
  • ROS1新手避坑:Ubuntu 20.04下rviz闪退(exit code -11)的终极解决与文件夹玄学
  • Conda配置镜像源后还是报错?可能是你没搞懂main、free和conda-forge仓库的区别
  • 从BGA到FC:一文看懂主流IC封装基板技术选型指南(附对比表格)
  • Rust 文件 I/O 高级应用指南
  • ASMR下载终极指南:如何用asmr-downloader轻松获取asmr.one资源
  • Process Explorer:高级进程管理工具解决Windows任务管理器无法应对的难题
  • 从Vulkan到SAPIEN再到RobotWin:一个云上机器人仿真环境的完整排错日志
  • FOC 实践手记【三】磁链观测器的构建与离散化实现
  • Cesium Terrain Builder:三维地形构建新方案,打造沉浸式地理可视化体验
  • 抖音合集智能解析引擎:如何实现大规模视频内容的自动化批量处理
  • 保姆级教程:在uni-app微信小程序里跑起你的第一个Three.js 3D模型(附避坑清单)
  • WooCommerce跨境电商独立站从零到一实战指南,手把手教你搭建!
  • FFmpeg实战:如何用命令行快速预览YUV文件(附常见格式参数详解)
  • 终极指南:如何用Sunshine搭建免费游戏串流服务器,实现跨设备畅玩
  • 多模态大模型上线前必做的7步混沌验证:从CLIP到Qwen-VL,我们踩过的57个推理崩塌坑
  • 你以为在独立思考,其实只是把脑子借给了微信群
  • DeepSeek-OCR-2部署教程:NVIDIA Container Toolkit配置与GPU直通验证