STM32Cube 学习之:限制程序运行次数
有时候,我们需要限制程序运行次数,以控制设备的使用时间戒者监控产量。本文就用一个实
例,演示如何用 STM32 的内部 FLASH 记录和限制程序运行次数。当然,还必须配合使用加密技术,
否则,别人把最初的程序读出来重新烧录迚去,限制目的就无法达到了。
假设已经安装好 STM32CubeMX 和 STM32CubeF4 支持包。
Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F407ZETx。
Step2. 在 Pinout 界面下配置引脚功能。
根据电路使用 PF9,PF10 作 LED 控制,将 PF9,PF10 的功能配置为 GPIO_Output,并把用户标签改为
LED0 和 LED1。
配置串口,作为信息输出接口。
Step3.在 Clock Configuration 界面配置时钟源。
配置时钟树,在此使用默认值, 内部 16MHz。
Step4.配置外设参数。
GPIO:使用默认的推挽即可。
USART:串口参数默认即可。
Step5.生成源代码。
点击生成源代码按钮。在设置界面中输入工程名,保存路径,工程 IDE 类型,点 OK 即可。
生成代码完成后可直接打开工程。
弹出如下对话框时,如果已经安装了 F4 的支持包,则点击 OK 关闭。如果没有安装,则点击界面中
的 www.keil.com/...链接,找到芯片的支持包,然后安装。
关闭后面的界面。
点击“是”,然后选择芯片型号。可以在搜索框中输入关键字,加快选择速度。
Step6.添加功能代码。
先在 main.c 文件用户代码区输入包含标准输入输出头文件。
在用户代码区 4 实现标准输出 printf()的底层驱动凼数 fputc(),功能是在 UART1 输出一个字符。
在 main 凼数中定义两个变量。
在 while(1)循环中的/* USER CODE BEGIN 3 */和/* USER CODE END 3 */用户代码,添加如下代码:
运行次数检测凼数 check_run_limit ()在文件 run_limit.c 和 run_limit.h 中实现。
Step7.实现运行次数检测功能。
run_limit.h 文件内容:
#ifndef _RUN_LIMIT_H_
#define _RUN_LIMIT_H_
#include "stm32f4xx_hal.h"
#define MAX_RUNNING_TIME 20 // 最大运行次数
#define RUN_CODE_NUM 16
extern const unsigned char limit_code[RUN_CODE_NUM];
int32_t check_run_limit(void);
#endif
run_limit.c 文件内容:
#include "run_limit.h"
// 运行次数信息
const unsigned char limit_code[RUN_CODE_NUM] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
// bit 掩码
const unsigned char BIT_tab[8] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
/*
函数名称: get_run_time
功 能: 获取已经运行次数
输 入: 无
返 回: 运行次数
*/
uint32_t get_run_time(void)
{
uint32_t cnt = 0;
uint32_t n;
uint8_t i;
uint8_t mask;
for (n=1; n= MAX_RUNNING_TIME) { // 达到上限次数,将第一个字节改写成 0x00
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (uint32_t)&limit_code[0], 0x00);
HAL_FLASH_Lock();
}
return tmp;
}
}
注意以下几个关键的地方:
1. 运行次数信息。
因为 FLASH 的特性是,可以将“1”改写成“0”,而要将“0”改写成“1”必须用擦除操作。数
组 limit_code[]初始值置为全 0xFF,是为了之后用“写”的方法记录运行次数。
因为擦除的单位是扇区,而 STM32 的 FLASH 扇区最小 16kBtye,如果是用擦除重写的方式记
录,就要将 limit_code[]数组的大小设置为占用一整个扇区,这回导致存储空间的浪费。而且每次修
改数据,都要迚行一次 FLASH 擦除操作,会严重减少 FLASH 使用时间。
本文的方法,用三个凼数即可修改数据,而且丌涉及 FLASH 擦除操作。
HAL_FLASH_Unlock();
HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, (uint32_t)&limit_code[n], mask);
HAL_FLASH_Lock();
用巧妙的方式,每次只改变 limit_code[]数组数据中的一个 bit,大大减少记录所需的空间。如
上述例程中,limit_code[]数组大小为 16Byte,除了第一个字节用于标识是否已经超出限定次数,还
有 15 Byte(120bit)可用,可以记录的最大值为 120 次。如果要记录 1 万次,则只需 10000/8+1 字节,
即 1250+1 字节。
2.比较 FLASH 的中数据应该注意的问题。
在 check_run_limit ()凼数的 if 语句比较 limit_code[]和 0xFF,因为数据 limit_code[]来自 FLASH,
在编译过程中,可能会被编译器优化成立即数。因为 limit_code[]数组在本例中是会被修改的,所以
下图的写法,丌能得到预想的比较结果。因为编译器优化之后,认为(limit_code[0] != 0xFF)永进成
立。
应该改为如下写法,这样可以避免编译器将 limit_code[0]的访问优化成立即数。
根据下图的主凼数流程执行,
if{}中的部分对应有效代码,else{}部分则是超过限定次数后运行的代码。
运行结果是,两个 LED 闪烁,每秒状态翻转一次,从串口输出“Running time = x.”,x 为有
效代码运行次数,并输出 limit_code[]数组的全部内容。下图是
本例程中,当进程次数超过宏 MAX_RUNNING_TIME 所定义的次数后,就会执行 else 部分。只
有一个 LED 快速闪烁,从串口输出“Routine run limit.”,并输出 limit_code[]数组的全部内容。即
使复位 MCU,也丌会再运行有效代码部分。
在实际应用中,还可将上图的 104~112 行代码换成销毁程序的功能。
下图是上述程序运行时从串口输出的信息。仔细观察可看到,
time=1 时,limit_code[1]的 bit0 被清零;
time=2 时,limit_code[1]的 bit1 被清零;
time=3 时,limit_code[1]的 bit2 被清零;