H753_CPP_Device_Electronic_Signature 1.0
STM32H753 96-bit device unique ID read and print tutorial
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1/* USER CODE BEGIN Header */
18/* USER CODE END Header */
19/* Includes ------------------------------------------------------------------*/
20#include "main.h"
21
22/* Private includes ----------------------------------------------------------*/
23/* USER CODE BEGIN Includes */
24#include <stdio.h>
25
26/* USER CODE END Includes */
27
28/* Private typedef -----------------------------------------------------------*/
29/* USER CODE BEGIN PTD */
30
31/* USER CODE END PTD */
32
33/* Private define ------------------------------------------------------------*/
34/* USER CODE BEGIN PD */
35
36/* USER CODE END PD */
37
38/* Private macro -------------------------------------------------------------*/
39/* USER CODE BEGIN PM */
40
41/* USER CODE END PM */
42
43/* Private variables ---------------------------------------------------------*/
44
45UART_HandleTypeDef huart1;
46
47/* USER CODE BEGIN PV */
48
49/* USER CODE END PV */
50
51/* Private function prototypes -----------------------------------------------*/
52void SystemClock_Config(void);
53static void MX_GPIO_Init(void);
54static void MX_USART1_UART_Init(void);
55/* USER CODE BEGIN PFP */
56
57/* USER CODE END PFP */
58
59/* Private user code ---------------------------------------------------------*/
60/* USER CODE BEGIN 0 */
61
62#ifdef REDIRECT_PRINTF
63#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
64
65PUTCHAR_PROTOTYPE
66{
67 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
68
69 return ch;
70}
71#endif
72
73/* USER CODE END 0 */
74
79int main(void)
80{
81
82 /* USER CODE BEGIN 1 */
83
84 /* USER CODE END 1 */
85
86 /* MCU Configuration--------------------------------------------------------*/
87
88 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
89 HAL_Init();
90
91 /* USER CODE BEGIN Init */
92
93 /* USER CODE END Init */
94
95 /* Configure the system clock */
97
98 /* USER CODE BEGIN SysInit */
99
100 /* USER CODE END SysInit */
101
102 /* Initialize all configured peripherals */
103 MX_GPIO_Init();
105 /* USER CODE BEGIN 2 */
106
107 printf("\x1b[2J\x1b[H"); // Clear the dumb terminal screen
108
109 /*
110 * Every STM32 has a factory-programmed 96-bit unique device identifier
111 * fused into silicon at manufacture time -- three consecutive 32-bit
112 * words. HAL_GetUIDw0/1/2() reads it via the HAL's dedicated accessors
113 * rather than dereferencing the UID base address directly, which keeps
114 * this code portable: the actual register address differs per STM32
115 * family, and HAL hides that difference behind the same three function
116 * calls on every one of them.
117 *
118 * No hashing, no packing into a shorter derived value -- the raw 96
119 * bits already ARE the identifier. An earlier version of this
120 * tutorial ran the UID through a third-party 32-bit hash function
121 * before printing anything; that added a dependency and a place for
122 * bugs to hide (the earlier version had one -- it hashed the wrong
123 * variable and never printed the hash result at all) without buying
124 * anything real for what this demo actually needs: identifying one
125 * specific, already-known board.
126 */
127 uint32_t uid[3];
128 uid[0] = HAL_GetUIDw0();
129 uid[1] = HAL_GetUIDw1();
130 uid[2] = HAL_GetUIDw2();
131
132 /*
133 * The 96 bits aren't arbitrary either -- ST's reference manual (RM0433,
134 * "Device electronic signature" > "Unique device ID register") documents
135 * an internal structure baked into these three words at manufacture
136 * time: the first word encodes the die's X/Y coordinates on the silicon
137 * wafer plus a wafer number, and the remaining two words together
138 * encode a 7-character lot number as ASCII text. This is genuinely how
139 * ST tracks a chip back to its manufacturing batch, not something added
140 * by this tutorial.
141 *
142 * This tutorial deliberately does NOT parse those sub-fields out here:
143 * the exact bit boundaries are given in a table in RM0433 and are worth
144 * reading directly from ST's own documentation rather than copying a
145 * bit-shift from a tutorial that could get a boundary wrong -- printing
146 * the three raw words, as done below, is already sufficient to uniquely
147 * identify this specific chip for the UID-comparison use case this
148 * tutorial is actually built around.
149 */
150
151 /*
152 * The UID isn't the only factory-programmed identification data on an
153 * STM32 -- ST groups it together with two more registers under the
154 * name "electronic signature" in the reference manual, and both are
155 * read the same simple way: a fixed memory address, no peripheral
156 * init required.
157 *
158 * DBGMCU->IDCODE identifies the SILICON ITSELF, not this individual
159 * chip or which board it happens to be soldered onto -- the same
160 * STM32H753ZI chip reports the identical value here whether it's on a
161 * Nucleo-H753ZI, a custom PCB, or any other carrier board. The two
162 * halves of this register don't carry the same guarantee, though:
163 * - DEV_ID identifies the part/die family and is identical for
164 * EVERY STM32H753ZI ever manufactured, full stop.
165 * - REV_ID identifies the silicon REVISION (stepping) -- it can
166 * differ between manufacturing batches of the same part number if
167 * ST ever revises the silicon (e.g. to fix an erratum), so two
168 * STM32H753ZI chips can legitimately report the same DEV_ID but a
169 * different REV_ID.
170 * This is different from the UID above either way: UID is unique PER
171 * CHIP, DEV_ID/REV_ID describe the part and its silicon revision, not
172 * the individual chip.
173 */
174 uint32_t devId = DBGMCU->IDCODE & DBGMCU_IDCODE_DEV_ID_Msk;
175 uint32_t revId = (DBGMCU->IDCODE & DBGMCU_IDCODE_REV_ID_Msk) >> DBGMCU_IDCODE_REV_ID_Pos;
176
177 /*
178 * FLASHSIZE_BASE holds this specific part's flash capacity in
179 * Kbytes, as a 16-bit half-word. Useful on STM32 product lines sold
180 * in multiple flash-size variants under the same part family, so
181 * firmware (or a bootloader) can confirm at runtime how much flash it
182 * actually has to work with instead of assuming from the part number
183 * on the box.
184 */
185 uint16_t flashSizeKb = *(__IO uint16_t *)FLASHSIZE_BASE;
186
187 printf("\r\n=== STM32H753ZI Device Electronic Signature ===\r\n\r\n");
188
189 printf("Unique Device ID (96-bit, factory-programmed, unique per chip):\r\n");
190 printf(" Word 0 : 0x%08lX\r\n", (unsigned long)uid[0]);
191 printf(" Word 1 : 0x%08lX\r\n", (unsigned long)uid[1]);
192 printf(" Word 2 : 0x%08lX\r\n\r\n", (unsigned long)uid[2]);
193
194 printf("Device Identification (DBGMCU->IDCODE -- board-independent):\r\n");
195 printf(" Device ID : 0x%03lX (same for every H753ZI)\r\n", (unsigned long)devId);
196 printf(" Revision ID : 0x%04lX (silicon revision -- can vary by mfg batch)\r\n\r\n", (unsigned long)revId);
197
198 printf("Flash Capacity (FLASHSIZE_BASE):\r\n");
199 printf(" %u Kbytes\r\n\r\n", flashSizeKb);
200
201 /*
202 * Practical use: hardcode a known board's three UID words and compare
203 * against them at boot to make one compiled firmware image behave
204 * differently depending on which physical board it's actually running
205 * on -- no jumpers, DIP switches, or external ID hardware needed.
206 *
207 * This is exactly how F439_CPP_TX-RX_LoRa_Project_01 (a two-board LoRa
208 * link) picks its role at startup: both boards run the IDENTICAL
209 * compiled binary, and a plain three-word comparison against each
210 * board's own known UID decides transmitter vs. receiver, e.g.:
211 *
212 * if (uid[0]==RX_UID0 && uid[1]==RX_UID1 && uid[2]==RX_UID2) {
213 * role = ROLE_RX;
214 * } else if (uid[0]==TX_UID0 && uid[1]==TX_UID1 && uid[2]==TX_UID2) {
215 * role = ROLE_TX;
216 * }
217 *
218 * The alternative -- two separate firmware builds, one compiled for
219 * "the TX board" and one for "the RX board" -- means every shared bug
220 * fix has to be made and tested twice, and the two builds can quietly
221 * drift apart over time. One binary that self-selects its role from a
222 * hardware-guaranteed identity sidesteps both problems.
223 *
224 * The compile-time alternative to all of this is a preprocessor
225 * #ifdef, with the role macro set per build configuration instead of
226 * detected from the UID at runtime:
227 *
228 * #ifdef ROLE_TX
229 * // transmitter-only code
230 * #elif defined(ROLE_RX)
231 * // receiver-only code
232 * #endif
233 *
234 * That sidesteps ever needing to know a board's UID in advance -- but
235 * it brings back the exact two-binary problem the UID check was meant
236 * to avoid: one build per role, each needing its own flash step, and
237 * shared logic edited in one path has to be mirrored (or refactored
238 * out) in the other. Runtime UID comparison trades "must know the
239 * UIDs ahead of time" for "only one binary to build, test, and flash";
240 * #ifdef trades that back the other way. Which one is right depends on
241 * whether the boards' identities are fixed and known ahead of time
242 * (favors UID comparison) or the role needs to be decided at build
243 * time before any hardware exists to read a UID from (favors #ifdef).
244 */
245
246 /* USER CODE END 2 */
247
248 /* Infinite loop */
249 /* USER CODE BEGIN WHILE */
250 while (1)
251 {
252 /* USER CODE END WHILE */
253
254 /* USER CODE BEGIN 3 */
255 }
256 /* USER CODE END 3 */
257}
258
264{
265 RCC_OscInitTypeDef RCC_OscInitStruct = {0};
266 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
267
270 HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
271
274 __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
275
276 while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
277
281 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
282 RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
283 RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
284 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
285 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
286 RCC_OscInitStruct.PLL.PLLM = 4;
287 RCC_OscInitStruct.PLL.PLLN = 60;
288 RCC_OscInitStruct.PLL.PLLP = 2;
289 RCC_OscInitStruct.PLL.PLLQ = 5;
290 RCC_OscInitStruct.PLL.PLLR = 2;
291 RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
292 RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
293 RCC_OscInitStruct.PLL.PLLFRACN = 0;
294 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
295 {
296 Error_Handler();
297 }
298
301 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
302 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
303 |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
304 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
305 RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
306 RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
307 RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
308 RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
309 RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
310 RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
311
312 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
313 {
314 Error_Handler();
315 }
316}
317
323static void MX_USART1_UART_Init(void)
324{
325
326 /* USER CODE BEGIN USART1_Init 0 */
327
328 /* USER CODE END USART1_Init 0 */
329
330 /* USER CODE BEGIN USART1_Init 1 */
331
332 /* USER CODE END USART1_Init 1 */
333 huart1.Instance = USART1;
334 huart1.Init.BaudRate = 19200;
335 huart1.Init.WordLength = UART_WORDLENGTH_8B;
336 huart1.Init.StopBits = UART_STOPBITS_1;
337 huart1.Init.Parity = UART_PARITY_NONE;
338 huart1.Init.Mode = UART_MODE_TX_RX;
339 huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
340 huart1.Init.OverSampling = UART_OVERSAMPLING_16;
341 huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
342 huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
343 huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
344 if (HAL_UART_Init(&huart1) != HAL_OK)
345 {
346 Error_Handler();
347 }
348 if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
349 {
350 Error_Handler();
351 }
352 if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
353 {
354 Error_Handler();
355 }
356 if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
357 {
358 Error_Handler();
359 }
360 /* USER CODE BEGIN USART1_Init 2 */
361
362 /* USER CODE END USART1_Init 2 */
363
364}
365
371static void MX_GPIO_Init(void)
372{
373 /* USER CODE BEGIN MX_GPIO_Init_1 */
374
375 /* USER CODE END MX_GPIO_Init_1 */
376
377 /* GPIO Ports Clock Enable */
378 __HAL_RCC_GPIOC_CLK_ENABLE();
379 __HAL_RCC_GPIOH_CLK_ENABLE();
380 __HAL_RCC_GPIOA_CLK_ENABLE();
381
382 /* USER CODE BEGIN MX_GPIO_Init_2 */
383
384 /* USER CODE END MX_GPIO_Init_2 */
385}
386
387/* USER CODE BEGIN 4 */
388
389
396void _Error_Handler(const char *file, int line)
397{
398 __disable_irq();
399
400#ifdef REDIRECT_PRINTF
401 char buf[80];
402
403 snprintf(buf, sizeof(buf),
404 "Trapped in _Error_Handler(). Called from: %s, line: %d\r\n",
405 file, line);
406 printf("%s", buf);
407#endif
408
409 /* User can add an implementation to report the HAL error return state. */
410 while(1)
411 {
412 }
413}
414
415/* USER CODE END 4 */
416
417
418#ifdef USE_FULL_ASSERT
426void assert_failed(uint8_t *file, uint32_t line)
427{
428 /* USER CODE BEGIN 6 */
429 /* assert_param() can fire from inside any HAL call -- including before
430 * REDIRECT_PRINTF's UART is fully set up, or from an unusual calling
431 * context -- so this only attempts to report when REDIRECT_PRINTF is
432 * available, then halts either way. Continuing after a failed HAL
433 * parameter check is undefined behavior, not something safe to log
434 * and ignore. */
435 __disable_irq();
436
437#ifdef REDIRECT_PRINTF
438 {
439 char buf[96];
440
441 snprintf(buf, sizeof(buf),
442 "assert_param failed: file %s, line %lu\r\n",
443 (char *)file, (unsigned long)line);
444 printf("%s", buf);
445 }
446#endif
447
448 while (1)
449 {
450 }
451 /* USER CODE END 6 */
452}
453#endif /* USE_FULL_ASSERT */
static void MX_USART1_UART_Init(void)
USART1 Initialization Function.
Definition main.c:323
void SystemClock_Config(void)
System Clock Configuration.
Definition main.c:263
int main(void)
The application entry point.
Definition main.c:79
void _Error_Handler(const char *file, int line)
This function is executed in case of error occurrence.
Definition main.c:396
static void MX_GPIO_Init(void)
GPIO Initialization Function.
Definition main.c:371
: Header for main.c file. This file contains the common defines of the application.