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

张开发
2026/4/17 21:43:38 15 分钟阅读

分享文章

【每周分享】以ADC芯片为例讲解易于开发调试的驱动初始化代
我们在使用ADC芯片时往往都需要编写基于MCU和C语言的ADC芯片驱动代码通过驱动代码以SPI总线等方式对ADC芯片的寄存器进行相关读写操作并且读取ADC的采样数据。大部分ADC芯片的总线接口方式都是SPI如下图所示在驱动代码中ADC芯片的初始化代码是非常重要的即对ADC芯片的寄存器进行相关配置的代码那究竟通过C语言以什么样的方式编写初始化代码会比较好呢会便于后面调试修改呢会增加代码可读性呢接下来我就以TI的ADC芯片ADS1262为例向大家展示一下我这边的初始化代码及相关宏定义及枚举操作等。不过在这之前我们先来看下下面的初始化代码即将寄存器值reg_data写入相应的ADC寄存器中如果后面需要通过修改寄存器值进行芯片调试估计还得去打开芯片手册看下每个寄存器的位定义然后计算一下十六进制值最后再修改reg_data的值去更新寄存器。所以这种代码操作方式的效率是不是比较低而且还容易出错即使你把魔法数字改成宏定义方式也无济于事。接下来看看我实现的这种方式。我们先来看看ADC芯片的寄存器主要讲解如下6个寄存器的操作包括如下要想在代码调试时快速且精准的进行寄存器值修改那就是要在代码中明确现在需要修改的是哪个寄存器的哪个位配置以及可以配置哪些值因此代码的相关名称怎么命名和C语言的相关技术如何使用就显得尤为重要了。因此我们就需要通过枚举宏定义和位运算符等操作进行初始化代码的编写。我们先用枚举方式定义好寄存器地址写寄存器值即向寄存器地址写入数据代码如下复制/*ADC register address define*/typedef enum{ID_REG 0x00, //decvice identification registerPOWER_REG 0x01, //power registerINTERFACE_REG 0x02, //interface registerMODE0_REG 0x03, //mode0 registerMODE1_REG 0x04, //mode1 registerMODE2_REG 0x05, //mode2 registerINPMUX_REG 0x06, //input multiplexer registerOFCAL0_REG 0x07, //offset calibration register 0OFCAL1_REG 0x08, //offset calibration register 1OFCAL2_REG 0x09, //offset calibration register 2FSCAL0_REG 0x0A, //full-scale calibration register 0FSCAL1_REG 0x0B, //full-scale calibration register 1FSCAL2_REG 0x0C, //full-scale calibration register 2IDACMUX_REG 0x0D, //IDAC multiplexer registerIDACMAG_REG 0x0E, //IDAC magnitude registerREFMUX_REG 0x0F, //reference multiplexer registerTDACP_REG 0x10, //TDACP control registerTDACN_REG 0x11, //TDACN control registerGPIOCON_REG 0x12, //GPIO Connection registerGPIODIR_REG 0x13, //GPIO direction registerGPIODAT_REG 0x14, //GPIO data registerADC2CFG_REG 0x15, //ADC2 configuration registerADC2MUX_REG 0x16, //ADC2 input multiplexer registerADC2OFC0_REG 0x17, //ADC2 offset calibration register 0ADC2OFC1_REG 0x18, //ADC2 offset calibration register 1ADC2FSC0_REG 0x19, //ADC2 full-scale calibration register 0ADC2FSC1_REG 0x1A, //ADC2 full-scale calibration register 1}ads1262_reg_addr_e;寄存器的名称命名方式可以采用“寄存器名称_REG”的方式并在每个寄存器后面加上注释备注一下寄存器的含义。然后用枚举和宏定义方式定义好每个寄存器比如以下定义INTERFACE寄存器的代码复制/*interface register*/#define ADS1262_INTERFACE_REG_TIMEOUT(x) (((x) 0x01) 3) //RWtypedef enum {TIMEOUT_CONFIG_DISABLE 0, //defaultTIMEOUT_CONFIG_ENABLE 1,}ads1262_inerface_reg_timeout_config_e;#define ADS1262_INTERFACE_REG_STATUS(x) (((x) 0x01) 2) //RWtypedef enum {STATUS_CONFIG_DISABLE 0,STATUS_CONFIG_ENABLE 1, //default}ads1262_inerface_reg_status_config_e;#define ADS1262_INTERFACE_REG_CRC(x) ((x) 0x03) //RWtypedef enum {CRC_CONFIG_DISABLE 0,CRC_CONFIG_CHECKSUM_MODE 1, //defaultCRC_CONFIG_CRC_MODE 2,}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个寄存器的定义代码如下所示复制/*power register*/#define ADS1262_POWER_REG_RESET(x) (((x) 0x01) 4) //RWtypedef enum {RESET_CONFIG_CLEAR 1,}ads1262_power_reg_reset_config_e;#define ADS1262_POWER_REG_VBIAS(x) (((x) 0x01) 1) //RWtypedef enum {VBIAS_CONFIG_DISABLE 0, //defaultVBIAS_CONFIG_ENABLE 1,}ads1262_power_reg_vbias_config_e;#define ADS1262_POWER_REG_INTREF(x) ((x) 0x01) //RWtypedef enum {INTREF_CONFIG_DISABLE 0,INTREF_CONFIG_ENABLE 1, //default}ads1262_power_reg_intref_config_e;/*mode0 register*/#define ADS1262_MODE0_REG_REFREV(x) (((x) 0x01) 7) //RWtypedef enum {REFREV_CONFIG_NORMAL_POLARITY 0, //defaultREFREV_CONFIG_REVERSE_POLARITY 1,}ads1262_mode0_reg_refrev_config_e;#define ADS1262_MODE0_REG_RUNMODE(x) (((x) 0x01) 6) //RWtypedef enum {RUNMODE_CONFIG_CONTINUOUS_CONVERSION 0, //defaultRUNMODE_CONFIG_PULSE_CONVERSION 1,}ads1262_mode0_reg_runmode_config_e;#define ADS1262_MODE0_REG_CHOP(x) (((x) 0x03) 4) //RWtypedef enum {CHOP_CONFIG_DISABLE 0, //defaultCHOP_CONFIG_INPUT_CHOP_ENABLE 1,CHOP_CONFIG_IDAC_ROTATION_ENABLE 2,CHOP_CONFIG_ALL_ENABLE 3,}ads1262_mode0_reg_chop_config_e;#define ADS1262_MODE0_REG_DELAY(x) ((x) 0x0F) //RWtypedef enum {DELAY_CONFIG_NO_DELAY 0, //defaultDELAY_CONFIG_8_7US 1, //8.7usDELAY_CONFIG_17US 2, //17usDELAY_CONFIG_35US 3, //35usDELAY_CONFIG_69US 4,DELAY_CONFIG_139US 5,DELAY_CONFIG_278US 6,DELAY_CONFIG_555US 7,DELAY_CONFIG_1_1MS 8, // 1.1msDELAY_CONFIG_2_2MS 9,DELAY_CONFIG_4_4MS 10,DELAY_CONFIG_8_8MS 11,}ads1262_mode0_reg_delay_config_e;/*mode1 register*/#define ADS1262_MODE1_REG_FILTER(x) (((x) 0x07) 5) //RWtypedef enum {FILTER_CONFIG_SINC1_MODE 0,FILTER_CONFIG_SINC2_MODE 1,FILTER_CONFIG_SINC3_MODE 2,FILTER_CONFIG_SINC4_MODE 3,FILTER_CONFIG_FIR_MODE 4, //default}ads1262_mode1_reg_filter_config_e;#define ADS1262_MODE1_REG_SBADC(x) (((x) 0x07) 4) //RWtypedef enum {SBADC_CONFIG_CONNECT_ADC1 0, //defaultSBADC_CONFIG_CONNECT_ADC2 1,}ads1262_mode1_reg_sbadc_config_e;#define ADS1262_MODE1_REG_SBPOL(x) (((x) 0x07) 3) //RWtypedef enum {SBPOL_CONFIG_PULLUP_MODE 0, //defaultSBPOL_CONFIG_PULLDOWN_MODE 1,}ads1262_mode1_reg_sbpol_config_e;#define ADS1262_MODE1_REG_SBMAG(x) ((x) 0x07) //RWtypedef enum {SBMAG_CONFIG_NO 0, //defaultSBMAG_CONFIG_0_5UA 1,SBMAG_CONFIG_2UA 2,SBMAG_CONFIG_10UA 3,SBMAG_CONFIG_50UA 4,SBMAG_CONFIG_200UA 5,SBMAG_CONFIG_10M 6, // 10MΩresistor}ads1262_mode1_reg_sbmag_config_e;/*mode2 register*/#define ADS1262_MODE2_REG_BYPASS(x) (((x) 0x07) 7) //RWtypedef enum {BYPASS_CONFIG_PGA_ENABLE 0, //defaultBYPASS_CONFIG_PGA_BYPASS 1,}ads1262_mode2_reg_bypass_config_e;#define ADS1262_MODE2_REG_GAIN(x) (((x) 0x07) 4) //RWtypedef enum {GAIN_CONFIG_1V 0, //default,1V/VGAIN_CONFIG_2V 1,GAIN_CONFIG_4V 2,GAIN_CONFIG_8V 3,GAIN_CONFIG_16V 4,GAIN_CONFIG_32V 5,}ads1262_mode2_reg_gain_config_e;#define ADS1262_MODE2_REG_DR(x) ((x) 0x0F) //RWtypedef enum {DR_CONFIG_2_5SPS 0, //2.5 SPS, data rateDR_CONFIG_5SPS 1,DR_CONFIG_10SPS 2,DR_CONFIG_16_6SPS 3,DR_CONFIG_20SPS 4, //defaultDR_CONFIG_50SPS 5,DR_CONFIG_60SPS 6,DR_CONFIG_100SPS 7,DR_CONFIG_400SPS 8,DR_CONFIG_1200SPS 9,DR_CONFIG_2400SPS 10,DR_CONFIG_4800SPS 11,DR_CONFIG_7200SPS 12,DR_CONFIG_14400SPS 13,DR_CONFIG_19200SPS 14,DR_CONFIG_38400SPS 15,}ads1262_mode2_reg_dr_config_e;/*input multiplexer register*/#define ADS1262_INPMUX_REG_MUXP(x) (((x) 0x0F) 4) //RWtypedef enum {MUXP_CONFIG_AIN0 0, //defaultMUXP_CONFIG_AIN1 1,MUXP_CONFIG_AIN2 2,MUXP_CONFIG_AIN3 3,MUXP_CONFIG_AIN4 4,MUXP_CONFIG_AIN5 5,MUXP_CONFIG_AIN6 6,MUXP_CONFIG_AIN7 7,MUXP_CONFIG_AIN8 8,MUXP_CONFIG_AIN9 9,MUXP_CONFIG_AINCOM 10,MUXP_CONFIG_TSMP 11,MUXP_CONFIG_APSMP 12,MUXP_CONFIG_DPSMP 13,MUXP_CONFIG_TTSP 14,MUXP_CONFIG_FLOAT 15,}ads1262_inpmux_reg_muxp_config_e;#define ADS1262_INPMUX_REG_MUXN(x) ((x) 0x0F) //RWtypedef enum {MUXN_CONFIG_AIN0 0,MUXN_CONFIG_AIN1 1, //defaultMUXN_CONFIG_AIN2 2,MUXN_CONFIG_AIN3 3,MUXN_CONFIG_AIN4 4,MUXN_CONFIG_AIN5 5,MUXN_CONFIG_AIN6 6,MUXN_CONFIG_AIN7 7,MUXN_CONFIG_AIN8 8,MUXN_CONFIG_AIN9 9,MUXN_CONFIG_AINCOM 10,MUXN_CONFIG_TSMN 11,MUXN_CONFIG_APSMN 12,MUXN_CONFIG_DPSMN 13,MUXN_CONFIG_TTSN 14,MUXN_CONFIG_FLOAT 15,}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芯片的初始化配置操作复制void ads1262_init(void){uint8_t write_byte 0x00;ads1262_cs_set(ADS1262_CS_HIGH);//config power registerwrite_byte ADS1262_POWER_REG_RESET(RESET_CONFIG_CLEAR) \| ADS1262_POWER_REG_VBIAS(VBIAS_CONFIG_ENABLE) \| ADS1262_POWER_REG_INTREF(INTREF_CONFIG_ENABLE);ads1262_write_register(POWER_REG, write_byte);//config interface registerwrite_byte ADS1262_INTERFACE_REG_TIMEOUT(TIMEOUT_CONFIG_DISABLE) \| ADS1262_INTERFACE_REG_STATUS(STATUS_CONFIG_ENABLE) \| ADS1262_INTERFACE_REG_CRC(CRC_CONFIG_DISABLE);ads1262_write_register(INTERFACE_REG, write_byte);//config mode0 registerwrite_byte ADS1262_MODE0_REG_REFREV(REFREV_CONFIG_NORMAL_POLARITY) \| ADS1262_MODE0_REG_RUNMODE(RUNMODE_CONFIG_CONTINUOUS_CONVERSION) \| ADS1262_MODE0_REG_CHOP(CHOP_CONFIG_DISABLE) \| ADS1262_MODE0_REG_DELAY(DELAY_CONFIG_NO_DELAY);ads1262_write_register(MODE0_REG, write_byte);//config mode1 registerwrite_byte ADS1262_MODE1_REG_FILTER(FILTER_CONFIG_FIR_MODE) \| ADS1262_MODE1_REG_SBADC(SBADC_CONFIG_CONNECT_ADC1) \| ADS1262_MODE1_REG_SBPOL(SBPOL_CONFIG_PULLUP_MODE) \| ADS1262_MODE1_REG_SBMAG(SBMAG_CONFIG_0_5UA);ads1262_write_register(MODE1_REG, write_byte);//config mode2 registerwrite_byte ADS1262_MODE2_REG_BYPASS(BYPASS_CONFIG_PGA_ENABLE) \| ADS1262_MODE2_REG_GAIN(GAIN_CONFIG_1V)\| ADS1262_MODE2_REG_DR(DR_CONFIG_7200SPS);ads1262_write_register(MODE2_REG, write_byte);//config input multiplexer registerwrite_byte ADS1262_INPMUX_REG_MUXP(MUXP_CONFIG_AIN0) \| ADS1262_INPMUX_REG_MUXN(MUXN_CONFIG_AIN1);ads1262_write_register(INPMUX_REG, write_byte);ads1262_cs_set(ADS1262_CS_HIGH);}通过以上的基于位或运算符的代码操作后面如果需要修改某个寄存器的某个位值直接用枚举值复制粘贴进行替换即可而且可以保证准确无误。虽然一开始对照寄存器进行代码编写时会比较繁琐费时但这是事半功倍的操作代码的可读性和可维护性是比较好的。以上是个人见解有更好的方式也欢迎分享比如使用表驱动法等也是非常好的方法很多归根结底目的是增加可读性和可维护性。另外对于其他的外置芯片驱动初始化代码包括DAC芯片和EEPROM芯片等同样可以以这种方式实现一劳永逸的操作。---------------------作者dffzh链接https://bbs.21ic.com/icview-3475456-1-1.html?_dsign9d19c4c8来源21ic.com此文章已获得原创/原创奖标签著作权归21ic所有任何人未经允许禁止转载。

更多文章