锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

外设驱动库开发笔记3:AD527x系列数字电位器驱动

时间:2022-09-19 19:30:00 r1216电位器r9011电位器

在某些情况下,我们需要使用更字电位器来实现我们的应用。我们经常使用它AD527x实现这类应用的系列数字电位器。AD527x系列数字电位器完全可以满足要求。为了减少重复工作,我们将在这里进行分系和实现AD527x驱动系列数字电位器。

1、功能概述

我们在这里讨论的AD527x系列数字电位器包括:AD5270、AD5271、AD5272和AD5724,它们的功能是相同的,主要在数字位置或通信接口上。

AD527x可变电阻性能和非易失性存储器系列数字电位器行业领先(NVM)这些设备的端到端电阻容差小于1%,并提供50次可编程(50次)-TP)存储器。将电阻值编程写入50-TP在存储器之前,可以进行无限调整。这些设备不需要任何外部电压源来帮助熔丝,并提供50个永久编程机会。在50-TP激活期间,永久熔丝指令将固定游标位置。

对于AD527x所有系列数字电位器都有16位宽的移位寄存器AD527x系列数字电位器的操作都是同过这个以为寄存器完成的。移位寄存器的格式如下所示。

16位移位寄存器由两个应设为0的未用位、四个控制位和10个RDAC数据位组成和数据MSB优先加载AD5271和AD5274只有8位数据,最后两位将被忽略。软件命令的功能由四个控制位决定,具体功能代码如下:

我们对AD527x基于这10个命令,系列数字电位器的操作实际上是基于的NOP命令可以忽略,因为它不会有任何操作。有命令5和命令7需要说。

命令7用于设置控制寄存器。控制寄存器仅后4有效。C0用于设置50-TP编程使能。C1用于设置RDAC的写保护。C2用于电阻容差校准。C3则是指示50-TP编程状态。具体结构如下:

命令5用于设置阅读50-TP内容。也就是说,这个命令是用来设置我下次读50-TP是那个类容,因为总共有50个。具体值如下:

共50个编码需要50个,使用D0到DF5位,编号1开始对应50个编程位置。

2.驱动设计与实现

我们已经明白了AD527x接下来,我们将设计和实现系列数字电位器的基本情况AD527x驱动系列数字电位器。

2.1、对象定义

同样,我们将根据对象操作的想法进行设计AD527x由系列数字电位器驱动。在这种情况下,我们必须首先定义它AD527x系列数字电位器对象。

2.1.1.抽象对象类型

在抽象出AD527x先分析一下系列数字电位器的对象类型AD527x系列数字电位器。一个对象至少包含两个特性:属性和操作。让我们分析一下AD527x包含这些属性和操作的系列数字电位器对象。

对于AD527x系列数字电位器包含多种设备,不同的设备在通信接口和档位上会有所不同,因此我们将设备类型作为其属性来区分哪种设备,然后区分接口和档位的差异。我们还将游标的当前位置和寄存器的值设置为属性,以确定设备的当前状态。当设备时I2C接口需要有设备地址,所以我们把设备地址设置为属性,这个属性只是I2C接口模式时才起作用。而在使用SPI接口设备需要一个片选信号,所以我们选择操作片作为信号AD527x该操作仅用于系列数字电位器的操作SPI只有当接口设备工作时。此外,AD527x系列数字电位器对象还需要在运行过程中实现数据的发送和接收以及必要的延迟函数,我们都以此为对象。根据以上分析,我们可以抽象出来AD527x系列数字电位器的对象类型如下:

/*定义用于SPI接口的对象类型*/ typedef struct AD527xObject {   AD527xType type; //设备类型 uint8_t devAddress; //设备地址,用于I2C接口 uint8_t conreg; //控制寄存器 uint16_t rdac; ///游标寄存器现值 void (*ChipSelcet)(AD527xCSType en); //片选信号,用于SPI接口 void (*Receive)(struct AD527xObject *rx,uint8_t *rData,uint16_t rSize); void (*Transmit)(struct AD527xObject *rx,uint8_t *wData,uint16_t wSize); void (*Delayms)(volatile uint32_t nTime); //ms延迟操作指针 }AD527xObjectType;

2.1.对象的初始化

我们需要使用一个对象的初始化。初始化函数至少包含两个方面:一是赋予对象变量必要的初始值;二是检查这些初始值是否有效。特别是,如果一些操作指针错误,可能会产生严重后果。基于这一原则,我们设计AD527x系列数字电位器的对象初始化函数如下:

/* 初始化AD527x对象,I2C界面必须初始化devAddress,SPI接口必须初始化void (*ChipSelcet)(bool) */ void AD527xInitialization(AD527xObjectType *rx,                           uint8_t address,                           AD527xType type,                          AD527xReceive recieve,
                          AD527xTransmit transmit,
                          AD527xChipSelcet cs,
                          AD527xDelayms delayms)
{
  if((rx==NULL)||(recieve==NULL)||(transmit==NULL)||(delayms==NULL))
  {
    return;
  }

  if((type==AD5270)||(type==AD5271)) //使用SPI接口
  {
    if(cs==NULL) //硬件电路实现片选
    {
      rx->ChipSelcet=DefaultChipSelcet;
    }
    else
    {
      rx->ChipSelcet=cs;
    }
    rx->devAddress=0x00;
  }
  else //使用I2C接口
  {
    if((address==0x58)||(address==0x5C)||(address==0x5E))
    {
      rx->devAddress=address;
    }
    else if((address==0x2C)||(address==0x2E)||(address==0x2F))
    {
      rx->devAddress=(address<<1);
    }
    else
    {
      rx->devAddress=0x00;
    }

    rx->ChipSelcet=NULL;
  }
  
  rx->type=type;

  rx->conreg=0x00;
  rx->rdac=0x0000;

  rx->Receive=recieve;
  rx->Transmit=transmit;
  rx->Delayms=delayms;

  ReadControlRegister(rx);

  SetSoftShutMode(rx,SOFT_NORMAL_MODE);
}

2.2、对象操作

前面我们已经描述过,对AD527x系列数字电位器的操作命令有9个。这9个命令皆是对寄存器进行读写操作的,所以我们这里将这些操作分为读寄存器操作和写寄存器操作,并以此设计驱动程序。

2.2.1、写寄存器操作

首先我们需要说明写寄存器操作是针对对象的操作函数,而不是对象变量包含的操作,因为我们只在对象变量中放入依赖于外界平台的基本操作。写寄存器操作会以回调的方式调用对象变量包含的基本操作。

因为AD527x系列数字电位器对象包括不同接口和不同档位的器件,所以我们设计写寄存器操作时需要考虑AD527x系列数字电位器对象的类型。而这个类型已在初始化时赋予了对象变量。据此我们设计写寄存器操作函数如下:

/* 写寄存器操作 */
static void AD527xWriteRegister(AD527xObjectType *rx,uint16_t cmd)
{
  uint8_t tData[2];

  tData[0]=(uint8_t)(cmd>>8);
  tData[1]=(uint8_t)cmd;

  if((rx->type==AD5270)||(rx->type==AD5271)) //SPI接口
  {
    rx->ChipSelcet(AD527xCS_ENABLE);
    rx->Delayms(1);
  }

  rx->Transmit(rx,tData,2);

  if((rx->type==AD5270)||(rx->type==AD5271)) //SPI接口
  {
    rx->Delayms(1);
    rx->ChipSelcet(AD527xCS_DISABLE);
  }
}

2.2.2、读寄存器操作

与写寄存器操作一样,读寄存器操作一样要考虑到AD527x系列数字电位器对象的类型。在使用SPI接口的对象类型种需要考虑片选信号的处理。我们设计读寄存器操作如下:

/* 读寄存器操作 */
static void AD527xReadRegister(AD527xObjectType *rx,uint16_t cmd,uint8_t *rData)
{
  uint8_t tData[2];

  if((rx->type==AD5270)||(rx->type==AD5271)) //SPI接口
  {
    rx->ChipSelcet(AD527xCS_ENABLE);
    rx->Delayms(1);
  }

  rx->Transmit(rx,tData,2);

  rx->Receive(rx,rData,2);

  if((rx->type==AD5270)||(rx->type==AD5271)) //SPI接口
  {
    rx->Delayms(1);
    rx->ChipSelcet(AD527xCS_DISABLE);
  }
}

2.2.3、面向命令的操作

我们已经实现了对继存存其的读操作和写操作,但我们并不想通过调用这两个函数并传递命令来实现我们的应用。所以我们将不同的操作命令所要完成的功能封装成函数,在这些函数中调用读写寄存器操作函数来完成。这样使用驱动就变得更为简便。例如我们设计读写RDAC的函数如下:

/* 设置AD527x游标位置 */
void SetRDACForAd527x(AD527xObjectType *rx,uint16_t data)
{
  uint16_t temp=0;
  
  if((rx->type==AD5271)||(rx->type==AD5274)) //256档
  {
    temp=data>255?255:data;
  }
  else if((rx->type==AD5270)||(rx->type==AD5272)) //1024档
  {
    temp=data>1023?1023:data;
  }

  temp=COMMAND_W_RDAC|temp;

  if(((rx->conreg)&0x02)!=0x02)
  {
    SetControlRegister(rx,PROGRAM_RDAC_ENABLE|rx->conreg);
  }

  AD527xWriteRegister(rx,temp);
}

/* 读取RDAC游标寄存器的内容 */
uint16_t ReadRDACFromAd527x(AD527xObjectType *rx)
{
  uint8_t rData[2];

  uint16_t cmd=COMMAND_R_RDAC;

  AD527xReadRegister(rx,cmd,rData);

  rx->rdac=(rData[0]<<8)+rData[1];
  
  return rx->rdac;
}

3、驱动的使用

我们已经实现了AD527x系列数字电位器的驱动。接下来我们来考虑如何使用这一驱动实现我们的应用。

3.1、声明并初始化对象

我们已经定义了AD527x系列数字电位器对象类型。所以我们先要使用对象类型声明一个AD527x系列数字电位器对象变量。形式如下:

AD527xObjectType ad527x;

当然,这里定义的这个对象变量还不能直接使用。我们需要使用初始化函数对这个对象变量进行初始化。初始化函数前面已经说过,传递的参数皆是与对象变量相关的。初始化函数的参数如下:

AD527xObjectType *rx,待初始化的对象变量。

uint8_t address,采用I2C接口通讯是的设备地址。

AD527xType type,对象的设备类型。

AD527xReceive recieve,数据接收函数指针。

AD527xTransmit transmit,数据发送函数指针。

AD527xChipSelcet cs,使用SPI接口通讯时,片选操作函数指针。

AD527xDelayms delayms,毫秒延时操作函数指针。

对于这些参数,对象变量我们已经定义了。对象类型根据实际器件输入即可。而设备地址在使用I2C接口时按要求输入即可,如果是SPI接口则任意uint8_t类型的值均可。最主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型图下:

/*定义片选信号函数指针类型*/
typedef void (*AD527xChipSelcet)(AD527xCSType en);

/*定义接收数据函数指针类型*/
typedef void (*AD527xReceive)(struct AD527xObject *rx,uint8_t *rData,uint16_t rSize);

/*定义发送数据函数指针类型*/
typedef void (*AD527xTransmit)(struct AD527xObject *rx,uint8_t *wData,uint16_t wSize);

/*定义ms延时操作指针*/
typedef void (*AD527xDelayms)(volatile uint32_t nTime);

对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。片选操作函数只在使用SPI接口是需要定义,否则可以传入NULL即可。具体函数定义如下:

/*定义片选信号函数*/
void AD527xCS(AD527xCSType en)
{
  if(AD527xCS_ENABLE==en)
  {
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_RESET);
  }
  else
  {
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET);
  }
}

/*定义接收数据函数*/
void AD527xReceiveData(struct AD527xObject *rx,uint8_t *rData,uint16_t rSize)
{
  HAL_SPI_Receive (&hspi, rData, rSize, 1000);
}

/*定义发送数据函数*/
void AD527xTransmitData(struct AD527xObject *rx,uint8_t *wData,uint16_t wSize)
{
  HAL_SPI_Transmit (&hspi, wData, wSize, 1000);
}

对于延时函数我们可以采用各种方法实现。我们曹勇的STM32平台和HAL库则可以直接使用HAL_Delay()函数。于是我们可以调用初始化函数如下:

AD527xInitialization(&ad527x,0x00,AD5270,AD527xReceiveData,AD527xTransmitData,AD527xCS,HAL_Delay);

这是使用SPI接口器件的初始化操作,使用I2C接口的初始化操作类似次操作即可。

3.2、基于对象进行操作

我们已经定义了对象变量并对其进行了初始化。接下来我们就要看看如何操作对象得到我们想要的结果。

我们在前面已经根据操作命令做了封装,所以我们需要什么养的功能只需要调用相应的函数就可以了。如我们想要设置RDAC为最大值则:

SetValueToAd5270(&ad527x,1023);

其中第1个参数为要操作的对象指针,第2个参数为要设置的游标位置值。

4、应用总结

我们已经实现AD527x系列数字电位器的驱动及基于此驱动的应用,得到了与我们预期一致的结果,说明驱动的设计时符合需求的。

在使用驱动时需注意,采用I2C接口的器件需要考虑设备地址的问题。设备地址由ADDR引脚的状态决定。由三种取值如下:

在使用驱动时需注意,采用SPI接口的器件需要考虑片选操作的问题。如果片选信号是通过硬件电路来实现的,我们在初始化时给其传递NULL值。如果是软件操作片选则传递我们编写的片选操作函数。

源码地址GitHub:https://github.com/foxclever/ExPeriphDriver

欢迎关注:

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章