0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CW32模块使用 ADS1115多路模数转换器

CW32生态社区 来源:多模数转换器 作者:多模数转换器 2024-09-19 11:50 次阅读

ADS1115 器件是兼容 IIC 的 16 位高精度低功耗模数转换器 (ADC),采用超小型无引线 X2QFN-10 封装和 VSSOP-10 封装。ADS111x 器件采用了低漂移电压基准振荡器。ADS1114 和 ADS1115 还采用可编程增益放大器(PGA)和数字比较器。这些特性加以较宽的工作电源电压范围使得 ADS1115 非常适合功率与空间受限的传感器测量。

ADS111x 可在数据速率高达每秒 860 个样本 (SPS) 的情况下执行转换。PGA 可提供从 ±256mV 到±6.144V 的输入范围,从而实现精准的大小信号测量。ADS1115 具有 一个输入多路复用器 (MUX),可实现两次差动输入测量或四次单端输入测量。在ADS1115 中可使用数字比较器进行欠压和过压检测。ADS1115既可在连续转换模式下工作,也可在单冲模式下工作。在单冲模式下,这些器件可在一次转换后自动断电;因此显著降低了空闲期间的功耗。

01模块来源

模块实物展示:

wKgaombrn4OAQHTbAAA5CLcWQDg31.webp



02 规格参数

工作电压:2.0-5.5V

工作电流:150uA

采集精度:16位

采集通道:4通道

控制方式:IIC

管脚数量:10 Pin(2.54mm间距排针

以上信息见厂家资料文件

03移植过程

我们的目标是将例程移植至CW32F030C8T6开发板上【实现4路ADC采集电压功能】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。

3.1查看资料

ADS1115是采用的IIC通信,所以首先要了解IIC的地址与时序,再确定根据寄存器的设置。

器件地址

器件地址的设置见下表:

wKgZombrn4WAUtHaAAAYOox_FcQ71.webp

说明:当模块上的ADDR引脚接入GND时,其器件地址为1001000,最后一位数据是读写位。

wKgaombrn4aAbIa_AAAkQhjD7n408.webp

时序

下图是读时序,步骤是:

IIC起始信号 -> 发送器件地址+0(写) -> 等待模块应答 -> 应答后发送寄存器地址 -> 等待模块应答 -> 重新发送起始信号 -> 发送器件地址+1(读) -> 等待模块应答 -> 应答后读取高8位数据 -> 读取完毕主机发送应答信号 -> 读取低8位数据 -> 读取完毕主机发送应答信号 -> 发送IIC停止信号

wKgZombrn4aAdHTQAACLah_E1Gk60.webp

下图是写时序,步骤是:

IIC起始信号 -> 发送器件地址+0(写) -> 等待模块应答 -> 应答后发送寄存器地址 -> 等待模块应答 -> 应答后写入高8位数据 -> 等待模块应答 -> 写入低8位数据 -> 等待模块应答 -> 发送IIC停止信号

寄存器说明

ADS1115有四个寄存器,可通过IIC接口使用地址指针进入。

地址0X00为转换寄存器,它包含最后一次转换的结果。

地址0X01为配置寄存器,用于更改ADS1115的工作模式和查询设备状态。

另外两个寄存器,Lo_thresh和Hi_thresh,设置用于比较器函数的阈值,我们用不到。

wKgaombrn4aASN8LAAA0MqjBsvA58.webp

配置寄存器有16位,用于控制工作模式、输入选择、数据速率、满量程范围和比较器模式。

wKgZombrn4eAD_gxAAAhgHT5t4M00.webp

第15位:OS,读操作时可以知道当前设备的工作状态;写操作时可以设置单次转换。本文配置为1(必须为断电模式下,当对OS写1时,设备会进入上电模式并完成一次数据转换,然后会自动将OS置0)

第14-12位:MUX为输入多路复用器,对输入模式进行选择,如下图有八种输入模式,分别是四种差分与四种单端输入,本文配置为A0单端输入(0x04)。(单端输入就是测量的数据有两个引脚,一个输出一个地。将测量的输出接入A0引脚,测量的地与ADS1115共地)

wKgaombrn4eAFg18AAAq0kJ_ZAc99.webp

第11-9位:PGA为可编程增益放大器,设置FSR(满刻度的范围),本文配置为±4.096V(0x01)后面电压计算公式与这个有关。

wKgZombrn4iAbfHeAAAimvGtfm849.webp

第8位:MODE选择持续转换模式与单次转换模式(单次转换模式需要OS位触发),本文配置为连续转换模式(0x00)

wKgaombrn4iAA5mlAAAQsgZIAmY15.webp

第7-5位:DR配置data rate数据传输速率,本文配置为128SPS(0x04)

wKgZombrn4iABMeBAAAUkEFPHcQ95.webp

第4-2位:对比较器的配置,我们不使用,默认为0即可(0x00) 第1-0位:本位配置为关闭比较器并将ALERT/RDY引脚设置为高阻抗模式(0x03)

wKgaombrn4mAB_ivAABBTMZDbpg29.webp

最终得到的配置结果为1100_0010_1000_0011(0xC283)。

当前配置的是A0的引脚,我们后续获取数据也是从A0引脚读取。

16位转换寄存器以二进制的补码格式保存最后一次转换的结果。需要注意的是,在上电之后,转换寄存器被清除为0,并保持为0,直到第一次转换完成。

wKgZombrn4mAcjKQAAA3HDxHuY052.webp

实现代码说明

读取到的ADC值如何换算为电压?

以PGA设置为4.96V为例。

电压 = 采集到的ADC值 * 分辨率

分辨率 = 测量电压范围 / (2^AD位数-1) = 4.096 / 2的15次方 = 0.000125V

分辨率也可以在数据手册中查看,见右图。其中125uV = 0.125mV = 0.000125V。

wKgaombrn4qAcitvAAApqMF8bX456.webp

/******************************************************************
 * 函 数 名 称:WriteADS1115
 * 函 数 说 明:向ADS1115的add地址写入dat数据
 * 函 数 形 参: add写入寄存器地址
 *             dat_H写入的高8位数据
 *             dat_L写入的低8位数据
 * 函 数 返 回:0写入成功
 *             1写入器件地址无应答
 *             2写入寄存器地址无应答
 * 作       者:LC
 * 备       注:器件地址=0X90
******************************************************************/
uint8_t WriteADS1115(uint8_t add, uint8_t dat_H, uint8_t dat_L)
{
    IIC_Start();//起始信号
    IIC_Write(0x90);//器件地址
    if( IIC_Wait_Ack() == 1 )
        return 1;
    IIC_Write(add);//寄存器地址
    if( IIC_Wait_Ack() == 1 )
        return 2;
    IIC_Write(dat_H);//写入高8位
    IIC_Wait_Ack();//等待应答
    IIC_Write(dat_L);//写入低8位
    IIC_Wait_Ack();//等待应答
    IIC_Stop();//停止信号
    return (0);
}
/******************************************************************
 * 函 数 名 称:ReadADS1115
 * 函 数 说 明:读取ADS1115的数据
 * 函 数 形 参:add读取的寄存器地址
 * 函 数 返 回:-1-读取失败  其他-读取成功
 * 作       者:LC
 * 备       注:无
******************************************************************/
float ReadADS1115(unsigned char add)
{
    int i =0;
    unsigned char dat[2]={0};
    unsigned int num = 0;
    float ret=0;
    IIC_Start();//起始信号
    IIC_Write(0x90);//器件地址+写
    if( IIC_Wait_Ack() == 1 )
        return -1;
    IIC_Write(add);//寄存器地址
    if( IIC_Wait_Ack() == 1 )
        return -1;
    do{
      //超时判断
      i++;
      if( i > 20 ) return -1;
      delay_1ms(1);
      IIC_Start();//重新发送起始信号
      IIC_Write(0x91);//器件地址+读
    }while(IIC_Wait_Ack() == 1);

    dat[0]=IIC_Read();//读高8位数据
    IIC_Send_Ack(0);//应答
    dat[1]=IIC_Read();//读低8位数据
    IIC_Send_Ack(1);//非应答
    IIC_Stop();//发送停止信号
    //数据整合
    num =  ((dat[0]< <8) | (dat[1]));
    //分辨率计算:测量电压范围/(2^AD位数-1)
    //    分辨率= 4.096/2^15=0.000125
    //      电压= 采集到的ADC值 * 分辨率
    if(num >32768)
        ret=(65535-num)*0.000125;
    else
        ret=num*0.000125;

    return ret;
}

3.2引脚选择

wKgZombrn4qAHBC1AABxTLbER3E10.webp

接线表

3.3移植至工程

工程模板参考入门手册的工程模板

移植步骤中的导入.c和.h文件与【CW32模块使用】DHT11温湿度传感器相同,只是将.c和.h文件更改为bsp_ads1115.c与bsp_ads1115.h。这里不再过多讲述,移植完成后面修改相关代码。

在文件bsp_ads1115.c中,编写如下代码。

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-20     LCKFB-LP    first version
 */

#include "bsp_ads1115.h"
#include "stdio.h"

/******************************************************************
 * 函 数 名 称:ADS1115_GPIO_Init
 * 函 数 说 明:对IIC引脚初始化
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:1100_0010_1000_0011  WriteADS1115(0x01,0xc2,0x83);
******************************************************************/
void ADS1115_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化结构体

    RCC_ADS1115_ENABLE();        // 使能GPIO时钟

    GPIO_InitStruct.Pins = GPIO_SCL|GPIO_SDA;               // GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;             // 开漏输出
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;                // 输出速度高
    GPIO_Init(PORT_ADS1115, &GPIO_InitStruct);              // 初始化

    //写入配置参数
    WriteADS1115(0x01,0xC2,0x83);
}

/******************************************************************
 * 函 数 名 称:IIC_Start
 * 函 数 说 明:IIC起始信号
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Start(void)
{
        SDA_OUT();

        SDA(1);
        delay_us(5);
        SCL(1);
        delay_us(5);

        SDA(0);
        delay_us(5);
        SCL(0);
        delay_us(5);

}
/******************************************************************
 * 函 数 名 称:IIC_Stop
 * 函 数 说 明:IIC停止信号
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Stop(void)
{
        SDA_OUT();
        SCL(0);
        SDA(0);

        SCL(1);
        delay_us(5);
        SDA(1);
        delay_us(5);

}
/******************************************************************
 * 函 数 名 称:IIC_Send_Ack
 * 函 数 说 明:主机发送应答
 * 函 数 形 参:0应答  1非应答
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
        SDA_OUT();
        SCL(0);
        SDA(0);
        delay_us(5);
        if(!ack) SDA(0);
        else     SDA(1);
        SCL(1);
        delay_us(5);
        SCL(0);
        SDA(1);
}
/******************************************************************
 * 函 数 名 称:IIC_Wait_Ack
 * 函 数 说 明:等待从机应答
 * 函 数 形 参:无
 * 函 数 返 回:1=无应答   0=有应答
 * 作       者:LC
 * 备       注:无
******************************************************************/

unsigned char IIC_Wait_Ack(void)
{

        char ack = 0;
        unsigned char ack_flag = 10;
        SDA_IN();
        SDA(1);
        delay_us(5);
        SCL(1);
        delay_us(5);
        while( (GETSDA()==1) && ( ack_flag ) )
        {
                ack_flag--;
                delay_us(5);
        }

        if( ack_flag <= 0 )
        {
                IIC_Stop();
                return 1;
        }
        else
        {
                SCL(0);
                SDA_OUT();
        }
        return ack;
}
/******************************************************************
 * 函 数 名 称:IIC_Write
 * 函 数 说 明:IIC写一个字节
 * 函 数 形 参:dat写入的数据
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Write(unsigned char dat)
{
        int i = 0;
        SDA_OUT();
        SCL(0);//拉低时钟开始数据传输

        for( i = 0; i < 8; i++ )
        {
                SDA( (dat & 0x80) >> 7 );
                                delay_us(2);
                dat< <=1;
                delay_us(6);
                SCL(1);
                delay_us(4);
                SCL(0);
                delay_us(4);

        }
}

/******************************************************************
 * 函 数 名 称:IIC_Read
 * 函 数 说 明:IIC读1个字节
 * 函 数 形 参:无
 * 函 数 返 回:读出的1个字节数据
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned char IIC_Read(void)
{
        unsigned char i,receive=0;
    SDA_IN();//SDA设置为输入
    for(i=0;i< 8;i++ )
        {
        SCL(0);
        delay_us(5);
        SCL(1);
        delay_us(5);
        receive< <=1;
        if( GETSDA() )
        {
            receive|=1;
        }
        delay_us(5);
    }
  return receive;
}

/******************************************************************
 * 函 数 名 称:WriteADS1115
 * 函 数 说 明:向ADS1115的add地址写入dat数据
 * 函 数 形 参:add写入寄存器地址 dat_H写入的高8位数据  dat_L写入的低8位数据
 * 函 数 返 回:0写入成功 1写入器件地址无应答  2写入寄存器地址无应答
 *              3写入高8位数据无应答  4写入低8位数据无应答
 * 作       者:LC
 * 备       注:器件地址=0X90
******************************************************************/
uint8_t WriteADS1115(uint8_t add,uint8_t dat_H,uint8_t dat_L)
{
          IIC_Start();
          IIC_Write(0x90);
          if( IIC_Wait_Ack() == 1 )
          {
                  printf("error 1rn");
                   return 1;
          }
          IIC_Write(add);
          if( IIC_Wait_Ack() == 1 )
          {
                  printf("error 2rn");
                  return 2;
          }
          IIC_Write(dat_H);
          IIC_Wait_Ack();
          IIC_Write(dat_L);
          IIC_Wait_Ack();
          IIC_Stop();
          return (0);
}



/******************************************************************
 * 函 数 名 称:ReadADS1115
 * 函 数 说 明:读取ADS1115的数据
 * 函 数 形 参:add读取的寄存器地址
 * 函 数 返 回:-1-读取失败  其他-读取成功
 * 作       者:LC
 * 备       注:无
******************************************************************/
float ReadADS1115(unsigned char add)
{
  int i =0;
  unsigned char dat[2]={0};
  unsigned int num = 0;
  float ret=0;
  IIC_Start();//起始信号
  IIC_Write(0x90);//器件地址+写
  if( IIC_Wait_Ack() == 1 )
      return -1;
  IIC_Write(add);//寄存器地址
  if( IIC_Wait_Ack() == 1 )
      return -1;
  do{
    //超时判断
    i++;
    if( i > 20 ) return -1;
    delay_ms(1);
    IIC_Start();//重新发送起始信号
    IIC_Write(0x91);//器件地址+读
  }while(IIC_Wait_Ack() == 1);

  dat[0]=IIC_Read();//读高8位数据
  IIC_Send_Ack(0);//应答
  dat[1]=IIC_Read();//读低8位数据
  IIC_Send_Ack(1);//非应答
  IIC_Stop();//发送停止信号
  //数据整合
  num =  ((dat[0]< <8) | (dat[1]));

        //数值计算取决于PGA配置
    //2的15次方=32768
    //设置的最大量程4.096
//        if(num >32768)
//                ret=((float)(65535-num)/32768.0)*4.096;
//        else
//                ret=((float)num/32768.0)*4.096;

  //分辨率计算:测量电压范围/(2^AD位数-1)
  //    分辨率= 4.096/2^15=0.000125
  //      电压= 采集到的ADC值 * 分辨率
  if(num>32768)
      ret=(65535-num)*0.000125;
  else
      ret=num*0.000125;

  return ret;
}

在文件bsp_ads1115.h中,编写如下代码。

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-20     LCKFB-LP    first version
 */
#ifndef _BSP_ADS1115_H_
#define _BSP_ADS1115_H_

#include "board.h"

#define RCC_ADS1115_ENABLE()      __RCC_GPIOB_CLK_ENABLE()
#define PORT_ADS1115              CW_GPIOB

#define GPIO_SCL                  GPIO_PIN_8
#define GPIO_SDA                  GPIO_PIN_9

//SDA输入模式
#define SDA_IN()   {                                                     
                        GPIO_InitTypeDef GPIO_InitStruct;                
                        GPIO_InitStruct.Pins = GPIO_SDA;                 
                        GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP;   
                        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;         
                        GPIO_Init(PORT_ADS1115, &GPIO_InitStruct);       
                   }
//SDA输出模式
#define SDA_OUT()  {                                                                                                         
                        GPIO_InitTypeDef GPIO_InitStruct;                
                        GPIO_InitStruct.Pins = GPIO_SDA;                 
                        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;      
                        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;         
                        GPIO_Init(PORT_ADS1115, &GPIO_InitStruct);       
                    }

#define SCL(BIT)  GPIO_WritePin( PORT_ADS1115, GPIO_SCL, BIT?GPIO_Pin_SET:GPIO_Pin_RESET )
#define SDA(BIT)  GPIO_WritePin( PORT_ADS1115, GPIO_SDA, BIT?GPIO_Pin_SET:GPIO_Pin_RESET )
#define GETSDA()  GPIO_ReadPin( PORT_ADS1115, GPIO_SDA )




void ADS1115_GPIO_Init(void);
unsigned char WriteADS1115(unsigned char add,unsigned char dat_H,unsigned char dat_L);
float ReadADS1115(unsigned char add);
#endif

04移植验证

在自己工程中的main主函数中,编写如下。

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-20     LCKFB-LP    first version
 */
#include "board.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "bsp_ads1115.h"

int32_t main(void)
{
    board_init();        // 开发板初始化

    uart1_init(115200);        // 串口1波特率115200

    ADS1115_GPIO_Init();

    printf("demo startrn");
    while(1)
    {
        //当前设置最大量程为4.096V
        printf("A0 = %.4frn", ReadADS1115(0x00) );//读取A0的值
        delay_ms(1000);
    }
}

移植现象:将A0接入GND、3.3V和5V。

wKgaombrn4uAJ71VAACHuiLvgdk80.webp

模块移植成功案例代码:

链接:https://pan.baidu.com/s/1A6SRdtHRT5N7uUNE6Y08YQ?pwd=LCKF

提取码:LCKF

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 传感器
    +关注

    关注

    2550

    文章

    51035

    浏览量

    753052
  • ads1115
    +关注

    关注

    0

    文章

    17

    浏览量

    12911
  • 多模数转换器

    关注

    0

    文章

    4

    浏览量

    1454
  • CW32
    +关注

    关注

    1

    文章

    203

    浏览量

    626
收藏 人收藏

    评论

    相关推荐

    求助,关于模数转换器ADS1115连续转换的问题求解

    ADS1115的continuous-conversion mode启用时,器件开始进行连续转换,并将转换的值存入寄存,采用4个单端输入模式。 问题:当我读取寄存
    发表于 11-29 14:26

    使用ADS1115模数转换器时,它的PGA的增益是如何设置的?

    使用ADS1115模数转换器时,它的PGA的增益是如何设置的,按照数据手册,设置FSR=4.096V或者6.144V,测量3V电压,都是3V左右,这是什么问题。
    发表于 12-03 07:18

    ADS1115模数转换用作单端信号转换时,vin-端连接数字地还是模拟地?

    1.ADS1115模数转换用作单端信号转换时,vin-端连接数字地还是模拟地? 2.不需使用的输入端口,怎么样处理最佳? 3.准备把信号处理一部分做成一个板子,MCU为另一块板子,通
    发表于 12-03 06:37

    ADS1115是什么意思呢

    内部基准、振荡和可编程比较且兼容 I2C 的ADS111x、超小型、低功耗 860SPS、16 位 ADC 。在操作之前,你需要一块ads1115,一块单片机,CCS,还要会一点I
    发表于 11-29 08:11

    ADS1115是什么?怎样去使用ADS1115

    ADS1115是什么?ADS1115有哪些特性?怎样去使用ADS1115呢?
    发表于 12-14 08:08

    TI公司IIC接口16位AD模块ADS1115

    TI公司16位的AD模块ADS1115,IIC接口,四通道,本程序是基于msp430利用两个IO口模拟IIC来驱动ADS1115
    发表于 07-20 17:21 66次下载

    ADS1115用户手册

    ADS1113、ADS1114 和 ADS1115 是具有 16 位分辨率的高精度模数转换器 (ADC),采用超小型的无引线QFN-10 封装或 MSOP-10 封装。
    发表于 11-11 11:55 61次下载

    ads1115电压测量范围

     ADS1115 是具有16 位分辨率的高精度模数转换器(ADC),采用超小型的无引线QFN-10 封装或MSOP-10 封装。ADS1115 在设计时考虑到了精度、功耗和实现的简易性
    发表于 10-23 08:42 1.7w次阅读

    基于51的ads1115详细程序介绍

     ADS1115 是具有16 位分辨率的高精度模数转换器(ADC),采用超小型的无引线QFN-10 封装或MSOP-10 封装。以下将是基于51的ads1115详细程序介绍:
    发表于 10-23 09:25 1.5w次阅读

    ads1115应用电路图

    I 公司的ADS1113,ADS1114 和ADS1115 是超小型16位精密模数转换器(ADC),具有板载基准电压和振荡,通过I2C 兼
    发表于 10-23 09:39 1.4w次阅读

    ads1115使用方法

    ADS1113.ADS1114 和ADS1115 是具有16 位分旁粹率的高精度模数转换器(ADC),采用超小型的无引线QFN-10 封装或MSOP-10 封装。ADS1113/4/5
    发表于 10-23 10:15 4.2w次阅读

    基于STM32的ADS1115使用例程

    基于STM32的ADS1115使用例程
    发表于 12-02 20:51 121次下载
    基于STM32的<b class='flag-5'>ADS1115</b>使用例程

    模数转换器16位ADC芯片ADS1115

    TI德州仪器ADS1115是VSSOP-10和UQFN-10封装中提供的精密、低功耗、16位、I2C兼容的模数转换器(ADC)。还包含一个可编程增益放大器(PGA)和一个数字比较。这些功能,加上广泛的工作电源范围,非常适合功率
    的头像 发表于 05-19 17:36 3270次阅读

    浅谈CW32系列模数转换器(ADC)

    CW32系列模数转换器(ADC)
    的头像 发表于 10-25 15:43 1128次阅读
    浅谈<b class='flag-5'>CW32</b>系列<b class='flag-5'>模数转换器</b>(ADC)

    基于CW32模块ADS1115多路模数转换器设计

    ADS1115 器件是兼容 IIC 的 16 位高精度低功耗模数转换器 (ADC),采用超小型无引线 X2QFN-10 封装和 VSSOP-10 封装。ADS111x 器件采用了低漂移电压基准
    的头像 发表于 11-12 11:20 325次阅读
    基于<b class='flag-5'>CW32</b><b class='flag-5'>模块</b>的<b class='flag-5'>ADS1115</b><b class='flag-5'>多路</b><b class='flag-5'>模数转换器</b>设计