外设驱动库开发笔记25:FM25xxx FRAM存储器驱动

在我们的项目中,时常会有参数或数据需要保存。铁电存储器的优良性能和操作方便常常被我们选用。FM25xxx FRAM存储器就是我们经常使用到的一系列铁电存储器,这一我们将讨论FM25xxx FRAM存储器的驱动设计、实现及使用。

1、功能概述

FM25xxx FRAM存储器是非易失性存储器,采用了先进的铁电存储。铁电随机存取存储器或F-RAM是非易失性的,其读写操作与RAM类似。它提供了151年的可靠数据保留,同时消除了由串行闪存、EEPROM和其他非易失性存储器引起的复杂性、开销和系统级可靠性问题。

1.1、硬件描述

FM25xxx系列FRAM存储器拥有从4K1M的各种容量。虽然不同型号的FM25xxx系列FRAM存储器内部存储矩阵存在差异,但都采用了相同的封装和引脚排布。具体的引脚分布如下图:

FM25xxx FRAM存储器的CS信号低电平有效,就是说CS信号处于低电平时,该设备被选中。当CS信号处于高电平时,设备进入低功耗待机模式,忽略其他输入,并对输出进行测试。当CS信号处于低电平时,设备内部激活SCK信号。CS的下降沿必须在每个操作码之前出现。

FM25xxx FRAM存储器的WP引脚低电平有效。当WPEN设置为“1”时,WP信号低电平可以防止对状态寄存器的写操作。这很重要,因为其他写保护特性是通过状态寄存器控制的。如果不使用此引脚,则必须将其连接到VDD。在FM25040中,WP引脚可以阻止对部件的所有写操作。

当主机CPU必须中断另一个任务的内存操作时,使用HOLD引脚。HOLD引脚处于低电平时,当前操作暂停。设备忽略SCKCS上的任何转换。所有等待的转换必须在SCK低的时候发生。如果不使用此引脚,则必须将其连接到VDD

FM25xxx FRAM存储器与串行闪存和EEPROM不同,以总线速度执行写操作,不存在写延迟。数据在每个字节成功传输到设备后立即写入内存数组。下一个总线周期可在不需要进行数据轮询的情况下开始。此外,与其他非易失性存储器相比,FM25xxx FRAM存储器具有较强的写持久性。FM25xxx FRAM存储器能够支持1014个读/写周期,或比EEPROM1亿倍的写周期。

这些功能使FM25xxx FRAM存储器非常适合需要频繁或快速写入的非易失性内存应用程序。从数据采集(写入周期的数量可能至关重要)到要求工业控制(串行闪存或EEPROM的长写入时间可能导致数据丢失)。FM25xxx FRAM存储器采用高速SPI总线,提高了F-RAM技术的高速写入能力。

1.2、通讯接口

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

FM25xxx FRAM存储器是一个SPI从设备,运行速度高达20 MHz。这种高速串行总线为SPI主机提供高性能的串行通信。许多常见的微控制器有硬件SPI端口,允许直接接口。使用普通的端口引脚来模拟端口是非常简单的,因为微控制器不需要。FM25xxx FRAM存储器支持SPI模式00 0)和SPI模式31 1)。

1.3、操作模式

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

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

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

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

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

FM25xxx FRAM存储器拥有从4K1M的不同容量,寻址范围的不同所需的地址为数据不相同。地址位数根据容量从8位到17位不等,分别对应13个字节。具体的容量与地址位关系如下:

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

2、驱动设计与实现

我们已经了解了FM25xxx FRAM存储器的通讯接口、存储格式等,再次基础上我们将设计并实现FM25xxx FRAM存储器的驱动程序。

2.1、对象的定义

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

2.1.1、对象的抽象

我们要得到FM25xxx FRAM存储器对象,需要先分析其基本特性。一般来说,一个对象至少包含两方面的特性:属性与操作。接下来我们就来从这两个方面思考一下FM25xxx FRAM存储器的对象。

先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑FM25xxx FRAM存储器对象属性。首先每一个FM25xxx FRAM存储器都有一个状态寄存器,该状态寄存器指示了写保护的控制和状态信息,所以我们将其作为对象的属性以标识FM25xxx FRAM存储器的状态。因为不同型号的FM25xxx FRAM存储器拥有不同的存储容量及寻址范围,为了区分不同设备和地址宽度我们将设备类型及数据地址的长度均作为对象的属性。

接着我们还需要考虑FM25xxx FRAM存储器对象的操作问题。我们要对FM25xxx FRAM存储器进行读写,但读写都需要同过具体的SPI接口进行,这依赖于具体的硬件平台,所以我们将针对SPI端口的读写作为对象的操作。FM25xxx FRAM存储器还有一个写保护引脚用于设置内部存储器的写保护问题,有一个片选信号应交用于选中操作设备,有一个Hold引脚用于操作控制,这些引脚的信号改变同样依赖于硬件平台来实现,所以我们也将它们作为对象的操作。在进行相关操作时,我们需要控制时序,则需要使用延时操作,但延时处理总是依赖于具体的软硬件平台,所以我们将延时处理作为对象的操作。

根据上述我们对FM25xxx FRAM存储器的分析,我们可以定义FM25xxx FRAM存储器的对象类型如下:

/* 定义FM25C对象类型 */
typedef struct FM25Object {uint8_t status;                          //状态寄存器FM25ModeType mode;           //设备类型FM25MemAddLengthType memAddLength;           //寄存器地址长度void (*Read)(uint8_t *rData,uint16_t rSize);       //读数据操作指针void (*Write)(uint8_t *wData,uint16_t wSize);    //写数据操作指针void (*WP)(FM25WPType wp);    //写保护操作void (*ChipSelect)(FM25CSType cs);     //片选信号void (*Hold)(FM25HoldType hold);                  //保持信号void (*Delayms)(volatile uint32_t nTime);       //延时操作指针
}FM25ObjectType;

2.1.2、对象初始化

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

/*FM25对象初始化*/
void Fm25cxxInitialization(FM25ObjectType *fram,     //FM25xxx对象实体FM25ModeType mode,    //设备类型Fm25Read read,                //读FM25xxx对象操作指针Fm25Write write,             //写FM25xxx对象操作指针Fm25Delayms delayms,   //延时操作指针Fm25WP wp,                    //写保护操作函数指针Fm25ChipSelect cs,          //片选信号函数指针Fm25Hold hold                 //保持信号操作函数指针)
{if((fram==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL)){return;}fram->Read=read;fram->Write=write;fram->Delayms=delayms;if(cs!=NULL){fram->ChipSelect=cs;}else{fram->ChipSelect=FM25ChipSelectDefault;}if(mode>=FM25Number){return;}fram->mode=mode;if(mode<FM25C160B){fram->memAddLength=FM258BitMemAdd;}else if(mode<FM25V10){fram->memAddLength=FM2516BitMemAdd;}else{fram->memAddLength=FM2524BitMemAdd;}ReadStatusForFM25xxx(fram);//写允许SetWriteEnableLatchForFM25xxx(fram);uint8_t cmd;//使能写保护,保护全部区域cmd=fram->status|FM25_WPEN|FM25_BPALL;WriteStatusForFM25xx(fram,cmd);ReadStatusForFM25xxx(fram);
}

2.2、写使能操作

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

2.2.1、设置写使能锁存器

FM25xxx FRAM存储器在启动后,写操作是被禁用的。发送WREN操作码允许用户为写操作发出后续操作码。这包括写入状态寄存器(WRSR)和写入内存(WRITE)。发送WREN操作码会导致设置内部写启用锁存器。状态寄存器中的标志位WEL表示锁存器的状态。WEL =“1”表示允许写操作。经过WRDIWRSR或写操作后,WEL位将自动清除,这可以防止在没有其他WREN命令的情况下进一步写入状态寄存器或F-RAM存储阵列。WREN命令总线时序如下所示:

根据上述时序图,我们可以设计FM25xxx FRAM存储器设置写使能锁存器的程序如下:

/* 设置写使能所存器*/
void SetWriteEnableLatchForFM25xxx(FM25ObjectType *fram)
{uint8_t opCode=FM25_WREN;fram->ChipSelect(FM25CS_Enable);fram->Write(&opCode,1);fram->ChipSelect(FM25CS_Enable);ReadStatusForFM25xxx(fram);
}

2.2.2、复位写使能锁存器

WRDI命令通过清除写使能锁存器禁用所有写活动。用户可以通过读取状态寄存器中的WEL位并验证WEL是否等于“ 0”来确认写操作是否被禁用。WRDI命令总线时序如下所示:

根据上述时序图,我们可以设计FM25xxx FRAM存储器复位写使能锁存器的程序如下:

/* 复位写使能所存器*/
void ResetWriteEnableLatchForFM25xxx(FM25ObjectType *fram)
{uint8_t opCode=FM25_WRDI;fram->ChipSelect(FM25CS_Enable);fram->Write(&opCode,1);fram->ChipSelect(FM25CS_Enable);ReadStatusForFM25xxx(fram);
}

2.3、操作状态寄存器

FM25Cxx系列存储器的状态寄存器不只是用来指示状态,还用于配置写保护。写状态寄存器受到WELWPENWP三重写保护,具体可见前面的写保护限制图。

2.3.1、读状态寄存器

RDSR命令可以通过总线获取状态寄存器的内容。读取状态寄存器提供有关写保护特性的当前状态的信息。按照RDSR操作码,FM25Cxx系列存储器将返回一个包含状态寄存器内容的字节。RDSR命令的总线时序如下:

根据上述时序图,我们可以设计FM25xxx FRAM存储器读状态寄存器的程序如下:

/*读FM25xxx状态寄存器*/
void ReadStatusForFM25xxx(FM25ObjectType *fram)
{uint8_t opCode=FM25_RDSR;uint8_t status;fram->ChipSelect(FM25CS_Enable);fram->Write(&opCode,1);fram->Delayms(1);fram->Read(&status,1);fram->ChipSelect(FM25CS_Enable);fram->status=status;
}

2.3.2、写状态寄存器

WRSR命令允许SPI总线主写入状态寄存器,并根据需要设置WPENBP0BP1位,从而更改写保护配置。在发出WRSR命令之前,WP引脚必须处于高电平或非活动状态。在发送WRSR命令之前,用户必须发送一个WREN命令来启用写操作。执行WRSR命令是一个写操作,因此清除写启用锁存器。WRSR命令的总线时序如下:

根据上述时序图,我们可以设计FM25xxx FRAM存储器写状态寄存器的程序如下:

/*写FM25xxx状态寄存器*/
void WriteStatusForFM25xx(FM25ObjectType *fram,uint8_t cmd)
{uint8_t data[2];data[0]=FM25_WRSR;data[1]=cmd;if(((fram->status)&0x02)!=0x02){SetWriteEnableLatchForFM25xxx(fram);}if((((fram->status)&FM25_WPEN)!=FM25_WPEN)&&(fram->WP!=NULL)){fram->WP(FM25WP_Disable);}fram->ChipSelect(FM25CS_Enable);fram->Write(data,2);fram->ChipSelect(FM25CS_Disable);ReadStatusForFM25xxx(fram);if(fram->WP!=NULL){fram->WP(FM25WP_Enable);}
}

状态寄存器中的写保护启用位(WPEN)控制硬件写保护(WP)引脚的效果。当WPEN位设置为“0”时,WP引脚的状态将被忽略。当WPEN位设置为“1”时,WP引脚上的一个低电平信号会阻止对状态寄存器的写入。因此,只有当WPEN =“1”WP =“0”时才写保护状态寄存器。

2.4、操作存储数据

FM25xxx FRAM存储器的SPI接口具有很高的时钟频率,突出了F-RAM技术的快速写入能力。与串行闪存和EEPROM不同,FM25xxx FRAM存储器可以以总线速度执行顺序写入,可以执行任意数量的顺序写操作。

2.4.1、写数据操作

FM25xxx FRAM存储器所有对内存的写入都以WREN操作码开始。写入操作码之后是一个存储地址,不同容量因寻址范围不同地址的为数也不相同。后续字节是按顺序写入的数据字节。只要总线主机继续发出时钟并将CS信号保持在低电平,地址就会在内部递增。如果到达最后一个地址,计数器将滚动到0x0000。写数据操作命令的总线时序如下:

根据上述时序图,我们可以设计FM25xxx FRAM存储器写数据存储器的程序如下:

/*向FM25xxx写入数据*/
void WriteBytesToFM25xxx(FM25ObjectType *fram,uint32_t regAddress,uint8_t *wData,uint16_t wSize)
{uint8_t data[wSize+4];uint8_t temp;uint16_t index=0;data[index++]=FM25_WRITE;if(fram->memAddLength==FM258BitMemAdd){data[index++]=(uint8_t)regAddress;if((fram->mode==FM25L04B)||(fram->mode==FM25040B)){temp=(uint8_t)(regAddress>>8);data[0]|=((temp&0x01)<<3);}}else if(fram->memAddLength==FM2516BitMemAdd){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;}for(int i;i<wSize;i++){data[index++]=wData[i];}if(((fram->status)&0x02)!=0x02){SetWriteEnableLatchForFM25xxx(fram);}if(((fram->status)&0x0C)!=0x00){WriteStatusForFM25xx(fram,fram->status|FM25_BPNONE);}fram->ChipSelect(FM25CS_Enable);fram->Write(data,index);fram->ChipSelect(FM25CS_Disable);WriteStatusForFM25xx(fram,fram->status|FM25_BPALL);
}

2.4.2、读数据操作

FM25xxx FRAM存储器的CS信号为低电平时,总线控制器可以发出一个读操作码。READ命令后面是一个存储地址,包含READ操作的第一个字节的地址。当操作码和地址发出后,设备将在接下来的8个时钟上读出数据。在读取数据字节期间忽略信息输入。后续字节是按顺序读出的数据字节,只要总线时钟存在且CS信号为低电平,地址就会在内部递增。如果到达最后一个地址,计数器将滚动到0x0000。读数据操作命令的总线时序如下:

根据上述时序图,我们可以设计FM25xxx FRAM存储器读数据存储器的程序如下:

/*从FM25xxx读取数据*/
void ReadBytesFromFM25xxx(FM25ObjectType *fram,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++]=FM25_READ;if(fram->memAddLength==FM258BitMemAdd){data[index++]=(uint8_t)regAddress;if((fram->mode==FM25L04B)||(fram->mode==FM25040B)){temp=(uint8_t)(regAddress>>8);data[0]|=((temp&0x01)<<3);}}else if(fram->memAddLength==FM2516BitMemAdd){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;}fram->ChipSelect(FM25CS_Enable);fram->Write(data,index);fram->Delayms(1);fram->Read(rData,rSize);fram->ChipSelect(FM25CS_Disable);
}

3、驱动的使用

我们设计了FM25xxx FRAM存储器的驱动程序,这个驱动程序是否能够按我们的期望有效工作呢?我们需要验证一下,下面我们将设计一个验证驱动的简单应用。

3.1、声明并初始化对象

使用基于对象的操作我们需要先得到这个对象,所以我们先要使用前面定义的FM25xxx FRAM存储器对象类型声明一个FM25xxx FRAM存储器对象变量,具体操作格式如下:

FM25ObjectType fm25;

声明了这个对象变量并不能立即使用,我们还需要使用驱动中定义的初始化函数对这个变量进行初始化。这个初始化函数所需要的输入参数如下:

FM25ObjectType *framFM25xxx对象实体

FM25ModeType mode,设备类型

Fm25Read read,读FM25xxx对象操作指针

Fm25Write write,写FM25xxx对象操作指针

Fm25Delayms delayms,延时操作指针

Fm25WP wp,写保护操作函数指针

Fm25ChipSelect cs,片选信号函数指针

Fm25Hold hold,保持信号操作函数指针

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

/* 定义读数据操作函数指针类型 */
typedef void (*Fm25Read)(uint8_t *rData,uint16_t rSize);/* 定义写数据操作函数指针类型 */
typedef void (*Fm25Write)(uint8_t *wData,uint16_t wSize);/* 定义延时操作函数指针类型 */
typedef void (*Fm25Delayms)(volatile uint32_t nTime);/* 定义写保护操作函数指针类型 */
typedef void (*Fm25WP)(FM25WPType wp);/* 定义片选操作函数指针类型 */
typedef void (*Fm25ChipSelect)(FM25CSType cs);/* 定义保持操作函数指针类型 */
typedef void (*Fm25Hold)(FM25HoldType hold);

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

/*读FM25寄存器值*/
static void ReadDataFromFM25(uint8_t *rData,uint16_t rSize)
{HAL_SPI_Receive (&fm25hspi,rData,rSize,1000);
}/*写FM25寄存器值*/
static void WriteDataToFM25(uint8_t *wData,uint16_t wSize)
{HAL_SPI_Transmit (&fm25hspi,wData,wSize,1000);
}/*片选操作*/
void ChipSelectForFM25(FM25CSType cs)
{if(cs==FM25CS_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);}
}/*写保护操作*/
void WriteProtectedForFM25(FM25WPType wp)
{if(wp==FM25WP_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);}
}/*保持信号操作*/
void HoldForFM25(FM25HoldType hold)
{if(hold==FM25Hold_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET);}
}

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

Fm25cxxInitialization(&fm25,      //FM25xxx对象实体FM25V10,           //设备类型ReadDataFromFM25,              //读FM25xxx对象操作指针WriteDataToFM25,                 //写FM25xxx对象操作指针HAL_Delay, //延时操作指针WriteProtectedForFM25,        //写保护操作函数指针ChipSelectForFM25,        //片选信号函数指针HoldForFM25                   //保持信号操作函数指针);

3.2、基于对象进行操作

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

/*FM25XXX数据读写操作*/
void FM25ReadWriteData(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;/*从FM25XXX读取单个字节,从随机地址读取*/readByte=ReadByteFromFM25xxx(&fm25,regAddress);/*向FM25XXX写入单个字节*/WriteByteToFM25xxx(&fm25,regAddress,writeByte);/*从FM25XXX读取多个字节,从指定地址最多到所在页的结尾*/ReadBytesFromFM25xxx(&fm25,regAddress,rData,rSize);/*向FM25XXX写入多个字节,从指定地址最多到所在页的结尾*/WriteBytesToFM25xxx(&fm25,regAddress,wData,wSize);
}

4、应用总结

在这一篇中,我们实现了FM25xxx FRAM存储器的驱动程序,并在次驱动程序的基础上设计了简单的验证应用。无论是写数据还是读数据均可顺利执行,说明我们的驱动设计是正确的。

FM25xxx FRAM存储器与其他非易失性内存技术不同,F-RAM没有有效的写延迟。由于底层内存的读和写访问时间相同,因此用户不会在总线上体验延迟。整个内存周期的时间比一个总线时钟还短。因此,任何操作(包括读或写)都可以在写之后立即执行。

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

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

源码下载:https://github.com/foxclever/ExPeriphDriver

欢迎关注:

 

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

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

相关文章

步进电机驱动技术1:基于TMC2660的步进电机驱动

步进电机的应用非常广泛&#xff0c;在各种设备中经常会遇到&#xff0c;而步进电机的驱动则是使用步进电机必不可少的部分&#xff0c;可以有多种方式来实现步进电机的驱动&#xff0c;在这里我们来考虑一下基于TMC2660驱动芯片的步进电机驱动。 1、功能概述 TMC2660是德国T…

外设驱动库开发笔记26:nRF24L01无线通讯驱动

现在无线在我们的生活中无处不在。而我们开发的物联网产品也大量使用无线通讯。在这一篇文章中&#xff0c;我们将讨论nRF24L01无线通讯模块驱动程序的开发与实现。 1、功能概述 nRF24L01是一款工作在2.4~2.5GHz世界通用ISM 频段的单片无线收发器芯片无线收发器包括&#xff…

外设驱动库开发笔记27:ESP8266无线通讯驱动

我们的物联网产品所使用的平台都支持无线通讯&#xff0c;而且无线通讯本身更的成本较低&#xff0c;受到大家的欢迎。在本篇文章中&#xff0c;我们将详细讨论并实现ESP8266无线通讯模块的驱动。 1、功能概述 ESP8266是由乐鑫公司出品的一款物联网芯片&#xff0c;因为价格较…

外设驱动库开发笔记28:W5500以太网控制器

以太网通讯是一种被广泛使用的数据通讯方式。在嵌入式应用中也经常使用&#xff0c;但协议栈的实现并不是一件容易的事。不过有些以太网控制器就带有协议栈&#xff0c;如W5500。在本篇中我们将讨论如何设计并实现W5500以太网控制器的驱动。 1、功能概述 W5500是WIZnet开发的…

外设驱动库开发笔记29:DS17887实时时钟驱动

一些时候&#xff0c;在我们的嵌入式产品中需要记录时间&#xff0c;所以我们就需要获取时钟&#xff0c;当然实现的方式多种多样&#xff0c;有的MCU本身就有这一功能&#xff0c;不过精度较低。当我们的应用要求较高时就需要使用专门的实时时钟芯片&#xff0c;如DS17887。在…

外设驱动库开发笔记30:宇电AI-BUS通讯驱动

嵌入式系统通常都会与外部设备进行通讯&#xff0c;这就涉及到通讯协议的问题。这些通讯协议有的是标准协议有的厂家自定义的协议&#xff0c;如宇电的AI-BUS。在本篇中&#xff0c;我们将讨论AI-BUS的驱动&#xff0c;以便于与宇电设备的通讯。 1、功能概述 宇电的设备使用基…

步进电机驱动技术3:基于ULN2003的步进电机驱动

在我们的项目中&#xff0c;经常使用到低电压小功率的步进电机&#xff0c;此类步进电机若采用驱动器控制不断成本高也过于复杂&#xff0c;我们可以直接使用场效应管或者达林顿管来实现对其的驱动。在本篇中&#xff0c;我们就来讨论一下基于ULN2003A达林顿管实现对步进电机的…

通讯接口应用笔记2:MAX3160实现多协议通讯

在一些应用需求中&#xff0c;我们需要对外部提供串行通讯端口&#xff0c;但这些端口所通讯的目标设备各有不同&#xff0c;接口协议也有RS232以及RS485和RS422多种。面对这种情况&#xff0c;我们当然可以同时设计多个串口以适应不同需要&#xff0c;但无疑对硬件资源是一种浪…

电机速度曲线规划1:梯形速度曲线设计与实现

电机驱动是很常见的应用&#xff0c;在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能&#xff0c;这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的梯形曲线规划的问题。 1、基本原理 梯形速度曲线控制算法是工业控制领域应用最为广泛…

文件系统应用笔记之一:FatFS在STM32F4上的移植

在实现如U盘文件读写&#xff0c;SD卡的文件读写等工作时&#xff0c;我们往往需要一个文件系统来支持我们的工作。特别在一些MCU应用中&#xff0c;文件系统的加入能明显改善系统交互的友好性。在这一篇中&#xff0c;我们就来讨论FatFS文件系统在STM32F4上的移植和应用。 1、…

通讯接口应用笔记3:使用W5500实现Modbus TCP服务器

前面我们设计实现了W5500的驱动程序&#xff0c;也讲解了驱动的使用方式。在最近一次的项目应用中&#xff0c;正好有一个使用W5500实现TCP通讯的需求&#xff0c;所以我们就使用该驱动程序轻松实现。这一篇中我们就来说一说基于我们W5500通讯驱动程序实现TCP通讯的过程。 1、…

电机速度曲线规划2:S形速度曲线设计与实现

电机驱动是很常见的应用&#xff0c;在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能&#xff0c;这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的S型曲线规划的问题。 1、基本原理 S型速度曲线控制算法是工业控制领域另一种常用的加…

USB应用开发笔记之一:STM32上实现USB主机读写U盘

在项目应用中&#xff0c;经常会有对外交换数据的需求。USB接口读写U盘无疑是一种颇为方便的选择。在这一篇中&#xff0c;我们就来讨论如何在STM32上实现USB主机读写U盘文件的方法。 1、应用概述 在我们的产品上有这样一个需求&#xff0c;希望通过大容量的U盘存取数据。我们…

Modbus协议栈综合实例设计

自我们开源了我们的Modbus协议栈之后&#xff0c;就一直有朋友来信说希望提供示例。这次我们整理了几个例子以供参考。 1、应用实例规划 在这次的实例中&#xff0c;我们使用的目标板拥有一个以太网接口、一个RS232串行接口和一个RS485串行接口&#xff0c;所以我们规划实现&a…

ThreadX应用开发笔记之二:移植ThreadX到STM32H7平台

前面我们将ThreadX成功移植到了STM32F4平台&#xff0c;但这只是我们的部分应用。我们希望将ThreadX的优势发挥到我们的更多应用中&#xff0c;所以在这一篇中我们就来实现将ThreadX移植到STM32H7平台中。 1、前期准备 在开始将ThreadX移植到STM32H7平台之前&#xff0c;我们需…

外设驱动库开发笔记31:S-Modlue远红外气体传感器驱动

在气体分析类产品中&#xff0c;我们经常会用到远红外气体传感器。我们就在碳氢类气体成分分析中使用了S-Modlue远红外气体传感器。接下来&#xff0c;我们将讨论S-Modlue远红外气体传感器驱动的设计与实现。 1、功能概述 S-MODULE EVO 使用非分散红外检测技术NDIR&#xff0c…

外设驱动库开发笔记32:HLPM025K3 PM2.5传感器驱动

现在人们对大气环境及室内环境都比较关注。PM2.5在生活中也是常见的词汇。在有些产品中就要求检测PM2.5的数值。检测PM2.5的手段多种多样&#xff0c;在要求不高时我们通常可以采用激光模块。在这一篇中&#xff0c;我们将讨论HLPM025K3 PM2.5传感器驱动的设计与实现。 1、功能…

外设驱动库开发笔记33:LCD1602液晶显示屏驱动

LCD1602是一种工业字符型液晶&#xff0c;能够同时显示16x02即32个字符。LCD1602液晶显示的原理是利用液晶的物理特性&#xff0c;通过电压对其显示区域进行控制&#xff0c;即可以显示出图形。在这一章我们就来讨论LCD1602液晶显示屏驱动的设计与实现。 1、功能概述 LCD1602液…

滤波器开发之四:基于算术平均的中值滤波器

在信号采集系统中&#xff0c;除了我们感兴趣的数据外&#xff0c;难免会有一些来自于环境的干扰信号。但我们总希望我们得到的数据是纯净而真实的&#xff0c;为了达到这个目标&#xff0c;我们不得不想办法去除这些干扰信号&#xff0c;于是滤波器就成为我们必不可少的帮手。…

LwIP应用开发笔记之十一:LwIP带操作系统UDP服务器

我们已经实现了在FreeRTOS系统上的LwIP的移植工作&#xff0c;但只是简单的在系统平台上跑了起来。我们还希望能做更多的事情&#xff0c;这一节我们就在FreeRTOS系统上实现基于LwIP的UDP服务器。 1、UDP协议简述 UDP协议全称是用户数据报协议&#xff0c;在网络中它与TCP协议…