STC15读取内部ID示例程序

STC15读取内部ID示例程序


  • 🎉本案例基于STC15F2K60S2为验证对象。

📑STC15 ID序列介绍

  • STC15系列STC最新一代STC15系列单片机出厂时都具有全球唯一身份证号码(ID号)。最新STC15系列单片机的程序存储器的最后7个字节单元的值是全球唯一ID号,用户不可修改,但IAP15系列单片机的 整个程序区是开放的,可以修改,建议利用全球唯一ID号加密时,使用STC15系列单片机,并将EEPROM功能使用上,从EEPROM起始地址0000H开始使用,可以有效杜绝对全球唯一ID号的攻击。
  • 除程序存储器的最后7个字节单元的内容是全球唯一ID号外,单片机内部RAM的F1H ~ F7H单元 (对于STC15F100W系列及STC15W104SW系列单片机是内部RAM的71H - 77H单元)的内容也为全球唯一ID号。用户可以在单片机上电后读取内部RAM单元F1H - F7H (对于STC15F100W系列及STC15W104SW系列单片机是内部RAM单元71H - 77H)连续7个单元的值来获取此单片机的唯一身份证号码(ID号 ), 使用“ MOV @Ri” 指令来读取。如果用户需要用全球唯一ID号进行用户自己的软件加密,建议用户在程序的多个地方有技巧地判断自己的用户程序有无被非法修改,提高解密的难度,防止解密者修改程序,绕过对全球唯一ID号的判断。
  • 使用程序区的最后7个字节的全球唯一ID号比使用内部RAM单元 F1H - F7H (或内部RAM单元71H - 77H)的全球唯一ID号更难被攻击。建议用户使用程序区最后7个字节的全球唯一ID号,而不要使用内部RAM单元 F1H - F7H (或内部RAM单元71H - 77H)的全球唯一ID号。

🌼有关ID号在大批量生产中的应用方法

  1. 先烧一个程序进去(选择下次下载用户程序时,不擦除用户EEPROM区);
    在这里插入图片描述
  2. 读程序区的ID号(STC15系列是程序区的最后7个字节),经用户自己的复杂的加密算法对程序区的ID号加密运算后生成一个新的数——用户自加密ID号,写入STC15系列用户EEPROM区的EEPROM。
  3. 再烧一个最终出厂的程序进去(选择下次下载用户程序时将用户EEPROM区一并擦除),如下图所示:
    在这里插入图片描述
  4. 在用户程序区的多处读程序区的ID号和用户自加密ID号比较(经用户自己的复杂的解密算法解密后),如不对应,则6个月后随机异常,或200次开机后随机异常最终出厂的程序不含加密算法。
  5. 另外,在程序区的多个地方判断用户自己的程序是否被修改,如被修改,则6个月后随机异常,或200次开机后随机异常,将不用的用户程序区用所谓的有效程序全部填满。

📝示例程序

  • ✨以STC15F2K60S2为例,主时钟频率:22.1184MHz,波特率:115200
    在这里插入图片描述
//#include "reg51.h"
#include <STC15F2K60S2.H>
//#include "STC15Fxxxx.H"
//typedef unsigned char u8;
//typedef unsigned    int u16;

#define FOSC 22118400L          //系统频率
#define BAUD 115200             //串口波特率

#define S1_S0 0x40              //P_SW1.6
#define S1_S1 0x80              //P_SW1.7
bit busy;
//sfr T2H = 0xd6; //定时器2高8位
//sfr T2L = 0xd7; //定时器2低8位
//sfr AUXR = 0x8e; //辅助寄存器
#define ID_ADDR_RAM 0xf1 //ID号的存放在RAM区的地址为0F1H
//ID号的存放在程序区的地址为程序空间的最后7字节
//#define ID_ADDR_ROM 0x03f9 //1K程序空间的MCU(如STC15F201EA, STC15F101EA)
//#define ID_ADDR_ROM 0x07f9 //2K程序空间的MCU(如STC15F402AD,
//STC15F202EA, STC15F102EA)
//#define ID_ADDR_ROM 0x0bf9 //3K程序空间的MCU(如STC15F203EA, STC15F103EA)
//#define ID_ADDR_ROM 0x0ff9 //4K程序空间的MCU(如STC15F404AD, STC15F204EA,
//STC15F104EA)
//#define ID_ADDR_ROM 0x13f9 //5K程序空间的MCU(如 STC15F206EA, STC15F106EA)
//#define ID_ADDR_ROM 0x1ff9 //8K程序空间的MCU(如STC15F2K08S2, STC15F1K08AD,
//STC15F408AD)
//#define ID_ADDR_ROM 0x27f9 //10K程序空间的MCU(如STC15F410AD)
//#define ID_ADDR_ROM 0x2ff9 //12K程序空间的MCU(如STC15F408AD)
//#define ID_ADDR_ROM 0x3ff9 //16K程序空间的MCU(如STC15F2K16S2,
//STC15F1K16AD)
//#define ID_ADDR_ROM 0x4ff9 //20K程序空间的MCU(如STC15F2K20S2)
//#define ID_ADDR_ROM 0x5ff9 //24K程序空间的MCU
//#define ID_ADDR_ROM 0x6ff9 //28K程序空间的MCU
//#define ID_ADDR_ROM 0x7ff9 //32K程序空间的MCU(如STC15F2K32S2)
//#define ID_ADDR_ROM 0x9ff9 //40K程序空间的MCU(如STC15F2K40S2)
//#define ID_ADDR_ROM 0xbff9 //48K程序空间的MCU(如STC15F2K48S2)
//#define ID_ADDR_ROM 0xcff9 //52K程序空间的MCU(如STC15F2K52S2)
//#define ID_ADDR_ROM 0xdff9 //56K程序空间的MCU(如STC15F2K56S2)
#define ID_ADDR_ROM 0xeff9 //60K程序空间的MCU(如STC15W4K60S4,STC15F2K60S2)

//-----------------------------------------
void InitUart();
void SendUart(u8 dat);
//-----------------------------------------
void main()
{
    u8   idata *iptr;
    u8 code *cptr;
    u8   i;
	 P0M0 = 0x00;
    P0M1 = 0x00;
    P1M0 = 0x00;
    P1M1 = 0x00;
    P2M0 = 0x00;
    P2M1 = 0x00;
    P3M0 = 0x00;
    ACC = P_SW1;
    ACC &= ~(S1_S0 | S1_S1);    //S1_S0=0 S1_S1=0
//	S1_USE_P30P31();        //UART1 使用P30 P31口   默认
    InitUart(); //串口初始化
    iptr = ID_ADDR_RAM; //从RAM区读取ID号
    for (i = 0; i < 7; i++) //读7个字节
    {
        SendUart(*iptr++); //发送ID到串口
    }
    cptr = ID_ADDR_ROM; //从程序区读取ID号
    for (i = 0; i < 7; i++) //读7个字节
    {
        SendUart(*cptr++); //发送ID到串口
    }
    while (1); //程序终止
}
/*----------------------------
串口初始化
----------------------------*/
void InitUart()//115200bps@22.1184MHz
{
	SCON = 0x50;		//8位数据,可变波特率
	 AUXR = 0x40;                //定时器1为1T模式
    TMOD = 0x00;                //定时器1为模式0(16位自动重载)
    TL1 = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
    TH1 = (65536 - (FOSC/4/BAUD))>>8;
    TR1 = 1;                    //定时器1开始启动
    ES = 1;                     //使能串口中断
    EA = 1;


}
/*----------------------------
发送串口数据
----------------------------*/
void SendUart(u8 dat)
{
	  while (busy);               //等待前面的数据发送完成
    ACC = dat;                  //获取校验位P (PSW.0)
    busy = 1;
    SBUF = ACC;                 //写数据到UART数据寄存器
}
/*----------------------------
UART 中断服务程序
-----------------------------*/
void Uart() interrupt 4
{
//    if (RI)
//    {
//        RI = 0;                 //清除RI位
//    }
    if (TI)
    {
        TI = 0;                 //清除TI位
        busy = 0;               //清忙标志
    }
}

📚程序源码

复制这段内容后打开百度网盘手机App,操作更方便哦
链接: https://pan.baidu.com/s/1z5i6KBB_6B397_o8QUTEMw
提取码: eaqb
STC15F2K60S2是一款单片机,它内部集成了一个12位的ADC模块,可以实现模拟信号的数字化转换。以下是一个简单的STC15F2K60S2内部ADC转换程序,供参考: ``` #include <reg52.h> sbit ADC_CS = P1^4; // ADC片选 sbit ADC_CLK = P1^5; // ADC时钟 sbit ADC_DIN = P1^6; // ADC数据输入 sbit ADC_DOUT = P1^7; // ADC数据输出 unsigned int getADCResult(unsigned char ch) { unsigned char i; unsigned int result = 0; ADC_CS = 0; // 使能ADC片选 ADC_CLK = 0; // 将ADC时钟置为0 ADC_DIN = 1; // 将ADC数据输入置为1 ADC_CLK = 1; // 将ADC时钟置为1 ADC_CLK = 0; // 将ADC时钟置为0 ADC_DIN = 1; // 将ADC数据输入置为1 ADC_CLK = 1; // 将ADC时钟置为1 ADC_CLK = 0; // 将ADC时钟置为0 ADC_DIN = (ch << 4) | 0x80; // 发送通道号和启动转换命令 ADC_CLK = 1; // 将ADC时钟置为1 ADC_CLK = 0; // 将ADC时钟置为0 ADC_DIN = 0; // 将ADC数据输入置为0 for (i = 0; i < 12; i++) // 读取ADC转换结果 { ADC_CLK = 1; ADC_CLK = 0; result <<= 1; result |= ADC_DOUT; } ADC_CS = 1; // 禁用ADC片选 return result; } void main() { unsigned int result; while (1) { result = getADCResult(0); // 读取通道0的ADC转换结果 // 处理ADC转换结果 } } ``` 在上述程序中,通过getADCResult函数实现了ADC转换过程。具体步骤如下: 1. 发送启动转换命令:首先向ADC发送启动转换命令,让ADC开始对指定通道的模拟信号进行转换。 2. 读取转换结果:转换结束后,通过时钟和数据线读取ADC转换结果。由于STC15F2K60S2内部ADC是12位的,因此需要读取12个时钟周期的数据。在每个时钟周期,将ADC_CLK置为1,然后读取ADC_DOUT的值,并将其左移一位,最终得到12位的转换结果。 3. 关闭ADC片选:转换结果读取完毕后,需要禁用ADC片选。 在主函数中,可以通过调用getADCResult函数来读取指定通道的ADC转换结果,并进行相应的处理。例如,上述程序读取了通道0的ADC转换结果,并可以在处理结果的代码块中进行相应的处理。 需要注意的是,上述程序仅为示例程序,实际应用中需要根据具体的需求进行相应的修改。同时,还需要根据具体的硬件连接情况,在程序中正确配置ADC_CS、ADC_CLK、ADC_DIN和ADC_DOUT等引脚。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值