STM32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭

大家好!我叫肖亚平,从小热爱上了电子技术,读书时阴差阳错的选择了电子这方面的专业,学习电子技术、一直到今年毕业。对于我的理解来说,学校里面学到的技术不是全部实用,但是必须有用,所以一直奋斗在前线。我对学习总结出一句话“压力不是有人努力,而是比你牛X N倍的人依然在努力

 

STM32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭。STM32芯片通过STlink可以把程序读出来。只要在相同的芯片上面,就可以跑起来,那么如何才能让一个程序只能够在一个芯片上正常运行呢?

经过一个下午的时间,总结出这个方法。通过读取ID号,然后在修改HEX文件来保护自己的程序不被抄袭,相对于开发成本中等的产品,还是有用的。但是,有一点麻烦。我用的是STM32F103ZET6芯片。下面将介绍详细的步骤和方法。

 

一.获取ID码

 

  1. 工欲善其事,必先利其器,准备好工具,需要一个STlink及下载软件,一个可以正常运行的硬件。
  2. uint16_t temp[12];   //存放芯片ID的临时变量
  3. uint16_t aa[12] = {0xee,0x01,0x02,0x03,0x04,
  4.                0x05,0x06,0x07,0x08,0x09,
  5.                0x10,0x11};//预置ID号,在HEX文中修改。
  6.  
  7. aa这个数组用于存放预置ID号

复制代码

 

  1. /******************************************************************************
  2. 函数名称:读取芯片唯一ID码
  3. 创建时间:2015-08-11
  4. 修改时间:2015-08-11
  5. 备    注:
  6. ******************************************************************************/
  7.  
  8. void Get_ChipID(void)
  9. {
  10.     u32 temp0,temp1,temp2;
  11. temp0 = *(__IO u32*)(0x1FFFF7E8);    //产品唯一身份标识寄存器(96位)
  12.     temp1 = *(__IO u32*)(0x1FFFF7EC);
  13. temp2 = *(__IO u32*)(0x1FFFF7F0);
  14.                                   
  15. //ID码地址: 0x1FFFF7E8   0x1FFFF7EC  0x1FFFF7F0 ,只需要读取这个地址中的数据就可以了。
  16.  
  17.     temp[0] = (u8)(temp0 & 0x000000FF);
  18.     temp[1] = (u8)((temp0 & 0x0000FF00)>>8);
  19.     temp[2] = (u8)((temp0 & 0x00FF0000)>>16);
  20.     temp[3] = (u8)((temp0 & 0xFF000000)>>24);
  21.     temp[4] = (u8)(temp1 & 0x000000FF);
  22.     temp[5] = (u8)((temp1 & 0x0000FF00)>>8);
  23.     temp[6] = (u8)((temp1 & 0x00FF0000)>>16);
  24.     temp[7] = (u8)((temp1 & 0xFF000000)>>24);
  25.     temp[8] = (u8)(temp2 & 0x000000FF);
  26.     temp[9] = (u8)((temp2 & 0x0000FF00)>>8);
  27.     temp[10] = (u8)((temp2 & 0x00FF0000)>>16);
  28.     temp[11] = (u8)((temp2 & 0xFF000000)>>24);         
  29. }

复制代码


 

  1. 通过void Get_ChipID(void)就可以得到ID码了,这个ID码可以用串口输出,也可以用STM32 ST-LINK Utility读出来。这里,两种方法我都会讲到。
  2.  
  3. 方法一:通过串口输出得到ID码。
  4.  
  5. /****************************************************************************************************************************
  6. 函数名称:串口2初始化配置
  7. 创建时间:2015-08-11
  8. 修改时间:2015-08-11
  9. 备    注:
  10. *****************************************************************************************************************************/
  11. void USART2_Config(void)
  12. {
  13. GPIO_InitTypeDef   GPIO_InitStructure;  //定义GPIO类型结构体
  14. USART_InitTypeDef  USART_InitStructure; //定义串口类型结构体
  15. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //配置GPIOA时钟,并使能时钟。
  16. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//配置USART2的时钟,并使能时钟
  17. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;       //配置PA.02作为TXD
  18. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //配置成推挽输出
  19. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO时钟为50MHZ
  20. GPIO_Init(GPIOA, &GPIO_InitStructure);           //初始化GPIO
  21. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;  //配置PA.03作为RXD
  22. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //配置GPIO输入浮空
  23. GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化GPIO
  24. USART_InitStructure.USART_BaudRate = 115200;  //配置串口波特率为115200
  25. USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8位数据位
  26. USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
  27. USART_InitStructure.USART_Parity = USART_Parity_No; //
  28. USART_InitStructure.USART_HardwareFlowControl = T_HardwareFlowControl_None; //硬件流程控制
  29. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //启动发送和接收
  30. USART_Init(USART2, &USART_InitStructure); //初始化串口2
  31.  
  32. USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能串口2接收中断 
  33. USART_Cmd(USART2, ENABLE); //使能外部中断
  34. }

复制代码



 

  1. /****************************************************************************************************************************
  2. 函数名称:USART2发送数据函数
  3. 创建时间:2015-08-11
  4. 修改时间:2015-08-11
  5. 备    注:
  6. *****************************************************************************************************************************/
  7. void Usart2_SendData(uint16_t uiSendDataNumber,uint16_t * uiData)
  8. {
  9.   static uint16_t uiTempData = 0; //发送数据临时变量
  10. for(uiTempData = uiSendDataNumber;uiTempData > 0;uiTempData--) //数据的个数
  11. {
  12. USART_SendData(USART2, *uiData++); //调用发送函数
  13. //           uiData++; //发送数据的地址加1,切换到下一个要发送数据的地址。
  14. while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);//发送缓冲区空状态标志位。只有当缓冲区为空时,才发送下一个数据。
  15.   }
  16. }

复制代码


 

串口配置好后,就可以调用Usart2_SendData(12,temp);函数,就能在串口助手看到ID码了。

 


 

  1. 芯片唯一ID码:32 FF DA 05 43 41 38 36 30 71 02 43   96位

复制代码

方法二:通过STlink得到ID码。

 

1、打开STM32 ST-LINK Utility,确保STlink与硬件连接正常。

2、在Address中输入“0x1FFFF7E8”芯片ID码的首地址。输入完成后,STlink自动读取ID码。

 




 

3、确定后,自动读取ID码

 

 

但是,用STlink读出来的只有64位,少了32位,这个“0x1FFFF7F0”地址中读取失败,我也不知道是什么原因,可能是厂家不允许下载器访问。


 

地  址

0x1FFFF7E8

0x1FFFF7EC

0x1FFFF7F0

ID数据

05 DA FF 32

36 38 41 43

空(不允许读)

 

我们把上面ID码整理一下:32 FF DA 05 43 41 38 36,由于在内存中是小端存储方式。所以是这样的。

 

通过两种方法,把我们想要的ID码得到后。就可以进行最关键的一步,修改HEX文件。


 

二.修改HEX文件加密

 

1、打开STM32 ST-LINK Utility,打开HEX文件。

 

 

  1. uint16_t  aa[12] = {0xee,0x01,0x02,0x03,0x04,
  2.                0x05,0x06,0x07,0x08,0x09,
  3.                0x10,0x11};//预置ID号,在HEX文中修改。

复制代码

 

现在就打HEX文件打开后。找到上面定义的数组,这个是关键所在,我花了一个下午时间,才搞清楚。



 

[size=9.0000pt]2、找到自定义ID在地址的位置后,开始修改。

 




 

3、把数组“aa[12]” 里面的内容改成与芯片的实际ID码一致。如果程序被读出来,在另外一片相同的芯片上面也运行不起来。因为设置的ID与芯片实际ID不一致。

 



 

4、现在如果下载,会提示你文件被修改,需要保存。我们保存成HEX文件到桌面。

 


 

5、重新打开刚才保存的HEX文件。现在可以看到,修改的ID已经在HEX文件里面了。

 




 

6、下载程序到单片机。

 

 

7、完成下载。到这儿就基本完成了。

 

8、看到这儿,有的人会问,为什么不在编译时直接放在数组里,还要在HEX文件中修改,这样做的目的是方便批量生产。


 

 

  1. 在程序中,我做了一个简单的判断。如下
  2.   Get_ChipID();  //先读出芯片实际ID          
  3.   for(i=0;i<12;i++)
  4.         {
  5.                         if(temp == aa) //判断设置的ID与实际ID是不是一致。如果一致。LED灯不亮
  6.                         {
  7.                                 ;
  8.                         }
  9.                         else
  10.                         {
  11.                                  GPIO_ResetBits(GPIOF, GPIO_Pin_6);
  12.             }
  13.   }

复制代码

  • 6
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
以下是基于STM32通过I2C读取MPU6050陀螺仪芯片数据的核心程序: ```c #include "stm32f10x.h" #include "i2c.h" #define MPU6050_ADDR 0xD0 // MPU6050 I2C 地址 #define MPU6050_PWR_MGMT_1 0x6B // MPU6050 电源管理寄存器地址 #define MPU6050_ACCEL_XOUT_H 0x3B // MPU6050 加速度计X轴高位数据寄存器地址 void MPU6050_Init(void) { I2C_WriteByte(MPU6050_ADDR, MPU6050_PWR_MGMT_1, 0x00); // 唤醒 MPU6050 I2C_WriteByte(MPU6050_ADDR, 0x1B, 0x08); // 设置陀螺仪量程为 +-500dps I2C_WriteByte(MPU6050_ADDR, 0x1C, 0x08); // 设置加速度计量程为 +-4g } void MPU6050_ReadAccel(short *accel) { uint8_t buf[6]; I2C_ReadBytes(MPU6050_ADDR, MPU6050_ACCEL_XOUT_H, buf, 6); // 读取加速度计X,Y,Z轴数据 accel[0] = (buf[0] << 8) | buf[1]; // 将高位和低位数据合并 accel[1] = (buf[2] << 8) | buf[3]; accel[2] = (buf[4] << 8) | buf[5]; } int main(void) { short accel[3]; I2C_Init(); // 初始化 I2C MPU6050_Init(); // 初始化 MPU6050 while(1) { MPU6050_ReadAccel(accel); // 读取加速度计X,Y,Z轴数据 // 做一些处理 } } ``` 其中,`I2C_WriteByte` 和 `I2C_ReadBytes` 函数是通过STM32的I2C外设实现的,用于向I2C设备写入数据和读取数据。在初始化函数中,先通过 `I2C_WriteByte` 函数设置MPU6050的电源管理寄存器,使其处于工作状态,然后设置陀螺仪和加速度计的量程。在读取函数中,通过 `I2C_ReadBytes` 函数读取MPU6050加速度计X,Y,Z轴的高位和低位数据,然后将其合并成16位数据。最后在主函数中循环读取加速度计数据,并进行一些处理。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值