[嵌入式C编程与固件开发] 一般芯片的驱动初始化代码编写方法

[复制链接]
 楼主| dffzh 发表于 2025-8-8 13:52 | 显示全部楼层 |阅读模式
我们在使用ADC芯片时,往往都需要编写基于MCU和C语言的ADC芯片驱动代码,通过驱动代码以SPI总线等方式对ADC芯片的寄存器进行相关读写操作,并且读取ADC的采样数据。大部分ADC芯片的总线接口方式都是SPI,如下图所示:
1.png
在驱动代码中,ADC芯片的初始化代码是非常重要的,即对ADC芯片的寄存器进行相关配置的代码,那究竟通过C语言以什么样的方式编写初始化代码会比较好呢?会便于后面调试修改呢?会增加代码可读性呢?接下来我就以TI的ADC芯片ADS1262为例向大家展示一下我这边的初始化代码及相关宏定义及枚举操作等。不过在这之前,我们先来看下下面的初始化代码:
2.png
即将寄存器值reg_data写入相应的ADC寄存器中,如果后面需要通过修改寄存器值进行芯片调试,估计还得去打开芯片手册看下每个寄存器的位定义,然后计算一下十六进制值,最后再修改reg_data的值去更新寄存器。所以,这种代码操作方式的效率是不是比较低?而且还容易出错,即使你把魔法数字改成宏定义方式也无济于事。
接下来看看我实现的这种方式。

我们先来看看ADC芯片的寄存器,主要讲解如下6个寄存器的操作,包括如下:
3.png
要想在代码调试时快速且精准的进行寄存器值修改,那就是要在代码中明确现在需要修改的是哪个寄存器的哪个位配置,以及可以配置哪些值,因此代码的相关名称怎么命名和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.    
  16.     //config mode0 register
  17.     write_byte = ADS1262_MODE0_REG_REFREV(REFREV_CONFIG_NORMAL_POLARITY) \
  18.                      | ADS1262_MODE0_REG_RUNMODE(RUNMODE_CONFIG_CONTINUOUS_CONVERSION) \
  19.                      | ADS1262_MODE0_REG_CHOP(CHOP_CONFIG_DISABLE) \
  20.                      | ADS1262_MODE0_REG_DELAY(DELAY_CONFIG_NO_DELAY);
  21.     ads1262_write_register(MODE0_REG, write_byte);

  22.     //config mode1 register
  23.         write_byte = ADS1262_MODE1_REG_FILTER(FILTER_CONFIG_FIR_MODE) \
  24.                      | ADS1262_MODE1_REG_SBADC(SBADC_CONFIG_CONNECT_ADC1) \
  25.                      | ADS1262_MODE1_REG_SBPOL(SBPOL_CONFIG_PULLUP_MODE) \
  26.                      | ADS1262_MODE1_REG_SBMAG(SBMAG_CONFIG_0_5UA);
  27.     ads1262_write_register(MODE1_REG, write_byte);

  28.     //config mode2 register
  29.     write_byte = ADS1262_MODE2_REG_BYPASS(BYPASS_CONFIG_PGA_ENABLE) \
  30.                      | ADS1262_MODE2_REG_GAIN(GAIN_CONFIG_1V)\
  31.                      | ADS1262_MODE2_REG_DR(DR_CONFIG_7200SPS);
  32.     ads1262_write_register(MODE2_REG, write_byte);

  33.     //config input multiplexer register
  34.     write_byte = ADS1262_INPMUX_REG_MUXP(MUXP_CONFIG_AIN0) \
  35.                      | ADS1262_INPMUX_REG_MUXN(MUXN_CONFIG_AIN1);
  36.     ads1262_write_register(INPMUX_REG, write_byte);
  37.         ads1262_cs_set(ADS1262_CS_HIGH);
  38. }
通过以上的基于位或运算符的代码操作,后面如果需要修改某个寄存器的某个位值,直接用枚举值复制粘贴进行替换即可,而且可以保证准确无误。
虽然一开始对照寄存器进行代码编写时会比较繁琐费时,但这是事半功倍的操作,代码的可读性和可维护性是比较好的。
以上是个人见解,有更好的方式也欢迎分享,比如使用表驱动法等也是非常好的,方法很多,归根结底,目的是增加可读性和可维护性。

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

您需要登录后才可以回帖 登录 | 注册

本版积分规则

109

主题

1163

帖子

22

粉丝
快速回复 在线客服 返回列表 返回顶部

109

主题

1163

帖子

22

粉丝
快速回复 在线客服 返回列表 返回顶部