外设驱动库开发笔记24:FM24xxx系列FRAM存储器驱动

虽然说使用EEPROM保存参数很有效,但操作及使用次数均有一下限制。当我们的一些参数需要不定时修改或存储时,使用FRAM就更为方便一点。这一节我们就来设计并实现FM24xxx系列FRAM的驱动。

1、功能概述

我们首先说一下铁电随机存取存储器,F-RAM是非易失性的,其读写操作与RAM类似。它提供了151年的可靠数据保存,同时消除了由EEPROM和其他非易失性内存引起的复杂性、开销和系统级可靠性问题。

1.1、硬件描述

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

FM24xxx系列FRAM存储器与EEPROM不同,F-RAM以总线速度执行写操作。没有发生写延迟。数据在每个字节成功传输到设备后立即写入内存数组。下一个总线循环可在不需要进行数据轮询的情况下开始。此外,与其他非易失性存储器相比,该产品具有较强的写持久性。此外,F-RAM在写期间的功耗比EEPROM低得多,因为写操作不需要在内部提高写电路的电源电压。F-RAM能够支持1014次方个读/写周期,或比EEPROM1亿倍的写周期。

这些功能使得FM24xxx系列FRAM存储器非常适合非易失性内存应用程序,因为它需要频繁或快速的写操作。例如,数据日志记录(写入周期的数量可能至关重要)和严格的工业控制(EEPROM的长写入时间可能导致数据丢失)。这些特性的组合允许更频繁地编写数据,同时减少系统的开销。

1.2、通讯接口

FM24xxx系列FRAM存储器采用I2C通讯接口。其设备地址的前4位固定为1010b,后3位则用于设备地址或页面地址,所以若后3为均用于设备地址则在同一条I2C总线上最多可以带8个同类设备。其与主机之间的连接是以图如下所示:

FM24xxx系列FRAM存储器采用8位或者16位内存地址,对于不同的存储容量,寻址的最终范围当然是不同的,其寻址范围为从512131072不等。关于设备地址与内存地址的分配不同型号和容量的FM24xxx系列FRAM存储器是不一样的。具体分配如下图所示:

从上表我们很容易明白,设备地址的低3位的定义决定了在同一条I2C总线上,最多可以挂载多少个FM24xxx设备。有3位用于设备地址则最多可挂载8个设备;有2位用于设备地址则最多可挂载4个设备;有1位用于设备地址则最多可挂载2个设备;有0位用于设备地址则最多可挂载1个设备。需要注意的是,不同定义的位的设备混用于同一总线时,相同的定义位必须一样,否则用作寄存器地址的位可能让总线上的总线无法识别。

2、驱动设计与实现

我们在前面一节已经简要的描述了FM24xxx FRAM存储器的读写操作方式,在这一节我们将设计并实现FM24xxx FRAM存储器的驱动程序。

2.1、对象的定义

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

2.1.1、对象的抽象

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

先来考虑属性,作为属性肯定是用于标识或记录对象特征的东西。我们来考虑FM24xxx FRAM存储器对象属性。首先作为I2C从设备都需要有一个设备地址用以区分总线上的不同设备,所以我们将其作为对象的一个属性。FM24xxx FRAM存储器不同的型号在容量、寻址等方面有一些差异,为了正确操作不同类型的设备,我们将其作为对象的一个属性。不同容量的FM24xxx FRAM存储器因寻址范围不同所使用的地址寄存器位数也不相同,为了便于操作我们将地址位长度也作为对象的一个属性。

接着我们还需要考虑FM24xxx FRAM存储器对象的操作问题。我们要对FM24xxx FRAM存储器进行读写,但读写都需要同过具体的I2C接口进行,这依赖于具体的硬件平台,所以我们将针对端口的读写作为对象的操作。FM24xxx FRAM存储器还有一个写保护引脚用于设置内部存储器的写保护问题,这同样依赖于硬件平台来实现,所以我们也将它作为对象的操作。

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

/*定义FM24XXX对象类型*/
typedef struct FM24Object {uint8_t devAddress;   //FM24xxx设备地址FM24ModeType mode;    //FM24xxx设备类型FM24MemAddLengthType memAddLength;    //存储器地址长度void (*WP)(FM24WPType wp);    //写保护操作void (*Read)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize);       //读数据操作指针void (*Write)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize);    //写数据操作指针void (*Delayms)(volatile uint32_t nTime);       //延时操作指针
}FM24ObjectType;

2.1.2、对象初始化

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

/*FM24XXX对象初始化*/
void Fm24cxxInitialization(FM24ObjectType *fram,     //FM24xxx对象实体uint8_t devAddress,          //FM24xxx设备地址FM24ModeType mode,                  //FM24xxx设备类型Fm24WP wp,                                         //FM24xxx写保护Fm24Read read,                       //读FM24xxx对象操作指针Fm24Write write,                    //写FM24xxx对象操作指针Fm24Delayms delayms           //延时操作指针)
{if((fram==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL)){return;}fram->Read=read;fram->Write=write;fram->Delayms=delayms;if((0xA0<=devAddress)&&(devAddress<=0xAE)){fram->devAddress=devAddress;}else if((0x50<=devAddress)&&(devAddress<=0x57)){fram->devAddress=(devAddress<<1);}else{fram->devAddress=0x00;}if(mode>=FM24Number){return;}fram->mode=mode;if((mode<FM24CL64B)&&(mode!=FM24C64B)){fram->memAddLength=FM248BitMemAdd;}else{fram->memAddLength=FM2416BitMemAdd;}fram->WP=wp;
}

2.2、写对象操作

所有写操作都以从站地址开始,然后是内存地址。总线主机通过将从站地址(R/W位)的LSB设置为“0”来指示写操作。寻址之后,总线主机将每个字节的数据发送到FM24xxx FRAM存储器,存储器生成一个确认条件。可以写入任意数量的顺序字节,如果在内部到达地址范围的末尾,地址计数器将从最高地址回滚到最低地址。

2.2.1、写单个字节

FM24xxx FRAM存储器允许在任意地址写一个字节数据。在内部,实际的存储器写发生在第8位数据传输之后。它将在确认发送之前完成。因此,如果用户希望在不改变存储器内容的情况下中止写操作,那么应该在第8个数据位之前使用STARTSTOP条件来完成。FM24xxx FRAM存储器不使用页面缓冲。具体的操作时序如下:

根据上述时序图,我们可以设计FM24xxx FRAM存储器写单个字节数据程序如下:

/*向FM24XXX写入单个字节*/
void WriteByteToFM24xxx(FM24ObjectType *fram,uint32_t regAddress,uint8_t data)
{uint8_t temp;uint8_t tData[3];uint16_t tSize=0;if(fram->memAddLength==FM248BitMemAdd){tData[tSize++]=(uint8_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>8);}else{tData[tSize++]=(uint8_t)(regAddress>>8);tData[tSize++]=(uint8_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[fram->mode]>>1)))<<1;fram->devAddress=(fram->devAddress & devAddMask[fram->mode])|temp;tData[tSize++]=data;fram->WP(FM24WP_Disable);fram->Write(fram,tData,tSize);fram->WP(FM24WP_Enable);
}

2.2.2、写多个字节

FM24xxx FRAM存储器也允许从一个地址开始顺序写入多个字节。存储器内部地址指针会跟随写操作自动增加,其他的过程这与写单个字节相同。具体的操作时序如下:

根据上述时序图,我们可以设计FM24xxx FRAM存储器写多个字节数据程序如下:

/*向FM24XXX写入多个字节,从指定地址最多到所在页的结尾*/
void WriteBytesToFM24xxx(FM24ObjectType *fram,uint32_t regAddress,uint8_t *wData,uint16_t wSize)
{uint8_t temp;uint8_t tData[wSize+2];uint16_t tSize=0;if(fram->memAddLength==FM248BitMemAdd){tData[tSize++]=(uint8_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>8);}else{tData[tSize++]=(uint8_t)(regAddress>>8);tData[tSize++]=(uint8_t)(regAddress&0xFF);temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[fram->mode]>>1)))<<1;fram->devAddress=(fram->devAddress & devAddMask[fram->mode])|temp;for(int i=0;i<wSize;i++){tData[tSize++]=wData[i];}fram->WP(FM24WP_Disable);fram->Write(fram,tData,tSize);fram->WP(FM24WP_Enable);
}

2.3、读对象操作

       与写操作相对应,FM24xxx FRAM存储器允许从当前地址指针位置或任意制定的地址指针位置读取单个或多个字节。为了执行选择性读取,总线主机发送最低位(R/W)设置为0的从站地址。根据写协议,总线主机随后发送加载到内部地址锁存器的地址字节。在FM24xxx FRAM存储器确认地址后,总线主机发出一个START条件。这将同时中止写操作,并允许发出读命令,并将从站地址最低为设置为“1”

2.3.1、读单个字节

FM24xxx FRAM存储器允许从当前地址指针位置或任意制定的地址指针位置读取单个字节。当然从当前地址指针读可以不用设置存储器地址,不过我们考虑一般性,同意为从任意制定地址读取一个字节。具体的操作时序如下:

根据上述时序图,我们可以设计FM24xxx FRAM存储器读单个字节数据程序如下:

/*从FM24XXX读取单个字节,从随机地址读取*/
uint8_t ReadByteFromFM24xxx(FM24ObjectType *fram,uint32_t regAddress)
{uint8_t wData[2];uint16_t wSize=0;uint8_t rData;uint8_t temp;if(fram->memAddLength==FM248BitMemAdd){wData[wSize++]=(uint8_t)regAddress;temp=(uint8_t)(regAddress>>8);}else{wData[wSize++]=(uint8_t)(regAddress>>8);wData[wSize++]=(uint8_t)regAddress;temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[fram->mode]>>1)))<<1;fram->devAddress=(fram->devAddress & devAddMask[fram->mode])|temp;fram->Read(fram,wData,wSize,&rData,1);return rData;
}

2.3.2、读多个字节

FM24xxx FRAM存储器允许从当前地址指针位置或任意制定的地址指针位置读取多个字节。如果从当前的地址指针开始读可以不用设置存储器地址。但更一般的是从任意地址开始读多个字节,所以我们考虑从任意地址开始读取。具体的操作时序如下:

根据上述时序图,我们可以设计FM24xxx FRAM存储器读多个字节数据程序如下:

/*从FM24XXX读取多个字节,从指定地址最多到所在页的结尾*/
void ReadBytesFromFM24xxx(FM24ObjectType *fram,uint32_t regAddress,uint8_t *rData,uint16_t rSize)
{uint8_t temp;uint8_t wData[2];uint16_t wSize=0;if(fram->memAddLength==FM248BitMemAdd){wData[wSize++]=(uint8_t)regAddress;temp=(uint8_t)(regAddress>>8);}else{wData[wSize++]=(uint8_t)(regAddress>>8);wData[wSize++]=(uint8_t)regAddress;temp=(uint8_t)(regAddress>>16);}temp=(temp&(~(devAddMask[fram->mode]>>1)))<<1;fram->devAddress=(fram->devAddress & devAddMask[fram->mode])|temp;fram->Read(fram,wData,wSize,rData,rSize);
}

在传输每个数据字节之后,在返回确认之前,FM24xxx存储器会递增内部地址锁存器。这允许不使用其它地址访问下一个顺序字节。到达最后一个地址后,地址锁存器将滚到0000h。一个读或写操作可以访问的字节数,理论上讲是没有限制。

2.4、休眠模式操作

FM24xxx FRAM存储器的某些型号的设备上实现了一种称为休眠模式的低功耗模式。当休眠命令0x86被锁定时,设备将进入这种低功耗状态。进入睡眠模式的操作时序如下:

根据上述时序图,我们可以设计FM24xxx FRAM存储器进入休眠模式的程序如下:

/*FM24XXX对象进入休眠模式*/
void FM24xxxEnterSleepMode(FM24ObjectType *fram)
{uint8_t wData[2];wData[0]=fram->devAddress;fram->devAddress=0xF8;wData[1]=0x86;fram->Write(fram,wData,2);fram->devAddress=wData[0];
}

一旦进入休眠模式,设备建议低功耗运行,但设备会继续监控I2C引脚。一旦主站设备发送一个FM24xxx FRAM存储器标识的从站设备地址,它将被唤醒并进入正常运行。

3、驱动的使用

FM24xxx FRAM存储器驱动程序我们已经实现了,但这一驱动程序是否能正确读写,是否能稳定工作,还需要验证。所以接下来我们将设计一个简单的应用验证FM24xxx FRAM存储器驱动程序。

3.1、声明并初始化对象

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

FM24ObjectType fm24;

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

FM24ObjectType *framFM24xxx对象实体

uint8_t devAddressFM24xxx设备地址

FM24ModeType modeFM24xxx设备类型

Fm24WP wpFM24xxx写保护

Fm24Read read,读FM24xxx对象操作指针

Fm24Write write,写FM24xxx对象操作指针

Fm24Delayms delayms,延时操作指针

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

//写保护操作
typedef void (*Fm24WP)(FM24WPType wp); /* 定义读数据操作函数指针类型 */
typedef void (*Fm24Read)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize);      /* 定义写数据操作函数指针类型 */
typedef void (*Fm24Write)(struct FM24Object *fram,uint8_t *wData,uint16_t wSize);   /* 定义延时操作函数指针类型 */
typedef void (*Fm24Delayms)(volatile uint32_t nTime);   

对于这几个函数我们根据样式定义就可以了,具体的操作可能与使用的硬件平台有关系。具体函数定义如下:

/*读FM24XXX寄存器值*/
static void ReadDataFromFM24(FM24ObjectType *fram,uint8_t *wData,uint16_t wSize,uint8_t *rData,uint16_t rSize)
{HAL_I2C_Master_Transmit(&fm24hi2c,fram->devAddress,wData,wSize,1000);HAL_I2C_Master_Receive(&fm24hi2c,fram->devAddress+1,rData, rSize, 1000);
}/*写FM24XXX寄存器值*/
static void WriteDataToFM24(FM24ObjectType *fram,uint8_t *wData,uint16_t wSize)
{HAL_I2C_Master_Transmit(&fm24hi2c,fram->devAddress,wData,wSize,1000);
}/*写保护操作*/
void FM24WriteProtected(FM24WPType wp)
{if(wp==FM24WP_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);}
}

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

Fm24cxxInitialization(&fm24,             //FM24XXX对象实体0xAE,           //FM24XXX设备地址FM24V10,           //FM24XXX对象类型FM24WriteProtected,       //写保护操作指针ReadDataFromFM24,       //读FM24XXX对象操作指针WriteDataToFM24,          //写FM24XXX对象操作指针HAL_Delay        //延时操作指针);

3.2、基于对象进行操作

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

/*FM24XXX数据操作*/
void FM24ReadWriteData(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;/*从FM24XXX读取单个字节,从随机地址读取*/readByte=ReadByteFromFM24xxx(&fm24,regAddress);/*向FM24XXX写入单个字节*/WriteByteToFM24xxx(&fm24,regAddress,writeByte);/*从FM24XXX读取多个字节,从指定地址最多到所在页的结尾*/ReadBytesFromFM24xxx(&fm24,regAddress,rData,rSize);/*向FM24XXX写入多个字节,从指定地址最多到所在页的结尾*/WriteBytesToFM24xxx(&fm24,regAddress,wData,wSize);
}

4、应用总结

我们实现了FM24xxx FRAM存储器的驱动程序,并基于这一驱动程序设计了简单的验证应用,能够写入数据并能将其读出来。这与我们预期的结果是一致的,驱动设计基本正确。

FM24xxx FRAM存储器内存数组可以使用WP引脚进行写保护。将WP引脚设置为高状态(VDD)将写保护所有地址。果试图对这些地址进行写操作,地址计数器不会增加。FM24xxx FRAM存储器将不会理会写入受保护地址的数据字节。如将WP设置为低状态(VSS)将禁用写保护,此时整个区域都是可以进行写操作的。

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

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

​欢迎关注:

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

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

相关文章

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

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

步进电机驱动技术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;于是滤波器就成为我们必不可少的帮手。…