外设驱动库开发笔记40:AT25xxx外部存储器驱动

  我们在前面开发过AT24CXX系列EEPROM存储器,它使用的是I2C接口。不过有时候我们也会使用SPI接口的EEPROM存储器。在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计、实现及使用。

1、功能概述

  AT25XXX系列EEPROM存储器采用SPI接口,因其操作简单且性价比高,常用于数据保存。出于开发面向AT25XXX系列EEPROM存储器操作的驱动目标,我们先来了解一下AT25XXX系列EEPROM存储器的基本情况。

1.1、硬件描述

  AT25XXX系列EEPROM存储器拥有从1K到2M的多种容量。AT25XXX系列EEPROM存储器采用SPI通讯接口。尽管容量跨度很大,但都采用相同的封装形式。具体的引脚排布及定义如下:

  为了更好地理解AT25XXX系列EEPROM存储器的操作过程,我们对CS、WP以及HOLD引脚做一个简单说明。
  首先来看一看CS引脚,CS为芯片选择引脚,低电平有效,AT25XXX系列EEPROM存储器被选中。当设备未被选中时,串行数据输入(SI)引脚将不接受数据,串行输出(SO)引脚将保持高阻抗状态。为了确保稳定的操作,CS引脚应在电源启动时跟随VCC。因此建议使用小于或等于10 kΩ的上拉电阻器连接到VCC。在电源启动后,要实现对AT25XXX系列EEPROM存储器的任何操作,都需要先将CS下拉到低电平。
  接下来我们看一看WP引脚,WP引脚为AT25XXX系列EEPROM存储器的写保护引脚。当写保护(WP)引脚保持高电位时,AT25XXX系列EEPROM存储器允许正常的读/写操作。当写保护(WP)引脚为低电平时,WPEN位设置为逻辑“1”时,所有对状态寄存器的写操作都被禁止。但如果内部写周期已经启动,那么写保护(WP)引脚变为低电平对状态寄存器的任何写操作都没有影响。当状态寄存器中的WPEN位设置为逻辑“0”时,写保护(WP)引脚的功能被屏蔽。
  最后我们来看一看HOLD引脚,暂停串行输入(HOLD)引脚低电平有效。暂停串行输入(HOLD)引脚与芯片选择(CS)引脚一起使用来暂停AT25XXX系列EEPROM存储器。当设备被选中,一个串行序列正在进行中,HOLD可以用来暂停与主设备的串行通信,而不需要重新设置串行序列。此时,串行数据输入(SI)引脚的输入将被忽略,而串行数据输出(SO)引脚将处于高阻抗状态。

1.2、通讯接口

  AT25XXX系列EEPROM存储器采用SPI通讯接口。AT25XXX系列EEPROM存储器由主机控制器(通常称为SPI主机)发送的一组指令控制。与AT25XXX系列EEPROM存储器的通信必须由SPI主设备(如微控制器)发起。SPI主设备必须在串行数据时钟(SCK)引脚上为AT25XXX系列EEPROM存储器生成串行时钟。AT25XXX系列EEPROM存储器总是作为一个从属操作,因为SCK总是一个输入。主机与AT25XXX系列EEPROM存储器通讯的拓扑图如下所示:

  SPI主机通过SPI总线与AT25XXX系列EEPROM存储器通信,SPI总线由四条信号线组成:芯片选择(CS)、串行数据时钟(SCK)、串行数据输入(SI)和串行数据输出(SO)。SPI协议定义了总共四种操作模式(模式0、模式1、模式2或模式3),每种模式在SCK极性和相位以及极性和相位如何控制SPI总线上的数据流方面有所不同。AT25XXX系列EEPROM存储器支持两种最常见的模式,SPI模式0和3。

1.3、命令操作

  AT25XXX系列EEPROM存储器被设计成直接与同步串行外围接口(SPI)接口。AT25XXX系列EEPROM存储器使用一个8位指令寄存器。所有的指令、地址和数据首先由高位开始传送,然后由高到低依次进行。指令列表及其操作代码如下:

  从上表我们知道,除了操作存储区域外,还可以操作状态寄存器。AT25XXX系列EEPROM存储器包括一个8位状态寄存器。状态寄存器位调节设备的各种特性。这些位可以通过指令进行更改。具体的结构如下:

  状态寄存器除了反应当前的状态外,实际上有些位还有配置功能。关于致谢为的属性及具体含义如下:

  通过写状态寄存器(WRSR)指令可以配置写保护使能和写保护的区域。块写保护位(BP1、BP0)决定了存储阵列的写保护区域。两位决定了阵列保护的四个级别,分别是:没有一个内存阵列被保护;上四分之一地址范围内存阵列被保护;上半部分地址范围内存阵列被保护;所有的内存阵列都是写保护的,这意味着所有的地址位都是只读的。块写保护级别和相应的状态寄存器控制位关系如下:

  而写保护使能 (WPEN)位用于启用或禁用写保护 (WP) 引脚。当WPEN位设置为逻辑“0”时,写入EEPROM数组的能力取决于块写保护(BP1、BP0)位的值。写入状态寄存器的权限是由WEL位控制的。当WPEN位设置为逻辑“1”时,状态寄存器是只读的。当WP引脚低且WPEN位设置为逻辑“1”时,硬件写保护就启用了。当设备被硬件写保护时,对状态寄存器的写操作,包括块写保护、WEL和WPEN位,以及对块写保护位所选择的内存阵列中的段的写操作被禁用。当启用硬件写保护时,只允许对未受块保护的内存段进行写。当WP引脚为高电平或WPEN位逻辑为“0”时,硬件写保护被禁用。当硬件写保护被禁用时,只允许对未被块保护的内存段进行写。当WPEN位被硬件写保护时,只要WP引脚保持低,它就不能被设置回逻辑“0”。写保护的关系如下所示:

  AT25XXX系列EEPROM存储器拥有从1K到2M的不同容量,寻址范围的不同所需的地址位数也不相同。地址位数根据容量从7位到18位不等,分别对应1到3个字节。具体的容量与地址位关系如下:

  需要注意的是,4K(512x8)容量的AT25XXX系列EEPROM存储器需要9为地址,但在实际操作时只用了1个字节来装载地址,最高位(第9位)地址借用了操作码的第4位来传送。

2、驱动设计与实现

  我们已经了解了AT25XXX存储器的基本功能及读写方式,接下来我们将开发操作AT25XXX系列EEPROM存储器的驱动程序。

2.1、对象定义

  在使用一个对象之前我们需要获得一个对象。同样的我们想要AT25XXX系列EEPROM存储器就需要先定义AT25XXX系列EEPROM存储器的对象。

2.1.1、对象的抽象

  我们要得到AT25XXX系列EEPROM存储器对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下AT25XXX系列EEPROM存储器的对象。
  先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑AT25XXX系列EEPROM存储器对象属性。首先AT25XXX系列EEPROM存储器有多种型号,不同型号在容量、地址位数等方面都有较大差异。为了区别不同类型的AT25XXX系列EEPROM存储器,我们将类型作为对象的属性。另外每一个AT25XXX对象都有一个状态寄存器,它标识了AT25XXX对象的当前状态,所以我们也将它作为对象的属性。
  接着我们还需要考虑AT25XXX系列EEPROM存储器对象的操作问题。我们总是要对AT25XXX对象进行数据读写,但读写操作使用SPI接口依赖于具体的硬件平台,所以我们将数据读写作为对象的操作。片选信号、写保护以及hold信号均依赖于具体的硬件定义来实现,所以我们将其作为对象的操作。还有用于时序控制的延时,其也要根据具体的软硬件平台来实现,所以我们也将其定义为对象的操作。
  根据上述我们对AT25XXX系列EEPROM存储器的分析,我们可以定义AT25XXX系列EEPROM存储器的对象类型如下:

/*定义AT25XXX对象类型*/
typedef struct At25Object {uint8_t status;				//状态寄存器At25ModeType mode;		//设备类型void (*Read)(uint8_t *rData,uint16_t rSize);       //读数据操作指针void (*Write)(uint8_t *wData,uint16_t wSize);    //写数据操作指针void (*Delayms)(volatile uint32_t nTime);       //毫秒延时操作指针void (*ChipSelect)(AT25xxxCSType cs);	//使用SPI接口时,片选操作void (*WP)(AT25WPType wp);						//写保护操作void (*Hold)(AT25HoldType hold);			//保持信号
}At25ObjectType;

2.1.2、对象初始化

  我们知道,一个对象仅作声明是不能使用的,我们需要先对其进行初始化,所以这里我们来考虑AT25XXX系列EEPROM存储器对象的初始化函数。一般来说,初始化函数需要处理几个方面的问题。一是检查输入参数是否合理;二是为对象的属性赋初值;三是对对象作必要的初始化配置。据此我们设计AT25XXX系列EEPROM存储器对象的初始化函数如下:

/* 初始化AT25XXX对象 */
void At25xxxInitialization(At25ObjectType *at,	//AT25XXX对象实体At25ModeType mode,		//AT25XXX对象类型AT25Read read,				//读AT25XXX对象操作指针AT25Write write,			//写AT25XXX对象操作指针AT25Delayms delayms,		//延时操作指针AT25ChipSelect cs			//片选操作函数指针)
{if((at==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL)){return;}at->Read=read;at->Write=write;at->Delayms=delayms;if(cs!=NULL){at->ChipSelect=cs;}else{at->ChipSelect=AT25ChipSelectDefault;}if(mode>=AT25Number){return;}at->mode=mode;if(mode<AT25080B){at->memAddLength=AT258BitMemAdd;}else if(mode<AT25M01){at->memAddLength=AT2516BitMemAdd;}else{at->memAddLength=AT2524BitMemAdd;}ReadStatusForAT25xxx(at);//写允许SetWriteEnableLatchForAT25xxx(at);uint8_t cmd;//使能写保护,保护全部区域cmd=at->status|AT25_WPEN|AT25_BPALL;WriteStatusForAT25xx(at,cmd);ReadStatusForAT25xxx(at);
}

2.2、对象操作

  我们已经完成了AT25XXX系列EEPROM存储器对象类型的定义和对象初始化函数的设计。但我们的主要目标是获取对象的信息,接下来我们还要实现面向AT25XXX系列EEPROM存储器的各类操作。

2.2.1、读数据操作

  读取AT25XXX系列EEPROM存储器需要先将CS线拉低以选择设备,尔后发送READ(0x03)指令,在后发送要读的寄存器地址。一旦接收完寄存器地址,后续的信号将被忽略。然后返回指定地址的数据。读AT25XXX系列EEPROM存储器数据的操作时序如下:

  根据上述时序图,我们可以编写读AT25XXX系列EEPROM存储器数据的程序如下:

/*从AT25xxx读取数据*/
void ReadDatasFromAT25xxx(At25ObjectType *at,uint32_t regAddress,uint8_t *rData,uint16_t rSize)
{uint8_t data[4];uint16_t index=0;uint8_t temp;uint16_t size=0;data[index++]=AT25_READ;if(at->memAddLength==AT258BitMemAdd){data[index++]=(uint8_t)regAddress;if(at->mode==AT25040B){temp=(uint8_t)(regAddress>>8);data[0]|=((temp&0x01)<<3);}}else if(at->memAddLength==AT2516BitMemAdd){data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}else{data[index++]=(uint8_t)(regAddress>>16);data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}temp=(uint8_t)(regAddress&regAddMask[at->mode]);if((rSize<=pageBytes[at->mode])&&(rSize<=(pageBytes[at->mode]-temp))){size=rSize;}else{size=pageBytes[at->mode]-temp;}at->ChipSelect(AT25CS_Enable);at->Write(data,index);at->Delayms(1);at->Read(rData,size);at->ChipSelect(AT25CS_Disable);
}

  如果只需要读取一个字节,那么在读取一个字节后CS信号需在读取后恢复高电平。如果想读取多个字节,那么CS信号必须持续低电平,在存储器内部字节地址将自动递增,数据将继续移位。当到达最高地址时,地址计数器将滚动到最低阶地址,从而允许在一个连续的读取循环中读取整个内存,而不管起始地址是什么。

2.2.2、写数据操作

  写AT25XXX系列EEPROM存储器,必须执行两条单独的指令。首先,必须通过写使能(WREN)指令使设备能够写。然后,可以执行写序列。如果设备没有启用写(WREN),设备将忽略写指令。在内部写周期中,除了RDSR指令外,所有命令都将被忽略。写AT25XXX系列EEPROM存储器的时序如下:

  根据上述时序图,我们可以编写写AT25XXX系列EEPROM存储器数据的程序如下:

/*向AT25xxx写入数据*/
void WriteDatasToAT25xxx(At25ObjectType *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize)
{uint8_t data[wSize+4];uint16_t index=0;uint8_t temp;uint16_t size=0;data[index++]=AT25_WRITE;if(at->memAddLength==AT258BitMemAdd){data[index++]=(uint8_t)regAddress;if(at->mode==AT25040B){temp=(uint8_t)(regAddress>>8);data[0]|=((temp&0x01)<<3);}}else if(at->memAddLength==AT2516BitMemAdd){data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}else{data[index++]=(uint8_t)(regAddress>>16);data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}temp=(uint8_t)(regAddress&regAddMask[at->mode]);if((wSize<=pageBytes[at->mode])&&(wSize<=(pageBytes[at->mode]-temp))){size=wSize;}else{size=pageBytes[at->mode]-temp;}for(int i;i<size;i++){data[index++]=wData[i];}if(((at->status)&0x02)!=0x02){SetWriteEnableLatchForAT25xxx(at);}if(((at->status)&0x0C)!=0x00){WriteStatusForAT25xx(at,at->status|AT25_BPNONE);}at->ChipSelect(AT25CS_Enable);at->Write(data,index);at->ChipSelect(AT25CS_Disable);WriteStatusForAT25xx(at,at->status|AT25_BPALL);
}

2.2.3、状态寄存器操作

  前面我们已经详细描述过状态寄存器的格式以及每一位的定义。有一些位是只读的,有一些位是可读写的,记下来我们实现针对状态寄存器中的操作。

(1)、读状态寄存器

  读状态寄存器(RDSR)指令提供对状态寄存器的访问。RDSR指令可以确定设备的状态以及块写保护(BP1, BP0)位表示所使用的内存阵列保护的范围。读取状态寄存器的方法是拉低CS信号,然后发送0x05操作码。操作码完成后,设备将返回8位状态寄存器值。具体时序如下:

  根据上述时序图,我们可以编写读AT25XXX系列EEPROM存储器状态寄存器的程序如下:

/*读AT25xxx状态寄存器*/
void ReadStatusForAT25xxx(At25ObjectType *at)
{uint8_t opCode=AT25_RDSR;uint8_t status;at->ChipSelect(AT25CS_Enable);at->Write(&opCode,1);at->Delayms(1);at->Read(&status,1);at->ChipSelect(AT25CS_Enable);at->status=status;
}

(2)、写状态寄存器

  写状态寄存器(WRSR)指令使SPI主机能够更改状态寄存器的选定位。在WRSR指令开始之前,必须执行一条WREN指令,将WEL位设置为逻辑“1”。WREN指令完成后,可以执行WRSR指令。在WRSR指令之后,AT25XXX系列EEPROM存储器将不会响应除RDSR以外的命令,直到自动计时的内部写周期完成。写周期结束后,状态寄存器中的WEL位复位为逻辑“0”。具体的时序图如下:

  根据上述时序图,我们可以编写写AT25XXX系列EEPROM存储器状态寄存器的程序如下:

/*写AT25xxx状态寄存器*/
void WriteStatusForAT25xx(At25ObjectType *at,uint8_t cmd)
{uint8_t data[2];data[0]=AT25_WRSR;data[1]=cmd;if(((at->status)&0x02)!=0x02){SetWriteEnableLatchForAT25xxx(at);}if((((at->status)&AT25_WPEN)!=AT25_WPEN)&&(at->WP!=NULL)){at->WP(AT25WP_Disable);}at->ChipSelect(AT25CS_Enable);at->Write(data,2);at->ChipSelect(AT25CS_Disable);ReadStatusForAT25xxx(at);if(at->WP!=NULL){at->WP(AT25WP_Enable);}
}

  WRSR指令对状态寄存器的第6位、第5位、第4位、第1位和第0位没有影响。只有第7位、第3位和第2位可以通过WRSR指令进行更改。这些可修改的位是写保护使能(WPEN)和块保护(BP1, BP0)位。这三个位元是非易失性位元,具有与常规EEPROM单元相同的特性和功能。当电源从设备中移除时,它们的值被保留。

2.2.4、写操作使能与失能操作

  通过写使能(WREN)指令和写失能(WRDI)指令实现对状态寄存器和EEPROM阵列的写操作的启用和禁用。这些功能改变了状态寄存器中WEL位的状态。

(1)写操作启用

  状态寄存器的写能门闩(WEL)位必须在每个写状态寄存器(WRSR)和写入内存阵列(Write)指令之前设置为逻辑“1”。这是通过向AT25XXX系列EEPROM存储器发送一条WREN(0x06)指令来完成的。首先,CS引脚被拉低以选择设备,然后发送WREN指令。然后CS信号被拉高,并将状态寄存器中的WEL位更新为逻辑“1”。具体的操作时序如下:

  根据上述时序图,我们可以编写AT25XXX系列EEPROM存储器操作启用的程序如下:

/* AT25XXX设置写使能所存器*/
void SetWriteEnableLatchForAT25xxx(At25ObjectType *at)
{uint8_t opCode=AT25_WREN;at->ChipSelect(AT25CS_Enable);at->Write(&opCode,1);at->ChipSelect(AT25CS_Enable);ReadStatusForAT25xxx(at);
}

(2)、写操作禁用

  为了防止误写,写禁用(WRDI)指令(0x04)通过将WEL位设置为逻辑“0”来禁用所有编程模式。WRDI指令与WP引脚的状态无关。具体的操作时序如下图所示:

  根据上述时序图,我们可以编写AT25XXX系列EEPROM存储器操作禁用的程序如下:

/* AT25XXX复位写使能所存器*/
void ResetWriteEnableLatchForAT25xxx(At25ObjectType *at)
{uint8_t opCode=AT25_WRDI;at->ChipSelect(AT25CS_Enable);at->Write(&opCode,1);at->ChipSelect(AT25CS_Enable);ReadStatusForAT25xxx(at);
}

3、驱动的使用

  在上一节我们设计并实现了AT25XXX系列EEPROM存储器的驱动程序,而这一节我们将设计一个简单的应用来验证这一驱动程序。

3.1、声明并初始化对象

  使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的AT25XXX系列EEPROM存储器对象类型声明一个AT25XXX系列EEPROM存储器对象变量,具体操作格式如下:
  At25ObjectType at25;
  声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:

At25ObjectType *at,AT25XXX对象实体
At25ModeType mode,AT25XXX对象类型
AT25Read read,读AT25XXX对象操作指针
AT25Write write,写AT25XXX对象操作指针
AT25Delayms delayms,延时操作指针
AT25ChipSelect cs,片选操作函数指针

  对于这些参数,对象变量我们已经定义了。而对象类型为枚举,根据实际使用的AT25XXX系列EEPROM存储器来选择就好了。主要的是我们需要定义几个函数,并将函数指针作为参数。这几个函数的类型如下:

/* 定义读数据操作函数指针类型 */
typedef void (*AT25Read)(uint8_t *rData,uint16_t rSize);
/* 定义写数据操作函数指针类型 */
typedef void (*AT25Write)(uint8_t *wData,uint16_t wSize);
/* 定义延时操作函数指针类型 */
typedef void (*AT25Delayms)(volatile uint32_t nTime);
/* 定义使用SPI接口时,片选操作函数指针类型 */
typedef	void (*AT25ChipSelect)(AT25xxxCSType cs);

  对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。片选操作函数用于多设备需要软件操作时,如采用硬件片选可以传入NULL即可。具体函数定义如下:

/*读AT25寄存器值*/
static void ReadDataFromAT25(uint8_t *rData,uint16_t rSize)
{HAL_SPI_Receive (&at25hspi,rData,rSize,1000);
}/*写AT25寄存器值*/
static void WriteDataToAT25(uint8_t *wData,uint16_t wSize)
{HAL_SPI_Transmit (&at25hspi,wData,wSize,1000);
}/*片选操作*/
void ChipSelectForAT25(AT25xxxCSType cs)
{if(cs==AT25CS_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);}
}

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

At25xxxInitialization(&at25,		//AT25XXX对象实体AT25M01,	//AT25XXX对象类型ReadDataFromAT25,		//读AT25XXX对象操作指针WriteDataToAT25,		//写AT25XXX对象操作指针HAL_Delay,		//延时操作指针ChipSelectForAT25		//片选操作函数);

3.2、基于对象进行操作

  我们定义了对象变量并使用初始化函数给其作了初始化。接着我们就来考虑操作这一对象获取我们想要的数据。我们在驱动中已经将获取数据并转换为转换值的比例值,接下来我们使用这一驱动开发我们的应用实例。

/*AT25XXX数据读写操作*/
void AT25ReadWriteData(void)
{uint16_t regAddress=0x02;uint8_t readByte;uint8_t writeByte=0x0A;uint8_t rData[2];uint16_t rSize=2;uint8_t wData[]={0x5A,0xA5};uint16_t wSize=2;/*从AT25XXX读取单个字节,从随机地址读取*/readByte=ReadByteFromAT25xxx(&at25,regAddress);/*向AT25XXX写入单个字节*/WriteByteToAT25xxx(&at25,regAddress,writeByte);/*从AT25XXX读取多个字节,从指定地址最多到所在页的结尾*/ReadBytesFromAT25xxx(&at25,regAddress,rData,rSize);/*向AT25XXX写入多个字节,从指定地址最多到所在页的结尾*/WriteBytesToAT25xxx(&at25,regAddress,wData,wSize);
}

4、应用总结

  在本片中我们讨论并设计了AT25XXX系列EEPROM存储器的驱动程序,并据此设计了一个简单的验证应用。无论是写数据还是读数据均可顺利执行,说明我们的驱动设计是正确的。
  需要注意的是,4K(512x8)容量的AT25XXX系列EEPROM存储器需要9为地址,但在实际操作时只用了1个字节来装载地址,最高位(第9位)地址借用了操作码的第4位来传送。
  在使用驱动时需注意,采用SPI接口的器件需要考虑片选操作的问题。如果片选信号是通过硬件电路来实现的,我们在初始化时给其传递NULL值。如果是软件操作片选则传递我们编写的片选操作函数。
  在这一驱动设计的过程中我们并未验证读写数据的正确性,事实上如果是比较重要的数据我们可以为其添加验证,如CRC验证等。

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499303.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

闪存中的键值对:无文件系统 minINI

许多嵌入式系统应用需要以持久的方式存储某种数据&#xff1a;校准值、设置或日志信息。对于较少的数据量&#xff0c;使用外部存储器或文件系统是一种过度大材小用。在许多系统中&#xff0c;我使用minINI以“ini-file”的方式存储键值解析&#xff0c;但它需要使用某种文件系…

外设驱动库开发笔记41:ADS1256 ADC驱动

我们经常会碰到多通道AD采集的需求&#xff0c;有时候甚至需要高精度的ADC器件。本篇我们将来设计并实现ADS1256模数转换器的驱动。并简单讨论该驱动使用方式。 1、功能概述 ADS1256是TI公司推出的一款低噪声高分辨率的24位Sigma-Delta(E-v)模数转换器(ADC)。E-vADC与传统的逐…

PID参数自整定库之一:继电反馈整定算法

在前述的篇章中&#xff0c;我们实现了PID控制器并在后续对其进行了改进。但作为经典PID控制器还存在PID参数整定的问题。通常我们可以采取人工整定的办法&#xff0c;但人工整定涉及到比较专业的知识&#xff0c;而且找到合适的参数本身也不是一件容易的事&#xff0c;所以人们…

外设驱动库开发笔记42:DAC8552 DAC驱动

模拟信号输出是经常会遇到的应用需求&#xff0c;解决的办法应多种&#xff0c;但我们使用最多的还是数模转换。对于不同的数模转换器我们需要为其编写适用的驱动程序&#xff0c;在这一篇中我们就来考虑如何实现DAC8552高精度模数转换器的驱动程序。 1、功能概述 该DAC8552是…

软件设计开发笔记2:基于QT设计串口调试工具

串口通信是我们经常会遇到的问题。很多时候当我们设计一个串口应用时&#xff0c;我们希望有一个简便的、可视的方式来验证它。这一篇中我们就来基于QT设计一个串口调试工具。 1、概述 在开始软件设计之前&#xff0c;我们来简略地分析一下这样一个小软件其要包含的主要内容有…

外设驱动库开发笔记43:GPIO模拟SPI驱动

SPI总线是我们常用的串行设备接口&#xff0c;一般情况下我们都会适应硬件SPI接口&#xff0c;但有些时候当硬件端口不足时&#xff0c;我们也希望可以使用软件来模拟SPI硬件接口&#xff0c;特别是要求不是很高的时候。在这一篇中我们将来讨论如何使用GPIO和软件来模拟SPI通讯…

外设驱动库开发笔记44:DDC114 ADC驱动

在产品设计过程中&#xff0c;很多时候都会用到ADC器件&#xff0c;而在一些特殊场合还需要一些特别的ADC器件。我们在这篇中将讨论常用于医疗器件方面的&#xff0c;DDC114这款电流输入ADC&#xff0c;并为其设计一个驱动程序。 1、功能概述 模数转换器DDC114是一款电流输入型…

PID控制器改进笔记之七:改进PID控制器之防超调设定

我们已经设计了PID控制器&#xff0c;并根据实际使用的情况对器进行了诸多的改进。在这一篇中我们将讨论如何改进PID控制器超调的问题。 1、问题提出 在前面的文章中&#xff0c;我们曾推导过增量式PID控制器的公式&#xff0c;并且对其进行了离散化以适用于程序实现&#xff…

软件设计开发笔记3:基于QT的Modbus RTU主站

Modbus是一种常见的工业系统通讯协议。在我们的设计开发工作中经常使用到它。在这一篇中我们将简单实现一个基于QT的Modbus RTU主站上位工具。 1、概述 Modbus RTU主站应用很常见&#xff0c;有一些是通用的&#xff0c;有一些是专用的。而这里我们希望实现一个主要针对我们的…

外设驱动库开发笔记45:MS4515DO压力传感器驱动

很多时候我们需要检测流量和压力这些参数&#xff0c;比如我们要检测大气压&#xff0c;或者通过测量差压来获得输送流体的流量等&#xff0c;都需要用到压力传感器。这一篇我们就来讨论MS4515DO压力传感器的数据获取。 1、功能概述 MS4515DO是TE公司推出的一款基于PCB安装的小…

外设驱动库开发笔记48:MCP4725单通道DAC驱动

在产品设计过程中&#xff0c;我们经常会遇到数模转换的应用需求。在本篇种我们就来讨论一下MCP4725单通道数模转换器的驱动设计与实现。 1、功能概述 MCP4725是一个低功耗&#xff0c;高精度&#xff0c;单通道&#xff0c;12位缓冲电压输出数字到模拟转换器(DAC)与非易失性存…

如何确保不使用动态内存

在许多嵌入式应用程序中&#xff0c;内存分配必须是静态的&#xff0c;而不是动态的。意味着在应用程序中不应使用对malloc()或free()等内容的调用&#xff0c;因为它们可能会在运行时失败&#xff08;内存不足、堆碎片&#xff09;。 但是&#xff0c;当与第三方库甚至 C/C 标…

go 单元测试 testing 打印输出_2020,你需掌握go 单元测试进阶篇

本文说明go语言自带的测试框架未提供或者未方便地提供的测试方案&#xff0c;主要是用于解决写单元测试中比较头痛的依赖问题。也就是伪造模式&#xff0c;经典的伪造模式有桩对象(stub),模拟对象(mock)和伪对象(fake)。比较幸运的是&#xff0c;社区有丰富的第三方测试框架支持…

一文读懂Git工作流

Git是目前最流行的代码管理工具&#xff0c;相信大家也都是在用Git来管理自己团队的源代码。 团队一般为了规范开发&#xff0c;保持良好的代码提交记录以及维护 Git 分支结构清晰&#xff0c;方便后续维护等&#xff0c;都会迫切需要一个比较规范的 Git 工作流。 本文就是在…

xbox360fsd更新游戏封面_游戏类短视频创作指南

一&#xff0e;起步阶段1.内容发布垂直&#xff0c;整体风格一致&#xff0c;选定一个品类的游戏内容风格持续更新注意&#xff1a;冷启动时期不要频繁更换游戏类型2.账号IP化 根据自身风格特色打造独特的风格账号。有利延长账号生命周期&#xff0c;提升粉丝转化率。搞笑、中二…

开发者们都在关注的网站

开发者们都在关注的网站 &#x1f609; 综合类&#xff08;5个&#xff09; 1、GitHub 全球最大的编程开源社区&#xff0c;很多优秀的开源项目都在上边&#xff0c;不知道这个都不要说自己是程序员&#x1f602; 访问地址&#xff1a;https://github.com 2、CSDN 全球最大中…

ios framework 调用第三方 framework_Python基础:标准库和常用的第三方库

Python的标准库有&#xff1a;名称作用datetime为日期和时间处理同时提供了简单和复杂的方法。zlib直接支持通用的数据打包和压缩格式&#xff1a;zlib&#xff0c;gzip&#xff0c;bz2&#xff0c;zipfile&#xff0c;以及 tarfile。random提供了生成随机数的工具。math为浮点…

作图神器ProcessOn - 免费好用

因工作需要&#xff0c;我经常需要花一些流程图&#xff0c;时序图&#xff0c;架构图什么的&#xff0c;之前使用的Windows系统&#xff0c;大部分情况下就用的Visio来画图。后来为了工作方便&#xff0c;换成了Mac电脑&#xff0c;结果发现Mac上没有Visio&#xff0c;然后就在…

三电平igbt死区时间计算_基于大功率三电平IGBT模块并联的参考设计

当前的可再生能源行业中&#xff0c;光伏和风力发电均面临着补贴逐步退坡&#xff0c;平价上网时代即将到来的挑战。为应对这一挑战&#xff0c;光伏逆变器和风力变流器厂家研发的新品单机功率越来越高&#xff0c;以取得更低的单位功率成本。市场上1.5MW的集中式光伏逆变器和3…

手把手教你搭建开发环境之Java开发

大家好呀&#xff0c;从今天开始&#xff0c;我们的手把手系列教程就正式开始啦。 如果你觉得本文对你有一些帮助&#xff0c;欢迎大家关注、点赞、分享给需要的小伙伴们&#xff0c;谢谢大家啦。 前言 Java虽然是一个比较老的语言&#xff0c;但到现在依然充满了活力&#x…