stm32f103使用PVD监测掉电、flash擦写与读取

文章背景:

擦写数据在很多产品上都有应用,比如简单的电子吹风筒上面,记录用户每次的风速档和温度档习惯,下次开机用户则不需要再次去选择自己习惯的档位,提升产品智能度,擦写数据不一定要在断电瞬间,比如在按键按下,档位发生了改变之后去进行,它的好处是此时系统供电电压稳定,则擦写成功率高。然而他的缺点也很明显,以STM32F103来说,由于其没有内部eeprom,所以对于它的擦写数据我们都是将其放在flash中执行的,其flash擦除方式仅有页擦除和全擦除,页擦除的时间大约需要40ms,而其擦除过程处于一个优先级较高的中断,会导致其他低优先级中断无法执行,40ms的时间对系统正常执行会带来很大影响,比如我制作的一款风筒由于把擦写数据放在按键按下切换档位后,导致每次按下按键风筒就会抖动,很明显就是控制电机转速的中断被擦写数据的中断给影响了;其次FLASH读取数据没有次数限制,但擦写数据有寿命限制,F103为1万次,所以如果过于频繁的切换档位的话,会导致FLASH寿命迅速减少。

STM32F1系列的擦写时间及允许擦写次数:
在这里插入图片描述

相比较来说掉电瞬间进行擦写数据就可以避免以上的两个问题了,但是它要求MCU的供电电压从触发掉电中断后至少要能维持MCU系统工作至少50ms左右才能保证高成功率的掉电擦写数据,这就要求MCU的电源需要一个较大容量的电容,我一般采用220uF或470uF的电容。

PVD配置:

STM32 PVD功能具体可以检测到上电、掉电瞬间,其处理方式有中断响应及事件响应。
其配置过程有三个步骤:1.开启PVD中断并设置其优先级; 2.配置响应中断或事件的阈值电压; 3.配置响应模式。

1.在STM32CUBEMX开启PVD中断:
在这里插入图片描述

生成工程之后在工程里面进行模式和阈值的配置:

void PVD_config()//掉电监测初始化
{
	
	__HAL_RCC_PWR_CLK_ENABLE();
	HAL_NVIC_SetPriority(PVD_IRQn,0,0);
	HAL_NVIC_EnableIRQ(PVD_IRQn);
	
	PWR_PVDTypeDef sConfigPVD;
	sConfigPVD.PVDLevel=PWR_PVDLEVEL_7;//level7为2.9V阈值,对于掉电来说阈值越高越快响应
	sConfigPVD.Mode=PWR_PVD_MODE_IT_RISING  ;//模式为掉电时触发中断,这里也不懂为什么设置成RISING才正常
	
	HAL_PWR_ConfigPVD(&sConfigPVD);
	
	HAL_PWR_EnablePVD();
}

然后将上面的PVD初始化函数放在主函数初始化里面执行,但是要在执行该函数前先延时一段时间,因为上电瞬间MCU供电电压还未稳定,如果不延时一段时间会导致误触发PVD中断,因为我们想要的效果是在掉电的时候触发中断:

在这里插入图片描述

最后在stm32f1xx.it.c文件中找到其对应的中断服务函数,把擦写数据放在里面即可:
在这里插入图片描述

flash擦写与读取:
这部分我直接提供我做的一款风筒的对应程序供大家进行参考及使用,其中我需要记忆三个数据,一个是3档温度,一个是5档风速,一个是离子工作的三个模式,在掉电时候将这三个数据写入flash,在上电后先将这三个数据读出再对风筒进行各项工作控制。

/*步骤:
1.解锁FLASH;
2.擦除FLASH;
3.写入FLASH;
4.上锁FLASH;
5.读取FLASH;
*/

uint32_t addr = 0x08007700;
uint32_t data_save[3];//第1个为memory_temp_level,第2个为memory_speed_level,第3个为memory_plasma_level
	
extern uint32_t memory_speed_level;//风速
extern uint32_t memory_temp_level;//温度
extern uint32_t memory_plasma_level;//离子

void data_assignment()//为其赋值,要写flash时先执行此函数为写入内容赋值
{
	data_save[0]=memory_temp_level;
	data_save[1]=memory_speed_level;
	data_save[2]=memory_plasma_level;
}

void WriteFlashTest(uint8_t L,uint32_t data_save[],uint32_t addr)
{
	uint32_t i=0;
	/* 1/4解锁FLASH*/
	HAL_FLASH_Unlock();
	/* 2/4擦除FLASH*/
	/*初始化FLASH_EraseInitTypeDef*/
	/*擦除方式页擦除FLASH_TYPEERASE_PAGES,块擦除FLASH_TYPEERASE_MASSERASE*/
	/*擦除页数*/
	/*擦除地址*/
	FLASH_EraseInitTypeDef FlashSet;
	FlashSet.TypeErase = FLASH_TYPEERASE_PAGES;
	FlashSet.PageAddress = addr;
	FlashSet.NbPages = 1;
	/*设置PageError,调用擦除函数*/
	uint32_t PageError = 0;
	HAL_FLASHEx_Erase(&FlashSet, &PageError);
	/* 3/4对FLASH烧写*/
	for(i=0;i<L;i++)
	{
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr+4*i, data_save[i]);//由于数据为uint8_t, add+1*i为每次加1个字节
	}
	/* 4/4锁住FLASH*/
	HAL_FLASH_Lock();
}

void ReadFlashTest(uint8_t L,uint32_t addr)//分别读取2个字节的值赋给memory_temp_level,memory_speed_level
{
	uint8_t i=0;
	for(i=0;i<L;i++)
	{
		if(i==0)
		{
		  memory_temp_level = *(__IO uint32_t*)(addr+i*4);
			if(memory_temp_level>3)//由于烧录程序后第一次上电时地址中还没有存入数据,其值将为0xffffffff,此时先给其赋值为3
			{
				memory_temp_level=3;
			}
		}
		
		
		if(i==1)
		{
		  memory_speed_level=*(__IO uint32_t*)(addr+i*4);
			if(memory_speed_level>5)//由于烧录程序后第一次上电时地址中还没有存入数据,其值将为0xffffffff,此时先给其赋值为5
			{
				memory_speed_level=5;
			}
		}
		
			if(i==2)
		{
		  memory_plasma_level=*(__IO uint32_t*)(addr+i*4);
			if(memory_plasma_level>3)//由于烧录程序后第一次上电时地址中还没有存入数据,其值将为0xffffffff,此时先给其赋值为3
			{
				memory_plasma_level=3;
			}
		}
		
	}
	
}
void flash_write()//将速度档位、温度档位、离子模式写进flash
{
	memory_speed_level=speed_level;
	memory_temp_level=temp_level;
	memory_plasma_level=plasma_level;
	data_assignment();//为数组赋值,要写flash时先执行此函数为写入内容赋值
	WriteFlashTest(3,data_save,addr);
}

在这里插入图片描述

### 回答1: 下面是 STM32掉电代码示例: ```c #include "stm32f1xx_hal.h" void SystemPower_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable Power Clock */ __HAL_RCC_PWR_CLK_ENABLE(); /* Enable GPIOs Clock */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); /* Configure all GPIO port pins in Analog Input mode (floating input trigger OFF) */ GPIO_InitStructure.Mode = GPIO_MODE_ANALOG; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Pin = GPIO_PIN_All; HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); /* Disable all GPIO port clocks */ __HAL_RCC_GPIOA_CLK_DISABLE(); __HAL_RCC_GPIOB_CLK_DISABLE(); __HAL_RCC_GPIOC_CLK_DISABLE(); /* Enter Stop Mode */ HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); } ``` 这个代码将所有 GPIO 都配置成了模拟输入模式,然后关闭了所有 GPIO 的时钟,并进入了低功耗模式。在低功耗模式下,只有低功耗寄存器可以工作,其他的外设都处于停止状态,可以实现掉电功能。需要注意的是,掉电前需要保存必要的数据,以便下次唤醒后恢复系统状态。 ### 回答2: STM32掉电代码主要完成低功耗的功能,使得芯片在掉电状态下仍然能够正常工作。以下是一个简单示例: #include "stm32f10x.h" #include "stm32f10x_pwr.h" int main(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PWR, ENABLE); // 使能PWR时钟 PWR_EnterSTANDBYMode(); // 进入掉电模式 while (1) { // 程序将不会执行到这里 } } 在该示例中,我们首先使能了PWR时钟以便使用掉电模式相关的函数。然后,调用PWR_EnterSTANDBYMode()函数进入掉电模式,程序将停止在此处,直到外部中断或复位事件唤醒芯片。在掉电模式下,CPU和大多数外设都被关闭,以节省功耗。 要注意的是,掉电模式的唤醒源可以是多种事件,如外部中断、定时器、看门狗等,可以根据实际需求进行配置和使用。在芯片被唤醒后,程序将从唤醒源指定的中断服务函数开始执行。 对于更复杂的掉电模式,你需要进一步了解和配置芯片的低功耗模式,如选择正确的时钟源、配置外设唤醒等。此外,为了使芯片进入掉电模式之前的准备工作,比如保存数据和关闭外设等,你可能还需要编写其他代码。 ### 回答3: STM32掉电代码主要是通过在程序中配置相应的寄存器来控制芯片进入低功耗模式以实现掉电的功能。以下是一个典型的STM32掉电代码示例: 首先,需要将芯片的时钟配置为低功耗时钟模式。在RCC寄存器中配置相应的控制位,例如选择内部低速时钟(LSI)作为掉电时钟源。 其次,需要对待写入的寄存器进行相应的配置。例如,将GPIO口的输入/输出模式设置为适当的状态,使其在掉电期间保持在合适的电平状态。 然后,需要使芯片进入低功耗模式。这可以通过将PWR寄存器中的控制位设置为相应的值来实现。例如,可以选择进入低功耗待机模式(Stop Mode)或者低功耗休眠模式(Sleep Mode)。 最后,在掉电模式下恢复时,需要重新配置时钟源和寄存器状态。通过相应的寄存器配置,恢复芯片的正常工作状态。 总之,STM32掉电代码的实现主要包括三个步骤:配置掉电时钟源、配置寄存器状态以保持适当电平、设置低功耗模式进入和恢复。具体的代码实现可以根据实际需求进行调整和优化。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值