STM32读取芯片唯一码UID

1 使用方法

STM32通过读取芯片唯一ID号来实现程序的保护,防止被抄袭。按照用户不同的用法,可以以字节(8位)为单位读取,也可以以半字(16位)或者全字(32位)读取。在这里要提醒读者,要注意大端小端模式。

通过 datasheet(STM32用户指南)来了解,打开书签,找到跟 ID有关的,终于找到了“Unique deviceID register (96 bits)”,那就是它了,错不了。

img

img

img

通过阅读datasheet,初步了解到,想获取 Device ID,只需要从这个地址“0x1FFF F7E8”开始12字节96bit,读取即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 通过地址手动获得芯片ID
/*定义STM32 MCU的类型*/
typedef enum {
STM32F0_,
STM32F1_,
STM32F2_,
STM32F3_,
STM32F4_,
STM32F7_,
STM32L0_,
STM32L1_,
STM32L4_,
STM32H7_,
} MCUTypedef;

uint32_t ID_Addr_Table[]={
[STM32F0_] = 0x1FFFF7AC, /*STM32F0唯一ID起始地址*/
[STM32F1_] = 0x1FFFF7E8, /*STM32F1唯一ID起始地址*/
[STM32F2_] = 0x1FFF7A10, /*STM32F2唯一ID起始地址*/
[STM32F3_] = 0x1FFFF7AC, /*STM32F3唯一ID起始地址*/
[STM32F4_] = 0x1FFF7A10, /*STM32F4唯一ID起始地址*/
[STM32F7_] = 0x1FF0F420, /*STM32F7唯一ID起始地址*/
[STM32L0_] = 0x1FF80050, /*STM32L0唯一ID起始地址*/
[STM32L1_] = 0x1FF80050, /*STM32L1唯一ID起始地址*/
[STM32L4_] = 0x1FFF7590, /*STM32L4唯一ID起始地址*/
[STM32H7_] = 0x1FF0F420 /*STM32H7唯一ID起始地址*/
};

/**
* 函数功能: 获取芯片ID
* 输入参数: MCUTypedef type
* 返 回 值: 无
* 说 明:每个芯片都有唯一的 96_bit unique ID
*/
void Get_ChipID2(void) {
uint32_t id[3] = {0};

uint8_t size = sizeof(ID_Addr_Table)/sizeof(ID_Addr_Table[0]);
//printf("\r\n size: %d\r\n", size);
uint32_t UID_BASE = 0x1FFF7A10
for(uint8_t i = 0; i < size; i++) {
if(ID_Addr_Table[i] == UID_BASE) {
id[0]=*(uint32_t*)(ID_Addr_Table[i]);
id[1]=*(uint32_t*)(ID_Addr_Table[i]+4);
id[2]=*(uint32_t*)(ID_Addr_Table[i]+8);
printf("\r\n芯片的唯一ID为: %08X-%08X-%08X\r\n",id[0], id[1], id[2]);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 函数功能: 获取MCU系列ID HAL库封装好的API
* 输入参数: 无
* 返 回 值: 无
* 说 明:每个芯片都有唯一的 96_bit unique ID
*/
void getSTM32SeriesID(void) {
uint32_t UIDw[3];
uint32_t HalVersion = HAL_GetHalVersion();
uint32_t REVID = HAL_GetREVID();
uint32_t DEVID = HAL_GetDEVID();
UIDw[0] = HAL_GetUIDw0();
UIDw[1] = HAL_GetUIDw1();
UIDw[2] = HAL_GetUIDw2();
printf("\r\n芯片的HAL库版本号为: %d\r\n", HalVersion);
printf("\r\n芯片修订标识符为: %d\r\n", REVID);
printf("\r\n芯片标识符为: %d\r\n", DEVID);
printf("\r\n芯片的唯一ID为: %08X-%08X-%08X\r\n", UIDw[0], UIDw[1], UIDw[2]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

/**
* 函数功能: 获取芯片ID
* 输入参数: 无
* 返 回 值: 无
* 说 明:每个芯片都有唯一的 96_bit unique ID
*/
void Get_ChipID(void) {
CPU_ID[0] = *(__IO uint32_t *)(0X1FFFF7F0); // 高字节
CPU_ID[1] = *(__IO uint32_t *)(0X1FFFF7EC); //
CPU_ID[2] = *(__IO uint32_t *)(0X1FFFF7E8); // 低字节

/* 芯片的唯一ID */
printf("\r\n芯片的唯一ID为: %08X-%08X-%08X\r\n",CPU_ID[0], CPU_ID[1], CPU_ID[2]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

/**
* 函数功能: 获取芯片ID
* 输入参数: 无
* 返 回 值: 无
* 说 明:每个芯片都有唯一的 96_bit unique ID
*/
void Get_UID(void) {
uint8_t UID[12];
uint8_t MAC[12];

uint8_t i = 0;
//用芯片唯一ID来做模块的MAC F0系列 0x1FFFF7AC F1系列 0x1FFFF7E8
for(i=0; i < 8; i++) {
MAC[i] = *(uint8_t *)(0x1FFFF7E8 + i);
UID[i] = MAC[i];
}
UID[8] = *(uint8_t *)(0x1FFFF7E8 + 8);
UID[9] = *(uint8_t *)(0x1FFFF7E8 + 9);
UID[10] = *(uint8_t *)(0x1FFFF7E8 + 10);
UID[11] = *(uint8_t *)(0x1FFFF7E8 + 11);
// Mac变量是UID经过简单的赋值及相加运算后得到的我们需在的8位设备ID。
// 实际应用的可以根据需求来自行设计算法要据UID计算长度不同的ID。
MAC[4] += UID[8];
MAC[5] += UID[9];
MAC[6] += UID[10];
MAC[7] += UID[11];

/* 芯片的唯一ID */
printf("\r\n UID: %0.2X-%0.2X-%0.2X-%0.2X\r\n",UID[8],UID[9],UID[10],UID[11]);
printf("\r\n MAC: %X-%X-%X-%X\r\n", MAC[4], MAC[5], MAC[6], MAC[7]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

/**
* 函数功能: 获取芯片ID
* 输入参数: 无
* 返 回 值: 无
* 说 明:每个芯片都有唯一的 96_bit unique ID
*/
void Get_UID2(void) {
uint8_t i = 0;
//用芯片唯一ID来做模块的MAC F0系列 0x1FFFF7AC F1系列 0x1FFFF7E8
for(i=0; i<12; i++) {
MAC[i] = *(uint8_t *)(0x1FFFF7E8 + i);
printf(" %0.2X", MAC[i]);
}

//芯片的唯一ID为: 30 FF 69 06 4D 50 35 35 39 25 08 43
if(MAC[0] == 0x30 && MAC[1] == 0xFF && MAC[2] == 0x69 &&
MAC[3] == 0x06 && MAC[4] == 0x4D && MAC[5] == 0x50 &&
MAC[6] == 0x35 && MAC[7] == 0x35 && MAC[8] == 0x39 &&
MAC[9] == 0x25 && MAC[10] == 0x08 && MAC[11] == 0x43) {
printf("\r\n pass ok \r\n");
} else {
printf("\r\n error \r\n");
}
}

注意:STM32单片机的存储方式为小端模式。

一般的大小端:
地址从小到大,先放低字节,再放高字节:小端模式
地址从小到大,先放高字节,再放低字节:大端模式