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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

这一章节介绍如何获取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 95M04的代码实现 对于提到的STM32系列中的具体型号如STM32F401RCT6以及其开发环境STM32CubeIDE,可以确认的是该集成开发环境中已经包含了FreeRTOS的支持[^1]。这意味着开发者无需手动移植FreeRTOS至项目中即可直接利用它来构建多线程应用程序。 针对具体的STM32 95M04芯片及其可能的应用场景,在未找到确切匹配此特定型号的情况下,可以通过相似的功能需求来进行推导并编写相应的代码实例。下面提供了一个基于通用STM32微控制器架构下使用FreeRTOS控制LED闪烁的基础示例: #### LED闪灯功能的FreeRTOS实现 以下是通过创建两个任务分别控制不同频率下的LED点亮/熄灭状态的一个简单例子: ```c #include "stm32f4xx_hal.h" #include "cmsis_os.h" #define LED_PIN GPIO_PIN_5 #define LED_GPIO_PORT GPIOA void StartDefaultTask(void const * argument); osThreadId defaultTaskHandle; int main(void){ HAL_Init(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = LED_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); osKernelInitialize(); defaultTaskHandle = osThreadCreate(osThread(Default), NULL); osKernelStart(); } void StartDefaultTask(void const * argument){ while(1){ HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN); osDelay(500); /* Delay for half a second */ } } ``` 上述代码展示了如何初始化硬件资源并通过定义的任务周期性改变指定引脚的状态从而达到使连接在其上的LED按一定间隔时间亮起再关闭的效果。需要注意实际应用时应依据目标板卡的具体接线情况调整对应的端口与针脚编号设置。 尽管本案例并未特别提及到所谓“95M04”的细节部分,但从整体逻辑出发仍具有一定的借鉴意义。如果存在更精确的要求,则建议查阅官方数据手册或者联系供应商获取进一步的技术支持文档资料。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

叶与花语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值