1. 程序存储器(Flash)的读取与校验
任务说明
本案例面向8051内核单片机(以STC89C52为例),要求编写程序读取芯片内部程序存储器(Flash)中已烧录的代码内容,逐字节累加计算校验和,并将校验结果输出到串口,用于验证程序在烧录后是否完整、未被篡改或损坏。
实现原理
8051单片机采用哈佛架构,程序存储器和数据存储器独立编址。读取程序存储器中代码字节不能通过普通数据指针(DPTR)直接完成,必须使用程序存储器读指令MOVC A, @A+DPTR。该指令将DPTR指向的Flash地址内容送入累加器A。通过循环递增DPTR并累加读取到的每个字节,即可计算整个程序空间的校验和。本案例将读取从地址0x0000开始、长度为CODE_LEN字节的Flash区域,累加结果通过串口以十六进制格式输出。
代码实现
#include <reg52.h>
#define CODE_LEN 0x1000 // 读取4KB程序空间(可根据实际固件大小调整)
#define BUF_LEN 8 // 串口发送缓冲区长度
typedef unsigned char u8;
typedef unsigned int u16;
// 串口初始化:9600bps,晶振11.0592MHz
void UART_Init(void) {
SCON = 0x50; // 模式1,8位UART,允许接收
TMOD = 0x20; // 定时器1工作在模式2(8位自动重装)
TH1 = 0xFD; // 波特率9600
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
ES = 0; // 不使用中断,采用查询发送
}
// 串口发送单字节
void UART_SendByte(u8 dat) {
SBUF = dat;
while (!TI); // 等待发送完成
TI = 0;
}
// 将16位校验和按十六进制格式发送(4位)
void UART_SendHex(u16 val) {
u8 nibble;
u8 i;
u8 code hex_tab[16] = {'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'};
for (i = 0; i < 4; i++) {
nibble = (val >> (12 - i*4)) & 0x0F; // 从高位到低位取半字节
UART_SendByte(hex_tab[nibble]);
}
}
// 读取Flash并计算校验和
u16 Flash_Checksum(void) {
u16 sum = 0;
u16 i;
u8 code *ptr; // code指针指向程序存储器
ptr = (u8 code *)0x0000; // 从Flash起始地址开始
for (i = 0; i < CODE_LEN; i++) {
sum += *ptr; // 编译器自动生成MOVC指令读取Flash
ptr++;
}
return sum;
}
void main(void) {
u16 checksum;
UART_Init();
checksum = Flash_Checksum();
// 发送提示符
UART_SendByte('C');
UART_SendByte('S');
UART_SendByte(':');
UART_SendHex(checksum);
UART_SendByte('\r');
UART_SendByte('\n');
while (1); // 任务完成,停留
}
关键说明
代码中u8 code *ptr声明了一个指向程序存储器的指针,编译器在解引用该指针时会自动生成MOVC A, @A+DPTR指令,从而正确读取Flash中的代码字节。累加使用16位无符号整数,若总字节数超过65535则自然溢出,但本案例读取4KB空间远小于该上限,不存在溢出问题。串口输出格式为CS:XXXX,其中XXXX为4位十六进制校验和。
验证方法
验证步骤:
- 使用Keil C51编译本代码,生成HEX文件;
- 将HEX文件烧录到STC89C52单片机(烧录时选择11.0592MHz晶振);
- 用USB转TTL模块连接单片机串口(P3.0-RXD, P3.1-TXD, GND共地);
- 打开串口调试助手,设置波特率9600、8数据位、1停止位、无校验;
- 复位单片机,观察串口助手接收窗口。
判定标准:
- 串口能收到以
CS:开头、后跟4位十六进制数字的字符串; - 将本代码编译生成的HEX文件用文本方式打开,对HEX文件中所有数据记录(以
:10开头的数据行)的十六进制数据字节进行累加(不含行号、长度、地址和校验字节),所得累加值低16位应与串口输出的校验和一致; - 若人为修改HEX文件中任意一个数据字节后重新烧录,串口输出的校验和应发生变化,证明程序能有效检测Flash内容变更。
落地与运行
所需工具:Keil C51编译器、STC-ISP烧录软件、STC89C52开发板或最小系统板、USB转TTL串口模块。
操作步骤:
- 在Keil中新建工程,选择芯片型号STC89C52RC,将上述代码粘贴到main.c并加入工程;
- 编译生成HEX文件(需勾选"Create HEX File"选项);
- 使用STC-ISP软件将HEX烧录到单片机;
- 连接串口,打开串口助手观察输出。
注意事项:若实际固件大小不足4KB,可减小CODE_LEN至实际代码长度(如0x0800对应2KB),但需保证该长度覆盖全部有效代码区域。读取未烧录的空白Flash区域(值为0xFF)也会计入校验和,因此CODE_LEN应精确对应实际代码占用空间,否则校验和会包含空白区数据。