#include <STC15F104W.h> // STC15F104W单片机头文件
#include <intrins.h> // 包含_nop_()等 intrinsic 函数
/**
* 基于STC15F104W的加密芯片模拟
* 利用STC15F104W内置EEPROM存储密钥和加密数据
* 功能特性:
* 1. 数据加密存储
* 2. 密钥管理
* 3. 数据完整性校验
* 4. 断电数据保持
*/
// EEPROM地址定义
#define EEPROM_KEY_ADDR 0x00 // 密钥存储地址,占用8字节
#define EEPROM_DATA_ADDR 0x10 // 数据存储起始地址
#define EEPROM_SIZE 128 // STC15F104W内置EEPROM大小(128字节)
// 密钥长度
#define KEY_LENGTH 8 // 密钥长度为8字节
/**
* 延时函数
* @param ms 延时毫秒数
* @note 该延时函数适用于12MHz晶振的STC15F104W
*/
void Delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 120; j++); // 循环120次约为1ms
}
/**
* 写EEPROM
* @param addr EEPROM地址
* @param dat 要写入的数据
* @note STC15F104W的EEPROM操作需要使用IAP指令
* @note 每次写入后需要延时5ms以确保数据写入完成
*/
void EEPROM_Write(unsigned char addr, unsigned char dat)
{
IAP_CONTR = 0x80; // 使能IAP操作,设置等待时间为0
IAP_CMD = 0x02; // 设置为写命令
IAP_ADDRH = addr >> 8; // 设置地址高8位
IAP_ADDRL = addr; // 设置地址低8位
IAP_DATA = dat; // 设置要写入的数据
IAP_TRIG = 0x5A; // 触发IAP操作,第一个触发字节
IAP_TRIG = 0xA5; // 触发IAP操作,第二个触发字节
_nop_(); // 空操作,确保操作完成
IAP_CONTR = 0x00; // 禁用IAP操作,关闭IAP功能
}
/**
* 读EEPROM
* @param addr EEPROM地址
* @return 读取的数据
* @note STC15F104W的EEPROM操作需要使用IAP指令
*/
unsigned char EEPROM_Read(unsigned char addr)
{
unsigned char dat;
IAP_CONTR = 0x80; // 使能IAP操作,设置等待时间为0
IAP_CMD = 0x01; // 设置为读命令
IAP_ADDRH = addr >> 8; // 设置地址高8位
IAP_ADDRL = addr; // 设置地址低8位
IAP_TRIG = 0x5A; // 触发IAP操作,第一个触发字节
IAP_TRIG = 0xA5; // 触发IAP操作,第二个触发字节
_nop_(); // 空操作,确保操作完成
dat = IAP_DATA; // 读取数据
IAP_CONTR = 0x00; // 禁用IAP操作,关闭IAP功能
return dat;
}
/**
* 简单的异或加密算法
* @param data 要加密的数据
* @param len 数据长度
* @param key 密钥
* @param key_len 密钥长度
* @note 异或加密是一种对称加密算法,相同的密钥可以用于加密和解密
* @note 当密钥长度小于数据长度时,会循环使用密钥
*/
void encrypt_data(unsigned char *data, unsigned char len, unsigned char *key, unsigned char key_len)
{
unsigned char i;
for (i = 0; i < len; i++)
{
data[i] ^= key[i % key_len]; // 异或加密,使用循环密钥
}
}
/**
* 简单的异或解密算法
* @param data 要解密的数据
* @param len 数据长度
* @param key 密钥
* @param key_len 密钥长度
* @note 由于异或运算的特性,加密和解密使用相同的算法
*/
void decrypt_data(unsigned char *data, unsigned char len, unsigned char *key, unsigned char key_len)
{
// 异或加密和解密使用相同的算法
// 因为 A ^ B ^ B = A,所以再次异或相同的密钥即可解密
encrypt_data(data, len, key, key_len);
}
/**
* 计算CRC校验值
* @param data 数据
* @param len 数据长度
* @return CRC校验值
* @note 使用CRC-8算法,多项式为0x31 (x^8 + x^5 + x^4 + 1)
* @note CRC校验用于检测数据传输或存储过程中的错误
*/
unsigned char calculate_crc(unsigned char *data, unsigned char len)
{
unsigned char crc = 0x00; // 初始CRC值
unsigned char i, j;
for (i = 0; i < len; i++)
{
crc ^= data[i]; // 与当前数据字节异或
for (j = 0; j < 8; j++) // 处理每个比特位
{
if (crc & 0x80) // 如果最高位为1
crc = (crc << 1) ^ 0x31; // 左移并与多项式异或
else
crc <<= 1; // 左移
}
}
return crc; // 返回计算得到的CRC值
}
/**
* 从EEPROM读取密钥
* @param key 密钥缓冲区
* @note 从EEPROM的密钥存储区域读取8字节密钥
*/
void read_key(unsigned char *key)
{
unsigned char i;
for (i = 0; i < KEY_LENGTH; i++)
{
key[i] = EEPROM_Read(EEPROM_KEY_ADDR + i); // 依次读取8字节密钥
}
}
/**
* 向EEPROM写入密钥
* @param key 密钥
* @note 向EEPROM的密钥存储区域写入8字节密钥
* @note 每次写入后需要延时5ms以确保数据写入完成
*/
void write_key(unsigned char *key)
{
unsigned char i;
for (i = 0; i < KEY_LENGTH; i++)
{
EEPROM_Write(EEPROM_KEY_ADDR + i, key[i]); // 依次写入8字节密钥
Delay_ms(5); // EEPROM写入需要延时
}
}
/**
* 验证密钥
* @param key 要验证的密钥
* @return 验证结果,0成功,1失败
* @note 比较输入密钥与存储在EEPROM中的密钥是否一致
*/
unsigned char verify_key(unsigned char *key)
{
unsigned char stored_key[KEY_LENGTH];
unsigned char i;
read_key(stored_key); // 读取存储的密钥
for (i = 0; i < KEY_LENGTH; i++)
{
if (key[i] != stored_key[i]) // 逐字节比较
return 1; // 验证失败
}
return 0; // 验证成功
}
/**
* 更改密钥
* @param old_key 旧密钥
* @param new_key 新密钥
* @return 操作结果,0成功,1失败
* @note 只有提供正确的旧密钥才能更改密钥
*/
unsigned char change_key(unsigned char *old_key, unsigned char *new_key)
{
// 验证旧密钥
if (verify_key(old_key))
return 1; // 旧密钥验证失败
// 写入新密钥
write_key(new_key);
return 0; // 密钥更改成功
}
/**
* 写入数据到加密芯片
* @param addr 数据地址(相对于数据存储区起始地址)
* @param data 数据
* @param len 数据长度
* @return 操作结果,0成功,1失败
* @note 数据会先加密再存储,并在数据末尾添加CRC校验值
*/
unsigned char write_data(unsigned char addr, unsigned char *data, unsigned char len)
{
unsigned char key[KEY_LENGTH];
unsigned char i;
unsigned char crc;
// 检查地址和长度是否合法
// 预留1字节用于存储CRC校验值
if (addr + len + 1 > (EEPROM_SIZE – EEPROM_DATA_ADDR))
return 1; // 地址或长度超出范围
// 读取密钥
read_key(key);
// 加密数据
encrypt_data(data, len, key, KEY_LENGTH);
// 计算CRC校验值
crc = calculate_crc(data, len);
// 写入加密后的数据
for (i = 0; i < len; i++)
{
EEPROM_Write(EEPROM_DATA_ADDR + addr + i, data[i]);
Delay_ms(5); // 延时确保写入完成
}
// 写入CRC校验值
EEPROM_Write(EEPROM_DATA_ADDR + addr + len, crc);
Delay_ms(5); // 延时确保写入完成
return 0; // 写入成功
}
/**
* 从加密芯片读取数据
* @param addr 数据地址(相对于数据存储区起始地址)
* @param data 数据缓冲区
* @param len 数据长度
* @return 操作结果,0成功,1失败,2 CRC校验错误
* @note 读取数据后会先验证CRC,然后解密
*/
unsigned char read_data(unsigned char addr, unsigned char *data, unsigned char len)
{
unsigned char key[KEY_LENGTH];
unsigned char i;
unsigned char stored_crc, calculated_crc;
// 检查地址和长度是否合法
if (addr + len + 1 > (EEPROM_SIZE – EEPROM_DATA_ADDR))
return 1; // 地址或长度超出范围
// 读取密钥
read_key(key);
// 读取加密数据
for (i = 0; i < len; i++)
{
data[i] = EEPROM_Read(EEPROM_DATA_ADDR + addr + i);
}
// 读取存储的CRC校验值
stored_crc = EEPROM_Read(EEPROM_DATA_ADDR + addr + len);
// 计算读取数据的CRC校验值
calculated_crc = calculate_crc(data, len);
// 验证CRC校验值
if (stored_crc != calculated_crc)
return 2; // CRC校验错误
// 解密数据
decrypt_data(data, len, key, KEY_LENGTH);
return 0; // 读取成功
}
/**
* 加密芯片初始化
* 首次使用时设置默认密钥
* @note 检查密钥存储区域是否为空白(全0xFF),如果是则设置默认密钥
*/
void crypto_chip_init(void)
{
unsigned char default_key[KEY_LENGTH] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
unsigned char i;
unsigned char blank_check = 0xFF;
// 检查密钥是否已设置
for (i = 0; i < KEY_LENGTH; i++)
{
blank_check &= EEPROM_Read(EEPROM_KEY_ADDR + i);
}
// 如果密钥未设置(EEPROM为全0xFF),设置默认密钥
if (blank_check == 0xFF)
{
write_key(default_key);
}
}
/**
* 主函数
* @note 测试加密芯片的基本功能
*/
void main(void)
{
unsigned char test_data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; // 测试数据
unsigned char read_data_buf[8]; // 存储读取的数据
unsigned char i; // 循环变量
unsigned char result; // 操作结果
// 初始化加密芯片
crypto_chip_init();
// 测试写入数据
result = write_data(0x00, test_data, 8);
if (result == 0)
{
// 写入成功,测试读取数据
result = read_data(0x00, read_data_buf, 8);
if (result == 0)
{
// 读取成功,验证读取的数据是否与写入的数据一致
for (i = 0; i < 8; i++)
{
if (test_data[i] != read_data_buf[i])
{
// 数据不一致,处理错误
// 实际应用中可以添加错误处理代码
}
}
}
else if (result == 1)
{
// 地址或长度错误
}
else if (result == 2)
{
// CRC校验错误
}
}
while (1); // 程序循环
}

我爱单片机