STM32开发(13)----获取唯一设备标识符UID

文章详细介绍了如何通过读取STM32芯片内存中的特定地址来获取其96位的唯一设备标识符UID,包括UID的组成和不同MCU系列的地址差异。实验过程展示了使用CubeMx配置工程,并通过添加代码实现UID的读取和打印,最后通过两种方法验证了UID的获取。


前言

这一章节介绍如何获取STM32芯片中的唯一的ID号的两种方法。


一、什么是UID

在许多项目中,识别设备是必要的。从简单的设备描述到更复杂的设备,如 USB 串行命名、安全密钥、加密密钥等。有许多方法可以在微控制器中实现唯一ID。从简单的硬编码到固件中,单独刷新信息闪存到设备首次运行时随机生成。当谈到STM32 MCU时,还有另一种更简单,更清晰的可能性。在制造过程中,96位ID被编码到微控制器上。

所谓的唯一 ID 由 3 个部分组成:

  1. 晶圆上的 X 和 Y 坐标以 BCD 格式表示
  2. 批号
  3. 晶圆编号

对于 UID 访问,您只需在指定地址读取内存。请记住,不同的MCU线在内存中具有此数据扇区的不同位置。在这篇文章的最后,我将总结其中的大部分。

由于STM32是32位处理器,我们必须执行三次32位偏移的读出,以获得完整的92位ID。当然,我们只能使用其中的一部分。

例如,STM32F0处理器的起始地址是0x1FFFF7AC。因此,要读取完整的 UID,我们必须读取以下地址:

#define ID1 (*(unsigned long *)0x1FFFF7AC)
#define ID2 (*(unsigned long *)0x1FFFF7B0)
#define ID3 (*(unsigned long *)0x1FFFF7B4)

我们还可以将起始地址定义为数组的开头:

unsigned long *id = (unsigned long *)0x1FFFF7AC;
id[0]
id[1]
id[2]

由于STM32中的无符号长整型是一个32位变量,因此按索引访问数组会导致索引*32位偏移与起始地址。
您可以在下面找到大多数STM32微控制器的唯一ID起始地址。
在这里插入图片描述

二、实验过程

这里Cubex配置非必须,主要是为了打印出UID,所以可以使用前面实验的代码

1.CubeMx配置

选择芯片stm32f103c6t6,新建工程

在这里插入图片描述

设置时钟源,最小系统外部晶振8Mhz,作为外部高速HSE时钟源。由于没有外接外部低速晶振,这里低速时钟源选择旁路时钟源。

在这里插入图片描述

配置时钟树,这里使用官方推荐的配置

在这里插入图片描述

CubeMX配置如下:
在这里插入图片描述
USART1的参数配置如下,波特率115200,传输数据长度为8 Bit,奇偶检验无,停止位1.其他参数默认
在这里插入图片描述

SYS选项卡中Debug选项选择串口(这个选项可以设置,不会有影响)
在这里插入图片描述

Code Generator中设置只拷贝使用到的库,分离.c和.h文件

在这里插入图片描述

设置好项目名称和路径,点击GENERATE CODE即可,生成后使用keil5 IDE打开。

在这里插入图片描述

2.代码实现

在usart.c文件后面添加如下代码,代码中添加了#ifdef宏定义进行条件编译,如果使用GUNC编译,则PUTCHAR_PROTOTYPE 定义为int __io_putchar(int ch)函数,否则定义为int fputc(int ch, FILE *f)函数。

/* USER CODE BEGIN 0 */
#include "stdio.h"
#ifdef __GNUC__
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
  #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
  #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
 
  return ch;
}
/* USER CODE END 0 */

两种获取方式如下:

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  
  /* USER CODE BEGIN 2 */
	//方法一
    uint32_t uid[3];
		
		uid[0] = (uint32_t)(READ_REG(*((uint32_t *)UID_BASE)));
		uid[1] = (uint32_t)(READ_REG(*((uint32_t *)(UID_BASE + 4U))));
		uid[2] = (uint32_t)(READ_REG(*((uint32_t *)(UID_BASE + 8U))));
    for(int8_t i = 0; i < 3; i++) {
        printf("%x \r\n", uid[i]);
    }   
		printf("===========================================\r\n"); 
		//方法二
    uint32_t *id = (uint32_t *)0x1FFFF7E8;
    for(int8_t i = 0; i < 3; i++) {
        printf("%x \r\n", *uint8_t *(id+i));
    }   
    printf("\r\n"); 
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
}

3.实验结果

这里打印的结果大家可能不同,这是正常的,相同才不正常

46528358 
46528358 

总结

本章简单介绍了获取STM32唯一设备标识符UID的方法。

### STM32读取芯片ID的方法 STM32微控制器提供了一个独特的96位设备唯一标识符UID),该标识符存储在只读寄存器中,基地址为`0x1FFF F7E8`[^2]。此UID对于每片STM32芯片来说都是独一无二的,因此可以用于多种用途,比如产品序列号、加密保护以及安全启动等功能。 以下是通过STM32 HAL库实现读取芯片UID的具体方法: #### 示例代码:读取STM32芯片的UID 下面是一个完整的C语言代码示例,展示如何使用HAL库读取STM32芯片的UID并打印到串口调试工具上。 ```c #include "stm32f1xx_hal.h" #include <stdio.h> // 定义UID寄存器的基地址 #define UID_BASE_ADDRESS ((uint32_t *)0x1FFFF7E8) void SystemClock_Config(void); static void MX_GPIO_Init(void); int main(void) { // 初始化HAL库 HAL_Init(); // 配置系统时钟 SystemClock_Config(); // GPIO初始化 MX_GPIO_Init(); uint32_t uid_high, uid_mid, uid_low; // 读取UID32uid_high = *UID_BASE_ADDRESS; // 读取UID中间32uid_mid = *(UID_BASE_ADDRESS + 1); // 读取UID32uid_low = *(UID_BASE_ADDRESS + 2); // 打印UID值到UART或其他调试接口 printf("STM32 Unique Device ID:\n"); printf("High: %X\n", uid_high); printf("Mid : %X\n", uid_mid); printf("Low : %X\n", uid_low); while (1) { // 主循环保持运行状态 } } /** * @brief 系统时钟配置函数 */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } /** * @brief GPIO初始化函数 */ static void MX_GPIO_Init(void) { __HAL_RCC_GPIOD_CLK_ENABLE(); /* Configure the User LED Pin as output push-pull */ GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_12; // Example pin for debugging purposes GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } /** * @brief 错误处理函数 */ void Error_Handler(void) { while(1) { // 用户可以根据需求定义错误处理逻辑 } } ``` 上述代码实现了以下功能: - **读取UID**:通过指针访问指定内存区域获取UID的高低三部分数据。 - **打印UID**:将UID分段显示以便于观察和记录。 - **硬件初始化**:包括系统时钟设置和基本GPIO配置。 需要注意的是,在实际项目开发过程中可能还需要考虑大小端模式的影响[^1]。 --- ### 大小端模式注意事项 由于不同平台可能存在大小端差异,如果需要跨平台传输或保存这些UID数值,则应统一采用某种固定的字节序形式进行转换后再操作。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶与花语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值