target icon

Purpose

This tutorial shows how to read the Device Unique Id of the STM32
microcontrollers running on the Pyboard.

list icon

Prerequisites
  • The Pyboard and the training daughter board from HyperPanel Lab.
  • An installation of a HyperpanelOS release already installed on a Linux PC, including tools for flashing and debugging. You need at least two USB ports on the computer (one for flashing and running, one for terminal).
  • The Olimex ARM-USB-OCD interface & cables.

list icon

Software release
hypv9wr64 or higher.

align left icon

Description

The Micropython board is running a STM32F405RG microcontroller. It can be usefull to find an easy way to identify the board with an unique identifier.

Following informations from STM RM0090 Reference manual datasheet.

Device electronic signature (page 1712)

Identifying devices is in many projects a necessity. From simple device description to more sophisticated appliances as USB serial naming, security keys, cryptography keys etc. During the manufaturing process of all STM microcontroller, a 96-bit ID is encoded onto the microcontroller. So for UID access, we simply need to read memory at specified address.

This Unique ID consists of 3 parts:

– X and Y coordinates on manufacturing location in BCD format
– lot number
– wafer number

The starting address of the UID depends on the microcontroller model:

F0, F3                      0x1FFFF7AC
F1                          0x1FFFF7E8
F2, F4                      0x1FFF7A10
F7                          0x1FF0F420
L0                          0x1FF80050
L0, L1 Cat.1,Cat.2          0x1FF80050
L1 Cat.3,Cat.4,Cat.5,Cat.6  0x1FF800D0

So for the Pyboard (F4), we just have to read 3 unsigned int from the base address 0x1FFF7A10

Example

#define STM_UID_ADD 0x1FFF7A10

unsigned int *p=(unsigned int*)STM_UID_ADD;

hsprintf(mess,“UID: %08x:%08x:%08x\r\n”,p[0],p[1],p[2]);

list icon

Documents
Code

hypos-tuto-229.c

/*
**  uid.c (tutorial #229) - Sample code for HyperpanelOS ==================  **
**                                                                           **
**  This simple code is located into the application container, it is run    **
**  by the VMK sub-operating system. On the other hand, the I/O container    **
**  runs all the drivers that are VMIO finite state machines.                **
**                                                                           **
**  The goal of this app is to get the unique id of the microcontroller.     **
**                                                                           **
**  =======================================================================  **
*/

/* Documentation ...................-----------------------------------------

   The Micropython board is running a STM32F405RG microcontroller. It can be
   usefull to find an easy way to identify the board with an unique identifier.

   Following informations from: STM RM0090 Reference manual datasheet

   Device ID in STM32 microcontrollers

   Identifying devices is in many projects a necessity. From simple device
   description to more sophisticated appliances as USB serial naming, security
   keys, cryptography keys etc. During the manufaturing process of all STM
   microcontroller, a 96-bit ID is encoded onto the microcontroller. So for UID
   access,we simply need to read memory at specified address.

   This Unique ID consists of 3 parts:

    - X and Y coordinates on manufacturing location in BCD format
    - lot number
    - wafer number

   The starting address of the UID depends on the microcontroller model:

   Device line  Starting address
   F0, F3                       0x1FFFF7AC
   F1                           0x1FFFF7E8
   F2, F4                       0x1FFF7A10
   F7                           0x1FF0F420
   L0                           0x1FF80050
   L0, L1 Cat.1,Cat.2           0x1FF80050
   L1 Cat.3,Cat.4,Cat.5,Cat.6   0x1FF800D0

   So for the Pyboard (F4), we just have to read 3 unsigned int from the
   base address 0x1FFF7A10

*/
/* Include files and external reference -------------------------------------*/

#include                      // Hyperpanel OS basic interfaces.    */
#include                    // Prototype of "asy_write()".        */

/* Internal defines of this module ------------------------------------------*/

#define TICK                  1000     // Code for tick event.
#define STM_UID_ADD     0x1FFF7A10     // STM32M4 unique device id address

/*  Internal global variables of this module --------------------------------*/

    static int      idto             ; // Timer identifier.

/*  Prototypes --------------------------------------------------------------*/

    static int  loop_app_task(void*) ; // Prototype
    static int  wait_evt(void)       ; // Prototype

/*  Beginning of the code ---------------------------------------------------

    loop_app_tsk         Application entry point
    wait_evt             Wait for applicative events
*/

/*  Procedure loop_app_tsk --------------------------------------------------

    Purpose : This is our task main loop.
*/

int loop_app_tsk (void *param)
  {
    int             ev = TICK        ; // Our event
    char            mess[64]         ; // Message to be sent on ASY0
    unsigned int   *p=(unsigned int*)  // Address of the unique device 
                          STM_UID_ADD; // identifier of the microcontroller.


/* Start a timer that will send an event every 200 ms .......................*/

    strcpy(mess,"Start\r\n") ;         // Start message.
    asy_write(0              ,         // Write on ASY0
      (unsigned char*)mess   ,         // the "mess" message
      strlen(mess)          );         // Count of bytes to be sent

    hsprintf(mess                    , // Put in the transmit buffer
            "UID: %08x:%08x:%08x\r\n", // 8 characters HH:MM:SS
            p[0],p[1],p[2])          ; //
    asy_write(0              ,         // Write on ASY0
      (unsigned char*)mess   ,         // the "mess" message
      strlen(mess)          );         // Count of bytes to be sent

    set_tto(CLOCK      ,               // Timer mode: clock
            2000       ,               // Duration in milliseconds
            TICK , 0   ,               // Event code and reserve field
            &idto     );               // Timer identifier

/* Main loop ................................................................*/

    wait_ev :                          // Beginning of loop label

    if (ev == TICK)                    // If the event is the tick event
      {                                //

/* Send information to serial port (message console) ........................*/

        strcpy(mess,"Empty loop\r\n"); //
               asy_write(0           , // Write on ASY0
               (unsigned char*)mess  , // the "mess" message
               strlen(mess)         ); // Count of bytes to be sent
      }                                //

    ev = wait_evt()                  ; // Unschedule until an event is received
    goto wait_ev                     ; // Wait for the next event

    return  0                        ; // Return code of the procedure 
  }

/*  Procedure wait_evt ------------------------------------------------------*/
/*
    Purpose : Unschedule until the next event is received, whatever it is.
*/

static int wait_evt (void)
  {
    int             waitlist[1][3]   ; // Parameter of "waitevt_task"
    int             ret              ; // Return code for "waiyevt_task"

/*****************************************************************************
 * Step 1 : Build a list with one WAIT_CODEINT entry that will accept all    *
 * ------   the event codes ranging from 0 to 20000. Then call               *
 *          "waitevt_task", we will be unscheduled until the next event will *
 *          be received                                                      *
 *****************************************************************************/

    waitlist[0][0]  = WAIT_CODEINT   ; // All events with
    waitlist[0][1]  = 0              ; // a code between 0
    waitlist[0][2]  = 20000          ; // and 20000

    waitevt_task(waitlist ,            // Address of waiting list
                 1        ,            // Size of "waitlist[]"
                 0        ,            // maximum waiting time = no
                 0        ,            // Do not purge previous events
                 &ret     )          ; // Return code

/*****************************************************************************
 * Step 2 : Here we are scheduled again. The VMK has written into its        *
 * ------   global variable "task_evt" a copy of the event that has          *
 *          scheduled us again.                                              *
 *****************************************************************************/

    return task_evt.code             ; // Return event code
  }